前言
在项目中,有时需要用到DDS信号发生器来产生一些基本信号用于驱动AD,虽然,Vivado或Quartus都有提供DDS IP核,但是都只能产生正弦信号/或余弦信号,像三角波信号、方波信号是没有办法产生的,这时,就需要我们自己设计一款能够生成任意波形的DDS信号发生器了。
DDS原理
DDS的作用是将一串脉冲转换成一个模拟波形,通常为一个正弦波、三角波或方波等,利用数字信号处理的方式来产生一个频率和相位可调的输出,该输出与一个参考时钟的频率相关。基本的DDS结构如图 1 所示,其主要部分为:相位累加器(产生输出波形相位角度的数据),相数转换器(将上述相位数据转换为瞬时输出幅度数据,通常是一个存储波形幅值的查找表),以及数模转换器(DAC)(将该幅度数据转换成采样模拟数据点)。
图1 DDS基本原理框图
DDS输出信号的频率可以按照下面的表达式计算:
其中,表示输出信号的频率,表示相位增量,表示相位累加器位宽,表示参考时钟的频率。当与参数确定后,DDS所能输出的信号的最小频率也随之确定了,即DDS的频率分辨率为: 。所以,根据所需要输出信号的最小频率即可计算出参数与的最佳值。
基于线性插值的DDS信号发生器
在某预研测试系统中,需要产生一路1Hz~100KHz的信号正弦或三角波信号,用来作为测试源。根据输出信号频率范围,可以计算出,当时,。即,当DDS的参考时钟频率为10MHz时,要产生频率范围为1Hz~100KHz的信号,相位累加器的位宽至少需要24bit。也就表示,所需要的波形幅值的查找表的深度至少需要,即需要一个至少能够存储16M个采样点的查找表,对FPGA来说几乎不可能有16M*14bit这么大容量的存储器资源。
那么有没有办法能够实现这样的一个DDS呢?当然是有办法的。那就是,使用一个采样点较少的查找表,然后结合线性插值思想,根据查找表中前后相邻的两个采样点,插值出中间的幅值。这样就可以使用较少的存储器资源实现相位累加器位宽如此之大的DDS系统了。
多的就不说了,直接上代码:
module dds #(parameter PHIN_W = 32,DAT_W = 14)(
input clk ,
input rst_n ,
input wave_sel,//0-sin 1-triangular
input [PHIN_W - 1 : 0] pha_inc ,//Phase increment
output [DAT_W - 1 : 0] dout
);
//parameters declare
localparam LUT_D = 1024 ,//Number of Sample Point
LUT_W = DAT_W ;
//Signal declare
reg [PHIN_W - 1 : 0] phase_acc ;
wire [9 : 0] addr_0 ;
wire [9 : 0] addr_1 ;
reg [PHIN_W - 11 : 0] weight ;
reg [PHIN_W - 11 : 0] weight_0 ;
reg [PHIN_W - 11 : 0] weight_1 ;
reg [PHIN_W - 11 : 0] weight_2 ;
reg [DAT_W - 1 : 0] sub ;
reg [PHIN_W - 11 + DAT_W : 0] mult ;//PHIN_W - 10 + DAT_W bit
reg [DAT_W - 1 : 0] sum ;
wire [DAT_W - 1 : 0] sin_dout_a ;
wire [DAT_W - 1 : 0] sin_dout_b ;
wire [DAT_W - 1 : 0] tri_dout_a ;
wire [DAT_W - 1 : 0] tri_dout_b ;
wire [DAT_W - 1 : 0] dout_a ;
wire [DAT_W - 1 : 0] dout_b ;
reg [DAT_W - 1 : 0] dout_a_d0 ;
reg [DAT_W - 1 : 0] dout_b_d0 ;
reg [DAT_W - 1 : 0] dout_a_d1 ;
reg [DAT_W - 1 : 0] dout_b_d1 ;
//phase accumulator
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
phase_acc <= 'd0;
end
else begin
phase_acc <= phase_acc + pha_inc;
end
end
assign addr_0 = phase_acc[PHIN_W - 1 -: 10];
assign addr_1 = phase_acc[PHIN_W - 1 -: 10] + 1;
//Pipeline 1
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
weight <= 'd0;
weight_0 <= 'd0;
weight_1 <= 'd0;
weight_2 <= 'd0;
end
else begin
weight <= phase_acc[PHIN_W - 1 - 10 : 0];
weight_0 <= weight;
weight_1 <= weight_0;
weight_2 <= weight_1;
end
end
//Pipeline 1
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
sub <= 'd0;
end
else begin
sub <= (dout_a >= dout_b) ? (dout_a - dout_b) : (dout_b - dout_a);
end
end
assign dout_a = (wave_sel == 1'b0) ? sin_dout_a : tri_dout_a;
assign dout_b = (wave_sel == 1'b0) ? sin_dout_b : tri_dout_b;
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
dout_a_d0 <= 'd0;
dout_b_d0 <= 'd0;
dout_a_d1 <= 'd0;
dout_b_d1 <= 'd0;
end
else begin
dout_a_d0 <= dout_a;
dout_b_d0 <= dout_b;
dout_a_d1 <= dout_a_d0;
dout_b_d1 <= dout_b_d0;
end
end
//Pipeline 2
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
mult <= 'd0;
end
else begin
mult <= sub * weight_2;
end
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
sum <= 'd0;
end
else begin
sum <= (dout_a_d1 >= dout_b_d1) ? dout_a_d1 - mult[PHIN_W + 3 -: DAT_W] :
dout_a_d1 + mult[PHIN_W + 3 -: DAT_W];
end
end
//output
assign dout = sum;
//ROM instantiate
xilinx_dpram #(.RAM_DEPTH (LUT_D ),
.RAM_WIDTH (LUT_W ),
.RAM_MODE ("SYNC" ),
.RAM_INIT ("sin.mem" )
)u_sin_rom(
.clk (clk ),//clk_a
.clk_b (clk ),//clk_b
.rst_n (rst_n ),
//port a
.wren_a (1'b0 ),
.rden_a (1'b1 ),
.addr_a (addr_0 ),
.din_a ({LUT_W{1'b0}} ),
.dout_a (sin_dout_a ),
//port b
.wren_b (1'b0 ),
.rden_b (1'b1 ),
.addr_b (addr_1 ),
.din_b ({LUT_W{1'b0}} ),
.dout_b (sin_dout_b )
);
xilinx_dpram #(.RAM_DEPTH (LUT_D ),
.RAM_WIDTH (LUT_W ),
.RAM_MODE ("SYNC" ),
.RAM_INIT ("tri.mem" )
)u_tri_rom(
.clk (clk ),//clk_a
.clk_b (clk ),//clk_b
.rst_n (rst_n ),
//port a
.wren_a (1'b0 ),
.rden_a (1'b1 ),
.addr_a (addr_0 ),
.din_a ({LUT_W{1'b0}} ),
.dout_a (tri_dout_a),
//port b
.wren_b (1'b0 ),
.rden_b (1'b1 ),
.addr_b (addr_1 ),
.din_b ({LUT_W{1'b0}} ),
.dout_b (tri_dout_b )
);
endmodule
图2 仿真波形图
当参考时钟频率为10MHz、相位增量为100时,输出信号的频率为:,在Questasim仿真波形中测量得到的频率大约为59.481Hz,频率误差小于1Hz,说明使用插值思想实现的DDS信号发生器基本是符合所提指标的。这样,我们只需要一个深度为1024的查找表,即可实现这样高分辨率的DDS信号发生器。