DDS(Direct Digital Synthesizer)直接数字式频率合成器,是一种新型频率合成技术,具有低成本、低功耗、高分辨率、相对带宽大和频率转换时间短等优点。较容易实现频率、相位以及幅度的数控调制,广泛应用在电信与电子仪器和通信领域。波形发生器是一种数据信号发生器,在调试硬件时,常常需要加入一些信号,以观察电路工作是否正常。加入的信号有:正弦波、三角波、方波和任意波形等。
其中设计方案如下图所示,我们将所需要的信号存在ROM中,当需要信号的时候,FPGA从ROM中读取信号,最后经过DA芯片转换,得到我们所需要的信号。在本文中,只介绍DDS形成数字信号的仿真过程,所用的FPGA为Xilinx Zynq7020,开发环境为Vivado 2015.4,以及matlab 2017。
1.建立vivado工程
2.例化IP(dds_ddr_0)
`timescale 1ns / 1ps
module dds_addr (clk, rst_n, addr_out);
input clk, rst_n; //系统时钟复位
output [7:0] addr_out; //输出的地址,对应到ROM内的数据
parameter N = 32;
parameter PWORD = 128; //相位控制字 (x/360)*256
parameter FWORD = 429497; //频率控制字F_out=B*(F_clk/2**32),fword=B
//5KHZ
reg [N-1:0] addr; //32位累加器
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
addr <= 0;
end
else
begin
/*每隔fword的大小,输出一位地址,若频率控制字FWORD等于2,那么地址计数器输出的就依次是0,2,4.....*/
addr <= addr + FWORD;
end
end
/*将累加器的地址的高八位赋值给输出的地址(ROM的地址*/
assign addr_out = addr[N-1:N-8] + PWORD;
endmodule
将此Verilog代码例化为IP核,添加至工程中。(在vivado中例化IP的教程可以参考: https://blog.csdn.net/FPGADesigner/article/details/75309278)
3.添加Vivado自带ROM的IP核(rom)
这里的rom.coe文件是存放在rom中的波形信息,用matlab生成,以下为matlab中生成正弦信号的程序。
clear
clc
n = 0:255 ;
yn = sin(2*pi/256*n) ;
yn = round((yn+1)*127);
plot(n,yn);
fid = fopen('rom.coe','wt');
fprintf(fid,'memory_initialization_radix = 10;\nmemory_initialization_vector = ');
for i = 1 : 256
if mod(i-1,8) == 0
fprintf(fid,'\n');
end
fprintf(fid,'%4d,',yn(i));
end
题外话:一些原始数据需要满足断电不丢失的的需求,要存储在ROM里,而在FPGA中,一般存入ROM中的文件格式有.mif、.hex、.coe这三种格式,.hex、.coe格式一般是用于Xilinx版本,.mif、.hex格式一般用于altera版本的ROM。
4.新建主程序(dds_main)
在新建的dds_main文件中添加以下代码:
`timescale 1ns / 1ps
module dds_main (clk, rst_n, q);
input clk, rst_n; //系统时钟复位
output [7:0] q; //输出波形数据
wire [7:0] addr_out; //8位地址,对应到ROM内的数据
/*****相位累加器模块*****/
dds_addr_0 dds_addr_inst (
.clk(clk), // input wire clk
.rst_n(rst_n), // input wire rst_n
.addr_out(addr_out) // output wire [7 : 0] addr_out
);
/*****波形数据模块*****/
rom rom_inst (
.clka(clk), // input wire clka
.addra(addr_out), // input wire [7 : 0] addra
.douta(q) // output wire [7 : 0] douta
);
endmodule
其中添加例化的IP进工程的方法为:
(1)在IP_Source中打开dds_addr_0.veo文件。
(2)复制dds_addr.veo代码中标红的部分。
(3)在主程序中粘贴代码,并修改名称和信号名称。
5.添加testbench文件
同上面方法一样建立dds_testbench文件,代码如下所示:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
module dds_testbench;
reg clk, rst_n;
wire [7:0] q;
initial begin
clk = 1;
rst_n = 0;
#200.1
rst_n = 1;
#50_000_000 $stop;
end
dds_main dds_dut(
.clk(clk),
.rst_n(rst_n),
.q(q)
);
always #10 clk = ~clk;
endmodule
最终工程如下所示:
6.仿真
配置没有问题就可以点击“Run Simulation”,开始仿真。
最终仿真结果如下:
注意:上面显示数字为十六进制,而在上面用matlab生成的.coe文件为十进制。若想生成.MIF文件的小伙伴可以用一个小工具生成,很方便,链接为:https://pan.baidu.com/s/1I7IffSSBXQQ4Bxziylw5vw)
借鉴了博客上很多小伙伴的资料,在这里一并感谢!
工程文件地址:https://github.com/EdwardBao1006/DA_ZYNQ