[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA基础篇连载-05 FPGA流水灯实验

文档创建者:FPGA课程
浏览次数:508
最后更新:2024-08-21
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 1-FPGA基础入门实验
本帖最后由 FPGA课程 于 2024-8-22 19:43 编辑

​软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!



1 概述
本章课程以大家熟悉的流水灯为例子,按一定的时间间隔,顺序循环点亮熄灭多个LED灯。本章详细讲解了VIVADO软件的使用,包括创建FPGA工程,编写Verilog代码,添加管脚约束,最后编译,下载bit文件到开发板测试。





    •      
    • LED灯简介
            
发光二极管,简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,它在照明领域应用广泛。相比其它灯光源,LED效率高、寿命较长、功耗小、低碳环保、材料不易受到环境影响而相对稳定。
发光二极管与普通二极管一样具有单向导电性,在LED当中只有正极接入正极,负极接入负极它才会有电流流通进去。电流从LED阳极流向阴极时,半导体晶体就发出从紫外到红外不同颜色的光线,光的强弱与电流有关。常用的是发红光、绿光或黄光的二极管。发光二极管的反向击穿电压大于5伏,使用时必须串联限流电阻以控制通过二极管的电流。
5fb04dafd2744972949921d308ef8015.jpg
直插型发光二极管
a1b2a38fdfd4490398d45e6dd3172f54.jpg
贴片型发光二极管
1.2 硬件电路分析
米联客F3_CZ02_7010/7020开发板共有8路LED,其中1路PS端的LED,7路PL端LED,所有的LED灯都是通过XC7Z010/XC7Z020直接驱动。当FPGA输出为高电平时,LED熄灭;当FPGA输出为低电平时,LED灯点亮。
865ddaac0add473a821b1fe7860933a5.jpg
1.3 程序设计
本次实验目的为4个LED实现流水灯的效果,LED变换间隔时间需要基于系统时钟来计数,实现间隔时间的计时,除此之外,添加系统复位使程序恢复至默认状态,输出结果由4个LED灯亮灭显示。命名模块名为run_led,此模块需要两个输入的端口,分别为系统时钟和系统复位,输出为4位的 LED 端口
d0e39dffea6f4d818bea542d5deed850.jpg
2工程管理文件
米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹
01_rtl:放用户编写的rtl代码
02_sim:仿真文件或者工程
03_ip:放使用到的ip文件
04_pin:放fpga的pin脚约束文件或者时序约束文件
05_boot:放编译好的bit或者bin文件(一般为空)
06_doc:放一些相关文档(一般为空)
0558bd4b04fa4fde8111d2030943de39.jpg
3 新建VIVADO工程
Step1:启动VIVADO,单击Create Project
aca3049e015f43a18187d3f3421d2ea7.jpg
Step2:单击NEXT
3741f578f18f4e94b08202dd9d1ba3b9.jpg
Step3:创建名为fpga_prj的工程到对应的文件目录,文件路径自定义,不能有中文或非法字符,之后单击NEXT
a9ab7fcbed234dd5b3f6ae1fb7395c90.jpg

Step4:选择RTL Project并且勾选复选框,之后单击NEXT
db39cb322e1f4feebdcd4c91cbed6021.jpg
Step5:选择芯片的型号和封装速度等级:
7020-400-2 的选项:ZYNQ-7000 系列,封装 CLG400,速度等级-2
本文本是比较通用于不同板子的的教程,教程中代码、配图可能与工程中代码稍有不同,请以实际工程为准。
551c3aceeb7f4115ad1ef0b3d82d3e57.jpg
Step:6 单击Finish完成工程创建。
8e88694d12a0434092384a912ffeeb77.jpg
4 添加代码管理文件
米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹
01_rtl:放用户编写的rtl代码
02_sim:仿真文件或者工程
03_ip:放使用到的ip文件
04_pin:放fpga的pin脚约束文件或者时序约束文件
05_boot:放编译好的bit或者bin文件(一般为空)
06_doc:放本一些相关文档(一般为空)
3e552c460ede4c0f9ff41e90b7a3a18e.jpg
5 添加工程文件
Step1:打开VIVADO软件
b4488aa820f24dc1a16b0eb6fbf8d9d1.jpg
Step2:单击 Add Sources
f5ba4b849e844fad84b57764c208ed4b.jpg
Step3:选择单击Add or Create Design Sources 然后单击NEXT
a8a7dc1cd2584898bedbda85f568e5bb.jpg
Step4:单击Create File 来创建文件
2b75aa8695f24c9a90d5ec50288f4558.jpg
Step5:创建一个run_led的文件,并且文件类型选择Verilog,路径可以选择存放在工程默认路径下,米联客推荐存放在创建的FPGA工程目录的uisrc\01_rtl目录下,方便管理。
767a5f69392f44c0b36c39980ded4f7b.jpg
Step6:添加完成后如下图所示之后单击finish完成文件的创建
71f187c757cf4805956a2e35f3cbc1c6.jpg
Step7:继续弹出的对话空中,可以设置一些端口,但是我们现在什么都不做。单击OK
61cb00d49f8641e4bbccc05b40c4b23d.jpg
Step8:创建完成后可以看到Design Sources文件夹中有了run_led.v这个文件,这个文件就是我们可以编写verilog程序的文件。
4430c6209cee4e289b1ef2c0bd957afe.jpg
6 Verilog FPGA流水灯实验
在硬件电路分析中介绍过,通过控制FPGA的IO输出电平的高低,可以控制LED灯的暗灭,首先我们可以通过给LED端口赋值的方法,点亮LED灯。
  1. `timescale 1ns / 1ns                 //仿真单位/仿真精度

  2. module run_led                                            
  3. (                                                              
  4. output [3:0]   O_led                //LED灯输出
  5. );
  6. assign O_led = 4'b0111;             //设置LED初始状,0表示灯亮,1表示灯灭
  7. endmodule
复制代码


以上代码实现点亮4个LED灯,本课目的是按顺序点亮并熄灭开发板上的4个LED灯,形成流水灯的效果。
FPGA的工作是无法离开时钟的(组合逻辑除外)。我们可以设计一个always块,实现计数(或者说是计时)功能,每当计数器计时到1s后,切换一次LED灯状态就行。每来一个时钟,让寄存器t_cnt的值累加1,直到加到预先设计的阈值再从0开始循环。
例如LED流水灯的间隔时间设置为1s,系统时钟I_sysclk的晶振频率为 100Mhz,时钟周期 = 1/频率(hz) = 10ns,计数器计时到1s需要1s/10ns = 100 000 000个时钟周期,由于计数器从0开始计时,所以计数器t_cnt最大计数为99 999 999。由计算器可以看到计数t_cnt对应的二进制共需要28位的位宽,因此32'd99_999_999的位宽足够了。
25222d6c7b884d3697c80458142240c7.jpg
除此之外,系统复位在 FPGA 系统中必不可少,当程序出现跑飞等异常情况时,可以使程序恢复至默认状态。
  1. `timescale 1ns / 1ns

  2. module run_led#
  3. (
  4. parameter T_INR_CNT_SET = 32'd99_999_999             //分频时钟计数,初始时钟过快,人眼观察会导致LED常亮
  5. )                                          //设置分频系数,降低流水灯的变化速度,该参数可以由上层调用时修改
  6. (                                                               
  7. input          I_sysclk,                                       //系统时钟信号
  8. input          I_rstn,                                         //全局复位
  9. );

  10. reg[32:0] t_cnt;
  11. always @(posedge I_sysclk or negedge I_rstn)begin
  12.     if(I_rstn==1'b0)                                             //系统复位
  13.        t_cnt <= 0;
  14.     else if(t_cnt == T_INR_CNT_SET)                             //计数t_cnt达到目标时清零
  15.        t_cnt <= 0;
  16.     else
  17.        t_cnt <= t_cnt + 1'b1;                                     //否则计数+1
  18. end

  19. endmodule
复制代码

计数模块完成后,需要将不同的值赋给LED端口。有三种方法可以实现流水灯的效果:通过位移的方法,向右位移,高位自动补0例如1000向右位移一位为0100,到最后一个状态时返回到第一个状态形成一种循环;通过状态机的方法,设置4个状态,对四个状态进行切换;通过剪切拼接的方法,将低位移到高位,通过{}拼接起来,数据始终由我们初始设定的4个数字组合形成。
2a8363cfb5dd46d2bdca8d8fcd440719.jpg
我们采用剪切拼接的方法,该方法代码简洁方便。设计一个always块(LED显示),每当计数模块计数到了1s,就让LED的输出变化一次。
  1. `timescale 1ns / 1ns

  2. module run_led#
  3. (
  4. parameter T_INR_CNT_SET = 32'd99_999_999             //分频时钟计数,初始时钟过快,人眼观察会导致LED常亮
  5. )                                          //设置分频系数,降低流水灯的变化速度,该参数可以由上层调用时修改(                                                              
  6. input          I_sysclk,                                       //系统时钟信号
  7. input          I_rstn,                                         //全局复位
  8. output [3:0]   O_led                                           //LED灯输出
  9. );

  10. reg [3:0] led_r;                                       //设置一个LED的寄存器,用来存储LED信号状态
  11. reg [32:0] t_cnt;                                         //计数器

  12. assign O_led = led_r;                                            //将寄存器内信号输出
  13. always @(posedge I_sysclk or negedge I_rstn)begin
  14.     if(I_rstn==1'b0)                                             //系统复位
  15.        t_cnt <= 0;
  16.     else if(t_cnt == T_INR_CNT_SET)                             //计数t_cnt达到目标时清零
  17.        t_cnt <= 0;
  18.     else
  19.        t_cnt <= t_cnt + 1'b1;                                     //否则计数+1
  20. end

  21. always @(posedge I_sysclk or negedge I_rstn)begin         //系统时钟的上升沿触发以及复位的下降沿触发
  22.     if(I_rstn==1'b0)
  23.         led_r <= 4'b0111;                             //设置LED 寄存器的初始状态,0表示灯亮,1表示灯灭
  24.     else if( t_cnt == 0)                             //当计数器计数达到预定值被清零时
  25.         led_r <= {led_r[0],led_r[3:1]};           //LED寄存器将最低为左移至最高位,通过{}来完成数据的拼接
  26. end

  27. endmodule
复制代码

7 添加管脚约束文件
管脚约束文件,即.xdc文件,默认工程路径,会存放在fpga_prj.srcs\constrs_1文件夹中,这里的“fpga_prj.srcs”对应的是创建工程的名称,米联客建议将引脚.xdc文件存放到创建的FPGA工程目录的uisrc\04_pin目录下,方便管理
添加管脚约束有三种方法,分别是新建XDC PIN脚约束文件、添加已经写好的约束文件、综合后添加管脚约束,用户根据实际情况选择其中一种方法,可以提高工作效率。
7.1 新建XDC PIN脚约束文件
Step1:单击
c70e4d9191ee432284e0759b49abaaec.jpg
(和添加.v文件一样)
Step2:选择Add or create constraints 然后单击NEXT
197c62de155e4b2eb2e55675a0e63108.jpg
Step3:点击Create File,创建一个新的.xdc文件。
92f9f033d4c5487da433abde1731db55.jpg
Step4:路径选择FPGA工程目录的uisrc\04_pin目录下,方便管理。
319eccfff85e4820b28c58c99aff1a9e.jpg
Step5:新建的xdc文件出现在工程中。
e5a51cff7e8a476992c8666574a098e2.jpg
:uisrc文件管理文件夹中存放的xdc文件
9b27193020654064b9e886e896975dc3.jpg
Step6:从工程中打开我们例程提供例的Pin脚文件,对照我们米联客提供的编译好的程序的PIN脚约束文件以及硬件原理图,完成xdc文件PIN脚的约束。教程是通用于不同板子的的教程,教程中代码、配图可能与工程中代码稍有不同,请以实际工程为准。
  1. #系统时钟周期约束
  2. create_clock -period 10.000 -name sysclk [get_ports I_sysclk_p]
  3. #时钟管脚物理,物理约束为具体的芯片管脚号约束
  4. set_property PACKAGE_PIN D9 [get_ports I_sysclk_p]
  5. #PL 一路差分 100M 时钟接到 BANK34 的 D9、D8 脚,由于 BANK34 是 DDR3L BANK 所以定义 FPGA IO 的时候可以使用 DIFF_SSTL135 电平约束。
  6. #电平约束不会改版实际的IO BANK电平,如果电平约束和实际的BANK电平不匹配,可能会导致工作异常
  7. set_property IOSTANDARD DIFF_SSTL135 [get_ports I_sysclk_p]

  8. #复位管脚约束,这里绑定到按键输入
  9. set_property PACKAGE_PIN U29 [get_ports I_rstn]
  10. #复位输入的电平约束为3V3的IO BANK电平
  11. set_property IOSTANDARD LVCMOS33 [get_ports I_rstn]
  12. set_property PULLUP true [get_ports I_rstn]

  13. #绑定led输出管脚到FPGA IO上
  14. set_property PACKAGE_PIN W24 [get_ports {O_led[4]}]
  15. set_property PACKAGE_PIN V23 [get_ports {O_led[3]}]
  16. set_property PACKAGE_PIN V26 [get_ports {O_led[2]}]
  17. set_property PACKAGE_PIN U25 [get_ports {O_led[1]}]
  18. set_property PACKAGE_PIN V22 [get_ports {O_led[0]}]
  19. set_property IOSTANDARD LVCMOS33 [get_ports {O_led[*]}]

  20. #对bit大小进行压缩,可以节省程序存储空间
  21. set_property CFGBVS VCCO [current_design]
  22. set_property CONFIG_VOLTAGE 3.3 [current_design]
  23. set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
  24. #set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
  25. #set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 2 [current_design]
  26. #set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]
复制代码

Step7:保存,XDC文件生成完成。
7.2添加XDC PIN脚约束文件
Step1:单击
ff41f1df4e5f455da5993e3d64d8a92d.jpg
(和添加.v文件一样)
Step2:选择Add or create constraints 然后单击NEXT
d9a0a2ef3f8f46b09b43f60cd2ac0dcc.jpg
Step3:单击Add Files
278c0b152a6144f1a06d6aa546266d9e.jpg
Step4:将要添加的.xdc文件添加进来,然后点击OK。
943636c486c04fa8bef3109e40375665.jpg
Step5:点击Finish完成约束文件的添加
7ac83945fac649a5a93bc2679330b405.jpg
7.3 综合添加管脚约束
Step1:打开RTL原理图。
8d56b488ea37437d81724d40d2f505cd.jpg
Step2:管脚配置。
选择I/O Planning
a203c21cd4cf41f79a660b18f237f29c.jpg
根据原理图配置Package Pin,本文本是比较通用于不同板子的的教程,教程中代码、配图可能与工程中代码稍有不同,请以实际工程为准。
859bf96bd81443ca931f9b2b58408ed9.jpg
Step3:保存,给XDC文件命名,生成.XDC文件,并保存到fpga_prj\uisrc\04_pin。
ba353641cac04482b6d80e29f1139564.jpg
创建一个fpga_pin.xdc的文件
43f9785ff7c24dc5bf9ad7f536ed74d7.jpg
8 优化管脚约束文件
对于ZYNQ为了减少bit文件大小,提高加载速度,在管脚约束文件中添加如下代码:
  1. #bit compress
  2. set_property CFGBVS VCCO [current_design]
  3. set_property CONFIG_VOLTAGE 3.3 [current_design]
  4. set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
复制代码

9 RTL仿真
编写testbench的主要目的是为了对使用的硬件描述语言设计的电路进行仿真验证,测试设计电路的功能、部分性能是否与预期的目标相符。一般编写的测试文件包以下内容:
  1. `timescale //仿真单位/仿真精度

  2. module Test_bench(); //通常 testbench没有输入与输出端口

  3.     信号或变量声明定义

  4.     逻辑设计中输入对应 reg型

  5.     逻辑设计中输出对应 wire 型

  6.     使用 initial  或 always语句产生激励 语句产生激励

  7.     例化设计模块

  8. endmodule
复制代码

9.1 添加仿真测试源码
仿真测试文件存放在工程目录uisrc\02_sim中,源码如下
  1. <blockquote>`timescale 1ns / 1ns
复制代码

为了减少我们的仿真时长,我们将T_INR_CNT_SET的计数值减小至1000。
9.2 仿真结果
为了方便观察现象,我们将LED的数值转换成二进制。
40816814ea18451d8723fd323bb0fec4.jpg
转换完成后,我们明显看到数值为1的高电平在向右进行偏移,反应到我们的开发板上,就形成了流水效果。
590a3b8454df40c4be0b2c595b30132d.jpg
10 编译并且产生bit文件
Step1:单击综合
Step2:单击执行
Step3:单击产生bit
e9f2d802959041e79cebcc799c9df114.jpg
1e7584383012454dbe659598b3d82357.jpg
11 下载程序
Step1:给开发板通电,并且连接下载器
Step2:单击OpenTarget 然后单击Auto Connect
de4fca676146445dacea1f73021d1ce3.jpg
Step3:连接成功后如下图所示:
644efcb3f31e4261b7367c791a63a32d.jpg
Step4:单击Program Device
eafa0145949d470b8b1b744a328547c7.jpg
Step5:单击选择上图的 FPGA,再单击Program Device,并且选择bit文件
9926351365c94f6fa9feb8c7fd295728.jpg

Step6:下载过程
d2153fa346f7490e8da4ad4223551b1d.jpg
12实验结果
(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)
请确保下载器和开发板已经正确连接,另外需要把核心板上的2P模式开关设置到JTAG模式,即ON ON,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)
下载过程下载完成后5个PL端LED流水灯就运行起来了。
9e9481e43f6d4740b79a8adb4c148e30.jpg
下载过程下载完成后4个PL端LED流水灯就运行起来了。
13总结
能做出结果一方面是证明代码工作,但是更重要的是从中学习到知识以及技巧,以及从一个简单的例子发散性思维去思考广泛的问题,举一反三。只有做到这样,对于初学者才能培养学习的兴趣,掌握更多的知识。一套书本,一套教程,给不同的人学习结果往往不一样,关键还是我们要学会思考,不要满足于功能的实现。以后做项目可以快一点,但是做完项目一定要总结,不能做一个丢一个,每次做的都是皮毛,而没有挖掘更深的本质。学习完流水灯实验至少有以下4个要点可以总结,包括IO约束、时钟约束
13.1 IO约束
通过本文你得自己掌握FPGA IO的管脚约束。我们编写的代码的顶层文件里面,引出的用户IO部分都是需要约束的。约束的意思就是把程序的引出管脚绑定到FPGA芯片上的IO管脚。
IO约束可以通过手工填入,也可以通过软件配置。
软件配置除了前面综合后点击Open Synthesized Design 窗口也可以通过打开原理图,如下图所示:
aa7dc444698344b893fbbd39583182de.jpg
修改后记得保存,否则不会更新到XDC约束文件中。
当然对于笔者,很多时候我都是直接编辑XDC文件。
13.2 时钟约束
一般我们需要对输入的时钟至少做周期约束,这个很简单,时钟约束的模板为:
  1. create_clock -name <clock_name> -period <period> [get_ports <clock port>]
复制代码
create_clock是生成约束约束命令。
name后面表示给这个时钟命名,您可以命为其他您所想要的名字,即使跟代码中的时钟名不同,都是可以的。
period后面表示约定该时钟的周期,默认单位为纳秒
所以我们这里对输入的100M时钟(有的板子是50M时钟)做周期约束,如下:
  1. create_clock -period 10.000 -name I_sysclk [get_ports I_sysclk]
复制代码
13.3 压缩程序
FPGA编译后的程序往往太大,通过压缩,可以减少大小,这样有几点好处:下载速度快,占用FLASH更小的体积,加载速度更快,通过配置FLASH为4线加载,以及配置FLASH的加载时钟。这个很简单,只要加入以下代码:
  1. <blockquote>set_property CFGBVS VCCO [current_design]
复制代码
如果你是ZYNQ 的FPGA,由于ZYNQ的FLASH不是通过FPGA加载的,而是通过ARM,那么只要以下几行代码:
  1. set_property CFGBVS VCCO [current_design]set_property CONFIG_VOLTAGE 3.3 [current_design]set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
复制代码
如果你是ZYNQ UltarScale+ 的FPGA,那么只要以下几行代码:
  1. set_property BITSTREAM.GENERAL.COMPRESS true [current_design]
复制代码

以上是代码各式说明含义呢?
FPGA工程师学习的东西挺多的,我们要还有了解硬件:
1、CFGBVS参数:
当 CFGBVS 连接至Bank 0的VCCO时,Bank 0的VCCO必须为2.5V或3.3V。如果Bank 14或15的I/O用于配置,则这些 Bank(14和15)的VCCO也必须为2.5V或3.3V。在 CFGBVS 连接至GND时,Bank 0的VCCO应为1.8V。如果Bank14或15的I/O用于配置,那么这些Bank14和15的VCCO也必须为 1.8V。
2、CONFIG_VOLTAGE
设置为对应配置电压我们这里是3.3V
3、BITSTREAM.GENERAL.COMPRESS
压缩BIT文件
4、BITSTREAM.CONFIG.CONFIGRATE
设置FLASH配置速度,仅纯FPGA有作用,对于ZYNQ不支持
5、BITSTREAM.CONFIG.SPI_BUSWIDTH
设置FLASH配置的位宽, 仅纯FPGA有作用,对于ZYNQ不支持
6、BITSTREAM.CONFIG.SPI_FALL_EDGE
设置FLASH接口的时钟加载沿, 仅纯FPGA有作用,对于ZYNQ不支持

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则