Easy Tutorial
❮ Programmer Joke 20 Linux Command Full Fight ❯

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:

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 ;

Follow on WeChat

❮ Programmer Joke 20 Linux Command Full Fight ❯