FPGA DDS 实现及仿真
前言
在信号处理中,经常使用DDS生成不同频率的正弦及余弦信号,与采样数据或调制数据进行复数乘法,完成频谱的搬移。例如在无线电调制中,使用DDS生成的正余弦信号,与调制波形相乘,完成上变频;在无线电解调中,使用DDS生成的正余弦吸纳后,与无线电采样信号相乘,完成信号的零频搬移。DDS在信号处理得到广泛应用。
一、DDS基本原理
DDS (Direct Digital Synthesizer)直接数字式频率合成器,其基本原理是利用相位累加器,每次累加一个频率控制字,通过查找表从ROM查找表中读取波形数据。调节频率控制字的数值,可以改变累加器的累加速度,进而可以调节从ROM查找表中读取波形数据的速度。即频率控制字越大,频率越高。相位控制字可以用来调节初始相位,即ROM地址自加的初始值。
频率分辨率与相位累加器的关系:△f=fclk/2^N;
其中:
△f:频率分辨率;
fclk:时钟频率;
N:为相位累加器的宽度
例如:时钟频率fclk=50M,相位累加器宽度N=26,则 △f=50000000/2^26=0.75Hz.
说明相位累加器的宽度越宽,频率分辨率越细。
输出频率与频率控制字的关系:fout = (fclk * phase_in)/2^N;
其中:
fout:输出的频率;
fclk:时钟频率;
phase_in:频率控制字;
N:为相位累加器的宽度
例如:时钟频率fclk=50M,相位累加器宽度N=26,频率控制字phase_in=50000,则 fout=50000000*50000/2^26=37.253kHz.
说明频率控制字越大,输出频率越大。
二、Matlab程序
首先使用Matlab生成Sine波形数据,存储成FPGA ROM可读取的COE文件,Matlab程序如下。clc;
clear all;
N=4096;
x=linspace(0,2*pi,N);%一个周期采样4096个点
sin_data=sin(x);
sin_coe=ceil(sin_data*32767);%生成 16位 有符号整数
figure;
plot(sin_coe);
%生成sin函数coe文件
fid = fopen('sin_coe.coe','wt');
fprintf(fid,'MEMORY_INITIALIZATION_RADIX=10;\n');
fprintf(fid,'MEMORY_INITIALIZATION_VECTOR=\n');
for i = 1:1:N-1
fprintf(fid,'%d,\n',sin_coe(i));
end
fprintf(fid,'%d;\n',sin_coe(N));%COE 文件结束符 ;
fclose(fid);
matlab生成的sine波形如下图。
三、Verilog程序
1.ROM IP核
首先使用FPGA的ROM IP核,读取COE文件。
使用双端口ROM,这样就可以同时输出sin和cos信号。
PortA 数据宽度使用16位,存储深度选择4096,与COE文件大小一致。PortB与PortA同样设置
最后Load Matlab生成的COE文件,生成IP核。
2.DDS生成
使用相位累加器phase_add对输入的相位数据进行累加,然后通过查找表法输出正余弦信号,余弦信号与正弦信号相位相差pi/4,对于4096点,即相差1024点,所以输出的初始相位加1024,则可输出余弦信号。
module DDS_Generator(
input clk,
input [25:0] phase_in, //输出频率 fout = (clk*phase_in)/2^N
output [15:0] sine,
output [15:0] cose
);
parameter N = 26; //频率分辨率 △f=fclk/2^N 例如:clk=50M N=26 △f=0.75
parameter addr_width = 12; //ROM 地址的宽度
reg [N-1:0] phase_add = 0;
reg [addr_width-1:0] addra= 0;
reg [addr_width-1:0] addrb= 0;
always@(posedge clk)
begin
phase_add <= phase_add + phase_in;
addra<= phase_add[N-1:N-addr_width];
addrb<= phase_add[N-1:N-addr_width]+1024;
end
sine_rom sine_rom(
.clka(clk),
.addra(addra),
.douta(sine),
.clkb(clk),
.addrb(addrb),
.doutb(cose)
);
endmodule
3.DDS仿真
编写测试文件,phase_in设置为50000,输入频率设置为50M,则生成的信号应该为37.252kH在,即周期大约为26us,仿真测试文件如下
module Test_DDS(
);
reg clk =0 ;
reg [25:0] phase_in=50000;
wire [15:0] sine;
wire [15:0] cose;
DDS_Generator # (
.N(26),
.add_width(12)
)
DDS_Generator(
.clk(clk),
.phase_in(phase_in),
.sine(sine),
.cose(cose)
);
always #10 clk=~clk;
endmodule
仿真结果如下图所示