DDS信号发生器
DDS信号发生器结构

DDS信号发生器如上图所示,大致可分为4个部分:相位累加器、相位量化器、波形ROM表、 D/A 转换器构成。
相位累加器:宽度为N位,它在系统时钟的控制下按相位步进K进行累加运算,并向相位量化器输出结果(相位步进在有些地方又被称之为频率控制字)。
相位量化器:对相位累加器输出的N位相位控制字在进行截断,输出M位结果到波形ROM表。
波形ROM表:一共存储有2^M个波形参数,它根据相位量化器输出的值从ROM中取出数据,并输出到DA转换器。
D/A 转换器:将数字量转换为模拟量输出。
DDS信号发生器输出频率
相位累加器每来一个时钟就会按相位步进(K)进行一次累加运算,溢出后又从零开始进行累加,如此往复;相位累加器完成一次溢出便对应着的完成了一次波形ROM的遍历,所以相位累加器的溢出频率就是DDS信号发生器的输出频率:


当K=1时便是DDS信号发生器的最小输出频率,又称DDS信号发生器的分辨率
DDS信号发生器优化
上面的DDS信号发生器在相位量化器中只进行了截断操作(直接舍弃低位),将截断的高位作为地址查表,虽然节省了大量ROM资源,但是会导致波形精度降低,甚至明显的出现台阶。
这里可以将截断操作后得到的ROM地址和余数同时输出到波形ROM模块,波形ROM模块根据地址取出当前数据和下一个数据,然后再根据余数进行信息化,以减小误差(波形ROM可以采用双口ROM设计,以便同时读出两个数据)。
混频
混频就是对信号进行频谱搬移,如将低频信号搬移到高频或者将高频信号搬移到低频,在数字信号处理中,频谱的搬移就是将一个本震信号和一个输入信号相乘,这样就可以得到一个复合的信号,相应的公式如下:

通过公式可以看出这个复合信号中包含一个高频信号和一个低频信号,对这个信号进行低通滤波就可以将输入信号从高频信号搬移到低频,相应的进行高通滤波可以将输入信号从低频搬移到高频。

上图中输出的复合信号fout由两个信号组成,分别是f1+f2和f1-f2,后面通过滤波器可以选择出f1+f2(高频)或f1-f2(低频)。
ROM IP核
COE文件格式
Xilinx的RAM/ROM IP核可以通过COE文件初始化其中内容,COE文件格式如下:
MEMORY_INITIALIZATION_RADIX用于描述进制,10表示10进制,16表示16进制,2表示二进制
MEMORY_INITIALIZATION_VECTOR为数据向量,每个数据以","隔开,采用";"结尾
下面是一个COE文件范例:
c
MEMORY_INITIALIZATION_RADIX=10;
MEMORY_INITIALIZATION_VECTOR=
0 ,
3 ,
6 ,
9 ,
12 ,
15 ,
18 ;
在实际工作中可以使用matlab等工具生成COE文件,如下是生成128个正弦波和余弦波COE文件的matlab程序,正弦波和余弦波的幅值为-127~+127:
c
clc;
close all;
clear;
% 生成采样点t,范围从0~(1-1/128),步进为1/128,共计128个元素,表示对在一个周期进行128等分采样
t = 0:(1/128):(1-1/128);
% 对一个周期的信号采样128个点
sin_data = 127 * sin(2.0 * pi * t);
cos_data = 127 * cos(2.0 * pi * t);
% 四舍五入
sin_data = round(sin_data);
cos_data = round(cos_data);
% 画图
figure(1);
plot(sin_data);
hold on;
plot(cos_data);
% sin_data写入文件
file = fopen('sin_data.coe', 'w+');
fprintf(file, 'MEMORY_INITIALIZATION_RADIX=10;\r\n');
fprintf(file, 'MEMORY_INITIALIZATION_VECTOR=\r\n');
fprintf(file, '%d ,\r\n', sin_data(1:127));
fprintf(file, '%d ;', sin_data(128));
fclose(file);
% cos_data写入文件
file = fopen('cos_data.coe', 'w+');
fprintf(file, 'MEMORY_INITIALIZATION_RADIX=10;\r\n');
fprintf(file, 'MEMORY_INITIALIZATION_VECTOR=\r\n');
fprintf(file, '%d ,\r\n', cos_data(1:127));
fprintf(file, '%d ;', cos_data(128));
fclose(file);
ROM IP核配置
Xilinx的Block Memory Generator IP可以生成单口RAM(只有一个读写端口、伪双口RAM(一个读端口一个写端口)、真双口RAM(两个端口均可读写)、单口ROM(只有一个读端口)、双口ROM(只有两个读端口),这些存储器本质上都是由FPGA内部的RAM生成。在此章节中需要使用到单口ROM IP和,下面将介绍使用Block Memory Generator IP生成单口ROM IP核的步骤。
- 开IP配置界面

- 基础配置

- A端口配置

- 其他配置

- 生成IP核

乘法IP核
- 打开乘法器IP配置页面

- 配置乘法IP核

- 生成IP核

DDS信号发生器和混频
通过Verilog实现DDS信号发生器,然后将DDS信号发生器例化两次输出不同频率,然后对输出频率进行混频。
- 按照上面的步骤配置ROM IP核,并用COE文件进行初始化,这里选用cos_data.coe初始化ROM IP
- 按照上面的步骤配置乘法IP核
- 编写DDS信号发生器代码
c
`timescale 1ns / 1ps
module dds(
input wire sclk ,
input wire resetn ,
input wire [31:0] fcw ,//频率控制字或相位步进
output wire [7:0] cos_data //dds输出信号
);
//相位累加器
reg [31:0] phase_accum = 32'd0;
//截断后的相位累加器
wire [6:0] phase_key;
//相位累加器递增
always @(posedge sclk) begin
if(!resetn)
phase_accum <= 32'd0;
else
phase_accum <= phase_accum + fcw;
end
//将相位截断
assign phase_key = carrier_phase_accum[31:25];
//例化ROM模块
single_rom_8x128 u_single_rom_8x128_inst0 (
.clka(sclk), // input wire clka
.addra(phase_key), // input wire [6 : 0] addra
.douta(cos_data) // output wire [7 : 0] douta
);
endmodule
- 编写混频代码
c
`timescale 1ns / 1ps
module mixer(
input wire sclk ,
input wire resetn ,
input wire [7:0] signal_a ,//输入信号A
input wire [7:0] signal_b ,//输入信号B
output wire [15:0] mixer_out //混频输出
);
mult_i8xi8 u_mult_i8xi8_inst0 (
.CLK(sclk), // input wire CLK
.A(signal_a), // input wire [7 : 0] A
.B(signal_b), // input wire [7 : 0] B
.P(mixer_out) // output wire [15 : 0] P
);
endmodule
- 编写顶层代码
c
`timescale 1ns / 1ps
module mixer_top(
input wire sclk ,
input wire resetn ,
input wire [31:0] fcw_a ,//DDS A频率控制器
input wire [31:0] fcw_b ,//DDS B频率控制器
output wire [7:0] cos_a ,//DDS A输出
output wire [7:0] cos_b ,//DDS B输出
output wire [15:0] mixer_out //混频输出
);
//DDS信号发生器A
dds u_dds_inst0(
.sclk (sclk ),
.resetn (resetn ),
.fcw (fcw_a ),//频率控制字或相位步进
.cos_data (cos_a ) //dds输出信号
);
//DDS信号发生器B
dds u_dds_inst1(
.sclk (sclk ),
.resetn (resetn ),
.fcw (fcw_b ),//频率控制字或相位步进
.cos_data (cos_b ) //dds输出信号
);
//混频
mixer u_mixer_inst0(
.sclk (sclk ),
.resetn (resetn ),
.signal_a (cos_a ),//输入信号A
.signal_b (cos_b ),//输入信号B
.mixer_out (mixer_out) //混频输出
);
endmodule
- 编写仿真激励文件
c
`timescale 1ns / 1ps
module tb_mixer_top( );
reg sclk ;
reg resetn ;
reg [31:0] fcw_a ;//DDS A频率控制器
reg [31:0] fcw_b ;//DDS B频率控制器
wire [7:0] cos_a ;//DDS A输出
wire [7:0] cos_b ;//DDS B输出
wire [15:0] mixer_out ;//混频输出
//产生激励时钟,50M
initial begin
sclk = 1'b0;
end
always #10 sclk = ~sclk;
//产生复位信号
initial begin
resetn = 1'b0;
repeat(100) @(posedge sclk);
resetn = 1'b1;
end
//设置频率控制字
initial begin
fcw_a = 32'd0;
fcw_b = 32'd0;
repeat(200) @(posedge sclk);
fcw_a = 32'd42949673; //0.5M,fcw_a = (0.5M * 4294967296)/50M = 42949672.96
fcw_b = 32'd257698038; //3.0M,fcw_b = (3.0M * 4294967296)/50M = 257698037.76
end
//例化仿真模块
mixer_top tb_mixer_top_inst0(
.sclk (sclk ),
.resetn (resetn ),
.fcw_a (fcw_a ),//DDS A频率控制器
.fcw_b (fcw_b ),//DDS B频率控制器
.cos_a (cos_a ),//DDS A输出
.cos_b (cos_b ),//DDS B输出
.mixer_out (mixer_out) //混频输出
);
endmodule
- 进行仿真测试,仿真输出结果如下:
