本系列文章详细介绍了DDS在Vivado中的仿真实现、综合、布线、生成bit流以及上板的过程。
(一)初始配置
1)先下载Vivado软件,本人使用的是2019.1版本的Vivado,使用其他版本Vivado的读者也可以类比操作,具体的下载安装过程很多文章都有,就不在本文中详细介绍了。
2)打开Vivado软件,点击create project,新建工程。
3)点击next
4)给文件命名,并保存到相应的路径,最好不要保存到C盘,设置好后直接点击next
5)之后如图中勾选,点击next
6)因为本人使用的是Zybo Z7系列ZYNQ7020开发板,因此相关配置如下图所示,读者可以根据自己使用的开发板进行相应的配置,配置好后点击next
本人使用的开发板Zybo Z7系列ZYNQ7020
对应的相关硬件配置
配置好之后,点击finish完成配置
(二)DDS程序编写
1)首先点击add source
2)建立design source文件,如图所示,点击next
3)点击Creat File
4)文件命名完点击ok
5)点击finish
6)之后点击OK,弹窗点击Yes就行
7)建立好设计文件直接编写设计文件就行,这里直接上代码
module DDS(
input clk,
input rst_n,
input [1:0] Mode_Sel,
input [1:0] fword_sel,
output DA_clk,
output reg [11:0] Data
);
reg [31:0] r_fword;
reg [11:0] r_pword;
reg [31:0] cnt;
wire [11:0] rom_addr;
wire[11:0] rom_addr1;
wire[11:0] rom_addr2;
wire[11:0] rom_addr3;
wire[11:0] rom_addr4;
always@(posedge clk or negedge rst_n)
begin
if(rst_n) begin
r_pword <= 0;
end
else begin
r_pword <= 2048; //这里时相位控制,单通道设计可以忽略
end
end
always@(posedge clk or negedge rst_n)
if(rst_n)
cnt <= 32'd0;
else
cnt <= cnt +r_fword;
assign rom_addr = cnt[31:20] + r_pword;
assign rom_addr1 = cnt[31:20] + r_pword;
assign rom_addr2 = cnt[31:20] + r_pword;
assign rom_addr3 = cnt[31:20] + r_pword;
assign rom_addr4 = cnt[31:20] + r_pword;
assign DA_clk=clk;
//例化不同的波形IP核
wire [11:0]Data_sin,Data_sin1,Data_square,Data_triangular,Data_sawtooth_wave;
sine_wave sine_DDS(
.clka(clk), // input wire clka
.addra(rom_addr), // input wire [11 : 0] addra
.douta(Data_sin) // output wire [11 : 0] douta
);//正弦
triangular_wave triangular_DDS(
.clka(clk), // input wire clka
.addra(rom_addr1), // input wire [11 : 0] addra
.douta(Data_triangular) // output wire [11 : 0] douta
);//三角
square_wave square_DDS(
.clka(clk), // input wire clka
.addra(rom_addr2), // input wire [11 : 0] addra
.douta(Data_square) // output wire [11 : 0] douta
);//方波
sawtooth_wave sawtooth_wave_DDS(
.clka(clk), // input wire clka
.addra(rom_addr3), // input wire [11 : 0] addra
.douta(Data_sawtooth_wave) // output wire [11 : 0] douta
);//锯齿
sine1_DDS sine_DDS1(
.clka(clk), // input wire clka
.addra(rom_addr4), // input wire [11 : 0] addra
.douta(Data_sin1) // output wire [11 : 0] douta
);//幅度改变的正弦
//矩阵混合模块
wire signed [11:0] a11, a12, a21, a22;
assign a11 = 12'h300;
assign a12 = 12'h600;
assign a21 = 12'h100;
assign a22 = 12'h800;
reg signed [23:0] a11s1;
reg signed [23:0] a12s2;
reg signed [23:0] a21s1;
reg signed [23:0] a22s2;
always @(posedge clk or negedge rst_n)begin
if(rst_n)begin
a11s1 <= 'b0;
a12s2 <= 'b0;
a21s1 <= 'b0;
a22s2 <= 'b0;
end
else begin//混合矩阵混合
a11s1 <= a11 * Data_sin;
a12s2 <= a12 * Data_square;
a21s1 <= a21 * Data_sin;
a22s2 <= a22 * Data_square;
end
end
reg signed [11:0] wave1;
reg signed [11:0] wave2;
always@(posedge clk or negedge rst_n)begin
if(rst_n)begin
wave1 <= 'b0;
wave2 <= 'b0;
end
else begin
wave1 <= (a11s1 >>> 12) + (a12s2 >>> 12);
wave2 <= (a21s1 >>> 12) - (a22s2 >>> 12);
end
end
//频率的切换
always@(*)
case(fword_sel)
0:r_fword=8590;//250HZ
1:r_fword=85899;//2500HZ
2:r_fword=858993;//25KHZ
3:r_fword=8589935;//250KHZ
endcase
//波形的切换
always@(*)
case(Mode_Sel)
0:Data=Data_sin;//正弦波
1:Data=Data_square;//方波
// 1:Data=Data_sin1;//与第一个正弦波相比幅度改变的正弦波
2:Data=Data_triangular;//三角波
// 3:Data=Data_sawtooth_wave;//锯齿波
3:Data=wave1;//正弦和方波进行一定比例混合的混合波
endcase
endmodule
此代码在2020版本综合时可能会报错,具体解决方法见本系列第三篇文章。
fword的数值问题要运用公式进行计算,具体的算法可以见这篇博文DDS公式计算,本文把该博文中用到的公式及频率控制字对应数值展示如下:
其中,本设计的系统时钟位125MHZ,另一位博主用的是50MHZ,因此相同的数值下,本设计所产生的频率为上述图示的2.5倍
8)因为代码中包含例化,所以还需要在Vivado中自制ip核,自制IP核可以用matlab编写coe文件,或者直接下载一个mif精灵生成coe文件,本文使用的方法是直接下载mif精灵。本设计使用的是小梅哥团队开发的mif精灵。下载过程很简单,具体下载过程见下面链接
mif精灵下载
9)下载完成后,打开界面,选择xilinx公司,之后直接生成各种波形即可,本设计使用的位宽是12位,数据深度是4096
(这里不知道是不是有一个小bug,生成方波和三角波时幅度最大值,即maxi,不能选4096,4096以下才可以,而锯齿波和正弦波可以选4096,不然对后续导入coe文件有影响。所以读者在产生文件时幅度最好不要超过深度)
10)生成完文件之后,把文件放到工程目录下,方便进行之后的导入
(sine_wave和sine_wave1是改变了一下幅度的最大值)
11)之后回到vivado界面,进行ip核配置,首先点击图示处
12)之后搜索block,选择下面选项
13)进行命名和相关配置,配置除了图示,其余保持默认即可
这里将之前生成的的coe文件导入即可
之后一直按照默认选项选择ok即可
然后一直依葫芦画瓢将方波、三角波、正弦波、锯齿波导入即可。
注:
1、本设计导入了两个不同幅度的波形,为了做幅度调节的对比使用。
2、ip核的命名一定要与下图标注的例化处命名保持一致
3、ip核生成之后命名就无法改变了,所以一定要注意提前命名的准确性
仿真和上板的相关步骤见下一节