一、DDS各参数意义
* 如图,一个量化的32点的正弦波,也就是说一个ROM里存了32个这样的数据,每次读出一个数据要1ms,分别读出1,2,3...30,31,32,共32个点,读取完整的正弦波需要1ms * 32 = 32ms的时间
该正弦波参数为 > 周期T = 1ms * 32 = 32ms, > 频率为 f = 1/T = 1/(1ms * (32/1)) 在读出一个数据时间不变(1ms)的情况下,想要让读出的正弦波频率增加一倍,那就要间隔读取,分别读出2,4,6,8,10...28,30,32,此时只需要读16个点 那么读出完整正弦波的参数为 > 周期T = 1ms * 16 = 16ms > 频率f = 1/T = 1/(1ms * 16) = 1/(1ms * (32/2))
想要读出的正弦波频率减少一倍,那就要插值读取,分别读出0.5,1,1.5,2,2.5,3...30.5,31,31.5,32,此时要读64个点 读出正弦波的参数为 > 周期T = 1ms * 64 = 64ms > 频率f = 1/T = 1/(1ms * 64) = 1/(1ms * (32/0.5))
这里,1ms即为Tclk,fclk = 1/Tclk = 1/1ms;32 = 2^5即为N=5,而32除以的数(1,2,0.5)即为频率控制字fword,那么fo = (fclk * fword)/(2^N)
通常,FPGA并不擅长浮点运算,第三种情况,上式的(32/0.5)是很难实现的,因此在正弦波周期一样的情况下,将精度N调高一位,N=6,(2^5 * 2)/(0.5 * 2),此时fword就不用为0.5,而是1
二、DDS的verilog代码实现 //相位调制器 always @(posedge clk or negedge rst_n)begin if (! rst_n) data_addr <= 0 ; else data_addr <= fword_acc[31 : 20 ] + syn_pword;end
为什么地址是由相位控制字加频率控制字高12位得到的? 1、本次实验使用的rom是宽度为14,深度为2^12 = 4096的数据,所以相位控制字根据rom的深度选择了12位宽 2、为什么ROM宽度是14,深度不取2^14?FPGA资源不够,没有这么多的寄存器存取这么多的数据 3、地址 = 相位 + 频率更迭,而相位宽度为12位,频率的宽度比相位多,所以频率控制字取高几位是由相位控制字的宽度决定的 4、取频率控制字高12位是如何完成频率变换的? 举例: 2^1 = 2'b10 2^2 = 3'b100 2^3 = 4'b1000 ...... 2^19 = 20'b1000_0000_0000_0000_0000 2^20 = 21'b1_0000_0000_0000_0000_0000 2^21 = 22'b10_0000_0000_0000_0000_0000
f = 1/T,N = 32 频率控制字为:2^20 fword_acc[31:0] + 2^20 相当于 (fword_acc[31:20] + 1)此时就是按照地址+1的速度寻址,假如Fclk = 50MHz(系统时钟),Tclk = 20ns,输出波形的周期就为:To = 20ns * 4096
频率控制字为:2^19 fword_acc[31:0] + (2^19 + 2^19) 相当于 (fword_acc[31:20] + 1),也就是要加两次频率控制字,才能实现一次地址+1,Tclk = 20ns,输出完整波形就要输出2次*4096个数据,输出的波形周期为:To = 20ns * (2 * 4096)
频率控制字为:2^21 fword_acc[31:0] + (2^21) 相当于 (fword_add[31:20] + 2'b10),加一次频率控制字,实现一次地址+2,Tclk = 20ns,因为是跳过一位地址取的数据,所以数据量减半,输出完整波形只需要输出4096/2个数据,输出的波形周期为:To = 20ns * (4096/2)
module dds_module( clk , rst_n , fword , pword , data_out );parameter FWORD_W = 32 ; //频率控制字位宽 parameter PWORD_W = 12 ; //相位控制字位宽 parameter DATAO_W = 14 ; //输出数据位宽 input clk;input rst_n;input [FWORD_W- 1 : 0 ] fword;input [PWORD_W- 1 : 0 ] pword;output [DATAO_W- 1 : 0 ] data_out;wire [DATAO_W- 1 : 0 ] data_out;reg [FWORD_W- 1 : 0 ] syn_fword;reg [PWORD_W- 1 : 0 ] syn_pword;reg [FWORD_W- 1 : 0 ] fword_acc;reg [PWORD_W- 1 : 0 ] data_addr;//同步寄存器 always @(posedge clk or negedge rst_n)begin if (! rst_n)begin syn_fword <= 0 ; syn_pword <= 0 ; end else begin syn_fword <= fword; syn_pword <= pword; end end //相位累加器 always @(posedge clk or negedge rst_n)begin if (! rst_n) fword_acc <= 0 ; else fword_acc <= syn_fword + fword_acc;end //相位调制器,不需要用寄存器,但是用了性能更好 always @(posedge clk or negedge rst_n)begin if (! rst_n) data_addr <= 0 ; else data_addr <= fword_acc[31 : 20 ] + syn_pword;end dds_rom dds_rom( .clka (clk), .addra (data_addr), .douta (data_out) );endmodule
四、仿真文件 `timescale 1 ns / 1 nsmodule dds_module_tb();parameter CYCLE = 20 ;parameter FWORD_W = 32 ; //频率控制字位宽 parameter PWORD_W = 12 ; //相位控制字位宽 parameter DATAO_W = 14 ; //输出数据位宽 reg clk;reg rst_n;reg [FWORD_W- 1 : 0 ] fword;reg [PWORD_W- 1 : 0 ] pword;wire [DATAO_W- 1 : 0 ] data_out;reg [FWORD_W- 1 : 0 ] fword1;reg [PWORD_W- 1 : 0 ] pword1;wire [DATAO_W- 1 : 0 ] data_out1; dds_module dds_module( .clk (clk), .rst_n (rst_n), .fword (fword), .pword (pword), .data_out (data_out) ); dds_module dds_module1( .clk (clk), .rst_n (rst_n), .fword (fword1), .pword (pword1), .data_out (data_out1) );initial clk = 0 ;always #(CYCLE/ 2 ) clk = ~ clk;initial begin rst_n = 1 ; #3 ; rst_n = 0 ; #(10 * CYCLE); rst_n = 1 ;end initial begin fword = 0 ; @(posedge rst_n); #(15 * CYCLE); fword = 32'h200000 ; #(100000 ) fword = 32'h800000 ;end initial begin fword1 = 0 ; @(posedge rst_n); #(15 * CYCLE); fword1 = 32'h200000 ;end initial begin pword = 0 ;end initial begin pword1 = 0 ; @(posedge rst_n) #(15 * CYCLE) pword1 = 1024 ;end initial begin #100000 ; #100000 ; $stop ;end endmodule
五、多种波形的实现 module dds_module( clk , rst_n , fword , pword , cft_model, data_out ); parameter FWORD_W = 32; //频率控制字位宽 parameter PWORD_W = 12; //相位控制字位宽 parameter MOD_W = 2; parameter DATAO_W = 14; //输出数据位宽 input clk; input rst_n; input [FWORD_W-1:0] fword; input [PWORD_W-1:0] pword; input [MOD_W-1:0] cft_model; output [DATAO_W-1:0] data_out; reg [DATAO_W-1:0] data_out; //中间变量 wire [DATAO_W-1:0] sine_data_out; reg [FWORD_W-1:0] syn_fword; reg [PWORD_W-1:0] syn_pword; reg [FWORD_W-1:0] fword_acc; reg [PWORD_W-1:0] data_addr; //同步寄存器 always @(posedge clk or negedge rst_n)begin if(!rst_n)begin syn_fword <= 0; syn_pword <= 0; end else begin syn_fword <= fword; syn_pword <= pword; end end //相位累加器 always @(posedge clk or negedge rst_n)begin if(!rst_n) fword_acc <= 0; else fword_acc <= syn_fword + fword_acc; end //相位调制器,不需要用寄存器,但是用了性能更好 always @(posedge clk or negedge rst_n)begin if(!rst_n) data_addr <= 0; else data_addr <= fword_acc[31:20] + syn_pword; end //正弦波 dds_rom dds_rom( .clka (clk), .addra (data_addr), .douta (sine_data_out) ); always @(posedge clk or negedge rst_n)begin if(!rst_n) data_out <= 0; else if(cft_model == 2'd0)//正弦波 data_out <= sine_data_out; else if(cft_model == 2'd1)//三角波 data_out[DATAO_W - 1 -: 12] <= data_addr; else if(cft_model == 2'd2)//0直流 data_out[DATAO_W - 1 -: 12] <= 8192 - 1; else if(cft_model == 2'd3)//方波 data_out <= (data_addr >= 2048) ? 14'd0 : 14'd16383; end endmodule
注: ( :- ) 12 ,向下取12位 data_out共有14位 取高12位,相当于左移两位,乘以4,每个输入的值放大4倍
四、matlab代码 clc; clear; f = 1 ;%取样点,1,2,0.5 x=0 :f:32 ; y=sin ((2 * pi / 32 )* x); stem(x,y); axis([0 ,32 ,- 1.5 ,1.5 ]); grid on;
五、部分效果