ROM存储1/4周期正弦信号构造DDS
简单的dds程编写过程中我遇到问题以及一些个人的思考。初次接触FPGA,如有问题请多多指教~
1.几个疑问,解决和没有解决的。
为何采用ROM而不是直接采用DDS核来进行正弦信号的合成?
实际中如果只需要合成正弦信号,那么DDS核是一个很好的选择,而且DDS核可以选择是否采用泰勒校正以获取更低的杂散。由于ROM表中的数据可以由我们自己选择,采用ROM做DDS具有更强的灵活性。
在使用chipscope时,添加ICON核和.cdc文件的区别?
ICON核的添加需要改变原有程序的结构,需要重新综合,正是由于这个核在程序内部,我们可以轻易地选择需要观察的信号;
.cdc文件的插入不改变程序结构,只需要重新translate等,由于是对综合后的程序进行插入,所以有些信号会被优化掉或者改名字等。
双口ROM有无缺陷?
不知道……modelsim反正中表明,无法仿真collision等……Block Ram的手册中没有提到双口ROM,也许看看双口RAM会有帮助。
2.Matlab仿真
注意:由于采用了1/4周期存储,要求整个周期的数值是中心对称的,半个周期的数值是轴对称的。这就意味着采样点中不应该有0值得存在。
Matlab仿真——ROM表存储数据
%% ROM产生 (无符号数) ROM_N=2^10; %ROM表深度 DATA_L=14; %ROM位宽 t=1:ROM_N; y=(2^DATA_L-1)*(sin(2*pi*(t-0.5)/ROM_N/4));%-0.5保证对称性 ROM_DATA=round(y);
Matlab仿真——DDS程序(请原谅我没有用case……)
%% 正弦波dds产生 F_CLK=10*10^6; %时钟频率10M PINC_IN_L=32; %增量长度 DDS_CLK=10*10^3; %dds输出频率10k PHASE_IN=0; %初始相位 pinc=round(DDS_CLK*2^PINC_IN_L/F_CLK); %相位增量 SIM_L=4000; %仿真长度 phase=PHASE_IN+pinc*(0:SIM_L-1); addr=mod((floor(phase/2^PINC_IN_L*ROM_N)),ROM_N*4); flag=floor(addr/ROM_N); dds_out=1:SIM_L; for i=1:SIM_L if(flag(i)==0) dds_out(i)=ROM_DATA(addr(i)+1); else if(flag(i)==1) dds_out(i)=ROM_DATA(2047-addr(i)+1); else if(flag(i)==2) dds_out(i)=-ROM_DATA(addr(i)-2048+1); else dds_out(i)=-ROM_DATA(4095-addr(i)+1); end end end end plot(dds_out);
3.DDS的解释
Xilinx的DDS核的User Guide中队DDS做了很详细的说明,本节不再重复此内容,本节将叙述一种全新的DDS理解方式。这种理解方式解决了频率控制字长于ROM表深度时DDS的理解上的问题。
连续还是离散?
连续还是离散,在于我们用什么眼光去看待。如下图所示,我们可以理解蓝色的图是离散的,而红色的线是连续的。然而,对于我们要获取的信息而言,这两幅图是完全没有区别的。抽样定理中有理想抽样和平顶抽样(采样保持电路)之分,然而在频域效果上,是并没有很大区别的。
ROM表存储的是连续的数值
按照上面的理论,可以认为ROM表中存储的是正弦信号的平顶采样结果。如下图所示(一个全周期的ROM表,对称性满足1/4周期存储的要求)
上图的频谱在信号与系统中有提及,上述波形的形成方式可以认为是在乘以周期为T的冲击函数,之后卷积一个宽度为T的窗函数。对应可以求得其频域。(具体可参考平顶抽样)
频率控制字
频率控制字控制对上述的阶梯函数的采样,如果这样理解的话,就没有所谓的相位截取取ROM表的值的疑惑了。频谱图能够清晰的表现这一过程,然而由于blog表达不便,此处不做详细说明。
此处能够解释DDS产生的杂散。
4.Verilog实现
从设计设计上来说,Verilog和Matlab代码应该完全一致,包括代码编写的思路,以及命名都应该统一。但是下面的程序没有做到这一点,带改进。
`timescale 1ns / 1ps module DDS_10k( input clk_10M, input rst, output[13:0] D_SIN, output[13:0] D_COS ); parameter pinc=32'd4294967; //10k reg[31:0] addr_temp=32'd0; reg[9:0] addra,addrb; reg mark_a,mark_b; always@(posedge(clk_10M)) //复位 begin if(rst==0) addr_temp<=32'd0; else addr_temp<=addr_temp+pinc; end always@(posedge(clk_10M)) //1/4周期控制 begin case(addr_temp[31:30]) 2'b00: begin addra<=addr_temp[29:20]; mark_a<=0; addrb<=~addr_temp[29:20]; mark_b<=0; end 2'b01: begin addra<=~addr_temp[29:20]; mark_a<=0; addrb<=addr_temp[29:20]; mark_b<=1; end 2'b10: begin addra<=addr_temp[29:20]; mark_a<=1; addrb<=~addr_temp[29:20]; mark_b<=1; end 2'b11: begin addra<=~addr_temp[29:20]; mark_a<=1; addrb<=addr_temp[29:20]; mark_b<=0; end endcase end //ROM表读取 ROM_SIN_1k18 rom_dds ( .clka(clk_10M), // input clka .addra(addra), // input [9 : 0] addra .douta(D_SIN[12:0]), // output [12 : 0] douta .clkb(clk_10M), // input clkb .addrb(addrb), // input [9 : 0] addrb .doutb(D_COS[12:0]) // output [12 : 0] doutb ); reg sin_mark_temp; reg cos_mark_temp; assign D_SIN[13]=sin_mark_temp; assign D_COS[13]=cos_mark_temp; //此处ROM没有添加register,因此输出和地址有一个周期的延时,故Mark也要有一周期延时 always@(posedge(clk_10M)) begin sin_mark_temp<=mark_a; cos_mark_temp<=mark_b; end endmodule