用verilog模擬DDS產生正弦波信號


前言:

DDS:直接數字頻率合成,正弦波0-2pi周期內,相位到幅度是一一對應的(這里我們使用放大后的整數幅度)。

主要思路:

個人理解,FPGA不擅長直接做數字信號計算,那樣太占用片上邏輯資源,所以需要事先建立 正弦波相位-幅度 表,然后在時鍾下,通過相位累加並用相位作為地址索引來查詢正弦波信號表。

正弦波相位-幅度 表:
存儲的是量化的正弦波在一個周期的幅度信息(幅度的地址即相位)。
幅度的地址數目決定了相位量化的誤差。
而存儲每一個幅度的比特數決定了幅度的量化誤差。
可以通過matlab以及Xilinx的IP核向導創建。

Verilog編寫的DDS模塊主要由三部分組成,

  • 相位累加器,用於決定輸出信號頻率的范圍和精度;
  • 正弦函數模塊,用於存儲經量化和離散后的正弦函數的幅值;
  • 查表模塊,對相位累加器的輸出地址查表。

兩種方法可以改變輸出信號的頻率:

  • 改變查表尋址的時鍾頻率,從而改變輸出波形的頻率。
  • 改變尋址的步長來改變輸出信號的頻率。
    步長即為相位增量。
    由累加器對相位增量進行累加,
    累加器的值作為查表地址。

相位累加器是 DDS 的核心所在,前面在低於時鍾頻率的任意頻率生成(相位累加器)中我們已經進行了敘述。
正弦函數模塊包含一個周期正弦波的數字幅度信息,每個地址對應正弦波中0-2pi范圍的一個相位點。查表模塊把輸入的地址相位信息映射成正弦波幅度的數字量信號。相位寄存器每經過 2^N/K 個時鍾后回到初始狀態,相應地正弦查詢表經過一個循環回到初始位置,輸出一個正弦波。

輸出正弦波周期為fo=fc* K/2^N ,最小分辨率為f=fc/2^N。(通過fc和K控制正弦波頻率精度) 其中,N 為累加器位寬,K 為步長,fc 為時鍾頻率。計數模(最大值):M=2^N。

一般正弦波表幅度地址位寬與累加的查表地址位寬不同,按前者位寬取后者對應高位的位寬即可。(具體見實例)

先用matlab生成1024點的正弦波數據:

clc;clear;
N = 10;                     %儲存單元地址線
depth=2^N;                 %存儲單元;
widths=N;                    %數據寬度為8位;
index = linspace(0,pi*2,depth);              
sin_value = sin(index);                
sin_value = sin_value * (depth/2 -1);  %擴大正弦幅度值    
sin_value = fix((sin_value)+0.5);
plot(sin_value);
number = [0:depth];
fid=fopen('sin_table.coe','w+');
fprintf(fid,'memory_initialization_radix=10;\n');
fprintf(fid,'memory_initialization_vector=\n');
for i = 1 : depth - 1  
    fprintf(fid, '%d,\n', sin_value(i));
end
fprintf(fid, '%d;', sin_value(depth));
fclose(fid);

Verilog程序

1、adder.v文件,相位累加模塊

`timescale 1ns/1ps
/***************************************
晶振頻率 fc = 100MHz
輸出頻率 fo = 1kHz(根據需要可以設為任意值)
控制參數 K  = (fo*2^N)/fc = 42950
參數 N = 2^32,(32為計數器的位寬)
****************************************/
module PHASE_ADDER(
    input clk,
    input rst,
    output reg [31:0] cnt,
    output reg clk_out
    );

always @(posedge clk or posedge rst) 
    if(rst)
        cnt <= 0;
    else
        cnt <= cnt + 32'd42950;  //計數器步長K

always @(posedge clk or posedge rst)
    if(rst)
        clk_out <= 1'b0;
    else if(cnt < 32'h7FFF_FFFF)
        clk_out <= 1'b0;
    else
        clk_out <= 1'b1;

endmodule

2、dds_top.v頂層設計

`timescale 10ns /1ns

module dds_top(
    input rst,
    input clk,
    output signed [15:0] sine_o
    );

    wire [31:0] phase;   //32bit內部連接線,傳遞相位增量
    wire clk_out;
    wire [9:0] addr;   //10bit相位信息

    PHASE_ADDER U_PHASE_ADDER(
        .clk    (clk    ),
        .rst    (rst    ),
        .cnt    (phase  ),
        .clk_out(clk_out)
        );
     
    assign addr = phase[31:22];//addr 10bit
    
    DDS_Table U_DDS_Table(
        .clka(clk),    // input wire clka
        .addra(addr),  // input wire [9 : 0] addra
        .douta(sine_o)  // output wire [15 : 0] douta
        );
endmodule

3、仿真測試文件

`timescale 1ns/1ps

module TB;
    
    reg clk;
    reg rst;
    wire clk_out;
    dds_top U_dds_top(
        .clk    (clk    ),
        .rst    (rst    )
    );

    initial begin
        clk = 0;
        rst = 0;
        #4 rst = 1;
        #3 rst = 0;
    end

    always #5 clk = ~clk;
endmodule 

matlab生成正弦數據:

sin_table

vivado和Modelsim聯合仿真結果:

仿真結果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM