赞
踩
本文是在:ZYNQ学习笔记(一):基于ZYNQ7020、AN108的DDS实验(VIO可控频率字)的基础上进一步修改。
在上一个实验的基础上:
把生成DDS信号的主频由50Mhz改为100Mhz
去掉VIO模块,替换为按键控制频率字
通过按键能够切换不同的输出频率,分别为:1,3,5,10,15,20,25,30Mhz
仅有PL端(FPGA)逻辑资源的程序固化
工程下载地址:KEY_DDS
顶层代码:
`timescale 1ns / 1ps
module key_dds(
input sys_clk,
input rst_n,
input key1, //按键 key1
//DA芯片接口
output da_clk,
output [7:0] da_data, //输出给DA的数据
//AD芯片接口
input [7:0] ad_data, //AD输入数据
output ad_clk
);
wire key1_value; //key1 消抖后的按键值
wire key1_flag; //key1 消抖后的按键值的有效标志
key_debounce u_key1_debounce(
.rst_n (rst_n),
.clk (sys_clk),
.key (key1),
.key_value (key1_value),
.key_flag (key1_flag)
);
wire clk_100M;
clk_wiz_0 pll_inst
(
// Clock out ports
.clk_out1(ad_clk), // 给ad_clk 25MHz的频率
.clk_out2(clk_100M),
// Status and control signals
.reset(~rst_n), // input reset
// Clock in ports
.clk_in1(sys_clk) // input clk_in1
);
wire [23 : 0] Fword;
key_control key_control_lg(
.clk(sys_clk),
.rst_n(rst_n),
.key1_value(key1_value),
.key1_flag(key1_flag),
.Fword(Fword)
);
wire [0:0] fre_ctrl_word_en;
//output
wire [0 : 0] m_axis_data_tvalid;
wire [7 : 0] m_axis_data_tdata;
wire [0 : 0] m_axis_phase_tvalid;
wire [23 : 0] m_axis_phase_tdata;
assign fre_ctrl_word_en = 1'b1;
dds_compiler_0 u_dds_compiler_0 (
.aclk(clk_100M), // input wire aclk
.s_axis_config_tvalid(fre_ctrl_word_en), // input wire s_axis_config_tvalid
.s_axis_config_tdata(Fword), // input wire [23 : 0] s_axis_config_tdata
.m_axis_data_tvalid(m_axis_data_tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tdata(m_axis_data_tdata), // output wire [7 : 0] m_axis_data_tdata
.m_axis_phase_tvalid(m_axis_phase_tvalid), // output wire m_axis_phase_tvalid
.m_axis_phase_tdata(m_axis_phase_tdata) // output wire [23 : 0] m_axis_phase_tdata
);
//DA数据发送
da_wave_send u_da_wave_send(
.clk (clk_100M),
.rst_n (rst_n),
.rd_data (m_axis_data_tdata),
.da_clk (da_clk),
.da_data (da_data)
);
// ILA
ila_0 u_ila (
.clk(ad_clk), // input wire clk
.probe0(Fword), // input wire [23:0] probe1
.probe1(da_data), // input wire [7:0] probe2
.probe2(ad_data) // input wire [7:0] probe3
);
endmodule
相较于实验一,程序做出的改进有:增加了按键消抖模块,它处理来自key1输入的按键信号,并输出消抖后的按键值和有效标志。clk_wiz_0 模块增加了一路100Mhz频率输出,用来给DDS还有DA数据发送模块提供时钟信号,删除了VIO模块,改用按键模块控制频率字。
PLL IP核配置如下:
这里除了保留提供给ADC采样的频率外,还要增加一个提供给DDS IP核的100Mhz时钟频率输出。
ILA IP核配置如下:
分别采集频率字、DA输出以及AD输入信号。
DDS IP核配置:
这里的Parameters选项设置,当用Hardware Parameters选项时,工程并不会报错,但是下载到板子上输出的频率并不是所预设的那些,但是当我更换为System Parameters选项,所设频率输出正常
后面我查找资料发现,System Parameters选项用于配置DDS IP核在整个系统中的全局性质和性能;而Hardware Parameters选项用于配置DDS IP核内部的硬件细节和运算方式,要用于优化DDS核的行为,这些参数通常更低级,与IP核的内部运算和控制有关。
当我使用PLL锁相环将系统时钟进行倍频,然后将倍频后的时钟信号输入到DDS IP核,在这种情况下是希望配置DDS核以适应整个系统的时钟设置和性能要求,关注点是DDS核如何与系统中的时钟倍频器协同工作,以生成正确的输出信号,所以应该是通过System Parameters来确定DDS核的基本工作方式。
此外Spurious Free Dynamic Range(sfdr)是意为无杂散动态范围,SFDR是指基波强度与最大杂波或谐波的强度之比,所以SFDR值越大则说明系统的噪声水平越低,灵敏度越高。SFDR 还决定了输出的数据位宽。
因为ADDA模块的位宽限制,Ootput Width最大只能等于8,所以我们把SDRF设置为48。
Frequency Resolution是意为频率分辨率,它决定了我们可以在频率域中分辨的最小频率间隔,更高的频率分辨率意味着我们可以更精确地分辨不同的频率成分。
频率分辨率与几个参数之间的关系:
这里我们还是同实验一一样,把Phase Width设为20,那么通过公式可以计算出Frequency Resolution的值~
这些也算对实验一DDS IP核知识的补充了。
按键消抖模块的代码:
`timescale 1ns / 1ps
module key_debounce(
input clk ,
input rst_n ,
input key , //外部输入的按键值
output reg key_value , //消抖后的按键值
output reg key_flag //消抖后的按键值的效标志
);
//reg define
reg [19:0] cnt ;
reg key_reg ;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 20'd0;
key_reg <= 1'b1;
end
else begin
key_reg <= key; //将按键值延迟一拍
if(key_reg != key) begin //检测到按键状态发生变化
cnt <= 20'd100_0000; //则将计数器置为20'd100_0000
//即延时100_0000 * 20ns(1s/50MHz) = 20ms
end
else begin //如果当前按键值和前一个按键值一样,即按键没有发生变化
if(cnt > 20'd0) //则计数器递减到0
cnt <= cnt - 1'b1;
else
cnt <= 20'd0;
end
end
end
//将消抖后的最终的按键值送出去
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_value <= 1'b1;
key_flag <= 1'b0;
end
//在计数器递减到1时送出按键值
else if(cnt == 20'd1) begin
key_value <= key;
key_flag <= 1'b1;
end
else begin
key_value <= key_value;
key_flag <= 1'b0;
end
end
endmodule
代码中的 if(key_reg != key) begin ,即每检测到按键被按下或松开,就让计数器从 100_0000 开始递减,时长 20ms。在这 20ms 期间,每当有抖动产生,计数器就被重置回 100_0000,即重新开始计时 20ms。
代码中的 else if(cnt == 20’d1) begin,只有在计数器递减到 1 时,即此时计数器计时完了 20ms,才会寄存按键的值。这样,每当按键被按下或松开,20ms 内的抖动就被消除了。
频率调节的代码:
`timescale 1ns / 1ps
module key_control(
input clk,
input rst_n,
input key1_value, // 消抖后的按键值
input key1_flag, // 消抖后的按键值的有效标志
output reg [23:0] Fword
);
reg [2:0] key_count; // 用于跟踪按键按下次数
always @(posedge clk or negedge rst_n) begin
if (rst_n == 1'b0) begin
Fword <= 'h28f5; // 初始频率
key_count <= 3'b000; // 初始按键计数
end else begin
// 根据按键状态选择不同的频率
if (key1_flag && key1_value) begin
key_count <= key_count + 1'b1;
if (key_count == 3'b111) begin
key_count <= 3'b000; // 重置计数器
end
end
case (key_count)
3'b000: Fword <= 'h28f5; // 1MHz
3'b001: Fword <= 'h7ae1; // 3MHz
3'b010: Fword <= 'hcccc; // 5MHz
3'b011: Fword <= 'h19999; //10MHz
3'b100: Fword <= 'h26666; //15MHz
3'b101: Fword <= 'h33333;//20MHz
3'b110: Fword <= 'h40000;//25MHz
3'b111: Fword <= 'h4cccc;//30MHz
endcase
end
end
endmodule
因为 key1_value为消抖后的按键值, key1_flag为消抖后的按键值的有效标志 ,只有当key1_flag的值为1时, key1_value的值才为真,所以当两者的值同为1时,代表按键真的被按下,此时记录按键状态加一 ,然后再根据按键状态选择不同的频率字~从而控制输出不同的频率信号。
DA数据发送模块代码同实验一,这里不再细说~
约束代码在原来的基础添加上按键的引脚分配:
########key N15 N16##################
set_property PACKAGE_PIN N15 [get_ports key1]
set_property IOSTANDARD LVCMOS33 [get_ports key1]
最后所生成的RTL原理图:
将示波器与开发板相连,在比特文件下载到开发板后,观察示波器输出波形:
按下PL_KEY1,
继续按下观察波形频率~
不再进行一一展示
当我按下第八次,频率回到1Mhz,生成信号的频率虽有误差但基本吻合,至此就完成了设计需求的前三个任务~
上面的任务结束后,为避免每次测试都要与电脑连接就挺麻烦的~故尝试着把工程固化到板子上,
先了解一下固化的逻辑:
1.ZYNQ固化必须用到PS块。所以纯PL工程中需要新建一个block块,加入并配置PS。在 ZYNQ 中,PS 作为主器件,PL 可以看作是 PS 的一个外设,因此需要由 PS 来配置 PL。
2.配置完成后系统会新生成一个.v文件,再将自己原本要固化的代码例化进去编译生成新的bit文件。
3.在SDK里生成固化代码及BOOT.bin文件,并把BOOT.bin文件下载到SD卡里。
4.选用的固化启动方式为SD卡启动。现在开始固化的流程~
创建block块。
可自定义名称,我这里直接默认点“OK”
添加IP核
双击该模块
如图进行设置~,这里QSPI与SD卡启动我都使能了
对DDR3进行配置选择,黑金zynq 7020开发板的DDR3芯片(共计8Gbit),型号为H5TQ4G63AFR-PBC(兼容MT41J256M16RE-125)。
配置完成后,按图所示依次点击。
这里手动把两个clk连在一起,鼠标左键直接拖就行,不连会报错,编译不过去。
Ctrl+S保存,回到左边sources窗口,右击前面新建的design_1.bd块文件,选Generate Output Products
依次点击Generate~OK,完成后还是右击design_1.bd块文件,选第二个
完成后点击OK就可以~
sources窗口点击刚生成的.v文件
把上面工程的顶层代码例化部分加入这个.v文件(源代码不能够动,直接加入就可),我分为了4部分加入
`timescale 1 ps / 1 ps
module design_1_wrapper
(DDR_addr,
DDR_ba,
DDR_cas_n,
DDR_ck_n,
DDR_ck_p,
DDR_cke,
DDR_cs_n,
DDR_dm,
DDR_dq,
DDR_dqs_n,
DDR_dqs_p,
DDR_odt,
DDR_ras_n,
DDR_reset_n,
DDR_we_n,
FIXED_IO_ddr_vrn,
FIXED_IO_ddr_vrp,
FIXED_IO_mio,
FIXED_IO_ps_clk,
FIXED_IO_ps_porb,
FIXED_IO_ps_srstb,
///1///
sys_clk,
rst_n,
key1,
da_clk,
da_data,
ad_data,
ad_clk
///
);
///2///
input sys_clk;
input rst_n;
input key1;
output da_clk;
output [7:0] da_data;
input [7:0] ad_data;
output ad_clk;
///
///3///
key_debounce u_key1_debounce(
.rst_n (rst_n),
.clk (sys_clk),
.key (key1),
.key_value (key1_value),
.key_flag (key1_flag)
);
clk_wiz_0 pll_inst
(
.clk_out1(ad_clk),
.clk_out2(clk_100M),
.reset(~rst_n),
.clk_in1(sys_clk)
);
key_control key_control_lg(
.clk(sys_clk),
.rst_n(rst_n),
.key1_value(key1_value),
.key1_flag(key1_flag),
.Fword(Fword)
);
dds_compiler_0 u_dds_compiler_0 (
.aclk(clk_100M),
.s_axis_config_tvalid(fre_ctrl_word_en),
.s_axis_config_tdata(Fword),
.m_axis_data_tvalid(m_axis_data_tvalid),
.m_axis_data_tdata(m_axis_data_tdata),
.m_axis_phase_tvalid(m_axis_phase_tvalid),
.m_axis_phase_tdata(m_axis_phase_tdata)
);
da_wave_send u_da_wave_send(
.clk (clk_100M),
.rst_n (rst_n),
.rd_data (m_axis_data_tdata),
.da_clk (da_clk),
.da_data (da_data)
);
ila_0 u_ila (
.clk(ad_clk),
.probe0(Fword),
.probe1(da_data),
.probe2(ad_data)
);
///
inout [14:0]DDR_addr;
inout [2:0]DDR_ba;
inout DDR_cas_n;
inout DDR_ck_n;
inout DDR_ck_p;
inout DDR_cke;
inout DDR_cs_n;
inout [3:0]DDR_dm;
inout [31:0]DDR_dq;
inout [3:0]DDR_dqs_n;
inout [3:0]DDR_dqs_p;
inout DDR_odt;
inout DDR_ras_n;
inout DDR_reset_n;
inout DDR_we_n;
inout FIXED_IO_ddr_vrn;
inout FIXED_IO_ddr_vrp;
inout [53:0]FIXED_IO_mio;
inout FIXED_IO_ps_clk;
inout FIXED_IO_ps_porb;
inout FIXED_IO_ps_srstb;
///4///
wire key1_value;
wire key1_flag;
wire clk_100M;
wire [23 : 0] Fword;
wire [0:0] fre_ctrl_word_en;
wire [0 : 0] m_axis_data_tvalid;
wire [7 : 0] m_axis_data_tdata;
wire [0 : 0] m_axis_phase_tvalid;
wire [23 : 0] m_axis_phase_tdata;
///
wire [14:0]DDR_addr;
wire [2:0]DDR_ba;
wire DDR_cas_n;
wire DDR_ck_n;
wire DDR_ck_p;
wire DDR_cke;
wire DDR_cs_n;
wire [3:0]DDR_dm;
wire [31:0]DDR_dq;
wire [3:0]DDR_dqs_n;
wire [3:0]DDR_dqs_p;
wire DDR_odt;
wire DDR_ras_n;
wire DDR_reset_n;
wire DDR_we_n;
wire FIXED_IO_ddr_vrn;
wire FIXED_IO_ddr_vrp;
wire [53:0]FIXED_IO_mio;
wire FIXED_IO_ps_clk;
wire FIXED_IO_ps_porb;
wire FIXED_IO_ps_srstb;
design_1 design_1_i
(.DDR_addr(DDR_addr),
.DDR_ba(DDR_ba),
.DDR_cas_n(DDR_cas_n),
.DDR_ck_n(DDR_ck_n),
.DDR_ck_p(DDR_ck_p),
.DDR_cke(DDR_cke),
.DDR_cs_n(DDR_cs_n),
.DDR_dm(DDR_dm),
.DDR_dq(DDR_dq),
.DDR_dqs_n(DDR_dqs_n),
.DDR_dqs_p(DDR_dqs_p),
.DDR_odt(DDR_odt),
.DDR_ras_n(DDR_ras_n),
.DDR_reset_n(DDR_reset_n),
.DDR_we_n(DDR_we_n),
.FIXED_IO_ddr_vrn(FIXED_IO_ddr_vrn),
.FIXED_IO_ddr_vrp(FIXED_IO_ddr_vrp),
.FIXED_IO_mio(FIXED_IO_mio),
.FIXED_IO_ps_clk(FIXED_IO_ps_clk),
.FIXED_IO_ps_porb(FIXED_IO_ps_porb),
.FIXED_IO_ps_srstb(FIXED_IO_ps_srstb));
endmodule
代码保存以后,把该文件置顶~
之后生成比特文件,此时我把生成的比特文件下载到板子上验证了一下,观察正常一切工作~
下面导出硬件~
勾选包含比特文件~
启动SDK软件 File→Launch SDK,弹出的页面依次点击OK,在SDK页面如图所示:
输入名称为FSBL,之后点击“Next”~
接下来的页面选ZYNQ FSBL,然后点Finish~
下面生成BOOT.BIN启动文件:
选择输出位置:
页面右下方点击Add,添加生成的FSBL.elf文件。
同样的步骤添加比特文件
两个文件都添加后,点击页面底部的Great Image
此时,你可以在之前设置的输出位置找到BOOT.bin文件~
拷贝文件到SD卡的boot分区,开发板设置为SD卡启动模式,连接示波器,开发板上电观察:
可以看到已经固化成功~
通过本次实验,我们成功地实现了一个DDS系统,验证了通过按键使DDS信号输出频率在1MHz、3MHz、5MHz、10MHz、15MHz、20MHz、25MHz和30MHz之间切换,并且在每个频率下保持稳定。此外还完成了对这个仅有PL端(FPGA)逻辑资源的DDS工程进行程序固化。这算是在zynq的学习上又前进了小小的一步~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。