Xilinx 官方为我们提供了非常丰富的
IP
核,如数学运算(乘法器、浮点运算器等)、信号处理(FFT、DDS 等),我们可以通过调用这些
IP
核来快速完成设计。然而随着系统的设计越来越复杂,官方提供的免费 IP
核有时并不能很好的适用我们的设计 ,这个时候就需要我们自己来实现这些功能。为了使这些模块或代码以后能够复用 ,可以通过自定义 IP
核的方式将这些模块集成到
Vivado
中的
IP
库 中,以达到简化系统设计和缩短产品上市时间的目的。
在 Vivado
软件中,通过创建和封装
IP
向导的方式来自定义
IP
核,支持将当前工程、工程中的模块或者指定文件目录封装成 IP
核,当然也可以创建一个带有
AXI4 接口的 IP
核 ,用于
PS
和
PL
的数据通信。本次实验选择常用的方式,即创建一个带有 AXI
接口的
IP
核,该
IP
核通过
AXI
协议实现
PS
和
PL
的数据通信。AXI 协议是一种高性能、高带宽、低延迟的片内总线,关于该协议的详细内容,我们会在后面的例程中向大家做详细的介绍。本次实验的系统框图如图
所示:
系统框图
框图中的 UART
用于打印信息,
Breath LED IP
核为自定义的
IP
核,
PS
通过
AXI
接口为
LED IP
模块发送配置数据,从而来控制 PL LED
灯。
2 实验任务
通过自定义一个 LED IP
核,来控制
PL LED
呈现呼吸灯的效果,并且
PS
可以通过AXI 接口来控制呼吸灯的开关和呼吸的频率。
3 硬件设计
step1 :创建一个新的 IP 核
1-1 New IP Location
Vivado-->Tasks-->Manage IP--> New IP Location...
1 点击 Manage IP
1-2 完成 Manage IP工程的创建
在弹出的界面中选择“Next ”
2 自定义 IP 核向导界面
设置 Manage IP
核的属性,在“IP Location:”一栏指定工程的路径,路径为:D:/ZYNQ18/ Embedded_System/custom_ip,其它保持默认即可。 注意,Part 一栏中设置开发板的型号,在后面的工程中会重新指定,这里直接保持默认。点击“Finish”完成 Manage IP工程的创建.
IP 设置选项
1-3 工程创建完成后,运行创建和封装 IP 向导。
选择创建和封装 IP 向导
封装 IP 向导界面
1-4 选择创建一个带 AXI 接口的 IP 核
封装 IP 选项
1-5 分别设置 IP 核名称( Name )、版本号( Version )、显示名( Display name )、描述( Description ) 和路径(IP location)。
IP 参数设置
1-6 对 AXI 接口进行设置
AXI4 接口设置
Name(名称):这里修改成 S0_AXI 。
Interface Tpye (接口类型 ):共三种接口类型可选,分别是
Lite
、
Full
和
Stream
。
AXI4-Lite
接口是简化版的 AXI4
接口,用于较少数据量的存储映射通信;
AXI4-Full
接口是高性能存储映射接口,用于较多数据量的存储映射通信;AXI4-Stream
用于高速数据流传输,非存储映射接口。本次实验只需少量数据的通信,因此接口类型选择默认的 Lite
接口。
Interface Mode (接口模式 ):接口模式有
Slave
(从机)和
Master
(主机)两种模式可选,
AXI
协议是主机和从机通过“握手”的方式建立连接,这里选择默认的 Slave
接口模式。
Data Width (数据宽度 ):数据位宽保持默认,即
32
位位宽。
Memory Size (存储器大小 ): 在
AXI4-Lite
接口模式下,该选项不可设置。
Number of Registers (寄存器数量 ):用于配置
PL LED
呼吸灯寄存器的数量,这里保持默认。
点击“Next
”按钮。
1-7 将 IP 添加至 IP 库中,完成 IP 核的创建和封装。
创建和封装 IP 核总结界面
在 IP Catalog
界面中可以看到刚刚添加的
IP
核,位于
User Repository
一栏中的
AXI Peripheral
下,名称为“breath_led_ip_v1.0
”。
新添加的 IP 核
1-8 对 breath_led_ip_v1.0 IP 核进行编辑
右击 breath_led_ip_v1.0 IP
核,选择“Edit in IP Packager
”,在弹出的界面中点击“
OK
”。
打开编辑 IP 核界面
设置工程名和路径
此时会打开一个新的工程,如下图 所示:
编辑 IP 核界面
1-9 添加代码
双击 breath_led_ip_v1_0
双击 breath_led_ip_v1_0
打开 breath_led_ip_v1_0.v
文件后,接下来开始编辑代码,来添加控制
PL LED
呼吸灯所需要的参数和端口信号。在代码的第 7
行添加如下代码:
parameter
START_FREQ_STEP
=
10'd100
,
这个参数用于设置呼吸灯默认的呼吸频率,然后在代码的第 18 行添加如下代码:
编辑 breath_led_ip_v1_0.v 文件
在代码例化 breath_led_ip_v1_0_S0_AXI
模块的地方,增加对参数
START_FREQ_STEP
和端口
led
的例化,代码如下:
. START_FREQ_STEP ( START_FREQ_STEP ),
和
.
led
(
led
),
代码添加后,按下键盘键 Ctrl+S 保存。如下图所示:
1-10 添加代码
双击 breath_led_ip_v1_0 下的 breath_led_ip_v1_0_S0_AXI_inst ,如下图所示:
双击 breath_led_ip_v1_0_S0_AXI_inst
breath_led_ip_v1_0_S0_AXI 模块实现了
AXI4
协议下的读写寄存器的功能,我们只需要对该模块稍作修改,即可实现控制 PL LED
呼吸灯的功能。向寄存器中写入数据和读出数据的部分代码如下图所示:
写寄存器数据部分代码
读寄存器数据部分代码
在创建和封装 IP
核向导中,我们总共定义了
4
个寄存器,代码中的
slv_reg0
至
slv_reg3
是寄存器地址0 至寄存器地址
3
对应的数据,通过例化呼吸灯模块,将寄存器地址对应的数据和呼吸灯模块的控制端口相连接,即可实现对呼吸灯的控制。
我们接下来开始编辑代码,同样在代码的第 7
行添加如下代码:
parameter
START_FREQ_STEP
=
10'd100
,
在代码的第 18
行添加如下代码:
output
led
,
编辑 breath_led_ip_v1_0_S0_AXI.v 文件
同时我们还需要在代码的第 401 行例化 breath_led.v 文件,代码如下:
breath_led
#(
.
START_FREQ_STEP
(
START_FREQ_STEP
)
)
u_breath_led
(
.
sys_clk
(
S_AXI_ACLK
),
.
sys_rst_n
(
S_AXI_ARESETN
),
.
sw_ctrl
(
slv_reg0
[
0
]),
.
set_en
(
slv_reg1
[
31
]),
.
set_freq_step
(
slv_reg1
[
9
:
0
]),
.
led
(
led
)
);
代码中的 slv_reg0
和
slv_reg1
是寄存器地址
0
和寄存器地址
1
对应的数据,我们通过寄存器地址
0
对应的数据来控制呼吸灯的使能(sw_ctrl
),寄存器地址
1
对应数据的最高位控制呼吸灯频率的设置有效信号(set_en
),寄存器地址
1
对应数据的低
10
位控制呼吸灯频率的步长(
set_freq_step
)。
添加完代码后,按下键盘 Ctrl+S
保存代码,添加完成后的代码如下图所示:
例化 breath_led 代码
1-11 Add or Create design source
此时工程中缺失 breath_led.v
文件,
breath_led.v
文件用于实现呼吸灯的功能,这个代码是在“呼吸灯实验”的基础上修改而来。
右击“Design Sources
”,选择“
Add Sources…
”,在弹出的界面中选择“
Add or Create design source
”,点击“NEXT
”,
点击“ Create File
” 创 建 一 个 新 的 文 件 , 在 弹 出 的 界 面 输 入 名 称
breath_led
, 路 径为../custom_ip/ip_repo/breath_led_ip_1.0/hdl
,点击“
OK
”按钮.
创建一个新的设计文件
设置设计文件的名称与路径
点击“Finish
”按钮完成创建,如下图所示:
在弹出的模块定义界面中点击“OK ”按钮,接下来在弹出的确认按钮中点击“ YES ”。
1-12 编辑breath_led.v
双击 u_breath_led(breath_led.v) 文件,如下图所示:
打开 breath_led.v 文件后,开始编辑代码,代码如下:
module breath_led(
input sys_clk , //时钟信号
input sys_rst_n , //复位信号
input sw_ctrl , //呼吸灯开关控制信号 1:亮 0:灭
input set_en , //设置呼吸灯频率设置使能信号
input [9:0] set_freq_step , //设置呼吸灯频率变化步长
output led //LED
);
//*****************************************************
//** main code
//*****************************************************
//parameter define
parameter START_FREQ_STEP = 10'd100; //设置频率步长初始值
//reg define
reg [15:0] period_cnt ; //周期计数器
reg [9:0] freq_step ; //呼吸灯频率间隔步长
reg [15:0] duty_cycle ; //设置高电平占空比的计数点
reg inc_dec_flag; //用于表示高电平占空比的计数值,是递增还是递减
//为 1 时表示占空比递减,为 0 时表示占空比递增
//wire define
wire led_t ;
//将周期信号计数值与占空比计数值进行比较,以输出驱动 led 的 PWM 信号
assign led_t = ( period_cnt <= duty_cycle ) ? 1'b1 : 1'b0 ;
assign led = led_t & sw_ctrl;
//周期信号计数器在 0-50_000 之间计数
always @ (posedge sys_clk) begin
if (!sys_rst_n)
period_cnt <= 16'd0;
else if(!sw_ctrl)
period_cnt <= 16'd0;
else if( period_cnt == 16'd50_000 )
period_cnt <= 16'd0;
else
period_cnt <= period_cnt + 16'd1;
end
//设置频率间隔
always @(posedge sys_clk) begin
if(!sys_rst_n)
freq_step <= START_FREQ_STEP;
else if(set_en) begin
if(set_freq_step == 0)
freq_step <= 10'd1;
else if(set_freq_step >= 10'd1_000)
freq_step <= 10'd1_000;
else
freq_step <= set_freq_step;
end
end
//设定高电平占空比的计数值
always @(posedge sys_clk) begin
if (sys_rst_n == 1'b0) begin
duty_cycle <= 16'd0;
inc_dec_flag <= 1'b0;
end
else if(!sw_ctrl) begin //呼吸灯开关关闭时,信号清零
duty_cycle <= 16'd0;
inc_dec_flag <= 1'b0;
end
//每次计数完了一个周期,就调节占空比计数值
else if( period_cnt == 16'd50_000 ) begin
if( inc_dec_flag ) begin //占空比递减
if( duty_cycle == 16'd0 )
inc_dec_flag <= 1'b0;
else if(duty_cycle < freq_step)
duty_cycle <= 16'd0;
else
duty_cycle <= duty_cycle - freq_step;
end
else begin //占空比递增
if( duty_cycle >= 16'd50_000 )
inc_dec_flag <= 1'b1;
else
duty_cycle <= duty_cycle + freq_step;
end
end
else //未计数完一个周期时,占空比保持不变
duty_cycle <= duty_cycle ;
end
endmodule
模块实现了呼吸灯的功能。呼吸灯的使能由输入的端口信号 sw_ctrl
控制,呼吸灯的呼吸频率由输入的端口信号 set_en
和
set_freq_step
控制。由代码的第
43
行至第
55
行代码可知,输入的
set_freq_step
范围是1~1000。
1-13 Run Synthesis
保存代码,并验证代码是否有语法错误。在左侧 Flow Navigator
导航栏中找到
SYNTHESIS
,点击该选项中的 “Run Synthesis”
,如下图所示:
编译代码
在弹出的窗口中点击“OK
”按钮,等待代码编译完成。
1-14 设置 IP 封装
接下来开始设置 IP
封装,将界面切换至
Package IP
,如果不小心关闭的话,可以通过
IP-XACT界面下的 component.xml
重新打开,如图:
打开 Package IP 界面
Package IP 界面
Identification 这一栏的选项直接保持默认,需要注意的是,我们可以点击下图
中
Categories
选项下的“+
”按钮来修改
IP
的分类,这里不做修改。
修改 IP 核分类
1-15 点击 Compatibility ,修改该 IP 核支持的器件。
点击 Family
一栏下的“
+
”图标,选择“Add Family Explicitly…
”,如下图所示:
“Compatibility 界面”
这里勾选“zynq(zynq-7000)
”,表示该
IP
核支持
zynq
器件。而
Life-cycle
表明该
IP
核当前的产品生命周期,这里选择“Pre-Production
”。
选择支持的器件
1-16 点击 File Groups ,然后点击界面上的“ Merge Changes from Gile Groups Wizard ”
如下图所示:
此时可以在 Verilog Synthesis
一栏中查看工程中的三个模块。
1-17 点击 Customization Parameters,点击界面上的“ Merge Changes from Customization Parameters Wizard”
如下图所示:
Customization Parameters 界面
此时多了 Hidden Parameters
一栏,展开这个界面,可以看到程序中自定义的参数
START_FREQ_STEP
,右击这个参数,选择“Edit Parameter…
”,弹出编辑参数的界面,如下图
所示:
打开编辑参数界面
在弹出的页面中勾选“Visible in Customization GUI
”,将此参数显示在
GUI
参数界面中;
Format 格式改为“
long
”;
勾选“Specify Range
”来设定此参数的范围。将
Type
改为“
Range of integers
”,
Minimum
的值改为
1
,Maximum 的值改为
1000
,将
Default Value
的值改为
100
,点击“
OK
”按钮,如下图所示:
参数设置
1-18 调整参数在 GUI 显示的位置
点击“Customization GUI
”,可以在“
Layout
”界面拖动
Page 0
下的参数来调整参数在
GUI
显示的位置,如下图所示:
拖动参数
1-19 更新总结界面,封装IP
点击“Review and Package”,然后点击“IP has been modified”更新总结界面,最后点击“ Re-Package IP”,如下图所示:
Review and Package 界面
关闭工程询问界面
IP 核封装完成后,在
IP
核所在路径(
...\custom_ip\ip_repo\breath_led_ip_1.0\drivers\breath_led_ip_v1_0\src
)
目录下,
Vivado
软件会自动生成
.c
和
.h
文件,方便在
SDK
软件中对
IP
核进行操作
,如下图所示:
软件生成的文件
在 Manage IP 工程界面下,点击菜单栏的“ File ”,选择“ Close Project ”关闭工程,如下图所示:
关闭工程
接下来在弹出的确认关闭界面选择“OK
”确认关闭工程。
确认关闭工程
step2 :创建 Vivado 工程,将自定义的 IP 核添加至本工程的 IP 库中
step1 是创建了一个自定义的
IP
核,接下来我们来创建
Vivado
工程,工程名为
user_led
,路径为 D:/ZYNQ18/Embedded_System/custom_ip 目录下,创建的步骤这里不再赘述。
在开始下一个步骤之前,我们先把步骤1
自定义的
IP
核添加至本工程的
IP
库中,点击菜单栏的“
Tools
”,选择“Setting
”,如下图所示:
打开设置界面
点击“IP”一栏下的“Repository
”,然后点击“
+
”来添加自定义的
IP
核,如下图所示:
IP 库添加界面
接下来会弹出添加自定义 IP
核的路径,选择
../custom_ip/ip_repo/breath_led_ip_1.0
,点击“
Select
”,在弹出的界面中可以看到识别到的 IP
核,点击“
OK
”按钮添加
IP
核,如下图所示:
添加 IP 核
最后点击“OK
”按钮,完成
IP
核的添加,如下图所示:
添加 IP 核完成
step3 :使用 IP Integrator 创建 Processing System
3-1 Create Block Design
3-2 添加 “ZYNQ7 Processing System” IP
3-3 添加 UART 控制器,修改 DDR3 的存储器类型型号,其它保持默认。
配置完成后,点击“
Run Block Automation
”,框图如下图所示:
ZYNQ7 框图
3-4 添加 Breath LED IP 核
添加完成后,可以双击 led IP 核来设置参数。这里不作修改。
接下来点击“Run Connection Automation
”来自动连线,连线完成后如图
所示:
3-5 Make External Regenerate Layout
此时原理图中还没有 LED
的引脚,右击
breath_led_ip_0
的
led
引脚,选择“
Make External”。将引出的
led_0
改为 led。点击“
Regenerate Layout
”图标,最终原理图界面如图
所示:
最终原理图界面
step4:生成顶层 HDL 模块
4-1 Generate Output Products
在 Sources
窗口中,选中
Design Sources
下的
sysetm.bd
,这就是我们刚刚完成的
Block Design
设计。右键点击 sysetm.bd
,在弹出的菜单栏中选择
“Generate Output Products”
,等待
Generate
完成。
4-2 Create HDL Wrapper
在 Sources
窗口中,右键点击
sysetm.bd
,在弹出的菜单栏中选择
“Create HDL Wrapper”
。
step5 :生成 Bitstream 文件并导出到 SDK
5-1 Open Elaborated Design
在左侧 Flow Navigator
导航栏中找到
RTL ANALYSIS
,点击该选项中的“
Open Elaborated Design
”。在弹出的对话框中点击“OK
”。如下图所示:
Elaborated Design 对话框
在 ELABORATED DESIGN
界面下方找到
I/O Ports
一栏。如果没有找到则通过在菜单栏中点击
Layout
,然后在下拉列表中选择 I/O Planning
。我们将在
I/O Ports
一栏对
PL
部分的接口进行管脚分配,
led
分配至BANK35 的
J16
引脚,该
BANK
的供电电压为
3.3V
,因此
I/O Std
一列对应的电平也需要修改。如图
所示:
管脚分配
5-2 保存管脚约束
设置完成后按快捷
Ctrl+S
保存管脚约束,在弹出的对话框输入文件名“
user_led
”。
保存约束
5-3 Generate Bitstream
点击“Generate Bitstream”。在连续弹出的对话框中依次点击“YES”、“OK”。Vivado 工具开始依次对设计进行
综合 、
实现 、并
生成 Bitstream 文件 。
5-4 导出硬件
File > Export > Export hardware。,勾选
“Include bitstream”。
5-5 Launch SDK