1DDS工作原理
一个基本DDS系统包含数字控制振荡器(Numerically Controlled Oscillator,NCO)、数/模转换器(Digital to Analog Converter,DAC)和低通滤波器(Low Pass Filter,LPF)。首先由NCO产生数字化的输出信号波,经DAC采样转换为模拟化输出波,然后通过LPF进行平滑处理输出最终波形。其流程如图1所示。
图1经典DDS工作流程
DDS实质是,以系统时钟fc为基准频率源,对其相位进行等间隔的采样。而相位等间隔的产生是由相位累加器来完成的。将累加相位与相位控制字相加得到的相位送至正弦ROM查找表,即可得到该相位的正弦值。这样的值在时钟fc上升沿到来时送至数/模转换器,转换为阶梯模拟波形,最终由LPF平滑为连续的输出波型。
2传统DDS方法
以DDS产生正余弦双路信号为例,传统DDS总体框架如图2所示。
图2传统DDS总体框架
K为频率控制字,P为相位控制字,fc为系统时钟频率,fo为最终输出波形频率。fo=K×fc/M。其中,M为一个周期的最小等分段总数,即ROM的深度。如果ROM输入地址位数为N位,则M=2N。需要注意的是,因为DDS的实质是一个采样过程,所以必须遵循奈奎斯特采样定律(即fo最大只能是时钟频率fc的一半)。但是此时一个周期只采样了2个点,会造成严重的失真,为了获得比较稳定连续的输出波形,一般取fo≤30%fc较为适宜。
3对传统DDS的优化设计
传统DDS方法的性能瓶颈主要存在于:运算速度受累加器运算速率的限制;输出频率分辨率受硬件ROM存储容量的限制。本文主要对传统DDS作以下两个方面的优化。
3.1基于流水线架构的累加器设计
流水线的主要思想是,将一个复杂的多步完成的运算拆分为一些简单的单步完成的运算操作[1]。从时间上看,不同单步运算都可以以重叠的方式执行,这样每个周期内系统的每个运算器都在全速工作,大大提高了整个系统的工作效率。本文构建了图3所示的16位流水线累加器。
图316位流水线累加器
该16位流水线累加器共4级锁存,4级加法。第1级锁存用于存储并稳定16位输入数据,中间每一级4位加法器均搭配一级锁存器。由流水线的原理可知,该累加器的整体速度取决于4位加法器[2]。
该流水线累加器的Verilog HDL实现代码如下(端口申明部分略):
always @ (posedge clk)
begin
ina <= din;//第1级锁存
end
always @ (posedge clk)
begin
{c1,sum1} <= ina[3:0] + inb[3:0];//第1~4位数据相加
tempa_1 <= ina[15:4];//第2级锁存,存储高12位数据
tempb_1 <= inb[15:4];
sum_reg1 <= sum1;
end
always @ (posedge clk)
begin
{c2,sum2} <= tempa_1[3:0] + tempb_1[3:0] + c1;//第5~8位数据相加
tempa_2 <= tempa_1[11:4];//第3级锁存,存储高8位数据
tempb_2 <= tempb_1[11:4];
sum_reg2 <= {sum2,sum_reg1};//拼接低7位数据
end
always @ (posedge clk)
begin
{c3,sum3} <= tempa_2[3:0] + tempb_2[3:0] + c2;//第9~12位数据相加
tempa_3 <= tempa_2[7:4];//第4级锁存,存储高8位数据
tempb_3 <= tempb_2[7:4];
sum_reg3 <= {sum3,sum_reg2};//拼接低12位数据
end
always @ (posedge clk)
begin
{c4,sum4} <= tempa_3 + tempb_3 + c3;//第13~16位数据相加
dout <= {sum4,sum_reg3};//拼接最终16位数据并输出
inb <= {sum4,sum_reg3};
end
流水线累加器在Modelsim下的仿真结果如图4所示。
图4流水线累加器的Modelsim仿真结果
3.2基于镜像算法的ROM设计
镜像算法是利用整体空间上的对称性,通过对局部的折叠、翻转、倒置等方法,达到以局部代替整体的效果。正弦与余弦函数无论在横轴上还是在纵轴上都具有很好的对称性,因此可以利用镜像的思想对ROM的存储方式进行优化,通过地址镜像以及数据镜像,达到压缩ROM空间、节约芯片资源的目的。
以构建正弦函数值ROM为例。在[0,π]区间内其波形是关于π/2对称的,因此其[2/π,π]区间内的波形可以通过对[0,π/2]的波形关于π/2进行翻转而成,同理[π,2π]区间内的波形可以通过对[0,π]的波形关于横轴翻转而成。所以实际上ROM只需要存储[0,π/2]内的正弦函数值,通过适当算法即可实现输出一个完整周期的正弦函数值,从而大大减小存储器的大小。
镜像优化的ROM结构框图如图5所示。
图5镜像优化的ROM结构框图
首先将总地址分为3部分:最高位作为数据镜像器的标志位,实现对数据的镜像转换;次高位作为地址镜像器的标志位,实现对ROM地址的镜像转换;而余下的通过地址镜像器后作为ROM地址位。如果将最高位和次高位合并起来看,它们实质上是构成一个象限选择器,其值从00至11分别代表第1、2、3、4象限。
由于余弦信号对于正弦信号而言,只是在相位上提前了π/2个单位,因此如果要产生双路正余弦信号,只需要在相位累加器相位输出的次高位上加1位,即可通过正弦ROM查找表得到余弦的信号。
基于镜像算法的ROM Verilog HDL实现代码如下(端口申明部分略):
assign addflag_sin = din_reg[15:14];//提取地址最高位和次高位
assign addflag_cos = din_reg[15:14] + 2'b01;//将正弦地址转换为余弦地址
assign add_trans_sin = addflag_sin[0];//正弦地址镜像器标志位
assign data_trans_sin = addflag_sin[1];//正弦数据镜像器标志位
assign add_trans_cos = addflag_cos[0];//余弦地址镜像器标志位
assign data_trans_cos = addflag_cos[1];//余弦数据镜像器标志位
assign din_addr_inv = ~din_reg[13:0];//对ROM输入地址镜像
always @ (posedge clk) //正弦地址镜像器控制
begin
if(!rst_n) addr_sin <= 14'd0;
else if (add_trans_sin) addr_sin <= din_addr_inv;
else addr_sin <= din_reg[13:0];
end
always @ (posedge clk) //余弦地址镜像器控制
begin
if(!rst_n) addr_cos <= 8'd0;
else if (add_trans_cos) addr_cos <= din_addr_inv;
else addr_cos <= din_reg[8:0];
end
rom rom_inst_sin(//实例化ROM
.clka(clk),
.addra(addr_sin),
.douta(data_untrans_sin));
rom rom_inst_cos(
.clka(clk),
.addra(addr_cos),
.douta(data_untrans_cos));
always @ (posedge clk)//正弦数据镜像器控制
begin
if(!rst_n) data_reg_sin <= 16'd0;
else if (data_trans_sin) data_reg_sin <= ~data_untrans_sin;
else data_reg_sin <= data_untrans_sin;
end
always @ (posedge clk)//余弦数据镜像器控制
begin
if(!rst_n) data_reg_cos <= 14'd0;
else if (data_trans_cos) data_reg_cos <= ~data_untrans_cos;
else data_reg_cos <= data_untrans_cos;
end
镜像优化后,ROM输出的双路正余弦函数值在Modelsim下的仿真结果如图6所示。
图6优化后ROM输出的双路正余弦函数值仿真结果