先说说DDS算法 的基本原理:
DDS是直接式数字频率合成的简称。它在无线通信系统实现中是一门关键技术。其实,DDS在FPGA 中的实现方法说白了就是在定义好的ROM数据中寻址并找到该地址对应的波形数据。在系统参考时钟的驱动下,通过控制频率控制字来驱动相位累加寄存器数值的 变化,从而是在ROM中寻址并找到对应数据,最后输出为波形数据。其中,在每一个系统时钟脉冲来临时,相位累加寄存器都会相应叠加一次频率控制字。其实, 频率控制字的功能就是控制步进长度,也就是形成的波形的频率和系统时钟频率的比例关系,最终可以确定输出波形的频率。比如要输出正弦波,系统参考时钟频率 为20M,输出每位宽度为10bit,则令频率控制字位宽为20bit,f(out)=(频率控制字/2^20)*f(clk)。这样就可以控制输出波形 的频率。
本人用的是QUETUS II开发环境。对于quatus ii 中ROM 可用的数据文件格式为mif。此文件数据可以用MATLAB来产生。
具体产生方法如下:
新建一个.m文件,然后输入:
width=10;//位宽
depth=2^width;//数据深度
t=linspace(0,6.28,depth);
sin_val=sin(t);
cos_val=cos(t);
sin_val=fix(sin_val*(2^width-1)/2+0.5);//四舍五入求结果
cos_val=fix(cos_val*(2^width-1)/2+0.5);
addr=[0:1023];
%存盘
file=fopen('f:/My Works/FPGA Examples/sin.mif','wt');
fprintf(file,'WIDTH=%d;\n',width);
fprintf(file,'DEPTH=%d;\n',depth);
fprintf(file,'ADDRESS_RADIX=UNS;\n');
fprintf(file,'DATA_RADIX=DEC;\n');//这个数据格式最好不要变,不然改动会很麻烦
fprintf(file,'CONTENT BEGIN\n');
for i=1:depth
fprintf(file,'%d:%d;\n',addr(i),sin_val(i));
end
fprintf(file,'END;\n');
fclose(file);
file=fopen('f:/My Works/FPGA Examples/cos.mif','wt');
fprintf(file,'WIDTH=%d;\n',width);
fprintf(file,'DEPTH=%d;\n',depth);
fprintf(file,'ADDRESS_RADIX=UNS;\n');
fprintf(file,'DATA_RADIX=DEC;\n');
fprintf(file,'CONTENT BEGIN\n');
for i=1:depth
fprintf(file,'%d:%d;\n',addr(i),cos_val(i));
end
fprintf(file,'END;\n');
fclose(file);
上述程序可以产生可用的mif文件数据,并可以导入ROM中。
最后就是编写verilog代码:
module DDS(clk,rst,
data,we,ddse,
sine,cosine
);
input clk;
input rst;
input we;
input ddse;
input[19:0] data;
output[9:0] sine;
output[9:0] cosine;
reg[19:0] ADD_A;
reg[19:0] ADD_B;
reg[9:0] sine_r;
reg[9:0] cosine_r;
wire[9:0] sine_d;
wire[9:0] cosine_d;
wire[9:0] rom_ad;
assign sine = sine_r;
assign cosine = cosine_r;
assign rom_ad = ADD_B[19:10];
always@(posedge clk or negedge rst) begin
if(!rst) begin ADD_A <= 20'd0;end
else case(we)
1'b1: ADD_A <= data;
1'b0: ADD_A <= 20'd0;
default: ADD_A <= 20'd0;
endcase
end
always@(posedge clk or negedge rst) begin
if(!rst) begin ADD_B <= 20'd0; end
else case(ddse)
1'b1: begin ADD_B <= ADD_A + ADD_B; end
1'b0: begin ADD_B <= 20'd0; end
default: begin ADD_B <= 20'd0; end
endcase
end
always@(posedge clk or negedge rst) begin
if(!rst) begin sine_r <= 10'd0;
cosine_r <= 10'd0;
end
else case(ddse)
1'b1: begin sine_r <= sine_d;
cosine_r <= cosine_d;
end
1'b0: begin sine_r <= 10'd0;
cosine_r <= 10'd0;
end
default: begin sine_r <= 10'd0;
cosine_r <= 10'd0;
end
endcase
end
rom_sin rom_sin1(
.address(rom_ad),
.clock(clk),
.q(sine_d)
);
rom_cos rom_cos1(
.address(rom_ad),
.clock(clk),
.q(cosine_d)
);
endmodule
从图中可以看到有很多毛刺,但是这是功能仿真,本人初学,不是很懂,希望高手看到能给予帮助与提示。小弟在此多谢!!!!
用户377235 2014-4-28 21:11
用户1600457 2012-3-23 12:19
用户1650588 2012-3-20 10:24
用户1600457 2011-7-29 15:25
用户1631420 2011-7-29 12:36