本文主要参考https://blog.csdn.net/syyzuiqiang/article/details/118103211?spm=1001.2014.3001.5501一文,在国产的紫光FPGA上实现DDS,使用的EDA软件是紫光PDS,开发板是国产紫光同创PGL22G-6MBG324(软件下载地址PDS Lite软件-紫光同创 (pangomicro.com),该版本为试用版)
一、下载PDS、新建工程添加源文件
(1)首先在紫光的官网上下载PDS,安装完成后打开软件
选择New Project新建工程
设置工程名及保存目录,创建子目录然后点击Next
选择RTL project 点击Next
进入添加设计文件界面,这里我们先不添加,按照上图勾选,然后点击Next
进入添加IP界面,按照上图勾选,然后点击Next
进入添加约束文件界面,按照上图勾选,然后点击Next
根据自己使用的板子选择对应的芯片,这里我使用的是Logos系列的PGL22G-6MBG324,然后点击next,finish工程建立完成。
(2)添加设计文件
右键Design,选择Add Source
选择添加或创建设计文件 Next
选择创建文件 Next
起好名字 OK,到这里就完成了新建工程与设计文件的添加。
二、DDS程序的编写
接下来就是程序的编写,由于我用的这块板子上没有拨码开关,所以使用的是按键切换波形和频率,如果板子上有拨码开关的话建议参考本文开头引用的文章的程序
(1)首先是顶层设计文件
module dds_1
(clk,
rst,
key_1, //按键1切换频率
key_2, //按键2切换波形
da_clk, //输出DAC时钟
data); //11位数据输出
input clk;
input rst;
input key_1;
input key_2;
output da_clk;
output reg [11:0] data;
reg [2:0] mode = 2'b0;
reg key_2_0 = 1;
reg key_2_1 = 1, key_2_enen = 1;
wire [11:0] sin_wave_douta;
wire [11:0] saw_wave_douta;
wire [11:0] sawtooth_wave_douta;
wire [11:0] square_wave_douta;
wire [11:0] addra;
wire add_mode;
wire end_mode;
wire non;
//按键切换波形模式,低电平有效
assign da_clk = clk;
always @(posedge clk or negedge rst)
begin
if(!rst) begin
key_2_0 <= 0;
key_2_1 <= 0;
end
else begin
key_2_1 <= key_2_enen;
key_2_0 <= key_2_1;
end
end
assign key_2_en = key_2_0&&(~key_2_1);//检测到按键按下(下降沿)
//按键消抖,按下延时20ms后检测
reg [0:21] del_cnt = 0;
wire add_del;
wire end_del;
always @(posedge clk or negedge rst)
begin
if(!rst) begin //低电平有效
del_cnt<=0;
end
else if(add_del) begin
if(end_del)
del_cnt<=0;
else
del_cnt<=del_cnt+1;
end
else
del_cnt<=0;
end
assign add_del = ~key_2;
assign end_del = add_del && del_cnt==1000000-1;
always @(posedge clk )
begin
if (end_del)
key_2_enen<=0;
else if (key_2)
key_2_enen<=1;
end
//波形控制
always @(posedge clk or negedge rst)
begin
if(!rst) begin //低电平有效
mode<=2'b0;
end
else if(add_mode) begin
if(end_mode)
mode<=2'b0;
else
mode<=mode+1;
end
end
assign add_mode=key_2_en; //高电平有效
assign end_mode=add_mode&&mode==3;
//例化IP
assign non = 0;
sin u_1
(.addr(addra),
.clk(clk),
.rd_data(sin_wave_douta),
.rst(non)
);
saw u_2
(.addr(addra),
.clk(clk),
.rd_data(saw_wave_douta),
.rst(non)
);
sawtooth u_3
(.addr(addra),
.clk(clk),
.rd_data(sawtooth_wave_douta),
.rst(non)
);
square u_4
(.addr(addra),
.clk(clk),
.rd_data(square_wave_douta),
.rst(non)
);
count u_0
(.clk_c(clk),
.rst_c(rst),
.key_1(key_1),
.q(addra)
);
//按键切换波形
always @(posedge clk)begin
case(mode)
1 : data = sin_wave_douta;
3 : data = saw_wave_douta;
2 : data = sawtooth_wave_douta;
0 : data = square_wave_douta;
default : data = square_wave_douta;
endcase
end
endmodule
(2)然后是相位累加器模块
module count
(clk_c,
rst_c,
key_1,
q);
input clk_c;
input rst_c;
input key_1;
output [9:0] q;
reg [31:0] fw = 8950;
reg [31:0] cnt = 32'b0;
reg key_1_0 = 1;
reg key_1_1 = 1,key_1_enen = 1;
reg [0:1] fwcnt = 0;
wire add_fwcnt;
wire end_fwcnt;
wire key_1;
//切换频率
always @(posedge clk_c or negedge rst_c)
begin
if(!rst_c) begin
key_1_0 <= 0;
key_1_1 <= 0;
end
else begin
key_1_1 <= key_1_enen;
key_1_0 <= key_1_1;
end
end
assign key_1_en = key_1_0&&(~key_1_1);//低电平有效
reg [0:21] del_cnt = 0;
wire add_del;
wire end_del;
always @(posedge clk_c or negedge rst_c)
begin
if(!rst_c) begin //低电平有效
del_cnt<=0;
end
else if(add_del) begin
if(end_del)
del_cnt<=0;
else
del_cnt<=del_cnt+1;
end
else
del_cnt<=0;
end
assign add_del = ~key_1;
assign end_del = add_del && del_cnt==1000000-1;
always @(posedge clk_c )
begin
if (end_del)
key_1_enen<=0;
else if (key_1)
key_1_enen<=1;
end
always @(posedge clk_c or negedge rst_c) //高电平有效
begin
if(!rst_c) begin //低电平有效
fwcnt<=0;
end
else if(add_fwcnt) begin
if(end_fwcnt)
fwcnt<=0;
else
fwcnt<=fwcnt+1;
end
end
assign add_fwcnt=key_1_en; //高电平有效
assign end_fwcnt=add_fwcnt&&fwcnt==3;
always @(posedge clk_c)
begin
case(fwcnt)
0 : fw = 8590; //100HZ
1 : fw = 42949;//500HZ
2 : fw = 128849;//1500HZ
3 : fw = 300647;//3500HZ
default : fw = 8590;
endcase
end
always @(posedge clk_c or negedge rst_c)
begin
if(!rst_c) begin //低电平有效
cnt<=0;
end
else
cnt<=cnt+fw;
end
assign q = cnt[31:22];
endmodule
(3)接下来是导入ROM
在工具栏找到IP或者可以通过右键设计文件添加
选中ROM,起好名字,点击Customize
在这里进行IP核的配置,在这里需要添加IP核的初始化文件sin.dat。PDS的初始化文件支持十六进制和二进制,格式相对比较简单,只要把十六进制或二进制数据写入到.dat文件中即可,这里我用的是matlab生成的初始化文件,具体方法如下:
首先是代码,用matlab生成正弦波(这里的地址深度为10即共有1024个数据,数据宽度是12即正弦波峰处为4095)
x = linspace(0,6.28,1024); %在0和2pi间取1024个点
y2 = sin (x)+1;
y3 = sawtooth (x,0.5)+1;
y4 = sawtooth (x,1)+1;
y5 = square (x)+1;
y2 =round (y2 * 2047);
y3 = round(y3 * 2047);
y4 = round(y4 * 2047);
y5 = round(y5 * 2047);
fid = fopen('c:/sin.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y2);
fclose(fid);
fid = fopen('c:/sawtooth.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y3);
fclose(fid);
fid = fopen('c:/saw.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y4);
fclose(fid);
fid = fopen('c:/square.txt','wt'); % 生成TXT文件
fprintf(fid,'%x\n',y5);
fclose(fid);
运行过后就会在c盘中生成四种波形数据的.txt文件,这时只需要将后缀改成.dat即可,然后导入到初始化文件
点击Generate,生成IP核
三、编译,综合,布局布线及添加约束文件
(1)编译,综合
(2)添加约束文件
PDS的约束文件可以通过手写代码或者图形界面来配置,这里介绍图形界面配置引脚的方法
①首先在工具栏上找到User Constraint Editor
②选中Device,点击I/O
③具体引脚分配情况如下
也可添加代码
define_attribute {p:data[11]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[11]} {PAP_IO_LOC} {P11}
define_attribute {p:data[11]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[11]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[11]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[11]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[10]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[10]} {PAP_IO_LOC} {T13}
define_attribute {p:data[10]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[10]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[10]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[10]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[9]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[9]} {PAP_IO_LOC} {R13}
define_attribute {p:data[9]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[9]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[9]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[9]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[8]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[8]} {PAP_IO_LOC} {P13}
define_attribute {p:data[8]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[8]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[8]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[8]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[7]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[7]} {PAP_IO_LOC} {P14}
define_attribute {p:data[7]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[7]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[7]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[7]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[6]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[6]} {PAP_IO_LOC} {R15}
define_attribute {p:data[6]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[6]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[6]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[6]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[5]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[5]} {PAP_IO_LOC} {R14}
define_attribute {p:data[5]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[5]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[5]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[5]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[4]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[4]} {PAP_IO_LOC} {T16}
define_attribute {p:data[4]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[4]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[4]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[4]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[3]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[3]} {PAP_IO_LOC} {R16}
define_attribute {p:data[3]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[3]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[3]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[3]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[2]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[2]} {PAP_IO_LOC} {U16}
define_attribute {p:data[2]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[2]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[2]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[2]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[1]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[1]} {PAP_IO_LOC} {V16}
define_attribute {p:data[1]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[1]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[1]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[1]} {PAP_IO_SLEW} {FAST}
define_attribute {p:data[0]} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:data[0]} {PAP_IO_LOC} {V18}
define_attribute {p:data[0]} {PAP_IO_VCCIO} {3.3}
define_attribute {p:data[0]} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:data[0]} {PAP_IO_DRIVE} {4}
define_attribute {p:data[0]} {PAP_IO_SLEW} {FAST}
define_attribute {p:da_clk} {PAP_IO_DIRECTION} {OUTPUT}
define_attribute {p:da_clk} {PAP_IO_LOC} {P12}
define_attribute {p:da_clk} {PAP_IO_VCCIO} {3.3}
define_attribute {p:da_clk} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:da_clk} {PAP_IO_DRIVE} {4}
define_attribute {p:da_clk} {PAP_IO_SLEW} {FAST}
define_attribute {p:clk} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:clk} {PAP_IO_LOC} {B5}
define_attribute {p:clk} {PAP_IO_VCCIO} {3.3}
define_attribute {p:clk} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:key_1} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:key_1} {PAP_IO_LOC} {V12}
define_attribute {p:key_1} {PAP_IO_VCCIO} {3.3}
define_attribute {p:key_1} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:key_2} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:key_2} {PAP_IO_LOC} {P17}
define_attribute {p:key_2} {PAP_IO_VCCIO} {3.3}
define_attribute {p:key_2} {PAP_IO_STANDARD} {LVCMOS33}
define_attribute {p:rst} {PAP_IO_DIRECTION} {INPUT}
define_attribute {p:rst} {PAP_IO_LOC} {U12}
define_attribute {p:rst} {PAP_IO_VCCIO} {3.3}
define_attribute {p:rst} {PAP_IO_STANDARD} {LVCMOS33}
#define_attribute {p:data[11]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[10]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[9]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[8]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[7]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[6]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[5]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[4]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[3]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[2]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[1]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[0]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:da_clk} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:clk} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:key_1} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:key_2} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:rst} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[11]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[10]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[9]} {PAP_IO_REGISTER} {TRUE}
#define_attribute {p:data[8]} {PAP_IO_REGISTER} {TRUE}
define_attribute {p:key_1} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:key_1} {PAP_IO_HYS_DRIVE_MODE} {NOHYS}
define_attribute {p:key_2} {PAP_IO_PULLUP} {TRUE}
define_attribute {p:key_2} {PAP_IO_HYS_DRIVE_MODE} {NOHYS}
(3)布局布线
四、生成bit流并下载到板子中
点击Generate Bitstream,生成bit流文件
接下来将bit流文件下载到板子中
(1)首先将板子接好,点击Configuration
(2)点击scan device,连接开发板
(3)选择bit流文件,点击open
(4)右键选择program,进行程序的下载
以上下载bit流文件的方式掉电之后会消失,如果要将程序固化到flash中还需进行以下操作
2.然后弹出如下界面,这里要根据硬件的 flash 型号来选择 flash 的厂家和设备型号,开发板用到的是 WINBOND 的 W25Q128Q。Flash Read Mode 选择 SPI X4 然后选择要转换的 sbit 文件, 点击 OK 即可转换
3.右键,选择Scan Outer Flash,选中刚刚生成的.sfc文件open
4.右键Outer Flash,program下载完成
五、结果展示
(1)100Hz正弦波
(2)100Hz方波
(3)100Hz三角波
(4)100Hz锯齿波
(5)3.5kHz正弦波
(6)3.5kHz方波
(7)3.5kHz三角波
(8)3.5kHz锯齿波