hls fifo_【正点原子FPGA连载】第四章呼吸灯实验--领航者ZYNQ之HLS 开发指南

1)摘自【正点原子】领航者ZYNQ之HLS 开发指南

2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761

3)全套实验源码+手册+视频下载:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html

4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900

5)正点原子资料更新和新品发布,请加正点原子公众号:正点原子 关注方法:微信→添加好友→公众号→输入:正点原子

第四章呼吸灯实验


在前面两个实验中我们学习了如何通过Vivado HLS工具来生成带有一个ap_none接口的IP核。在本章我们将通过呼吸灯实验,来学习如何使用Vivado HLS工具生成一个带有AXI4-Lite总线接口的IP核,并学习Vivado HLS工具C/RTL协同仿真平台的使用,以及在Vivado中对综合结果进行验证的流程。
本章包括以下几个部分:
44.1简介
4.2实验任务
4.3HLS设计
4.4IP验证
4.5下载验证
4.1简介
呼吸灯采用PWM的方式,在固定的频率下,通过调整占空比的方式来控制LED灯亮度的变化。PWM(Pulse Width Modulation),即脉冲宽度调制,它利用微处理器输出的PWM信号,实现对模拟电路控制的一种非常有效的技术,广泛应用于测量、通信、功率控制等领域。
在由计数器产生的固定周期PWM信号下,如果其占空比为0,则LED灯不亮;如果其占空比为 100%,则LED灯最亮。所以将占空比从0到 100%,再从100%到0不断变化,就可以实现LED灯的“呼吸”效果。
PWM 占空比调节示意图如下图所示:

2e587df671d8f4eb7792e7298cca1df7.png

图 4.1.1 呼吸灯PWM占空比示意图


由上图可知,LED高电平的时间由长渐渐变短,再由短渐渐变长,如果LED灯是高电平点亮,则LED 灯会呈现出亮度由亮到暗,再由暗到亮的过程。
在本章我们将实现一个频率可调和开关可控制的呼吸灯,因此就需要一个接口来配置呼吸灯的频率和开关。我们可以通过Vivado HLS生成一个带有AXI4-Lite总线接口的IP核,然后在ZYNQ PS端通过AXI4-Lite总线来配置此IP核。
AXI的英文全称是 Advanced eXtensible Interface,即高级可扩展接口,它是 ARM 公司所提出的 AMBA (Advanced Microcontroller Bus Architecture)协议的一部分。AXI协议包含了AXI4、AXI4-Lite和AXI-Stream三种协议。
AXI4 协议支持突发传输,主要用于处理器访问存储器等需要指定地址的高速数据传输场景。AXI-Lite 为外设提供单个数据传输,主要用于访问一些低速外设中的寄存器。而AXI-Stream 接口则像 FIFO 一样,数据传输时不需要地址,在主从设备之间直接连续读写数据,主要用于如视频、高速 AD、PCIe、DMA接口等需要高速数据传输的场合。
AXI4-Lite接口是简化版的AXI4接口,用于较少数据量的存储映射通信。本次实验只需要配置呼吸灯IP核的频率和开关,因此接口类型选择AXI4-Lite接口。
4.2实验任务
本节实验任务是使用正点原子ZYNQ开发板(核心板)上的 PL LED,实现呼吸灯的效果,即由灭渐亮,然后再由亮渐灭,并且PS可以通过AXI接口来控制呼吸灯的开关和呼吸的频率。
4.3HLS设计
我们在电脑中的“F:ZYNQHigh_Level_Synthesis”目录下新建一个名为breath_led的文件夹,作为本次实验的工程目录。然后打开Vivado HLS工具,创建一个新的工程。设置工程名为“breath_led_hls”,选择工程路径为刚刚创建的文件夹。需要注意的是,工程名以及路径只能由英文字母、数字和下划线组成,不能包含中文、空格以及其他特殊字符。如下图所示:

f9ec1191576fe1f01c72962b8cd5d50a.png

图 4.3.1 工程配置界面


设置好工程名及路径之后,点击“Next”,进入如下界面设置顶层函数:

d0fe66c64ccefefa43784d9aaa6eb862.png

图 4.3.2 设置顶层函数


工程创建完成后,在工程面板中的“source”目录上点击右键,然后在打开的列表中选择“New File”新建源文件,在弹出的对话框中输入源文件的名称“breath_led.c”,如图1.3.3所示。源文件默认的保存路径为HLS工程目录,为方便源文件的管理,我们在工程目录下新建一个名为“src”的文件下,将源文件保存在src目录下。

56dfb17227d23f41e6256c08f00cf572.png

图 4.3.3 输入源文件名


我们在图1.3.3中输入的源文件的后缀名为“.c”,即使用C语言进行设计。如果使用C++语言进行设计,那么后缀名需要设置为“.cpp”。设置好文件名和路径之后,点击“保存”。
“breath_led.c”文件源代码如下:

  1. 1 #include "breath_led.h"
  2. 2
  3. 3 void breath_led(uint32 sw_ctrl, uint32 freq_step, uint1* led)
  4. 4 {
  5. 5 #pragma HLS INTERFACE ap_none port=led
  6. 6 #pragma HLS INTERFACE s_axilite port=freq_step
  7. 7 #pragma HLS INTERFACE s_axilite port=sw_ctrl
  8. 8 #pragma HLS INTERFACE ap_ctrl_none port=return
  9. 9 uint32 duty_cycle,period_cnt;
  10. 10 if(sw_ctrl == 1){
  11. 11 for(duty_cycle=0; duty_cycle<50000; duty_cycle=duty_cycle+freq_step){
  12. 12 for(period_cnt=0; period_cnt<50000; period_cnt++)
  13. 13 *led = (period_cnt <= duty_cycle) ? 1 : 0 ;
  14. 14 }
  15. 15
  16. 16 for(duty_cycle=50000; duty_cycle>0; duty_cycle=duty_cycle-freq_step){
  17. 17 for(period_cnt=0; period_cnt<50000; period_cnt++)
  18. 18 *led = (period_cnt <= duty_cycle) ? 1 : 0;
  19. 19 }
  20. 20 }else
  21. 21 *led = 0;
  22. 22 }


在代码的第1行,包含了一个名为“breath_led.h”的头文件,这个是我们自己创建的头文件。“breath_led.h”的创建方式和“breath_led.c”的创建方式相同。创建此头文件的目的是为了函数声明,以便在测试文件中引入此头文件,从而在测试文件中可以调用我们所编写的函数。
在代码的第3行,我们在定义函数参数类型时,使用了“uint1”这种数据类型,它表示1位无符号整数。在代码的第5行到代码的第7行,#pragma为HLS优化指令,它表示led使用的是“ap_none”协议,呼吸灯开关“sw_ctrl”和频率控制字“freq_step”使用的是AXI4-Lite协议。在代码的第8行“ap_ctrl_none”表明没有添加包级别的协议,而是完全在端口接口级别用端口级别协议来做控制。
在代码的第10行是一个条件判断语句,它通过判断sw_ctrl的值来控制呼吸灯的开关。当sw_ctr的值为1时,呼吸灯打开,对应着代码的第11行到第19行;当sw_ctrl的值为0时,呼吸灯关闭,对应着代码的第21行。
在代码第12行的for循环语句通过不断增加每个PWM周期中的占空比(duty_cycle),从而实现呼吸灯从暗到亮的过程。变量freq_step用于控制呼吸灯占空比变化的“步长”,步长越大,占空比变化的越快,那么呼吸灯由暗到亮的时间越短,即呼吸频率变快。
在代码的第13行是一个三目运算符,它通过判断period_cnt和duty_cycle(占空比)值的大小来控制LED灯的输出状态。如果条件满足,即period_cnt小于duty_cycle,led赋值为1,表示点亮LED;反之,led赋值为0,表示熄灭LED。
第16至19行的for循环和第11至14行的for循环基本相同,唯一的区别是第16行中的占空比duty_cycle不断减小,从而实现呼吸灯从亮到暗的过程。
头文件“breath_led.h”的代码如下:

  1. 1 #include "ap_cint.h"
  2. 2
  3. 3 #ifndef _BREATH_LED_H
  4. 4 #define _BREATH_LED_H_
  5. 5
  6. 6 void breath_led(int sw_ctrl, uint32 freq_step, uint1* led);
  7. 7
  8. 8 #endif


在代码的第1行我们引入了“ap_cint.h”的头文件来包含任意精度数据类型。在代码的第3行到第4行表示如果没有定义“breath.h”头文件则定义这个头文件,这样可以避免头文件的重复定义。在代码第6行,我们声明了“breath_led”函数,从而在测试文件中可以调用此函数。
在工程面板中的“Test Bench”目录上点击右键,然后在打开的列表中选择“New File”新建测试文件, 在弹出的对话框中输入测试文件的名称“breath_led_tb.c”,如图1.3.4所示。测试文件的保存路径为src目录。

1c11f686cabf4e84f89896891dc599af.png

图 4.3.4 新建测试文件


在测试文件中编写如下代码:

  1. 1 #include "breath_led.h"
  2. 2
  3. 3 int main(void)
  4. 4 {
  5. 5 uint1 led;
  6. 6
  7. 7 breath_led(1, 2000, &led); //打开呼吸灯,频率步长设置为2000
  8. 8 breath_led(0, 2000, &led); //关闭呼吸灯
  9. 9
  10. 10 printf("test passed!n");
  11. 11 return 0;
  12. 12 }


在测试文件中,我们通过调用“breath_led”函数,设置呼吸灯开关控制“sw_ctrl”为1打开呼吸灯,设置呼吸灯频率步长“freq_step”为2000来实现验证呼吸灯功能。
代码输入完成后,按快捷键Ctrl+S保存。然后点击工具栏中向右的绿色三角形对C代码进行综合,如下图所示:

4b3124eec4da4d0827987988f8c5b94c.png

图 4.3.5 运行C综合


综合完成后,会自动打开综合结果(solution)的报告,如下图所示:

193e33026ca723de78faa1352ceba40f.png

图 4.3.4 综合报告


在所示的综合报告中,给出了设计的性能评估、资源评估以及接口等信息。本次实验中,我们重点关注综合工具为我们生成的接口信息,如下图所示:

3f31a271efa1d9fa08c1fbff30ce4156.png

图 4.3.5 接口信息


从图中Protocol一栏可以看出,红色方框里的协议类型为“s_axi”,它表示AXI从接口,其中s表示Slave(从端口)。然后在Source Object一栏可以看到,接口类型为“AXILiteS”,即AXI4-Lite从接口。主机可以通过AXI4-Lite总线协议配置此IP核。
下面我们进行仿真,来测试HLS设计是否满足要求。因为Vivado HLS工具不支持“ap_ctrl_none”包级别协议的仿真,所以我们在仿真前先删除所有的优化指令,如下图所示:

330423a7dbb23e9cb08df2cfb829d687.png

图 4.3.6 删除优化指令


然后再点击工具栏中的“Solution”按钮,在打开的列表中选择“Run C/RTL Cosimulation”进入C/RTL协同仿真,如下图所示:

978c339a211ac77e3dfb704d5e02bb65.png

图 4.3.7 运行C/RTL协同仿真


弹出C/RTL仿真配置界面,我们在配置界面中选择使用“Vivado Simulator”仿真器,“Dump Trace”选择“all”表示生成仿真波形。如下图所示:

0a79c79da303ed5c9a5723d81cbe46c3.png

图 4.3.10 C/RTL仿真配置界面


点击“OK”按钮,C/RTL仿真完成后,会在控制台看到下图所示信息,表明C/RTL协同仿真通过。

8fbf3b4ff2b3bee7cbc1f6b44865e2e0.png

图 4.3.11 C/RTL协同仿真通过界面


接着在工具栏中点击波形按钮,如下图所示:

9fa81d452ba538e0de9d8a0811851a7a.png

图 4.3.12 波形按钮


Vivado HLS会自动打开Vivado Simulation仿真工具,将图中所示信号添加到波形窗口中,如下图所示:

e061611345263648bebd67bc57cbc150.png

图 4.3.13 添加波形文件


波形如下图所示:

6ca58ccaf65fa16caeaad96df3d3cc32.png

图 4.3.14 波形图

ef2363308273fb72168f3713d1a72259.png

图 4.3.15 波形图


当呼吸灯控制“sw_ctrl”为1,呼吸灯功能打开时,生成PWM波的占空比逐渐增大后又逐渐减小,说明呼吸灯由暗到亮后又由亮变暗。当呼吸灯控制sw_ctrl为0,输出“led_ap_vld”信号为高,数据有效时,led的值一直是0,表示呼吸灯关闭。通过C/RTL协同仿真表明HLS综合得到的RTL设计符合预期的功能要求。
至此C/RTL协同仿真设计完成,通过仿真C/RTL协同仿真我们验证了HLS设计的正确性。下面我们将设计打包成IP模块,以供Vivado开发套件中的其他工具(如IP集成器)使用。
将之前删除的优化指令重修添加到Vivado HLS工程中,在工具栏中点击黄色的“田”字按钮,导出RTL,如下图所示:

73a3054d577144a954d9f32e5d9c9b94.png

图 4.3.16 导出RTL


在弹出的对话框中保持默认设置,直接点击“OK”,如下图所示:

bfb9effc8f0d11686458a4de4a6b1a48.png

图 4.3.17 将设计导出成IP


设计导出完成后,HLS设计部分就结束了,我们在HLS工程目录下可以找到导出的IP核,如下图红色方框所示:

e78383db3003623f9aca0f6fb3eb072d.png

图 4.3.18导出得到的IP


我们到计算机工程目录所指向的文件夹中同样可以看到以ZIP压缩文件形式存在的IP核,如下图所示:

0db6db76c20bdcc936c96d7c24a4cd75.png

图 4.3.19 文件夹中的IP核


HLS设计结束之后,我们将在Vivado中对导出的IP核进行验证。
4.4IP验证
在IP验证环节,我们会使用Vivado工具的IP集成器将生成的IP核添加到Block Design中,然后完成设计后将程序下载到领航者开发板上进行验证。
我们在《领航者ZYNQ之嵌入式开发指南》第一章“Hello World实验”中详细介绍了如何使用Vivado创建工程,以及如何使用IP集成器(IP INTEGRATOR)创建Block Design。如果大家对这一部分内容不熟悉的话,一定要先按照《领航者ZYNQ之嵌入式开发指南》中第一章的描述,把这一流程完整的操作一遍,然后才能进行本章的IP验证环节。
在本次实验中,我们创建一个名为“key_led_ip_test”的Vivado工程。为了方便工程管理,我们将Vivado工程的目录与HLS工程目录保持一致,如下图所示:

92a8fd52744037f3bd6c31e0cb071e75.png

图 4.4.1 创建Vivado工程


Vivado工程创建完成之后,本次实验的工程目录如下图所示:

2cf71b8925826cc6908484098024672f.png

图 4.4.2 按键控制LED实验工程目录


图 2.4.2中,以“_hls”结尾的文件夹为Vivado HLS工程,以“_ip_test”结尾的是用于IP验证的Vivado工程。
工程创建完成后,需要先将HLS设计过程中导出的IP核拷贝到Vivado工程目录下。我们在Vivado工程目录下新建一个名为“ip_repo”的文件夹,然后将图 2.3.26中的压缩包拷贝到该文件夹中并解压,解压完成后如下图所示:

2ad4ca9624dd56787b14d04e8cb3333c.png

图 4.4.3 拷贝并解压IP


接下来在Vivado中将该IP添加到工程的IP库中。添加IP核完成之后,创建一个名为“system”的Block Design。在设计中添加breath_led和ZYNQ两个IP核,并配置ZYNQ。最后连接两个IP并创建外部端口。如果大家对IP的配置和连接方式不清楚的话,请大家参考《领航者ZYNQ之嵌入式开发指南》中第五章“AXI GPIO 按键控制 LED 实验”第3小节硬件设计部分。最终完成的设计如下图所示:

31bcb4a6b36981e6a169807005b16445.png

图 4.4.4 完成后的Block Design


到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
然后我们还要为设计创建约束文件navigator.xdc,并在文件中添加以下管脚约束信息:
set_property -dict {PACKAGE_PIN J16 IOSTANDARD LVCMOS33} [get_ports led]
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成 Bitstream 之后,在菜单栏中选择 File > Export > Export hardware 导出硬件,并在弹出的对话框 中,勾选“Include bitstream”。然后在菜单栏选择 File > Launch SDK,启动 SDK 软件。
在 SDK 软件中新建一个 BSP 工程和一个空的应用工程,应用工程名为“breath_led”。然后为应用工程 新建一个源文件“main.c”,我们在新建的 main.c 文件中输入本次实验的代码。代码的主体部分如下所示:

  1. 1 #include "xbreath_led.h"
  2. 2
  3. 3 int main()
  4. 4 {
  5. 5 XBreath_led led;
  6. 6
  7. 7 XBreath_led_Initialize(&led, XPAR_BREATH_LED_0_DEVICE_ID);
  8. 8
  9. 9 while(1)
  10. 10 {
  11. 11 XBreath_led_Set_freq_step(&led,40);
  12. 12 XBreath_led_Set_sw_ctrl(&led,1);
  13. 13 sleep(5);
  14. 14 XBreath_led_Set_sw_ctrl(&led,0);
  15. 15 sleep(2);
  16. 16 XBreath_led_Set_freq_step(&led,200);
  17. 17 XBreath_led_Set_sw_ctrl(&led,1);
  18. 18 sleep(5);
  19. 19 }
  20. 20 return 0;
  21. 21 }


第1行引入的“xbreath_led.h”是Vivado HLS工具自动为我们生成的头文件,这个头文件包含了初始化和配置Vivado HLS生成IP核的函数。第7行初始化Vivado HLS生成的呼吸灯IP核。第11行到第13行设置呼吸灯频率步进为40,并使呼吸灯工作5秒。第14行到第15行熄灭呼吸灯2秒。第16行到第18行设置呼吸灯频率步进为200,并使呼吸灯工作5秒。
4.5下载验证
编译工程并生成比特流.bit 文件。将下载器一端连接电脑,另一端与开发板上的 JTAG 下载口连接,连 接电源线,并打开开发板的电源开关。 点击 Vivado 左侧“Flow Navigator”窗口最下面的“Open Hardware Manager”,此时 Vivado 软件识别 到下载器,点击“Hardware”窗口中“Progam Device”下载程序,在弹出的界面中选择“Program”下载程 序。 配置 PL 完成后,接下来我们要下载软件程序。在应用工程 user_led 上右击,选择“Run As”,然后选择 第一项“1 Launch on Hardware (System Debugger)”。程序下载完成后,可以看到核心板上的 LED1(PL LED)每7秒钟 LED 灯的呼吸频率切换一次,在切换频率之前,LED 灯会熄灭2秒钟。如下图所示:

29ad62220fc525bfdf148ff623f28d59.png

图 4.5.1 呼吸灯实验现象

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值