FPGA——DDS原理及代码实现

一、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

  • 相位控制字pword的参数解释,如果从x轴为8的数据开始取,那么相当于正弦波相移了90°,pword = 8,这就是相位控制字

图片

图片

二、DDS的verilog代码实现

  • 如何写代码?

    • 照着DDS结构图书写即可
  • 对于相位调制器代码的解释

<!---->

//相位调制器
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 1ns / 1ns
module 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;

五、部分效果

图片

欢迎关注公众号【AdriftCoreFpga】,获取第一时间更新

AdriftXCore
1 声望0 粉丝