当前位置: 首页 > news >正文

FPGA实现频率、幅度、相位可调的DDS以及DDS Compiler IP核的使用验证

文章目录

  • 一、DDS介绍
  • 二、DDS原理
    • 2.1 频率计算
    • 2.2 相位改变
    • 2.3 波形切换
  • 三、Matlab生成波形文件
  • 四、FPGA实现DDS
    • 4.1 Verilog代码
    • 4.2 仿真验证
      • 4.2.1 改变频率
      • 4.2.2 切换波形
      • 4.2.3 相位调节
      • 4.2.4 幅度调节
  • 五、Xilinx DDS Compiler的使用
    • 5.1 功能框图
      • 5.1.1 相位累加器
      • 5.1.2 SIN/COS LUT
    • 5.2 端口说明
    • 5.3 工作原理
      • 5.3.1 光栅化
      • 5.3.2 输出频率计算
      • 5.3.3 相位增量Δθ的计算
      • 5.3.4 累加器位宽计算
    • 5.4 配置通道
      • 5.4.1 CONFIG 通道 TDATA 结构
      • 5.4.2 输入相位通道TDATA结构
      • 5.4.3 输出数据通道TDATA结构
    • 5.5 IP配置
    • 5.6 仿真验证IP输出
    • 5.7 仿真验证IP动态改变频率


一、DDS介绍

  DDS(Direct Digital Synthesizer)也叫直接数字式频率合成器,用于生成精确的模拟信号波形。它通过数字方式直接合成信号,而不是通过模拟信号生成技术。DDS通常由相位累加器、波形存储器(如ROM或RAM)、DAC和低通滤波器组成,DDS结构图如下所示:

在这里插入图片描述

在这里插入图片描述

二、DDS原理

  如上图所示,DDS输出给DAC的波形来源于ROM表,相位累加器作为ROM表的输入地址,然后读出数据。这个ROM表里面存放的就是完整的波形数据,例如:正弦波、方波、三角波、锯齿波等等。

2.1 频率计算

  假设ROM的深度的位宽为N,所以ROM一共有 2 N 2^N 2N个数据。读取ROM的时钟为 f c l k f_{clk} fclk,如果一个时钟周期读地址+1,那么读出一个完整的波形需要的时间为:
t i m e = 2 N ∗ 1 f c l k time = 2^N * \frac{1}{f_{clk}} time=2Nfclk1

  那么读出的波形频率 f o u t f_{out} fout

f o u t = 1 t i m e = f c l k 2 N f_{out} =\frac{1}{time} = \frac{f_{clk}}{2^N } fout=time1=2Nfclk

  如果每一个时钟周期读地址+2,那么读出一个完整的波形需要的时间为:
t i m e = 2 N ∗ 1 f c l k ∗ 1 2 time = 2^N * \frac{1}{f_{clk}}*\frac{1}{2} time=2Nfclk121
  那么读出的波形频率 f o u t f_{out} fout
f o u t = 1 t i m e = f c l k 2 N ∗ 2 f_{out} =\frac{1}{time} = \frac{f_{clk}}{2^N } * 2 fout=time1=2Nfclk2
  我们可以看出,只要控制读rom地址的时间,就能得到不同频率的波形,如果一个时钟周期读地址+B,那么我们可以得到 f o u t f_{out} fout
f o u t = f c l k 2 N ∗ B f_{out} = \frac{f_{clk}}{2^N } * B fout=2NfclkB
  这个B就是DDS结构图中的频率控制字,那么对上式进行变形可以得到:
B = f o u t ∗ 2 N f c l k B= \frac{f_{out} * 2^N}{f_{clk}} B=fclkfout2N

2.2 相位改变

  已知我们整个波形的采样点是 2 N 2^N 2N个,我们改变读rom的起始地址,就能改变读出来的波形相位,假设每次相位变换为1°,则相位控制字p_word:

p w o r d = 2 N 360 p_{word} = \frac{ 2^N}{360 } pword=3602N

  假设每次相位变换为N°,则相位控制字p_word:
p w o r d = N ∗ 2 N 360 p_{word} =N*\frac{ 2^N}{360 } pword=N3602N

2.3 波形切换

  我们通过波形切换信号来选择波形输出的是正弦波还是三角波还是其他波。

三、Matlab生成波形文件

  从上一章我们可以知道,实现DDS最关键的就是ROM,所以我们需要提前在rom里存放一个完整的波形文件,我们就用Matlabl来生成四个声正弦波、方波、三角波、锯齿波的coe文件。深度都是2048、数据位宽根据DA芯片的位宽来选择,我们这里生成的数据位宽是14位无符号数据。三角波matlab代码如下:

% 参数设置
bitWidth = 14;   % 位宽
depth = 2048;    % 深度
maxValue = 2^bitWidth - 1; % 无符号数的最大值% 生成三角波
t = linspace(0, 1, depth);
triangleWave = round(maxValue * (abs(2 * (t - floor(t + 0.5)))));% 将三角波限制在无符号范围内
triangleWave = uint16(triangleWave);% 打开文件以写入
fileID = fopen('triangle_wave.coe', 'w');% 写入COE文件头
fprintf(fileID, 'memory_initialization_radix=10;\n');
fprintf(fileID, 'memory_initialization_vector=\n');% 写入数据
for i = 1:depthfprintf(fileID, '%d', triangleWave(i));if i < depthfprintf(fileID, ',\n');elsefprintf(fileID, ';\n');end
end% 关闭文件
fclose(fileID);disp('COE文件生成成功!');

  其它波形也同样生成。

四、FPGA实现DDS

4.1 Verilog代码

  本次实验实现从10Hz 到10Mhz的DDS,频率分别为 10hz、50hz、100hz、500hz、
1khz、5khz、10khz、50khz、100khz、500khz、1mhz、2mhz、3mhz、4mhz、5mhz、10mhz、

  我们来算一下各种频率控制字:我们频率控制字的位宽设置为32位(越大精度越高),给dds读取的时钟频率用50M系统倍频到125M,根据上面的频率控制字的公式,我们算出10hz的频率控制字为:
B = f o u t ∗ 2 N f c l k = 10 ∗ 2 32 125 ∗ 1 0 6 = 344 B= \frac{f_{out} * 2^N}{f_{clk}}= \frac{10 * 2^{32}}{125*10^{6}}=344 B=fclkfout2N=12510610232=344
  以此类推可以算出其它频率的频率控制字,整个dds代码如下:

module dds (input                                               sys_clk ,           //输入125M时钟input                                               rst     ,           //锁相环lock信号input                                               wave_change ,       //波形切换信号input                                               pha_change  ,       //相位改变信号input                                               fre_change  ,       //频率改变信号input                                               ran_change  ,       //幅度改变信号output  reg     [13:0]                              wave_data   
);/***************function**************//***************parameter*************//***************port******************/             /***************mechine***************//***************reg*******************/
reg             [31:0]                              fre_word        ;   //32位宽的频率控制字
reg             [31:0]                              fre_add         ;   //32位宽的相位累加器
reg             [3:0]                               fword_sel       ;   //频率选择
reg             [10:0]                              p_word          ;   //11位宽的相位控制字
reg             [1:0]                               wave_sel        ;   //波形选择
reg             [1:0]                               ran_sel         ;   //幅度选择
reg             [13:0]                              r_wave_data     ;   //输出波形数据存储器
/***************wire******************/
wire            [13:0]                              triangular_wave ;
wire            [13:0]                              square_wave     ;
wire            [13:0]                              sin_wave        ;
wire            [13:0]                              sawtooth_wave   ;
wire            [10:0]                              addr            ;   //一共2048个采样点/***************component*************/sawtooth_rom u0_sawtooth_rom (.clka (sys_clk        ),      .addra(addr           ),      .douta(sawtooth_wave  )       
);sin_rom u0_sin_rom (.clka (sys_clk    ),      .addra(addr       ),      .douta(sin_wave   )       
);square_rom u0_square_rom (.clka (sys_clk        ),      .addra(addr           ),      .douta(square_wave    )       
);triangular_rom u0_triangular_rom (.clka (sys_clk            ),      .addra(addr               ),      .douta(triangular_wave    )       
);
/***************assign****************/
assign addr = fre_add[31 : 21] + p_word;/***************always****************///每当改变频率信号拉高时,切换频率控制字
always @(posedge sys_clk) beginif(rst == 1'b0)fword_sel <= 4'd0;else if(fre_change == 1'b1)fword_sel <= fword_sel + 1;elsefword_sel <= fword_sel;
endalways @(posedge sys_clk) beginif(rst == 1'b0)fre_word <= 'd0;elsecase (fword_sel)4'd0:   fre_word <= 32'd344;      //10hz4'd1:   fre_word <= 32'd1718;     //50hz4'd2:   fre_word <= 32'd3436;     //100hz4'd3:   fre_word <= 32'd17180;    //500hz4'd4:   fre_word <= 32'd34360;    //1khz4'd5:   fre_word <= 32'd171799;   //5khz4'd6:   fre_word <= 32'd343597;   //10khz4'd7:   fre_word <= 32'd1717987;  //50khz4'd8:   fre_word <= 32'd3435974;  //100khz4'd9:   fre_word <= 32'd17179869; //500khz           4'd10:  fre_word <= 32'd34359738; //1MHZ4'd11:  fre_word <= 32'd68719477; //2MHZ4'd12:  fre_word <= 32'd103079215;//3MHZ4'd13:  fre_word <= 32'd137438953;//4MHZ4'd14:  fre_word <= 32'd171798692;//5MHZ4'd15:  fre_word <= 32'd343597384;//10MHZdefault:fre_word <= 32'd344;endcase
endalways @(posedge sys_clk) beginif(rst == 1'b0)fre_add <= 'd0;elsefre_add <= fre_add + fre_word;
endalways @(posedge sys_clk) beginif(rst == 1'b0)p_word <= 'd0;else if(pha_change == 1'b1)p_word <= p_word + 'd57;   //每次偏移10°elsep_word <= p_word;
end//波形切换
always @(posedge sys_clk) beginif(rst == 1'b0)wave_sel <= 'd0;else if(wave_change == 1'b1)wave_sel <= wave_sel + 1'b1;elsewave_sel <= wave_sel;
end//幅度切换
always @(posedge sys_clk) beginif(rst == 1'b0)ran_sel <= 'd0;else if(ran_change == 1'b1)ran_sel <= ran_sel + 1'b1;elseran_sel <= ran_sel;
endalways @(posedge sys_clk) beginif(rst == 1'b0)r_wave_data <= 'd0;elsecase (wave_sel)2'b00: r_wave_data <= sin_wave;2'b01: r_wave_data <= square_wave;2'b10: r_wave_data <= sawtooth_wave;2'b11: r_wave_data <= triangular_wave;default: r_wave_data <= sin_wave;endcase
endalways @(posedge sys_clk) beginif(rst == 1'b0)wave_data  <= 'd0;elsecase (ran_sel)2'd0: wave_data <= r_wave_data;2'd1: wave_data <= {1'b0,r_wave_data[13:1]};    //幅度的1/22'd2: wave_data <= {2'b00,r_wave_data[13:2]};    //幅度的1/42'd3: wave_data <= {3'b000,r_wave_data[13:3]};    //幅度的1/8default: wave_data <= r_wave_data;endcase
end
endmodule

4.2 仿真验证

4.2.1 改变频率

  我们一开始不改变频率,就用默认的频率控制字344 来跑一下,观察波形和频率是否正确,仿真代码如下:

`timescale 1ns / 1ps
module tb_dds();reg                                              sys_clk ;
reg                                              wave_change ;
reg                                              pha_change  ;
reg                                              fre_change  ;
reg                                              ran_change  ;
wire          [13:0]                             wave_data   ;initial beginsys_clk =0;wave_change =0;pha_change  =0;fre_change  =0;ran_change  =0;
endalways #10 sys_clk = ~sys_clk;clk_wiz_0 u_dds_clk(.clk_out1 (dds_clk    ),     .locked   (rst        ),      .clk_in1  (sys_clk    )
);   dds u_dds(.sys_clk      ( dds_clk      ),.rst          ( rst          ),.wave_change  ( wave_change  ),.pha_change   ( pha_change   ),.fre_change   ( fre_change   ),.ran_change   ( ran_change   ),.wave_data    ( wave_data    )
);endmodule

  打开仿真
在这里插入图片描述

  我们可以看到四种波形可以正常输出,我们选取一个周期时间看为 99.882960ms,打开计算器

在这里插入图片描述
   可以算出波形频率≈10hz,我们改变一下 fword_sel为10,这样输出的频率应该为1Mhz,代码修改如下:

//每当改变频率信号拉高时,切换频率控制字
always @(posedge dds_clk) beginif(rst == 1'b0)fword_sel <= 4'd10;else if(fre_change == 1'b1)fword_sel <= fword_sel + 1;elsefword_sel <= fword_sel;
end

   打开仿真
在这里插入图片描述  看一个波形周期为1000ns,可以算出波形频率为1Mhz
在这里插入图片描述
  由此可以看频率切换没问题,我们来观察一些波形切换。

4.2.2 切换波形

  我们修改仿真,只观察输出端口的波形数据,让仿真运行一段时间然后给一个切换波形的信号,以此切换四次可以回到初始的sin波形,为了让仿真快一点,我们频率控制字还是1Mhz,仿真代码如下:

`timescale 1ns / 1ps
module tb_dds();reg                                              sys_clk ;
reg                                              wave_change ;
reg                                              pha_change  ;
reg                                              fre_change  ;
reg                                              ran_change  ;
wire          [13:0]                             wave_data   ;initial beginsys_clk =0;wave_change =0;pha_change  =0;fre_change  =0;ran_change  =0;#10000;@(posedge dds_clk)wave_change = 1;@(posedge dds_clk)wave_change = 0;#10000;@(posedge dds_clk)wave_change = 1;@(posedge dds_clk)wave_change = 0;#10000;@(posedge dds_clk)wave_change = 1;@(posedge dds_clk)wave_change = 0;#10000;@(posedge dds_clk)wave_change = 1;@(posedge dds_clk)wave_change = 0;#10000;$stop;
endalways #10 sys_clk = ~sys_clk;clk_wiz_0 u_dds_clk(.clk_out1 (dds_clk    ),     .locked   (rst        ),      .clk_in1  (sys_clk    )
);   dds u_dds(.sys_clk      ( dds_clk      ),.rst          ( rst          ),.wave_change  ( wave_change  ),.pha_change   ( pha_change   ),.fre_change   ( fre_change   ),.ran_change   ( ran_change   ),.wave_data    ( wave_data    )
);endmodule

  打开波形观察:
在这里插入图片描述
  我们在一定的时间给一个wave_change脉冲信号,我们可以看到波形也跟着切换了。切换顺序为sin、方波、锯齿波、三角波和我们代码一致。

4.2.3 相位调节

  我们先修改代码,再添加一个sin_rom,让其读地址没有加频率控制字,波形为sin_wave1:

/***************component*************/sawtooth_rom u0_sawtooth_rom (.clka (sys_clk        ),      .addra(addr           ),      .douta(sawtooth_wave  )       
);sin_rom u0_sin_rom (.clka (sys_clk    ),      .addra(addr       ),      .douta(sin_wave   )       
);sin_rom u1_sin_rom (.clka (sys_clk    ),      .addra(fre_add[31 : 21]  ),      .douta(sin_wave1   )       
);square_rom u0_square_rom (.clka (sys_clk        ),      .addra(addr           ),      .douta(square_wave    )       
);triangular_rom u0_triangular_rom (.clka (sys_clk            ),      .addra(addr               ),      .douta(triangular_wave    )       
);
/***************assign****************/
assign addr = fre_add[31 : 21] + p_word;

  修改仿真代码,在某个时间给重复给18个周期的相位切换信号,由于代码里我们每一个切换信号,相位偏移10°,所以18个切换信号,相位应该会偏移180°,仿真代码如下:

`timescale 1ns / 1ps
module tb_dds();reg                                              sys_clk ;
reg                                              wave_change ;
reg                                              pha_change  ;
reg                                              fre_change  ;
reg                                              ran_change  ;
wire          [13:0]                             wave_data   ;initial beginsys_clk =0;wave_change =0;pha_change  =0;fre_change  =0;ran_change  =0;#10000;repeat(18) begin@(posedge dds_clk)pha_change = 1;endpha_change = 0;#10000;$stop;
endalways #10 sys_clk = ~sys_clk;clk_wiz_0 u_dds_clk(.clk_out1 (dds_clk    ),     .locked   (rst        ),      .clk_in1  (sys_clk    )
);   dds u_dds(.sys_clk      ( dds_clk      ),.rst          ( rst          ),.wave_change  ( wave_change  ),.pha_change   ( pha_change   ),.fre_change   ( fre_change   ),.ran_change   ( ran_change   ),.wave_data    ( wave_data    )
);endmodule

  打开波形观察:
在这里插入图片描述
  由上图可以看到,相位调节也没问题。

4.2.4 幅度调节

  修改仿真代码,在某个时间给一个给幅度切换信号,持续一段时间后再给一个幅度调节信号,仿真代码如下:

`timescale 1ns / 1ps
module tb_dds();reg                                              sys_clk ;
reg                                              wave_change ;
reg                                              pha_change  ;
reg                                              fre_change  ;
reg                                              ran_change  ;
wire          [13:0]                             wave_data   ;initial beginsys_clk =0;wave_change =0;pha_change  =0;fre_change  =0;ran_change  =0;#10000;@(posedge dds_clk)ran_change = 1;@(posedge dds_clk)ran_change = 0;#10000;@(posedge dds_clk)ran_change = 1;@(posedge dds_clk)ran_change = 0;#10000;$stop;
endalways #10 sys_clk = ~sys_clk;clk_wiz_0 u_dds_clk(.clk_out1 (dds_clk    ),     .locked   (rst        ),      .clk_in1  (sys_clk    )
);   dds u_dds(.sys_clk      ( dds_clk      ),.rst          ( rst          ),.wave_change  ( wave_change  ),.pha_change   ( pha_change   ),.fre_change   ( fre_change   ),.ran_change   ( ran_change   ),.wave_data    ( wave_data    )
);endmodule

  打开波形观察:
在这里插入图片描述
  由上图可以看到,幅度调节也没问题。

五、Xilinx DDS Compiler的使用

5.1 功能框图

  该核心由两个主要部分组成:相位发生器和 SIN/COS LUT,它们可以单独使用,也可以与可选的抖动发生器一起使用。

在这里插入图片描述

5.1.1 相位累加器

  相位发生器由一个累加器和一个可选加法器组成),用于添加相位偏移(如上图1所示。当核心定制时,相位增量 (PINC) 和相位偏移 (POFF) 可以独立配置为固定、可编程(使用 CONFIG 通道)或流式(使用输入 PHASE 通道),意思为相位增量和相位偏移有两种路径到相位累加器,如下图路径1和路径2所示:

在这里插入图片描述

  1. 当设置为固定时,DDS输出频率就是在设置IP时设置的,生成后无法更改。
  2. 当设置为可编程时,CONFIG 通道 TDATA 字段将包含一个用于相关输入(PINC 或 POFF)的子字段,如果已选择两个输入都可编程,则两个输入都包含一个子字段。如果 PINC 和 POFF 均未设置为可编程,则不存在 CONFIG 通道
  3. 当设置为流式传输时,输入相位通道 TDATA 字段将有一个子字段用于相关输入(PINC 或 POFF),如果两者都被选择为流式传输,则将同时包含这两个子字段。如果 PINC 和 POFF 均未设置为流式传输,并且核心配置为具有相位发生器,则没有输入相位通道

5.1.2 SIN/COS LUT

  仅配置为 SIN/COS LUT 时,相位发生器未实现,PHASE_IN 信号使用输入 PHASE 通道输入,并使用查找表转换为正弦和余弦输出。内核可以配置为仅正弦输出、仅余弦输出或两者(正交)输出。每个输出都可以独立配置为取反。可以使用可选的泰勒级数校正来提高精度

5.2 端口说明

  DDS IP所有的端口说明如下,实际上根据不同配置,端口数量不一样:
在这里插入图片描述

端口名称 输入方向 是否可编程端口说明
aclk inputno上升沿有效
aclken inputyes时钟使能信号
aresetninyes低电平有效同步清除,至少持续两个时钟周期
s_axis_config_tvalid inputyesCONFIG 通道的 TVALID
s_axis_config_tready outyesCONFIG 通道的 TREAD
s_axis_config_tdatainputyesCONFIG 通道的 TDATA
s_axis_config_tlast inputyesCONFIG 通道的最后一个数据指示信号
s_axis_phase_tvalid inputyes输入相位通道的 TVALID
s_axis_phase_tready outputyes输入相位通道的 TREADY
s_axis_phase_tuser inputyes输入相位通道的 TUSER
s_axis_phase_tlast inputyes输入相位通道的 TLAST
端口名称 输入方向 是否可编程端口说明
m_axis_phase_tvalid outputyes输出相位的TVALID
m_axis_phase_tready inputyes输出相位的TREADY
m_axis_phase_tdata outputyes输出相位通道的 TDATA
m_axis_phase_tuser outputyesTUSER 用于输出PHASE通道
m_axis_phase_tlast outputyesTLAST 用于输出相位通道
m_axis_data_tvalid outputyes输出数据的TVALID
m_axis_data_tready inputyes输出数据的TREADY
m_axis_data_tdata outputyes输出数据通道的 TDATA
m_axis_data_tuser outputyesTUSER 用于输出数据通道
m_axis_data_tlast outputyesTLAST 用于输出数据通道

5.3 工作原理

  相位发生器与 SIN/COS LUT 配合使用,可提供相位截断 DDS 或泰勒级数校正 DDS。可在两个模块之间添加可选抖动发生器,以提供相位抖动 DDS。工作原理如下:

在这里插入图片描述

  1. 输入寄存器:由于输入的相位增量可以设置为可编程,因此这个寄存器就是将外部输入的相位增量打一拍进来。
  2. 相位累加器:相位增量进行累加,和前文DDS原理的相位累加器一样
  3. 量化器:截断相位累加器从而给后面的查找表,例如前文fpga实现dds,相位累加器的位宽是32位,但是ROM的地址位宽只有11位,所以需要截断相位累加器,使其高11位作为ROM的读地址。
  4. 查找表:可以是ROM,或者RAM也可以是LUT,深度就是 2 B θ 2^{Bθ} 2。查找表的深度和宽度分别影响信号的相位角分辨率和幅度分辨率

5.3.1 光栅化

  DDS 的光栅化操作模式不会截断累积相位。 光栅化操作适用于所需频率为系统时钟的有理分数的配置(输出频率 = 系统频率 * N/M,其中 0 < N < M)。 支持从 9 到 16384 的 M 值。SIN/COS LUT 相应地配置为从 0 到 M-1 的值,这些值描述了一个完整的圆。由于光栅化操作模式下没有相位截断,因此不需要抖动或泰勒校正,因为它们可以减轻相位截断的影响。在光栅化操作中,相位噪声显著降低。因此,输出相位角分辨率和幅度分辨率仅由 LUT 表输出宽度决定。在光栅化模式下,在适用的情况下利用象限对称性来减少内存使用

5.3.2 输出频率计算

  1. 标准模式下的输出频率:

f o u t = f c l k 2 B θ ∗ Δ θ f_{out} = \frac{f_{clk}}{2^{B_θ} } * {Δθ} fout=2BθfclkΔθ

  例如: f c l k f_{clk} fclk = 120MHz, B θ B_θ Bθ=10, Δ θ Δθ Δθ=12,那么输出的频率:

f o u t = f c l k 2 B θ ∗ Δ θ = 120 × 1 0 6 × 12 2 10 = 1.406250 M H z f_{out} = \frac{f_{clk}}{2^{B_θ} } * {Δθ}=\frac{120 × 10^6 ×12}{2^{10}}=1.406250MHz fout=2BθfclkΔθ=210120×106×12=1.406250MHz

  精度就是 精度 = f c l k 2 B θ = 0.117187 M H z 精度 = \frac{f_{clk}}{2^{B_θ} } =0.117187MHz 精度=2Bθfclk=0.117187MHz

  产生输出频率fout Hz所需的相位增量值Δθ为:
Δ θ = f o u t ∗ 2 B θ f c l k Δθ= \frac{f_{out} * 2^{B_θ}}{f_{clk}} Δθ=fclkfout2Bθ

  如果 DDS 核心采用时分复用方式来处理多个通道,则每个通道的有效时钟频率会降低。对于 C 通道,所需的相位增量为:

Δ θ = C ∗ f o u t ∗ 2 B θ f c l k Δθ= \frac{C*f_{out} * 2^{B_θ}}{f_{clk}} Δθ=fclkCfout2Bθ

  1. 光栅化操作模式下的输出频率:

  单通道配置的 DDS 波形的输出频率 fout 是系统时钟频率 fclk、模数 M 和相位增量值 ΔΘ 的函数。输出频率定义为:

f o u t = f c l k ∗ Δ θ M f_{out} = \frac{f_{clk}*Δθ}{M } fout=MfclkΔθ
  例如: f c l k f_{clk} fclk = 120MHz, M M M=1000, Δ θ Δθ Δθ=12,那么输出的频率:
f o u t = 120 × 1 0 6 × 12 1000 = 1.44 M H z f_{out} = \frac{120 × 10^6 ×12}{1000}=1.44MHz fout=1000120×106×12=1.44MHz
  精度就是 精度 = f c l k M = 0.12 M H z 精度 = \frac{f_{clk}}{M} =0.12MHz 精度=Mfclk=0.12MHz

5.3.3 相位增量Δθ的计算

  对于标准模式,0 到 2 N − 1 2^{N-1} 2N1 范围内的相位增量值描述范围 [0,360]°(其中 N 是相位累加器中的位数)。对于光栅化模式,由于内部实现,相位增量值必须被视为无符号。相位增量值 [0 到 Modulus-1] 描述范围 [0,360]°

  1. 标准化操作
      例如 f c l k f_{clk} fclk=100MHz, B θ B_θ Bθ=18,想要生成 f o u t f_{out} fout=19MHz的波形,Δθ:

Δ θ = f o u t ∗ 2 B θ f c l k = 19 × 1 0 6 × 2 18 100 × 1 0 6 = 49807.36 Δθ= \frac{f_{out} * 2^{B_θ}}{f_{clk}} = \frac{19 × 10^6×2^{18}}{100×10^6} =49807.36 Δθ=fclkfout2Bθ=100×10619×106×218=49807.36

  该值必须取整,因此实际上的 Δ θ Δθ Δθ=49807,带入到输出频率计算公式里:
f o u t = f c l k 2 B θ ∗ Δ θ = 100 × 1 0 6 × 49807 2 18 = 18.9998627 M H z f_{out} = \frac{f_{clk}}{2^{B_θ} } * {Δθ}=\frac{100 × 10^6 ×49807}{2^{18}}=18.9998627MHz fout=2BθfclkΔθ=218100×106×49807=18.9998627MHz

  1. 光栅化操作
      例如 f c l k f_{clk} fclk=100MHz, M M M=1536,想要生成 f o u t f_{out} fout=19MHz的波形,Δθ:

Δ θ = f o u t ∗ M f c l k = 19 × 1 0 6 × 1536 100 × 1 0 6 = 291.84 Δθ= \frac{f_{out} * M}{f_{clk}} = \frac{19 × 10^6×1536}{100×10^6} =291.84 Δθ=fclkfoutM=100×10619×106×1536=291.84

  该值必须取整,因此实际上的 Δ θ Δθ Δθ=292,带入到输出频率计算公式里:

f o u t = 100 × 1 0 6 × 292 1536 = 19.0104167 M H z f_{out} = \frac{100 × 10^6 ×292}{1536}=19.0104167MHz fout=1536100×106×292=19.0104167MHz

5.3.4 累加器位宽计算

  相位宽度与系统时钟频率一起决定了 DDS 的频率分辨率。累加器必须具有足够的场宽度才能达到所需的频率分辨率。例如:如果所需分辨率为 1 Hz,时钟频率为 100 MHz,则累加器所需的宽度为:

B θ = l o g 2 ( f c l k Δ f ) = l o g 2 ( 100 × 1 0 6 1 ) = 26.5754 = 27 b i t B_{θ} = log_2(\frac{f_{clk}}{Δf})=log_2(\frac{100 ×10^6}{1})=26.5754=27 bit Bθ=log2(Δffclk)=log2(1100×106)=26.5754=27bit

5.4 配置通道

  CONFIG 通道是非阻塞的,这意味着 DDS Compiler 的其他通道不会等待来自 CONFIG 通道的数据。要对 CONFIG 通道进行编程,必须进行 N 次传输,其中 N 是通道数。每次传输都包含从通道 0 开始按顺序排列的每个通道的 PINC 和/或 POFF 值。对于通道 (索引 N-1),只有最后一次传输必须使 TLAST 置位。否则会导致 event_s_config_tlast_missing 或 event_s_config_tlast_unexpected 输出在一个周期内置位。

在这里插入图片描述

  在光栅化模式下,相位发生器的相位信号可以精确地表示为整数,这是由于相位发生器与系统时钟之间存在有理分数关系,并且相位向量不会发生截断。因此,正弦/余弦信号的保真度仅取决于正弦/余弦表条目的准确性和精度。与标准模式不同,没有时间基准抖动。标准操作模式中描述的时间基准抖动的影响不会影响光栅化配置。 由于相位累加器没有相位向量截断,并且所有可能的相位值在正弦/余弦表中都有相应的条目,因此无需补偿或减轻相位误差,因为没有相位误差。抖动和泰勒级数校正是两种减少相位误差影响的技术。由于它们不是必需的,因此在光栅化模式下不提供

5.4.1 CONFIG 通道 TDATA 结构

  当 CONFIG 通道配置为为每个 TDM 通道提供 PINC 和 POFF 值时,每个字段都会进行符号扩展以适应字节边界,然后这些面向字节的字段将在最低有效位置与 PINC 连接起来。例如,对于 11 位的相位宽度,PINC 将占用位 10:0,而 POFF 将占用位 26:16。因此,s_axis_config_tdata 总体上将是 31:0。

  1. PINC 和 POFF 都设置为可编程:

在这里插入图片描述

  1. PINC 仅设置为可编程

在这里插入图片描述
3. POFF 仅设置为可编程

在这里插入图片描述

5.4.2 输入相位通道TDATA结构

  1. PINC 和 POFF 均设置为 Streaming

在这里插入图片描述

  1. PINC 仅设置为 Streaming

在这里插入图片描述

  1. POFF 仅设置为 Streaming

在这里插入图片描述

  1. DDS 配置为仅 SIN/COS LUT

在这里插入图片描述

5.4.3 输出数据通道TDATA结构

在这里插入图片描述

5.5 IP配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  我们这里算一下相位增量是否正确,根据上面的公式:

Δ θ = f o u t ∗ 2 B θ f c l k = 2 × 1 0 6 × 2 24 100 × 1 0 6 = 335544.32 Δθ= \frac{f_{out} * 2^{B_θ}}{f_{clk}} = \frac{2 × 10^6×2^{24}}{100×10^6} =335544.32 Δθ=fclkfout2Bθ=100×1062×106×224=335544.32

在这里插入图片描述
  和配置界面的增量一样。

5.6 仿真验证IP输出

  我们先试一下最简单功能,因此仿真只需要给IP一个时钟和复位即可,仿真代码如下:

`timescale 1ns / 1ps
module tb_dds_ip();reg                                                 aclk    ;reg                                                 aresetn ;wire                                                m_axis_data_tvalid  ;wire            [31:0]                              m_axis_data_tdata   ;wire                                                m_axis_phase_tvalid ;wire            [23:0]                              m_axis_phase_tdata  ;wire            [15:0]                              sin ;wire            [15:0]                              cos ;assign sin = m_axis_data_tdata[31:16];
assign cos = m_axis_data_tdata[15:0];initial beginaclk   =0;aresetn =0;#1000;aresetn =1;
endalways #5 aclk = ~aclk;dds_compiler_0 u_dds_compiler_0 (.aclk(aclk),                                // input wire aclk.aresetn(aresetn),                          // input wire aresetn.m_axis_data_tvalid(m_axis_data_tvalid),    // output wire m_axis_data_tvalid.m_axis_data_tdata(m_axis_data_tdata),      // output wire [31 : 0] m_axis_data_tdata.m_axis_phase_tvalid(m_axis_phase_tvalid),  // output wire m_axis_phase_tvalid.m_axis_phase_tdata(m_axis_phase_tdata)    // output wire [23 : 0] m_axis_phase_tdata
);
endmodule

  允许并打开仿真

在这里插入图片描述
在这里插入图片描述
  我们可以看到频率= 1s/500ns=2Mhz

5.7 仿真验证IP动态改变频率

  接下来我们试一下动态更改频率,打开IP修改配置如下:
在这里插入图片描述
  仿真代码如下:

`timescale 1ns / 1ps
module tb_dds_ip();reg                                                 aclk    ;reg                                                 aresetn ;reg                                                 s_axis_config_tvalid;reg             [23:0]                              s_axis_config_tdata =24'hA3D70   ;wire                                                m_axis_data_tvalid  ;wire            [31:0]                              m_axis_data_tdata   ;wire                                                m_axis_phase_tvalid ;wire            [23:0]                              m_axis_phase_tdata  ;wire            [15:0]                              sin ;wire            [15:0]                              cos ;assign sin = m_axis_data_tdata[31:16];
assign cos = m_axis_data_tdata[15:0];initial beginaclk   =0;aresetn =0;s_axis_config_tvalid = 0;#10000;aresetn =1;#10000;@(posedge aclk)s_axis_config_tvalid = 1;s_axis_config_tdata =24'hA3D70;   //4MHZ@(posedge aclk)s_axis_config_tvalid = 0;s_axis_config_tdata =24'hA3D70 + 24'h51EB8;   //5mhz#20000;@(posedge aclk)s_axis_config_tvalid = 1;@(posedge aclk)s_axis_config_tvalid = 0;endalways #5 aclk = ~aclk;dds_compiler_0 u_dds_compiler_0 (.aclk(aclk),                                // input wire aclk.aresetn(aresetn),                          // input wire aresetn.s_axis_config_tvalid(s_axis_config_tvalid),  // input wire s_axis_config_tvalid.s_axis_config_tdata(s_axis_config_tdata),    // input wire [23 : 0] s_axis_config_tdata.m_axis_data_tvalid(m_axis_data_tvalid),    // output wire m_axis_data_tvalid.m_axis_data_tdata(m_axis_data_tdata),      // output wire [31 : 0] m_axis_data_tdata.m_axis_phase_tvalid(m_axis_phase_tvalid),  // output wire m_axis_phase_tvalid.m_axis_phase_tdata(m_axis_phase_tdata)    // output wire [23 : 0] m_axis_phase_tdata
);
endmodule

  运行并打开仿真:
在这里插入图片描述
  我们放大看一下频率是多少:

在这里插入图片描述
  我们可以看到,在相位增量改变之前,我们的频率还是2Mhz。

在这里插入图片描述
  我们可以看到,在相位增量改变之后,我们的频率变成了4Mhz,符合我们的相位增量24’HA3D70。


http://www.mrgr.cn/news/34975.html

相关文章:

  • Python_yield
  • inplace-abn报错
  • 2024年网络安全人才平均年薪 24.09 万,跳槽周期 31 个月,安全工程师现状大曝光!_2024网络安全人才市场状况研究报告
  • 智能密码、指纹锁语音芯片ic方案 可存放40s语音内容 NVD语音芯片
  • 电器行业文件加密怎么做?防泄密哪种方法实用?
  • CSS注释
  • C++中序列式容器和关联式容器
  • Vue3:shallowRef与shallowReactive
  • 数据处理与统计分析篇-day09-数据透视表与日期时间处理
  • 记一次Copilot被封解封的经历
  • 企业数据安全与资产处置的最佳选择 —— 淼一科技
  • 宝马撑不住了,买i7居然能省出一辆问界M9
  • USB总线同步数据采集卡6路高速模拟量采集带DIO功能USB2884/2885/2886
  • 智能挖耳勺和普通挖耳勺区别在哪? 4款智能挖耳勺推荐!
  • android 页面布局(1)
  • hrm人力资源管理系统,绩效,考勤,薪酬,五险一金,等全面人力管理(源码+配套方案)
  • 【C++掌中宝】走进C++引用的世界:从基础到应用
  • 人工智能AI数据库,太酷了吧!(附医疗/金融/零售行业方案)
  • 【排列距离 / B】
  • 大厂离职故事:创业路上,不怕犯错(电梯节能)