7.6 Verilog DDS Design
Category Verilog Tutorial
DDS Principle
DDS (Direct Digital Synthesizer) technology is a method that, based on the Nyquist sampling theorem and digital processing techniques, samples a series of analog signals without distortion, stores the obtained digital signals in memory, and under the control of a clock, converts the digital quantities into analog signals through digital-to-analog conversion.
The DDS module mainly consists of a phase accumulator, a lookup table, a DAC converter, and a low-pass filter, with the basic structure as follows.
The phase accumulator is the core component of DDS, used to implement phase accumulation and output the corresponding amplitude. The phase accumulator is composed of a *-bit wide adder and a *-bit wide register. Under the control of a clock, it feeds back the last accumulation result to the adder input to achieve the accumulation function, so that the phase increment per clock cycle is **, and the phase accumulation result is output as an address to the ROM lookup table part.
The amplitude lookup table stores the binary digital amplitude corresponding to each phase. Within each clock cycle, the lookup table addresses the phase address information output by the phase accumulator and then outputs the corresponding binary amplitude digital discrete value. Assuming the lookup table address is * bits and the output data is * bits, then the capacity of the lookup table is
The DAC converter converts digital signals into analog signals. In fact, the signal output by the DAC is not continuous but is a summation of each input digital quantity according to the weight of each code, and then outputs an analog signal in units of its resolution. The actual output signal is a stepped analog line signal, so it needs to be smoothed, usually by filtering with a filter.
The low-pass filter is used because the analog signal output by the DAC converter has the defect of being stepped, so it needs to be smoothed to filter out most of the stray signals, making the output signal a more ideal analog signal.
When DDS works, the frequency control word * is added to the *-bit phase accumulator to obtain the phase value. In each clock cycle, it is given to the ROM lookup table in binary form, converting the phase information into digital sine amplitude values, and then converted into a stepped analog signal through digital-to-analog conversion. After the signal is filtered by the system to filter out most of the stray signals, a relatively pure sine wave can be obtained.
From the perspective of frequency decomposition, the ROM lookup table inputs a frequency of **. So the DDS output frequency can be expressed as:
From the perspective of phase, the phase increment controlled by the output in time ** is:
Considering the angular velocity of the output frequency at this time
The relationship between the DDS output frequency and the input frequency can also be derived from the above two formulas.
DDS Design
Design Description
The following only designs the DDS circuit before the DAC.
The designed DDS features include:
1) Frequency controllable;
2) Starting phase controllable;
3) Amplitude controllable;
4) Sine wave, triangular wave, and square wave can be selected for output;
5) Resource optimization: The waveform storage file only uses a quarter of the sine wave data.
Generate ROM
The ROM module is best used with a customized IP core, which will have better optimization in terms of timing and area. A customized ROM also needs to specify a data file, such as the ROM data file suffix for ISE is .coe
, and for Quartus II, the ROM data file suffix is .mif
.
For the convenience of simulation, the ROM module is written here with a code, an address width of 8 bits, and a data width of 10 bits.
To save space, only a quarter of the sine wave is stored, and then a complete cycle sine wave data waveform can be obtained by shifting according to symmetry.
To achieve a variety of DDS modes, ROM programs for triangular waves and square waves have also been added.
The implementation code is as follows (all included in the file mem.v).
Example
module mem(
input clk, //reference clock
input rstn , //resetn, low effective
input en , //start to generating waves
input [1:0] sel , //waves selection
input [7:0] addr ,
output dout_en ,
output [9:0] dout); //data out, 10bit width
//data out from ROMs
wire [9:0] q_tri ;
wire [9:0] q_square ;
wire [9:0] q_cos ;
//ROM addr
This is a Chinese to English translation, please provide the English translation for this text. Do not provide any explanations or text apart from the translation.
Chinese: q <= 10'd512 + ROM_t[64-addr[5:0]]; //flip and shift up
end
end
else begin
q <= 'b0 ;
end
end
endmodule
DDS Control Module
Example
module dds(
input clk, //reference clock
input rstn , //resetn, low effective
input wave_en , //start to generating waves
input [1:0] wave_sel , //waves selection
input [1:0] wave_amp , //waves amplitude control
input [7:0] phase_init, //initial phase
input [7:0] f_word , //frequency control word
output [9:0] dout, //data out, 10bit width
output dout_en);
//phase accumulator
reg [7:0] phase_acc_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
phase_acc_r <= 'b0 ;
end
else if (wave_en) begin
phase_acc_r <= phase_acc_r + f_word ;
end
else begin
phase_acc_r <= 'b0 ;
end
end
//ROM address
reg [7:0] mem_addr_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
mem_addr_r <= 'b0 ;
end
else if (wave_en) begin
mem_addr_r <= phase_acc_r + phase_init ;
end
else begin
mem_addr_r <= 'b0 ;
end
end
//ROM instantiation
wire [9:0] dout_temp ;
mem u_mem_wave(
.clk (clk), //reference clock
.rstn (rstn), //resetn, low effective
.en (wave_en), //start to generating waves
.sel (wave_sel[1:0]), //waves selection
.addr (mem_addr_r[7:0]),
.dout_en (dout_en),
.dout (dout_temp[9:0])); //data out, 10bit width
//amplitude
//0 -> dout/1 //1 -> dout/2 //2 -> dout/4 //3 -> dout/8
assign dout = dout_temp >> wave_amp ;
endmodule
testbench
Example
``
timescale 1ns/1ns
module test ; reg clk ; reg rstn ; reg wave_en ; reg [1:0] wave_sel ; reg [1:0] wave_amp ; reg [7:0] phase_init ; reg [7:0] f_word ; wire [9:0] dout ; wire dout_en ;
//(1)clk, reset and other constant registers initial begin clk = 1'b0 ; rstn = 1'b0 ; #100 ; rstn = 1'b1 ; #10 ; forever begin #5 ; clk = ~clk ; //system clock, 100MHz end end
//(2)signal setup ; parameter clk_freq = 100000000 ; //100MHz integer freq_dst = 2000000 ; //2MHz integer phase_coe = 2; //1/4 cycle, that is pi/2
initial begin wave_en = 1'b0 ;
7.6 DDS Design in Verilog