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

news2024/9/24 12:19:33

文章目录

  • 一、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:depth
    fprintf(fileID, '%d', triangleWave(i));
    if i < depth
        fprintf(fileID, ',\n');
    else
        fprintf(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) begin
    if(rst == 1'b0)
        fword_sel <= 4'd0;
    else if(fre_change == 1'b1)
        fword_sel <= fword_sel + 1;
    else
        fword_sel <= fword_sel;
end

always @(posedge sys_clk) begin
    if(rst == 1'b0)
        fre_word <= 'd0;
    else
        case (fword_sel)
            4'd0:   fre_word <= 32'd344;      //10hz
            4'd1:   fre_word <= 32'd1718;     //50hz
            4'd2:   fre_word <= 32'd3436;     //100hz
            4'd3:   fre_word <= 32'd17180;    //500hz
            4'd4:   fre_word <= 32'd34360;    //1khz
            4'd5:   fre_word <= 32'd171799;   //5khz
            4'd6:   fre_word <= 32'd343597;   //10khz
            4'd7:   fre_word <= 32'd1717987;  //50khz
            4'd8:   fre_word <= 32'd3435974;  //100khz
            4'd9:   fre_word <= 32'd17179869; //500khz           
            4'd10:  fre_word <= 32'd34359738; //1MHZ
            4'd11:  fre_word <= 32'd68719477; //2MHZ
            4'd12:  fre_word <= 32'd103079215;//3MHZ
            4'd13:  fre_word <= 32'd137438953;//4MHZ
            4'd14:  fre_word <= 32'd171798692;//5MHZ
            4'd15:  fre_word <= 32'd343597384;//10MHZ
            default:fre_word <= 32'd344;
        endcase
end

always @(posedge sys_clk) begin
    if(rst == 1'b0)
        fre_add <= 'd0;
    else
        fre_add <= fre_add + fre_word;
end

always @(posedge sys_clk) begin
    if(rst == 1'b0)
        p_word <= 'd0;
    else if(pha_change == 1'b1)
        p_word <= p_word + 'd57;   //每次偏移10°
    else
        p_word <= p_word;
end

//波形切换
always @(posedge sys_clk) begin
    if(rst == 1'b0)
        wave_sel <= 'd0;
    else if(wave_change == 1'b1)
        wave_sel <= wave_sel + 1'b1;
    else
        wave_sel <= wave_sel;
end

//幅度切换
always @(posedge sys_clk) begin
    if(rst == 1'b0)
        ran_sel <= 'd0;
    else if(ran_change == 1'b1)
        ran_sel <= ran_sel + 1'b1;
    else
        ran_sel <= ran_sel;
end

always @(posedge sys_clk) begin
    if(rst == 1'b0)
        r_wave_data <= 'd0;
    else
        case (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
end

always @(posedge sys_clk) begin
    if(rst == 1'b0)
        wave_data  <= 'd0;
    else
        case (ran_sel)
            2'd0: wave_data <= r_wave_data;
            2'd1: wave_data <= {1'b0,r_wave_data[13:1]};    //幅度的1/2
            2'd2: wave_data <= {2'b00,r_wave_data[13:2]};    //幅度的1/4
            2'd3: wave_data <= {3'b000,r_wave_data[13:3]};    //幅度的1/8
            default: 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 begin
    sys_clk =0;
    wave_change =0;
    pha_change  =0;
    fre_change  =0;
    ran_change  =0;
end

always #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) begin
    if(rst == 1'b0)
        fword_sel <= 4'd10;
    else if(fre_change == 1'b1)
        fword_sel <= fword_sel + 1;
    else
        fword_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 begin
    sys_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;
end

always #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 begin
    sys_clk =0;
    wave_change =0;
    pha_change  =0;
    fre_change  =0;
    ran_change  =0;
    #10000;
    
    repeat(18) begin
        @(posedge dds_clk)
        pha_change = 1;
    end
    pha_change = 0;
    #10000;
    
    $stop;
end

always #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 begin
    sys_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;
end

always #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 begin
    aclk   =0;
    aresetn =0;
    #1000;
    aresetn =1;
end

always #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 begin
    aclk   =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;

end

always #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.coloradmin.cn/o/2160326.html

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

相关文章

通俗讲解javascript的实例对象、原型对象和构造函数以及它们之间的关系

今天通俗讲解一下js的对象&#xff0c;因为要通俗&#xff0c;所以可能描述不甚准确。 在js中&#xff0c;想要创建一个对象&#xff0c;首先要写出构造函数&#xff08;跟其它的语言不太一样哦&#xff0c;其它语言一般都会先写一个class 类名&#xff09;。 构造函数写法如…

【PGCCC】 复合索引和部分索引,竟然能让查询速度提升 275 倍!

索引对于加速数据库查询和提高 PostgreSQL 应用程序的性能至关重要。但是&#xff0c;并非所有索引都以相同的方式发挥作用。复合索引和部分索引是两种常见类型&#xff0c;每种类型都有不同的用途和对性能的影响。本文我们将深入探讨复合索引和部分索引是什么、它们如何运作以…

关于在vue2中给el-input等输入框的placeholder加样式

::v-deep {.el-input--medium,.el-input__inner {height: 100%;background: #163670;border: 1px solid #4cc0f6;border-radius: 6px 6px 6px 6px;&::placeholder {color: #13EFFF;}} } 效果如下&#xff1a; .el-date-editor .el-range-input{&::placeholder {color:…

SAP MIGO M7146不支持移动原因

移动类型 Z91 查看配置&#xff1a;Z91 匹配的原因没有921 倒是Z92的原因里面有921 那解决方案有2种&#xff0c;但是要根据具体业务要求来 1、审视一下是否移动原因用错了 &#xff1f;换一个移动原因 2、确实是这个移动类型 要用到这个移动原因 &#xff0c;那就在上图 移…

Python编码系列—Python观察者模式:实现事件驱动架构的利器

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

【数字ic自整资料】SV约束constraint

参考链接&#xff1a; SV--随机约束&#xff08;一&#xff09;_sv constraint-CSDN博客 SV--随机约束&#xff08;二&#xff09;_sv constraint f循环-CSDN博客 [SV]Constraint 遇到的问题_父类和子类 constraint-CSDN博客 目录 1、随机化的概念理解 2、约束(constrain…

基于报位时间判断船舶设备是否在线,基于心跳时间判断基站网络是否在线

文章目录 引言I 在线船舶查询在线或者离线船舶显示在线状态统计在线船舶II 树状显示船舶设备数据结构统计船舶设备在线数和总数III 基站网络是否在线IV 知识扩展统计某个key的数据,例如统计船舶分类下的在线船舶MyBatis引言 本文采用的数据库是SQL Server,开发语言为Java。 …

无线协议wlan在华为模拟器中的实现

无线技术 wifi6:标准为802.11&#xff1b; wifi发展趋势&#xff1a; vlan基本概念&#xff1a; wlan组网架构&#xff1a; 1)fat胖AP;能够独立工作&#xff0c;可以单独配置&#xff1b;小型网络使用&#xff0c;功能少&#xff1b; 2)fit瘦APAC&#xff1a;适用大型网络…

《深度学习》—— PyTorch的介绍及PyTorch的CPU版本安装

文章目录 一、PyTorch的简单介绍二、pytorch的CPU版本安装三、 torch、torchvision、torchaudio 三个库的介绍 一、PyTorch的简单介绍 PyTorch是一个由Facebook AI实验室开发的深度学习框架&#xff0c;它基于Python&#xff0c;并提供了高效的GPU加速和灵活的模型定义能力。1…

基于vue框架的传统服饰剪裁交流平台5m953(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,服装分类,服装资讯 开题报告内容 基于Vue框架的传统服饰剪裁交流平台开题报告 一、研究背景与意义 随着全球化进程的加速&#xff0c;文化多样性的保护与传承日益受到重视。传统服饰作为各民族历史文化的瑰宝&#xff0c;不仅承载…

阅读记录:Gradient Episodic Memory for Continual Learning

1. Contribution 提出了一组指标来评估模型在连续数据上的学习情况。这些指标不仅通过测试准确性来表征模型&#xff0c;还通过其跨任务迁移知识的能力来表征模型。针对持续学习任务&#xff0c;提出了GEM模型&#xff08;Gradient Episodic Memory&#xff09;&#xff0c;它…

C++中stack类和queue类

感谢大佬的光临各位&#xff0c;希望和大家一起进步&#xff0c;望得到你的三连&#xff0c;互三支持&#xff0c;一起进步 数据结构习题_LaNzikinh篮子的博客-CSDN博客 初阶数据结构_LaNzikinh篮子的博客-CSDN博客 收入专栏&#xff1a;C_LaNzikinh篮子的博客-CSDN博客 其他专…

基于MT79815G CPE 板子上挂usb3.0的5G 模块,WIFI能跑多少速度呢

关于MT79815G CPE 板子上挂usb3.0的5G 模块&#xff0c;WIFI能跑多少速度的问题&#xff0c;我们以启明智显 ZX7981P智能无线接入型路由器&#xff08;CPE&#xff09;挂广合通5G模组为例说明&#xff1a; 一般来说&#xff0c;用 ZX7981P&#xff0c;通过软加速&#xff0c;U…

Java:列表操作

目录 1、判断列表是否为空或者为NULL2、列表包含3、列表排序4、列表截取5、列表合并6、列表求极值7、列表转字符串8、列表去重的四种方式9、列表转数组 1、判断列表是否为空或者为NULL Optional.ofNullable(list).orElse(Collections.emptyList()).isEmpty() // true为空或NU…

【JAVA-数据结构】时间空间复杂度计算案例

接着上一篇文章&#xff0c;对应举一些例子。 1.时间复杂度 【实例1】 // 计算func2的时间复杂度&#xff1f; void func2(int N) {int count 0;for (int k 0; k < 2 * N ; k) {count;} int M 10;while ((M--) > 0) {count;} System.out.println(count); } 基本操作…

在云渲染中3D工程文件安全性怎么样?

在云渲染中&#xff0c;3D工程文件的安全性是用户最关心的问题之一。随着企业对数据保护意识的增强&#xff0c;云渲染平台采取了严格的安全措施和加密技术&#xff0c;以确保用户数据的安全性和隐私性。 云渲染平台为了保障用户数据的安全&#xff0c;采取了多层次的安全措施。…

电子信息制造业数据安全如何防护?有什么加密方案?

电子信息制造业数据加密解决方案 问题 1.电子文档&#xff08;源代码、设计图纸、设计方案等&#xff09;均要做数据保护措施&#xff0c;防止内部人员有意或无意造成数据泄露&#xff1b; 2.与外部企业之间往来的外发文件&#xff0c;管控不当&#xff0c;容易造成泄密&…

工业能源物联网的建设与维护该如何实现

随着全球对可持续发展的重视&#xff0c;智能电网和微电网的应用逐渐成为能源转型的重要方向。在新型电力系统中&#xff0c;负荷侧资源不再是单纯消耗的“消费者”&#xff0c;而是既消耗电能又可生产电能的“产消者”。比如&#xff0c;电力用户利用屋顶建设光伏发电&#xf…

防火墙详解(二)通过网页登录配置华为eNSP中USG6000V1防火墙

配置步骤 步骤一 打开eNSP&#xff0c;建立如下拓扑。防火墙使用&#xff1a;USG6000V1。 Cloud的作用是通过它可以连接本地的网卡&#xff0c;然后与我们的电脑进行通信。 由于防火墙USG6000V&#xff0c;不能直接开启&#xff0c;需要的导入包&#xff0c;所以需要在华为官网…

可视挖耳勺神器怎么样?可视耳勺热销第一名品牌!

耳道作为我们身体的重要部分&#xff0c;它的清洁健康很重要。传统挖耳勺的材质偏硬、表面摩擦力大并且在不可视的情况下进行盲目掏耳&#xff0c;很容易出现刮伤耳道肌肤导致耳朵出血感染等意外。而网上出现了一种新型的掏耳神器--可视耳勺&#xff0c;它到底怎么样&#xff1…