DDS.v

This module is a signal generator that outputs I/Q vectors with a given phase increment between samples, a given interval in clocks between samples, and it outputs a pulse every given number of samples. It has a reset port, a start port, output sample and cycle ticks, and an up-converted output. Once started, it runs until it is reset. It is phase continuous with respect to changes of frequency, and more generally, the interval between phase increments and the interval between timer pulse outputs can be changed on the fly.

Ports

clkinputThe clock input.
resetinputThe reset input.
goinputA multi-bit trigger to start conversions.
PhIncrinputThe phase increment per Cordic rotation on a 2wpi phase scale per revolution. Its bit width is wdp.
clockintervalinputThe interval in clocks between output samples
phasecountinputThe number of samples between cycle ticks.
I, QoutputThe main I/Q-pair output.
IN, QNoutputThe two's-complement negative of the I and Q outputs.
outoutputThe up-converted (I, Q, −I, −Q ... ) data stream.
quadinputA two-bit quadrant counter used for timing and to generate the upconverted out output from I, Q, IN, and QN
busyoutputWhen asserted, the module is running and outputting samples. It is first asserted when all go trigger conditions are met.
validoutputAssertedfor one clock cycle when the Cordic rotation has finished and that data are valid on the Iout, Qout, IoutN, and QoutN outputs.
cycleoutputA timer tick that is asserted for one clock cycle every phasecount samples.
turntickoutputA tick that indicates completion of a turn of the I/Q vector.

Timing diagram

Timing

Parameters

Parameters, their defaults, and descriptions:

wd16The bit width of the outputs I, Q, IN, QN, and out.
wpi16The width of the internal phase accumulator. A phase of 2wpi corresponds to 360 degrees. The input PhIncr is added to the accumulator every sample cycle.
wdp13The width of the phase increment PhIncr input port.
wpc16The width of the sample counter. The width of the phasecount input port is one larger.
wl11The width of the inter-sample-interval sample counter. The width of the input port clockinterval is one greater.
gb1The width of the go trigger input port.

Notes

For normal use, the sequence of events looks something like this:

  1. Set initial PhIncr, clockinterval, and phasecount to values appropriate for the measurement data point.
  2. Start the DDS with a go pulse.
  3. Allow a time for transients in the system being excited by the DDS to damp.
  4. Synchronize the start and stop of the integrator or other data acquisition with cycle pulses.
  5. Read out data.
  6. For each data point, set new values of PhIncr, clockinterval, and phasecount and repeat steps 3 to 5. Because of phase continuity, for incremental changes of the parameters, the full-length damping time of step 3 may not be necessary.

The intention of the cycle timing pulses are to provide a fiducial that indicate an integral number cycles of the I/Q output. With integration synchronized to this fiducial, end effects are minimized, lessening the need for windowing. For example, with a 40-MHz clock and the settings of the example below, the 71.81-Hz cycle pulses separate 280 cycles of 20.1057-kHz I/Q output. The fundamental relation is

I/Q cycle count × 2wpi = phasecount × PhiIncr

where all parameters are integers, and phase increments occur every 17 clock cycles.

Note that because the timing parameters can be changed on the fly, the phase of the DDS output is not in any way synchronized with the cyclepulses. For this reason analysis of one or more signals should be in parallel with analysis of a reference signal, which can be the DDS signal itself.

Example instantiation:

localparam
	wd = 16,	wpi = 16, 
	wdp = 13,	wpc = 16, 
	wl = 11,	gb = 1, 
	clockinterval	= 17,		// l
	phasecount	= 32768,	// count between pulses
	PhIncr		= 280;		// phase increment;

wire	signed	[wd-1:0] I1, Q1, IN1, QN1, out1;
wire			busy, valid, cycletick;

DDS 
    #(.wd(wd), .wpi(wpi), .wdp(wdp), .wpc(wpc), .wl(wl), .gb(gb))
    dds1 (
        .clk(clk),		.reset(reset),
        .go(go),
        .clockinterval(clockinterval[wl:0]),
        .phasecount(phasecount[wpc:0]),
        .PhIncr(PhIncr[wdp-1:0]),
        .quad(quad),
        // outputs
        .I(I1),			.Q(Q1),
        .IN(IN1),		.QN(QN1),
        .out(out1),		// upconverted output
        .busy(busy),		.valid(valid),
        .cycle(cycletick)
    );