使用软件: Vivado
DDS信号发生器
基本原理
DDS基本原理
[百度] DDS信号发生器采用直接数字频率合成(Direct Digital Synthesis,简称DDS)技术,把信号发生器的频率稳定度、准确度提高到与基准频率相同的水平,并且可以在很宽的频率范围内进行精细的频率调节。采用这种方法设计的信号源可工作于调制状态,可对输出电平进行调节,也可输出各种波形。
DDS基本结构图:
由图可以看出,DDS 主要由相位累加器、相位调制器、波形数据表以及 D/A转换器构成。
其中相位累加器由 N 位加法器与 N 位寄存器构成。每个时钟周期的时钟上升沿,加法器就将频率控制字与累加寄存器输出的相位数据相加,相加的结果又反馈至累加寄存器的数据输入端,以使加法器在下一个时钟脉冲的作用下继续与频率控制字相加。这样,相位累加器在时钟作用下,不断对频率控制字进行线性相位累加。即在每一个时钟脉冲输入时,相位累加器便把频率控制字累加一次。
相位累加器输出的数据就是合成信号的相位。相位累加器的溢出频率,就是DDS 输出的信号频率,相位累加器输出的数据,作为波形存储器的相位采样地址,这样就可以把存储在波形存储器里的波形采样值经查表找出,完成相位到幅度的转换。波形存储器的输出数据送到 D/A 转换器,由 D/A 转换器将数字信号转换成模拟信号输出。
DDS信号流程示意图如下:
这里相位累加器位数为 N 位(N 的取值范围实际应用中一般为 24~32),相
当于把正弦信号在相位上的精度定义为 N 位,所以其分辨率为1/2N
若DDS的时钟频率为Fcl,频率控制字fword=B,则输出频率Fout=BxFclk/2N
因此理论上由以上三个参数就可以得出任意的Fout输出频率。且可得出频率分辨率由时钟频率和累加器的位数决定的结论。当参考时钟频率越高,累加器位数越高,输出频率分辨率就越高。
为了合理控制ROM的容量,此处选取ROM查询的地址时,可以采用截断式,即只取32位累加器的高M位。这里相位寄存器输出的位数一般取10~16位。
DDS模块
在本设计中参考时钟Fclk 频率为50 MHz,相位累加器位数 N 取32 位,频率控制字位数 M 取 12 位。经过以上的分析,可以得出 DDS 模块的端口模块如下图所示:
端口功能描述:
ROM IP的使用
单击 IP Catalog,在右边窗口 Search 位置输入rom,在Memories &Storage Elements 下可以看到有两个与 ROM 相关的 IP,一个是 Distributed Memory Generator,另一个是Block Memory Generator,先简单说说两个的差别,两者最主要的差别是生成的 Core所占用的 FPGA 资源不一样,从 Distributed Memory Generator 生成的 ROM/RAM Core 占用的资源是 LUT(查找表,查找表本质就是一个小的 RAM);从 Block Memory Generator 生成的 ROM/RAM Core 占用的资源是 Block Memory(嵌入式的硬件 RAM)
在 IP Catalog 窗口选择 Distributed Memory Generator 并双击。
进入IP配置界面,如下。
(1)Documentation:IP 相关文档入口,点击后出现如下内容
(2)IP Location:生成 IP 的存放路径,可以通过点击 … 设置更换存放路径,默认是存放在工程路径下的 bin_counter.srcs\ sources_1\ ip,我这里就保持默认。
(3)Switch to Default:点击后所有的设置恢复到默认值
(4)Component Name:设置生成 IP Core 的名称,我这里将名字设置为 dist_mem_rom_ip;
(5)Depth:设置 ROM 深度,我这里设置 256;
(6)Data Width:设置数据位宽,我这里设置 8;
(7)Memory Type:选择存储类型,这里选择 ROM
(8)Input Options:设置是否对输入的地址增加一级寄存器,这里设置选择 Registered,对输入的地址增加一级寄存器(也可以选择 Non Registered,不增加寄存器)。
(9)Output Option:设置是否对输出的地址增加一级寄存器,这里设置选择 Both,对输出数据信号 spo 和增加一级寄存器后的输出数据信号 qspo 都从端口引出。下面还有个Single Port Output CE 可选配,当勾选后,ROM Core 就会多出一个 qspo_ce 的端口,这个信号是用来控制使能输出寄存器的时钟。这里没有用这个功能,不勾选该项。
(10)Pipeline Stages:流水线级数,在生成 ROM 模式下,不可配。
(11)添加 COE 文件位置,可以通过选择路径添加已有的 COE文件,软件会通过 COE 文件生成 ROM 初始化文件 MIF 文件。关于如何创建 COE 文件看下一步骤。
(12) 编辑按钮,当(11)中未加载 COE 文件时,点击这里按钮,会弹出提示框,点击YES 可进入到创建 COE 文件的流程,这里点击 YES。
(13)COE Options:设置 ROM 中初始化数据没有覆盖的区域的数值,比如 ROM 深度 256,添加的 COE 文件中仅初始化了200 个数据,这里就是统一设置剩下的 56 个数据的数值。
(14)Reset Options:添加输出寄存器复位信号,可添加同步复位或异步复位,这里就不勾选添加复位信号。
(15)ce overrides 设置
CE Overrides Sync Controls:在选择了输出寄存器同步复位和时钟使能才能启用,启用后,同步控制由时钟使能限定。
Sync Controls Overrides CE:在选择了输出寄存器同步复位和时钟使能才能启用,
启用后,同步控制信号会工作,不受时钟使能限定。
前面未勾选时钟使能信号 CE,这里就不可设置,保持默认即可。ROM IP 的各种参数设置完后点击 OK。
选择 COE 文件存放的路径位置和设置好 COE 文件名,点击 Save。
出现如上图弹窗,点击 Generate。
等 IP 生成完成后,出现如下图弹窗,点击 OK。
波形数据存储器
为了能产生相应的波形,我们需要创建 ROM 来分别存储正弦波、方波、三角波的波形数据。其中单端口的 ROM 主要配置数据如下图所示,其初始化文件选为已经生成的相应波形的 coe 文件。此处 coe 位宽为 14,深度为 4096。
关于生成 coe 文件需要注意的是,由于本次设计使用的是 Xilinx 出品的芯
片,所以在使用 Mif 精灵生成该文件时,应选择 Xilinx,同时 maxi 中数据应改
为 16383,其余设置如下图所示:
Mif精灵文件生成器网站:http://www.corecourse.cn/forum.php?mod=viewthread&tid=28374
选择sine wave生成正弦波数据。
然后载入生成的正弦波数据如下所示:
点击ok,保存生成即可。
代码及仿真
verilog代码
`timescale 1ns / 1ps
//
// Module Name: DDS_Moudle
//
//
module DDS_Moudle(
clk,
Reset_n,
Fword_i, //频率控制字
Pword, //相位控制字
Data //输出
);
input clk;
input Reset_n;
input[31:0] Fword_i;
input[11:0] Pword;
output[13:0] Data; //输出14位?4096
//频率控制字同步寄存器
reg[31:0] Fword_r;
always@(posedge clk) begin
Fword_r <= Fword_i;
end
//相位控制字同步寄存器
reg[11:0] Pword_r;
always@(posedge clk)
Pword_r <= Pword;
//相位累加器
reg[31:0] Freq_ACC;
always@(posedge clk or negedge Reset_n) begin
if(!Reset_n)
Freq_ACC <= 0;
else
Freq_ACC <= Fword_r + Freq_ACC;
end
//相位累加器与相位控制字的结果输出
//wire[31:0] Freq_ACC_out;
//assign Freq_ACC_out <= Freq_ACC+Pword_r;
//波形数据表地址
wire[11:0] Rom_Addr;
/*为什么是取ROM高12位地址
因为频率控制器就是12位,所以取高12位 Freq_ACC_out[31:20]
B=2^20
*/
assign Rom_Addr = Freq_ACC[31:20]+Pword_r;
rom rom(
.clka(clk),
.addra(Rom_Addr),
.douta(Data)
);
endmodule
仿真代码(Fword有值,Pword=0时)
`timescale 1ns / 1ps
//
// Module Name: sim_DDS_Moudle
// Revision 0.01 - File Created
// Additional Comments:
//
//
module sim_DDS_Moudle();
reg clk;
reg reset_n;
reg[31:0] Fword;
reg[11:0] Pword;
wire[13:0] Data;
DDS_Moudle uut(clk,reset_n,Fword,Pword,Data);
initial clk=1;
always #10 clk=~clk; //时钟周期20ns,仿真50M Hz
initial begin
reset_n=0;
Fword=65536;
Pword=0;
#201;
reset_n=1;
#2000000;
Fword=65536*1024;
#2000000;
Fword=65536*32;
#2000000;
$stop;
end
endmodule
仿真结果及分析
点击下图按钮,获得10ms长的仿真结果
鼠标右键Data,找到[Waveform style],选择[Analog],查看模拟波形
经过调整缩放之后波形如下所示:
计算周期:
找到波形最高点然后点击Add marker键
找到另一个最高点,点击Add marker键
由上图可以看到,此时周期为1310.72μs
可以由此算出此时的频率:762.9394513125 Hz
从代码中可以看到此时Fword为65536=216
根据公式:Fout=BxFclk/2N
B=Fword=65536=216
Fclk = 50M
N=32
所以Fout=Fclk/216=762.939453125
同理可以算出Fword为其他值的时候的Fout输出值,与预期结果一致。
增加Pword(相位控制字之后)仿真代码
因为生成正弦信号数据总数为4096个,Pword=1024是它的1/4,与Pword=0相差90度;Pword=2048与Pword=0相差180度。
令PwordA=0;PwordB为1024和2048,从仿真结果中查看相位变化
`timescale 1ns / 1ps
//
// Create Date: 2022/10/16 19:06:41
// Design Name:
// Module Name: sim_DDS_Moudle
// Revision 0.01 - File Created
// Additional Comments:
//
//
module sim_DDS_Moudle();
reg clk;
reg reset_n;
reg[31:0] FwordA,FwordB;
reg[11:0] PwordA,PwordB;
wire[13:0] DataA,DataB;
DDS_Moudle DDS_ModuleA(clk,reset_n,FwordA,PwordA,DataA);
DDS_Moudle DDS_Moduleb(clk,reset_n,FwordB,PwordB,DataB);
initial clk=1;
always #10 clk=~clk; //时钟周期20ns,仿真50M Hz
initial begin
reset_n=0;
FwordA=65536;
PwordA=0;
FwordB=65536;
PwordB=1024;//因为数据一共4096个,取1/4个,相位相差90度
#201;
reset_n=1;
#5000000;
FwordA=65536*1024;
FwordB=65536*1024;
PwordA=0;
PwordB=2048; //与A相位相差180度
#1000000;
$stop;
end
endmodule
运行结果
从下图中可以看出前5ms结果中,DataA和DataB波形明显相差90度
从下图中可以看出前5ms结果中,DataA和DataB波形明显相差180度