05 FPGA流水灯实验

发布时间 2023-12-29 10:04:25作者: 米联客(milianke)

软件版本:VIVADO2021.1

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA

登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!

1 概述

本章课程以大家熟悉的流水灯为例子,详细讲解了VIVADO软件的使用,包括创建FPGA工程,编写Verilog代码,添加管脚约束,最后编译,下载bit文件到开发板测试。对于初学XILINX FPGA的读者请注意,bit文件断电后就丢失了。如果实现上电能够启动程序,需要把BIT文件打包成bin或者MCS文件才可以。如果打包,固化到FLASH。由于是基础课程,读者一定不要停留在实现了功能的目的,而是要真正掌握代代码的编写能力,写出优秀的代码。即便是功能实现了,但是不同的代码也代表了不同的水平,作为编程者一定要时刻要求自己写出高水平的代码。

在开始具体的实验前,我们先温习下FPGA的开发流程。

1.1 FPGA的开发流程概述

如上图所示,FPGA的开发流程以中心轴线来说包含了:

1.FPGA代码设计

2.绑定IO约束

3.综合

4.布局布线

5.编译下载/固化

以下内容对每个阶段做一些简介,具体用户理解可以结合后面的代码加以理解。

1.2 FPGA代码设计

该阶段我们根据项目的设计目标,设计FPGA的代码,一般中大型项目中,大量使用现成的IP或者自己编写的一些IP构建代码。所以该阶段使用成熟的IP可以大大提高FPGA项目开发的效率。所以大家在选型FPGA的时候优先会考虑AMD(美国)、Intel(美国)、Anlogic(中国)大厂FPGA,这些大厂都提供了较为完整的成熟IP库提供给客户使用。

在一些小型的项目中,我们会自己编写一些IP构建自己的第三方IP库以便后期更加高效的完成中大型项目的开发。

这个阶段的代码一般需要通过行为级的仿真来验证代码的功能逻辑是否正常。不过有时候编写testbench仿真文件是一个复杂的工作,而项目中大量使用成熟的IP来构建项目工程,这时候我们可能也会跳过仿真步骤直接进入下个步骤。

1.3 绑定IO

    FPGA的IO可以灵活定义,代码的功能最终需要通过IO去和外部实现通信或者功能的执行。该阶段通过IO约束文件,导入到工程中。

1.4综合

FPGA代码本质是电路设计,通过代码以及软件工具实现自动化的电路设计,在电路设计中,网表(Netlist)是用于描述电路元件相互之间连接关系的;综合就是逻辑综合器根据约束条件把 Verilog HDL 或 VHDL 描述的 RTL 级设计,转换为可与 FPGA/CPLD 的门阵列基本结构相映射的网表文件。FPGA开发环境的综合器就是能够将 Schematic 或者 HDL 文本语言描述的电路,编译成由与或阵列、RAM、触发器、寄存器等逻辑单元组成的电路结构的门级网表文件的工具,即 RTL - to - Netlist。

在综合阶段加入正确的时序约束有利于综合工具更加高效的工作,综合阶段的仿真也更加接近实际硬件的真实运行结果。

1.5布局布线

网表提供的是逻辑关系上FPGA内部资源包括LUT(FPGA的查找表单元)结构、PLL、BRAM链接,布局布线工具需要把这些结构配置到FPGA的具体位置。FPGA芯片内部的位置一般通过坐标进行了标定,这样不同的FPGA都会有自己资源的坐标。布局布线工具就可以通过算法把网表映射到时间的资源上。

在布局布线阶段加入正确的时序约束有利于布局布线工具更加高效的工作,该阶段的仿真结果几乎等同于实际硬件的运行结果。

需要注意的是,综合阶段的时序约束,可能在布局布线后会需要调整,也就是说布局布线阶段的才是最合实际硬件运行相符的。

2 硬件电路分析

硬件接口和子卡模块请阅读"附录 1"

配套工程的 FPGA PIN 脚定义路径为 fpga_prj/uisrc/04_pin/ fpga_pin.xdc。

3 新建VIVADO工程

Step1:启动VIVADO,单击Create Project

Step2:单击NEXT

Step3:创建名为fpga_prj的工程到对应的文件目录,文件路径自定义,不能有中文或非法字符,之后单击NEXT

Step4:选择RTL Project并且勾选复选框,之后单击NEXT

Step5:选择芯片的型号和封装速度等级:

100T的选项:Artix-7 系列,封装FGG484,速度等级-2

本文本是比较通用于不同板子的的教程,教程中代码、配图可能与工程中代码稍有不同,请以实际工程为准。

Step:6 单击Finish完成工程创建。

4 添加代码管理文件夹

米联客的代码管理规范,在对应的FPGA工程路径下创建uisrc路径,并且创建以下文件夹

01_rtl:放用户编写的rtl代码

02_sim:仿真文件或者工程

03_ip:放使用到的ip文件

04_pin:放fpga的pin脚约束文件或者时序约束文件

05_boot:放编译好的bit或者bin文件(一般为空)

06_doc:放本一些相关文档(一般为空)

5 添加工程文件

Step1:打开VIVADO软件

Step2:单击 Add Sources

 

 

 

Step3:选择单击Add or Create Design Sources 然后单击NEXT

Step4:单击Create File 来创建文件

Step5:创建一个run_led的文件,并且文件类型选择Verilog,路径可以选择存放在工程默认路径下,米联客推荐存放在创建的FPGA工程目录的uisrc\01_rtl目录下,方便管理。

Step6:添加完成后如下图所示之后单击finish完成文件的创建

Step7:继续弹出的对话空中,可以设置一些端口,但是我们现在什么都不做。单击OK

Step8:创建完成后可以看到Design Sources文件夹中有了run_led.v这个文件,这个文件就是我们可以编写verilog程序的文件,注意以下FPGA型号以用户实际使用的芯片为准。

6 Verilog FPGA流水灯实验

双击run_led.v打开流水程序源码,并且修改为如下代码。

`timescale 1ns / 1ns

 

module run_led#

(

parameter T_INR_CNT_SET = 32'd49_999_999            //分频时钟计数,初始时钟过快,人眼观察会导致LED常亮

)                                                               //设置分频系数,降低流水灯的变化速度

(                                                               //该参数可以由上层调用时修改

 input          I_sysclk,                                       //系统时钟信号

 input          I_rstn,                                         //全局复位

 output [3:0]   O_led                                           //LED灯输出

);

 

reg [3:0] led_r;                                      //设置一个LED的寄存器,用来存储LED信号状态

reg[32:0] t_cnt;

 

assign O_led = led_r;                                            //将寄存器内信号输出

always @(posedge I_sysclk or negedge I_rstn)begin

    if(I_rstn==1'b0)                                             //系统复位

       t_cnt <= 0;

    else if(t_cnt == T_INR_CNT_SET)                             //计数t_cnt达到目标时清零

       t_cnt <= 0;

    else

       t_cnt <= t_cnt + 1'b1;                                     //否则计数+1

end

 

always @(posedge I_sysclk or negedge I_rstn)begin         //系统时钟的上升沿触发以及复位的下降沿触发

    if(I_rstn==1'b0)

        led_r <= 4'b1000;                             //设置LED 寄存器的初始状态,1表示灯亮,0表示灯灭

    else if( t_cnt == 0)                            //当计数器计数达到预定值被清零时

        led_r <= {led_r[0],led_r[3:1]};           //LED寄存器将最低为左移至最高位,通过{}来完成数据的拼接

end

endmodule

可以看出这是一个空的工程,我们现在要添加代码同时也要添加工程信息。

这样我们就编写好了代码下面还要添加管脚约束文件。

7 添加管脚约束文件

管脚约束文件,即.xdc文件,默认工程路径,会存放在fpga_prj.srcs\constrs_1文件夹中,这里的"fpga_prj.srcs"对应的是创建工程的名称,米联客建议将引脚.xdc文件存放到创建的FPGA工程目录的uisrc\04_pin目录下,方便管理

添加管脚约束有三种方法,分别是新建XDC PIN脚约束文件、添加已经写好的约束文件、综合后添加管脚约束,用户根据实际情况选择其中一种方法,可以提高工作效率。

7.1 新建XDC PIN脚约束文件

Step1:单击(和添加.v文件一样)

Step2:选择Add or create constraints 然后单击NEXT

Step3:点击Create File,创建一个新的.xdc文件。

Step4:路径选择FPGA工程目录的uisrc\04_pin目录下,方便管理。

Step5:新建的xdc文件出现在工程中。

:uisrc文件管理文件夹中存放的xdc文件

Step6:从工程中打开我们例程提供例的Pin脚文件,对照我们米联客提供的编译好的程序的PIN脚约束文件以及硬件原理图,完成xdc文件PIN脚的约束(本教程是通用于不同板子的的教程,教程中代码、配图可能与工程中代码稍有不同,请以实际工程为准。)

#系统时钟周期约束

create_clock -period 20.000 -name sysclk [get_ports I_sysclk]

#时钟管脚物理,物理约束为具体的芯片管脚号约束

set_property PACKAGE_PIN V4 [get_ports I_sysclk]

#电平属性为SSTL135,代表了1.35V的IO BANK,电平约束不会改版实际的IO BANK电平,如果电平约束和实际的BANK电平不匹配,可能会导致工作异常

set_property IOSTANDARD SSTL135 [get_ports I_sysclk]

 

#复位管脚约束,这里绑定到按键输入

set_property PACKAGE_PIN R14 [get_ports I_rstn]

#复位输入的电平约束为3V3的IO BANK电平

set_property IOSTANDARD LVCMOS33 [get_ports I_rstn]

set_property PULLUP true [get_ports I_rstn]

 

#绑定led输出管脚到FPGA IO上

set_property PACKAGE_PIN E21 [get_ports {O_led[3]}]

set_property PACKAGE_PIN D21 [get_ports {O_led[2]}]

set_property PACKAGE_PIN E22 [get_ports {O_led[1]}]

set_property PACKAGE_PIN D22 [get_ports {O_led[0]}]

set_property IOSTANDARD LVCMOS33 [get_ports {O_led[*]}]

 

#bit compress

set_property CFGBVS VCCO [current_design]

set_property CONFIG_VOLTAGE 3.3 [current_design]

set_property BITSTREAM.GENERAL.COMPRESS true [current_design]

set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]

set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]

set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]

Step7:保存,XDC文件生成完成。

7.2 添加XDC PIN脚约束文件

Step1:单击(和添加.v文件一样)

Step2:选择Add or create constraints 然后单击NEXT

Step3:单击Add Files

 

Step4:将要添加的.xdc文件添加进来,然后点击OK

Step5:点击Finish完成约束文件的添加

7.3 综合并添加管脚约束

Step1:打开RTL原理图。

 

Step2:管脚配置。

选择I/O Planning

根据原理图配置Package Pin,(本文本是比较通用于不同板子的的教程,教程中代码、配图可能与工程中代码稍有不同,请以实际工程为准。)

Step3:保存,给XDC文件命名,生成.XDC文件,并保存到fpga_prj\uisrc\04_pin

创建一个fpga_pin.xdc的文件

8 优化管脚约束文件

对于ZYNQ为了减少bit文件大小,提高加载速度,在管脚约束文件中添加如下代码:

#bit compress

set_property CFGBVS VCCO [current_design]

set_property CONFIG_VOLTAGE 3.3 [current_design]

set_property BITSTREAM.GENERAL.COMPRESS true [current_design]

set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]

set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]

set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]

9 RTL仿真

9.1 添加仿真测试源码

仿真测试文件存放在工程目录uisrc\02_sim中,源码如下:

`timescale 1ns / 1ns

 

module tb_run_led();

 

reg I_sysclk,I_rstn;      //时钟信号以及复位信号

wire [3:0]  O_led;        //仿真的LED 信号

 

run_led#

(

.T_INR_CNT_SET(1000)       //设置一个较小的时钟计数参数,可以大大缩小我们仿真需要的时间

)

run_led_inst(

 .I_sysclk(I_sysclk),      //例化时钟接口

 .I_rstn(I_rstn),          //例化复位接口

 .O_led(O_led)             //例化led接口

);

 

initial begin

   I_sysclk  <= 1'b0;     //时钟信号的寄存器设置初值

   I_rstn    <= 1'b0;     //复位信号的寄存器设置初值

   #100;                  //延时100个时间单位

   I_rstn    <= 1'b1;     //复位恢复高点平,模拟复位完成

end

 

always #10 I_sysclk=~I_sysclk;    //每过20个时钟周期,模拟的系统时钟信号进行一次翻转

 

endmodule

为了减少我们的仿真时长,我们将T_INR_CNT_SET的计数值减小至1000。

9.2 仿真结果

为了方便观察现象,我们将LED的数值转换成二进制。

转换完成后,我们明显看到数值为1的高电平在向右进行偏移,反应到我们的开发板上,就形成了流水效果。

10 编译并且产生bit文件

Step1:单击综合

Step2:单击执行

Step3:单击产生bit

11 下载程序

Step1:给开发板通电,并且连接下载器

Step2:单击OpenTarget 然后单击Auto Connect

Step3:连接成功后如下图所示:

Step4:单击Program Device

Step5:单击选择上图的 FPGA,再单击Program Device,并且选择bit文件

Step6:下载过程

12 实验结果

(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)

请确保下载器和开发板已经正确连接,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)

下载过程下载完成后4PLLED流水灯就运行起来了。

13 总结

能做出结果一方面是证明代码工作,但是更重要的是从中学习到知识以及技巧,以及从一个简单的例子发散性思维去思考广泛的问题,举一反三。只有做到这样,对于初学者才能培养学习的兴趣,掌握更多的知识。一套书本,一套教程,给不同的人学习结果往往不一样,关键还是我们要学会思考,不要满足于功能的实现。以后做项目可以快一点,但是做完项目一定要总结,不能做一个丢一个,每次做的都是皮毛,而没有挖掘更深的本质。学习完流水灯实验至少有以下4个要点可以总结,包括IO约束、时钟约束

13.1 IO约束

通过本文你得自己掌握FPGA IO的管脚约束。我们编写的代码的顶层文件里面,引出的用户IO部分都是需要约束的。约束的意思就是把程序的引出管脚绑定到FPGA芯片上的IO管脚。

IO约束可以通过手工填入,也可以通过软件配置。

软件配置除了前面综合后点击Open Synthesized Design 窗口也可以通过打开原理图,如下图所示:

修改后记得保存,否则不会更新到XDC约束文件中。

当然对于笔者,很多时候我都是直接编辑XDC文件。

13.2 时钟约束

一般我们需要对输入的时钟至少做周期约束,这个很简单,时钟约束的模板为:

create_clock -name <clock_name> -period <period> [get_ports <clock port>]

create_clock是生成约束约束命令。

name后面表示给这个时钟命名,您可以命为其他您所想要的名字,即使跟代码中的时钟名不同,都是可以的。

period后面表示约定该时钟的周期,默认单位为纳秒

所以我们这里对输入的50M时钟(有的板子是100M时钟)做周期约束,如下:

create_clock -period 10.000 -name CLK_i [get_ports CLK_i]

13.3 压缩程序

FPGA编译后的程序往往太大,通过压缩,可以减少大小,这样有几点好处:下载速度快,占用FLASH更小的体积,加载速度更快,通过配置FLASH为4线加载,以及配置FLASH的加载时钟。这个很简单,只要加入以下代码:

set_property CFGBVS VCCO [current_design]

set_property CONFIG_VOLTAGE 3.3 [current_design]

set_property BITSTREAM.GENERAL.COMPRESS true [current_design]

set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]

set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]

set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]

如果你是ZYNQ 的FPGA,由于ZYNQ的FLASH不是通过FPGA加载的,而是通过ARM,那么只要以下几行代码:

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,那么只要以下几行代码:

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不支持