DDS-IP核的理论知识和应用案例

dds,director digital synthesis,直接频率合成技术,是指通过固定频率的参考时钟(采样时钟)生成指定频率的正余弦信号。采用fpga配合dac芯片,可以实现频率、相位可调的模拟信号用于一些特定的领域。
01、dds-ip核理论部分
1.1、4个重要概念
pinc, phase increase,频率控制字,用于生成期望频率的波形数据;
poff, phase offset,相位控制字,用于生成期望初始相位的波形数据;
spurious free dynamic range (db),无杂散动态范围,用于调整正弦和余弦输出数据的位宽,假设需要的正弦和余弦输出数据的位宽是16bit,动态范围的数值设置为16*6 = 96 ;
frequency resolution(hz),频率分辨率,ip核输出正余弦信号能够达到的最小频率。参考时钟/采样时钟(f_clk)、频率控制字(delta_theta)、输出频率(f_out),三个变量之间的计算关系如下:
上述公式中2^n(公式中的分母),n取决于频率分辨率,分辨率越小,n的数值越大,频率控制字(delta_theta)的位宽越大。
相位控制字与位宽n之间的关系如下:
poff = phase * (2^n-1)/360,比如期望输出的正弦波初始相位是90度,那么poff = 90* (2^n-1)/360 = (2^n-1)/4。
1.1.1、输出信号的频率f_out和频率控制字delta_theta之间的关系
前面的公式:
假设系统的参考时钟是f_clk=250mhz, 通过公式易知,频率控制字delta_theta的取值范围还是0 ~ 2^n,对应输出信号的频率,其取值范围是0~f_clk。根据采样定理,所产生的信号频率不能超过时钟频率(采样时钟频率,250mhz)的一半,在实际应用中,为了保证信号的输出质量,输出频率不要高于时钟频率的33%,以免混叠或谐波落入有用输出频带内。
1.2、dds-ip 核配置
step1:在ip-catalog中搜索dds,找到 dds compiler。
step2:设置pinc/poff的数据形式,fixed-programmable-streaming。
三种模式的区别如下:
fixed是固定相位增量,ip核运行过程中不可更改,即输出的信号频率固定不变;
programmable可编程,可在dds运行过程中随时写入频率控制字改变输出波形的频率,用于偶尔改变频率;
streaming应用于频繁改变频率。
输出信号如果勾选为正弦和余弦信号,输出信号的结果如下:高位bit31:16为正弦信号,低位bit15:0为余弦信号。
step3:ip核复位信号
step4:ip核配置summary
02、dds-ip核应用部分
2.1 、使用dds-ip 核合成高频信号
工程需求 :参考时钟是250mhz,使用8路dds合成具备2g采样率的信号,输出到dac芯片。
解决方案 :参考时钟是250mhz,周期为4ns,2g采样率的信号,采样周期为0.5ns,使用dds-ip核输出250mhz的信号,相邻2个采样点之间的时间间隔是4ns,要满足2g采样率,原始4ns的周期内需要有8个采样点,dds-ip核的信号输出通过移相实现,每个dds-ip核移相的大小为频率控制字的1/8。
2.2、代码实现
逻辑代码顶层,模拟dac芯片内部2g采样时钟信号,对8路dds-ip核的输出信号连续采样,合成高频信号。`timescale 1ns / 1ps//////////////////////////////////////////////////////////////////////////////////// company: // engineer: // // create date: 2021/06/09 19:15:35// design name: // module name: dds_ctrl// project name: // target devices: // tool versions: // description: // // dependencies: // // revision:// revision 0.01 - file created// additional comments:// //////////////////////////////////////////////////////////////////////////////////module dds_ctrl(input wire sclk, input wire sclk_2ghz,input wire rst_n, input wire s_axis_phase_tvalid, // 初始相位控制字 input wire [23:0] s_axis_phase_tdata, input wire s_axis_config_tvalid, input wire [23:0] s_axis_config_tdata, // 频率控制字 output wire m_axis_data_tvalid0, output wire [15:0] m_axis_data_tdata0, output wire m_axis_data_tvalid1, output wire [15:0] m_axis_data_tdata1,output wire m_axis_data_tvalid2, output wire [15:0] m_axis_data_tdata2,output wire m_axis_data_tvalid3, output wire [15:0] m_axis_data_tdata3,output wire m_axis_data_tvalid4, output wire [15:0] m_axis_data_tdata4,output wire m_axis_data_tvalid5, output wire [15:0] m_axis_data_tdata5,output wire m_axis_data_tvalid6, output wire [15:0] m_axis_data_tdata6,output wire m_axis_data_tvalid7, output wire [15:0] m_axis_data_tdata7,output wire analog_data_valid,output wire [127:0] analog_data_128bit,output reg [2:0] cnt,output reg [15:0] sample_data); wire [23: 0] config_pinc_0; wire [23: 0] config_poff_0; wire [23: 0] config_pinc_1; wire [23: 0] config_poff_1; wire [23: 0] config_pinc_2; wire [23: 0] config_poff_2; wire [23: 0] config_pinc_3; wire [23: 0] config_poff_3; wire [23: 0] config_pinc_4; wire [23: 0] config_poff_4; wire [23: 0] config_pinc_5; wire [23: 0] config_poff_5; wire [23: 0] config_pinc_6; wire [23: 0] config_poff_6; wire [23: 0] config_pinc_7; wire [23: 0] config_poff_7; assign analog_data_valid = m_axis_data_tvalid0 |m_axis_data_tvalid1 | m_axis_data_tvalid2 |m_axis_data_tvalid3 | m_axis_data_tvalid4 |m_axis_data_tvalid5 | m_axis_data_tvalid6 | m_axis_data_tvalid7; assign analog_data_128bit ={ m_axis_data_tdata0, m_axis_data_tdata1, m_axis_data_tdata2, m_axis_data_tdata3, m_axis_data_tdata4, m_axis_data_tdata5, m_axis_data_tdata6, m_axis_data_tdata7 }; mux1_8 inst_mux1_8 (.sclk (sclk),.rst_n (rst_n) , .config_pinc (s_axis_config_tdata) , // 频率控制字 .config_poff (s_axis_phase_tdata), // 初始相位控制字 .poff_dds_ctrl (s_axis_config_tdata), .config_pinc_0 (config_pinc_0) ,.config_poff_0 (config_poff_0) , .config_pinc_1 (config_pinc_1) ,.config_poff_1 (config_poff_1) ,.config_pinc_2 (config_pinc_2) ,.config_poff_2 (config_poff_2) ,.config_pinc_3 (config_pinc_3) ,.config_poff_3 (config_poff_3) ,.config_pinc_4 (config_pinc_4) ,.config_poff_4 (config_poff_4) ,.config_pinc_5 (config_pinc_5) ,.config_poff_5 (config_poff_5) ,.config_pinc_6 (config_pinc_6) ,.config_poff_6 (config_poff_6) ,.config_pinc_7 (config_pinc_7) ,.config_poff_7 (config_poff_7) ); dds_compiler_0 your_instance_name0 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_0), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_0), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid0), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata0) // output wire [15 : 0] m_axis_data_tdata); dds_compiler_0 your_instance_name1 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_1), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_1), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid1), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata1) // output wire [15 : 0] m_axis_data_tdata); dds_compiler_0 your_instance_name2 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_2), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_2), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid2), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata2) // output wire [15 : 0] m_axis_data_tdata); dds_compiler_0 your_instance_name3 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_3), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_3), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid3), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata3) // output wire [15 : 0] m_axis_data_tdata); dds_compiler_0 your_instance_name4 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_4), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_4), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid4), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata4) // output wire [15 : 0] m_axis_data_tdata); dds_compiler_0 your_instance_name5 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_5), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_5), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid5), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata5) // output wire [15 : 0] m_axis_data_tdata); dds_compiler_0 your_instance_name6 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_6), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_6), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid6), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata6) // output wire [15 : 0] m_axis_data_tdata); dds_compiler_0 your_instance_name7 ( .aclk(sclk), // input wire aclk .aresetn(rst_n), // input wire aresetn .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(config_poff_7), // input wire [23 : 0] s_axis_phase_tdata .s_axis_config_tvalid(s_axis_config_tvalid), // input wire s_axis_config_tvalid .s_axis_config_tdata(config_pinc_7), // input wire [23 : 0] s_axis_config_tdata .m_axis_data_tvalid(m_axis_data_tvalid7), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata7) // output wire [15 : 0] m_axis_data_tdata); reg [15:0] sample_data; //(* keep = true *) reg [2:0] cnt;always @(posedge sclk_2ghz or negedge rst_n) begin if (~rst_n) begin cnt <= 3'b0; end else if (analog_data_valid == 1'b1) begin if (cnt == 3'd7) begin cnt <= 3'b0; end else begin cnt <= cnt + 1'b1; end end else begin cnt <= 3'b0; end end always @(posedge sclk_2ghz or negedge rst_n ) begin if (~rst_n) begin sample_data <= 16'b0; end else begin case (cnt) 3'd0: sample_data <= m_axis_data_tdata0; 3'd1: sample_data <= m_axis_data_tdata1; 3'd2: sample_data <= m_axis_data_tdata2; 3'd3: sample_data <= m_axis_data_tdata3; 3'd4: sample_data <= m_axis_data_tdata4; 3'd5: sample_data <= m_axis_data_tdata5; 3'd6: sample_data <= m_axis_data_tdata6; 3'd7: sample_data > 1 ; // 125 mhz --(2097151) // localparam poff_dds_ctrl = 2642415 ; // 150 mhz // localparam poff_dds_ctrl = 4194303 > > 2 ; // 250 mhz assign config_pinc_0 = config_pinc; assign config_pinc_1 = config_pinc; assign config_pinc_2 = config_pinc; assign config_pinc_3 = config_pinc; assign config_pinc_4 = config_pinc; assign config_pinc_5 = config_pinc; assign config_pinc_6 = config_pinc; assign config_pinc_7 = config_pinc; assign config_poff_0 = config_poff; assign config_poff_1 = config_poff+ (poff_dds_ctrl > > 3); assign config_poff_2 = config_poff+ (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3); assign config_poff_3 = config_poff+ (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3); assign config_poff_4 = config_poff+ (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3); assign config_poff_5 = config_poff+ (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3); assign config_poff_6 = config_poff+ (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3); assign config_poff_7 = config_poff+ (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3) + (poff_dds_ctrl > > 3); endmodule仿真激励文件
`timescale 1ns / 1ps//////////////////////////////////////////////////////////////////////////////////// company: // engineer: // // create date: 2021/06/08 19:56:44// design name: // module name: tb_top// project name: // target devices: // tool versions: // description: // // dependencies: // // revision:// revision 0.01 - file created// additional comments:// //////////////////////////////////////////////////////////////////////////////////module tb_top();reg aclk;reg sclk_2ghz;reg rst_n;reg s_axis_phase_tvalid;reg [23:0] s_axis_phase_tdata; reg s_axis_config_tvalid; reg [23:0] s_axis_config_tdata; wire m_axis_data_tvalid0; wire m_axis_data_tvalid1; wire m_axis_data_tvalid2; wire m_axis_data_tvalid3; wire m_axis_data_tvalid4; wire m_axis_data_tvalid5; wire m_axis_data_tvalid6; wire m_axis_data_tvalid7; wire [15:0] m_axis_data_tdata0; wire [15:0] m_axis_data_tdata1; wire [15:0] m_axis_data_tdata2; wire [15:0] m_axis_data_tdata3;wire [15:0] m_axis_data_tdata4;wire [15:0] m_axis_data_tdata5; wire [15:0] m_axis_data_tdata6;wire [15:0] m_axis_data_tdata7; wire analog_data_valid;wire [127:0] analog_data_128bit; wire [15:0] sample_data;wire [2:0] cnt; initial begin s_axis_config_tvalid = 1'b1; //s_axis_config_tdata = 24'd16777; // ---1 mhz 频率控制字 //s_axis_config_tdata = 24'd167770; // ---10mhz 频率控制字 --- 由频率分辨率决定 --- (10*10^6*2^22)/250*10^6 = (0.0167772)* 10*10^6 = 167770 //s_axis_config_tdata = 24'd335540; // ---20mhz 频率控制字 //s_axis_config_tdata = 24'd838850; // ---50mhz 频率控制字 //s_axis_config_tdata = 24'd1677700; // ---100mhz 频率控制字 //s_axis_config_tdata = 24'd2097125; // ---125mhz 频率控制字 //s_axis_config_tdata = 24'd2516550; // ---150mhz 频率控制字 s_axis_config_tdata = 24'd4194250; // ---250mhz 频率控制字 //s_axis_config_tdata = 4194250 < < 1; // ---500mhz 频率控制字 //s_axis_phase_tdata = 24'd0; // --- 相位控制字 - 0度 s_axis_phase_tdata = 24'd524288; // --- 相位控制字 - 45度 //s_axis_phase_tdata = 24'd1048576; // --- 相位控制字 - 90度 s_axis_phase_tvalid = 1'b1; rst_n = 0;#10rst_n = 1;//#1000//s_axis_phase_tdata = 24'd1048576; // --- 相位控制字 - 90度 //#500//s_axis_phase_tdata = 24'd524288; // --- 相位控制字 - 45度 end initialbegin aclk = 0; forever #2 aclk = ~aclk;end initialbegin sclk_2ghz = 0; forever #0.25 sclk_2ghz = ~sclk_2ghz;end dds_ctrl your_dds_ctrl ( .sclk(aclk), // input wire aclk .sclk_2ghz(sclk_2ghz), .rst_n(rst_n), .s_axis_phase_tvalid(s_axis_phase_tvalid), // input wire s_axis_phase_tvalid .s_axis_phase_tdata(s_axis_phase_tdata), // input wire [23 : 0] s_axis_phase_tdata .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_tvalid0(m_axis_data_tvalid0), // output wire m_axis_data_tvalid .m_axis_data_tdata0(m_axis_data_tdata0), // output wire [15 : 0] m_axis_data_tdata .m_axis_data_tvalid1(m_axis_data_tvalid1), .m_axis_data_tdata1(m_axis_data_tdata1), // output wire [15 : 0] m_axis_data_tdata .m_axis_data_tvalid2(m_axis_data_tvalid2), .m_axis_data_tdata2(m_axis_data_tdata2), // output wire [15 : 0] m_axis_data_tdata .m_axis_data_tvalid3(m_axis_data_tvalid3), .m_axis_data_tdata3(m_axis_data_tdata3), // output wire [15 : 0] m_axis_data_tdata .m_axis_data_tvalid4(m_axis_data_tvalid4), .m_axis_data_tdata4(m_axis_data_tdata4), // output wire [15 : 0] m_axis_data_tdata .m_axis_data_tvalid5(m_axis_data_tvalid5), .m_axis_data_tdata5(m_axis_data_tdata5), // output wire [15 : 0] m_axis_data_tdata .m_axis_data_tvalid6(m_axis_data_tvalid6), .m_axis_data_tdata6(m_axis_data_tdata6), // output wire [15 : 0] m_axis_data_tdata .m_axis_data_tvalid7(m_axis_data_tvalid7), .m_axis_data_tdata7(m_axis_data_tdata7), // output wire [15 : 0] m_axis_data_tdata .analog_data_valid(analog_data_valid), .analog_data_128bit(analog_data_128bit), .cnt(cnt), .sample_data(sample_data)); endmodule2.3、仿真验证结果
图2-3-1、生成50mhz正弦波信号,采样率为2g
图2-3-2、生成100mhz正弦波信号,采样率为2g
图2-3-3、生成250mhz正弦波信号,采样率为2g
2.4、仿真验证总结
1、单个dds调制模块在使用的时候,如果输出频率高于采用时钟频率的一半时候,dds输出信号会失真。比如设置输出频率超过时钟频率的一半(大于125mhz),导致单个dds输出信号失真。实验发现尽管单个dds输出信号失真,整合后的输出信号(8路dds移相后输出)仍然可以保证设置的信号频率,比如图2-3-3中单个dds输出信号失真,但是合成后的信号依然质量较高;
2、每个dds-ip核移相的大小为频率控制字的1/8,频率控制字的位宽为24bits,频率分辨率为100hz, 即整个dds模块可以输出频率为100hz或者100hz整数倍的波形数据。频率控制字的位宽决定了频率分辨率的大小,频率控制字的位宽越大,dds输出信号的频率分辨率越小,系统输出信号的频率范围越宽。
2.5、板级测试结果
图2-5-1 50mhz正弦波信号,通过采样率为2g的dac芯片输出模拟信号
图2-5-2 250mhz正弦波信号,通过采样率为2g的dac芯片输出模拟信号

虚拟内存的设置步骤及其注意事项
金立Max Pro正式在印度市场开始发售
TWS蓝牙耳机你了解多少
零跑汽车计划2019年推出两款微车,与大道用车达成战略合作
应用于智能门锁的离合组件的驱动芯片AT8837简介
DDS-IP核的理论知识和应用案例
亚马逊宣布全新云服务 运用XilinxFPGA
乘法器的使用方法你知道哪些?
我国激光企业要顺应“国内大循环”潮流,扩大内需成为战略基点
三星新专利上的新专利:可折叠手机背部的变焦镜头
美的暖通获TUV南德全球首个热泵产品碳中和达成核查声明
三维荧光光谱在水质分析行业的应用
中汽协报告5月汽车销量双位数增长
简述TI的阻抗跟踪电池电量计技术
国微技术将以人民币1000万元购买鸿芯微纳技术公司约0.99%的股权
苹果侵犯知识产权公司WiLan的专利,iPad Pro有望今年完成
英创信息技术其他成熟工控主板简介
如何正确找到你想要的数据集成工具
Arm 新技术助力汽车产业拥抱软件定义的未来
智能座舱空间建设的“芯”思考