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.xdnf.cn/news/149713.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统接口--信号量、互斥锁、原子操作和自旋锁的区别

1、基本概念 1.1 信号量&#xff08;Semaphore&#xff09; &#xff08;1&#xff09;定义 信号量是一种计数器&#xff0c;用于控制对共享资源的访问&#xff0c;允许多个线程同时访问一定数量的资源。 &#xff08;2&#xff09;特点 计数型信号量&#xff1a; 信号量有…

FSMC寄存器方式初始化及LCD函数

环境 芯片:STM32F103ZET6 库&#xff1a;stm32f10x.h 原理图 代码 Driver.FSMC.h #ifndef __DRIVER_FSMC #define __DRIVER_FSMC #include "stm32f10x.h"void Driver_FSMC_Init(void);#endifDriver.FSMC.c #include "Driver_FSMC.h"/*** 配置FSMC(Flexi…

最新免费商用无版权素材(免费图库 抠图 PNG 插画素材)

今天我要给大家揭秘几个私藏的宝藏网站——那些免费、商用无版权的素材资源站&#xff01;&#x1f389; 这些站点简直好用到爆炸&#x1f4a5;&#xff0c;每次需要找素材&#xff0c;它们总能秒速响应&#xff0c;让我们的工作效率直线飙升&#xff01;&#x1f680; 这里不…

32岁前端干了8年,是继续做前端开发,还是转其它工作_ui设计师转开发

前端发展有瓶颈&#xff0c;变来变去都是那一套&#xff0c;只是换了框架换了环境。换了框架后又得去学习&#xff0c;虽然很快上手&#xff0c;但是那些刚毕业的也很快上手了&#xff0c;入门门槛越来越低&#xff0c;想转行或继续卷&#xff0c;该如何破圈? 这是一位网友的自…

nmap 命令:网络扫描

一、命令简介 ​nmap​&#xff08;Network Mapper&#xff09;是一个开放源代码的网络探测和安全审核的工具。它最初由Fyodor Vaskovich开发&#xff0c;用于快速地扫描大型网络&#xff0c;尽管它同样适用于单个主机。 ​nmap​的功能包括&#xff1a; 发现主机上的开放端…

基于 BERT 的自定义中文命名实体识别实现

基于 BERT 的自定义中文命名实体识别实现 在自然语言处理中,命名实体识别(Named Entity Recognition,NER)是一项重要的任务,旨在识别文本中的特定实体,如人名、地名、组织机构名等。本文将介绍如何使用 BERT 模型实现自定义中文命名实体识别,并提供详细的代码分析和解读…

SpringBoot3自动配置(持续更新)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;JavaWeb关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 自动配置 在SpringBoot中&#xff0c;自动配置是一个核心的特性&#xff0c;…

(done) 声音信号处理基础知识(1) (course overview)

来源&#xff1a;https://www.youtube.com/watch?viCwMQJnKk2c 声学处理应用场景如下 这个系列的内容包括如下&#xff1a; 作者的 slack 频道 油管主的 github repo: https://github.com/musikalkemist/AudioSignalProcessingForML

将PDF文件转换为音频播客、讲座和摘要PDF2Audio;Open WebUI Ollama的代码执行工具

✨ 1: PDF to Audio Converter PDF转音频转换器可以将PDF文件转换为音频播客、讲座和摘要&#xff0c;支持多种自定义选项。 PDF to Audio Converter 是一款强大的工具&#xff0c;能够将PDF文档转换为音频格式&#xff0c;例如播客、讲座和摘要等。该工具利用OpenAI的GPT模型…

5.MySQL表的约束

目录 表的约束空属性&#xff08;非空约束&#xff09;默认值&#xff08;default约束&#xff09;列描述&#xff08;comment&#xff09;zerofill主键&#xff08;primary key约束&#xff09;自增长&#xff08;auto_increment&#xff09;唯一键&#xff08;unique约束&…

Python酷玩之旅_mysql-connector

前言 Python作为数据科学、机器学习等领域的必选武器&#xff0c;备受各界人士的喜爱。当你面对不同类型、存储于各类介质的数据时&#xff0c;第一时间是不是要让它亮个相&#xff1f;做个统计&#xff0c;画个图表&#xff0c;搞个报表… 等等。 正如Java中的JdbcDriver一样…

高效驱动,掌控动力:TB67H400AFNG 马达驱动器

在如今智能设备和自动化应用领域中&#xff0c;驱动器的性能直接决定了系统的可靠性与效率。东芝的TB67H400AFNG有刷直流马达驱动器凭借其卓越的性能&#xff0c;成为众多行业解决方案中的关键部件。无论是工业控制、自动化设备还是消费类电子产品&#xff0c;TB67H400AFNG都能…

嵌入式综合实验平台-嵌入式综合实训实验箱

1、基本介绍 嵌入式综合实验平台&#xff08;eeLab-BaseKit&#xff09;是中智讯公司开发的一款信息类学科的综合型实验设备&#xff0c;配套丰富的教学实验和开发例程,可以满足电子信息工程专业的专业核心课程及专业方向课程的教学、实验和实训需求&#xff0c;包括《嵌入式接…

如何使用ssm实现基于VUE的儿童教育网站的设计与实现+vue

TOC ssm676基于VUE的儿童教育网站的设计与实现vue 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全…

OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【LMS调测】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 基本概念 LMS全称为Lite Memory Sanitizer&#xff0c;是一种实时…

基于单片机的精确电压表DA-AD转换

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采用DAC0832和ADC0832检测电压&#xff0c;0到8.5V&#xff0c;设计复位电路 LED管显示实际稳压值&#xff0c;初始电压0 二、硬件资源 基于KEIL5编写C代码&#xff0c…

regPractice-正则表达式练习

正则表达式练习(java) 1 . 校验密码强度 密码的强度必须是包含大小写字母和数字的组合&#xff0c;不能使用特殊字符&#xff0c;长度在8-10之间。 ^(?.*\d)(?.*\[a-z])(?.*[A-Z])[a-zA-Z\d]{8,10}$**(?pattern)**属于正向前瞻断言 正向前瞻断言的语法是 (?pattern)&a…

【React与Vue】如何在页签中监听 LocalStorage 变化?这些方法你都试过吗?

在开发中&#xff0c;你是否会碰到过这样的需求&#xff1a;需要监听 LocalStorage 的变化。这在不同浏览器页签间是相对简单的&#xff0c;因为浏览器提供了内置的 storage 事件。但在同一个浏览器页签下&#xff0c;却没有直接的方式实现。今天&#xff0c;我们探讨下有几种高…

化工企业如何精准网络营销

合作咨询联系竑图 hongtu201988 网络推广已成为各行各业不可或缺的一部分&#xff0c;尤其是化工企业&#xff0c;其产品的专业性和复杂性更需要通过有效的网络推广手段来触达目标客户群体。以下是一份针对化工企业的网络推广方案&#xff0c;旨在通过多渠道的营销策略&#x…

【机器学习】---元强化学习

目录 1. 元学习简介1.1 什么是元学习&#xff1f;1.2 元学习的应用 2. 强化学习基础2.1 什么是强化学习&#xff1f;2.2 强化学习的基本框架2.3 深度强化学习 3. 元强化学习的概念与工作原理3.1 元强化学习是什么&#xff1f;3.2 元强化学习与普通强化学习的区别 4. 元强化学习…