04-13-2020 01:26 PM - edited 04-13-2020 01:34 PM
I am currently using multiple DDS Compiler cores as a source for an I/Q mixer, external to my FPGA. That is to say I am using the COSINE outputs as the I channel source, the SINE outputs as the Q channel source, and an external local oscillator (LO) to mix the signals up to a higher frequency.
As with most I/Q mixing there is some imbalance based on the frequency I have my I and Q channel set to. There are two things that cause this imbalance, the phase not being perfectly out of phase and the amplitudes of the two signals not being the same (there is also the case where the frequencies of the two signals aren't the same either, but we aren't going to worry about that for this case). From a digital perspective, this imbalance never happens with the core, but when the signal is brought out to hardware phase and amplitude shift occur, especially at higher frequencies.
Fixing amplitude is fairly easily, I just multiply the outputs of the core by whatever fraction I want to get them balanced. Phase is a bit trickier. The only solution I have come up with is instantiating twice as many DDS cores, run half of them as SINE generators and run the other half as COSINE generators and change the phase offset of one of them. This method takes about a lot of memory, so I was wondering, is there was a better way of doing this? If there is a better way to amplitude balance as well, I would be open to hear it.
04-13-2020 02:44 PM
We used to do this kind of thing for very wide band radar signals. I your case I think you would want to make two memories, one sine the other cosine. The phase generator can just be done in your code you don't need the core generator to do that for you. Then you can just put an adder in the phase path to either the sine or cosine rom, something like this.
logic[31:0] phase, freq, cos_phase, sin_phase, correction;
always_ff @(posedge clk) begin
phase <= phase + freq; // phase accumulator
cos_phase <= phase;
sin_phase <= phase + correction;
04-13-2020 04:18 PM
That is essentially what I have now. I just wanted to know if there was a method that didn't require me to make twice as many memory blocks.
The DDS module is nice because it uses a sine and cosine LUT in a single memory block, but that also means if I want to only use a sine LUT and not a cosine, it doesn't save me any memory.