FPGA开发流程
FPGA开发流程
- 前言
- 需求分析
- 模块设计
- 工程搭建(Vivado)
- 功能仿真(Modelsim)
- 生成下载文件
- 上板验证
- Flash固化
前言
本文是以“流水灯设计”进行FPGA开发流程讲解。需求如下:
使开发板上的4个LED灯顺序点亮并熄灭,循环往复产生流水灯的效果,流水间隔时间为0.5s。
需求分析
开发板上LED灯硬件连接的示意图如下图所示,当FPGA输出低电平时,LED灯点亮;输出高电平时,LED灯熄灭。
硬件实物图如下图所示:
FPGA引脚分配关系如下表所示:
引脚名称 | IO方向(相对于FPGA) | 电平标准(IOSTANDARD) | 引脚位置(PACKAGE_PIN) | 备注 |
---|---|---|---|---|
led[0] | Output | LVCOMS33 | F19 | |
led[1] | Output | LVCOMS33 | E21 | |
led[2] | Output | LVCOMS33 | D20 | |
led[3] | Output | LVCOMS33 | C20 |
模块设计
该需求功能是4个 LED 灯实现流水灯的效果,因此给模块命名为 flow_led。
两个灯流水的间隔时间为 0.5s,间隔时间的控制需要通过计数器来实现,即通过计数器来实现计时的功能,因此本次实验需要用到系统时钟;
除此之外,系统复位在 FPGA 系统中也是必不可少的,当程序出现跑飞等异常情况时,可以使程序恢复至默认状态;
由以上分析可知,本次实验需要2个输入的端口,分别为系统时钟和系统复位,输出为 4位的 LED 端口,模块框图如下图所示:
模块端口与功能描述如下表所示:
信号名 | 位宽 | 方向 | 端口说明 |
---|---|---|---|
sys_clk | 1 | 输入 | 系统时钟,50MHz |
sys_rstn | 1 | 输入 | 系统复位按键,低电平有效 |
led | 4 | 输出 | LED,低电平有效 |
前面需求分析过硬件设计部分,当IO输出低电平0时,点亮LED灯;当IO输出高电平1时,LED灯熄灭,因此要想控制LED实现流水灯效果,第一次的时候给led端口赋值4‘b1110;等待0.5s后,给led端口赋值4’b1101;等待0.5s后,给led端口赋值4’b1011;等待0.5s后,给led端口赋值4’b0111;后面依次类推,4个LED即可实现流水的效果。
LED 灯流水的间隔为 0.5s,因此接下来还需要做一个 0.5s 的延时,延时的功能可以通过计数器实现,每当计数器计时到 0.5s 后,切换一次 LED 灯的显示状态即可。当计时完成后,开始控制 LED 灯的状态。对于流水的功能非常适合移位的操作来实现,由此得到模块内部的框图如下图所示:
系统时钟 sys_clk 的时钟周期为 20ns(对应开发板板载的晶振频率为 50Mhz),计数器计时 0.5s 需要0.5s/20ns=500_000_000ns/20ns = 25_000_000 个时钟周期,由于计数器是从 0 开始计数,所以计数器最大计数到25000000-1,刚好是 0.5s。
由此绘制出 flow_led 模块的波形图如下图所示:
由上图可知, tcnt 循环从 0 计数到 24999999,表示计时 0.5s。每当计数器计数到最大值时,此时切换LED 灯的状态。
流水灯(flow_led.v)代码编写如下:
// 流水灯模块, 流水间隔0.5smodule flow_led (// 模块时钟及复位input sys_clk,input sys_rstn,// LED输出output [3:0] led
);//------------------------------------
// Local Signal
//------------------------------------reg [24:0] tcnt = 0; // 计数器reg [3:0] led_ff = 4'b1110; // 移位寄存器//------------------------------------
// User Logic
//------------------------------------
// 计数器计时0.5salways @ (posedge sys_clk or negedge sys_rstn)if (!sys_rstn)tcnt <= 25'd0;else if (tcnt < 24'd24_999_999)tcnt <= tcnt + 25'd1;elsetcnt <= 25'd0;
// led输出移位寄存器always @ (posedge sys_clk or negedge sys_rstn)if (!sys_rstn)led_ff <= 4'b1110;else if (tcnt == 24'd24_999_999)led_ff <= {led_ff[2:0],led_ff[3]};elseled_ff <= led_ff;
//------------------------------------
// Output Port
//------------------------------------assign led = led_ff;endmodule
流水灯(flow_led.v)的 Testbench 仿真代码编写如下:
`timescale 1ns/1nsmodule tb_flow_led;//*************************** Parameters ***************************parameter PERIOD_CLK = 20;//*************************** Signals ***************************// 模块时钟及复位reg sys_clk = 0;reg sys_rstn = 0;// LED输出wire [3:0] led;//*************************** Test Logic ***************************always # (PERIOD_CLK/2) sys_clk = ~sys_clk;initialbegin#100;sys_rstn = 1;#5000;$stop;end//*************************** Instance ***************************flow_led u_flow_led(// 模块时钟及复位.sys_clk (sys_clk),.sys_rstn (sys_rstn),// LED输出.led (led));endmodule
由于流水灯模块定义的流水0.5s间隔较长,虽然 0.5s 从时间上来说比较短暂,但是对于仿真来说,仿真 0.5s 需要的时间较长。尤其是对于复杂的程序来说,有时候仿真几十毫秒,就需要好几个小时,因此为了降低仿真的等待时间和提升效率,一般需要对程序中延时较长的代码进行修改,流水灯模块修改后的代码如下(将流水时间间隔改为500ns):
// 流水灯模块, 流水间隔0.5smodule flow_led (// 模块时钟及复位input sys_clk,input sys_rstn,// LED输出output [3:0] led
);//------------------------------------
// Local Signal
//------------------------------------reg [24:0] tcnt = 0; // 计数器reg [3:0] led_ff = 4'b1110; // 移位寄存器//------------------------------------
// User Logic
//------------------------------------
// 计数器计时0.5salways @ (posedge sys_clk or negedge sys_rstn)if (!sys_rstn)tcnt <= 25'd0;// else if (tcnt < 24'd24_999_999)else if (tcnt < 24'd24)tcnt <= tcnt + 25'd1;elsetcnt <= 25'd0;
// led输出移位寄存器always @ (posedge sys_clk or negedge sys_rstn)if (!sys_rstn)led_ff <= 4'b1110;// else if (tcnt == 24'd24_999_999)else if (tcnt == 24'd24)led_ff <= {led_ff[2:0],led_ff[3]};elseled_ff <= led_ff;
//------------------------------------
// Output Port
//------------------------------------assign led = led_ff;endmodule
工程搭建(Vivado)
-
双击打开 Vivado,点击”Create Project“;
-
点击 ”Next“;
-
工程命名;
-
选择工程类型”RTL Project“,点击”Next“;
-
一直点击 ”Next“;
-
选择芯片型号;
-
添加代码和仿真文件;
功能仿真(Modelsim)
-
设置选择仿真工具为 Modelsim;
-
设置Modelsim安装路径和库路径;
-
点击开始仿真;
-
添加要观察的信号至波形窗口;
-
点击“Run all”,查看仿真波形是否跟功能一致;
-
由上图可知, led=4’b1110的持续时间为 500ns, led=4’b1101 的持续时间同样也是 500ns,即 LED 灯的流水间隔为 500ns,波形和预期相符。
需要注意的是,在仿真结束之后,将代码中定义的 LED 流水间隔时间改回 0.5s,否则 500ns 的时间间隔太短,人的肉眼会看不到流水的效果。
生成下载文件
-
编写约束文件;
############## NET - IOSTANDARD ################## set_property CFGBVS VCCO [current_design] set_property CONFIG_VOLTAGE 3.3 [current_design] #############SPI Configurate Setting################## set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] set_property CONFIG_MODE SPIx4 [current_design] set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design] ############# clock ######################### set_property IOSTANDARD LVCMOS33 [get_ports sys_clk] set_property PACKAGE_PIN Y18 [get_ports sys_clk] create_clock -period 20.000 [get_ports sys_clk] ############# reset ########################## set_property IOSTANDARD LVCMOS33 [get_ports sys_rstn] set_property PACKAGE_PIN F20 [get_ports sys_rstn] ########################### LED Define ########################### set_property PACKAGE_PIN F19 [get_ports {led[0]}] set_property PACKAGE_PIN E21 [get_ports {led[1]}] set_property PACKAGE_PIN D20 [get_ports {led[2]}] set_property PACKAGE_PIN C20 [get_ports {led[3]}]set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}]
-
添加约束文件;
-
Bitstream 设置,同时生成固化文件;
-
点击生成 Bitstream;
-
生成文件路径如下:
上板验证
- 先连接硬件,把 JTAG 下载器和开发板连接,然后开发板上电,下图为开发板的硬件连接图:
-
点击 Open target 按钮->Auto Connect, 在 hardware 界面下会显示 xc7a35t_0( AX7035 开发板)或 xc7s50_0( AX7050 开发板) 的图标,说明 JTAG 连接已经建立 ;
-
右键选择 xc7a35t_0( AX7035 开发板)或 xc7s50_0( AX7050 开发板) ,在弹出的选项里选择Program Device 项 ;
-
在弹出的 Program Device 对话框中, 选择 flow_led 项目生成的 bit 文件,点击 Program 按钮烧写 FPGA。
-
烧写完成后 xc7a35t_0( AX7035 开发板)或 xc7s50_0( AX7050 开发板) 的状态会变成Programmed, 这时我们可以看到开发板上的四个 LED 灯已经在做流水灯动作了 。
Flash固化
直接下载 Bit 文件到 FPGA 后,开发板重新上电后配置程序已经丢失,还需要JTAG 下载,只有固化到 Flash,才能实现上电不丢失。具体步骤如下:
-
在如下图中右键选择 xc7a35t_0( AX7035 开发板)或 xc7s50_0( AX7050 开发板) 芯片,在弹出的列表中选择 Add Configruation Memory Device… ;
注意:如果发现此项变为灰色不能选,是因为我们提供的工程中已经添加了 FLASH 配置,不能再添加 FLASH, 如下:
当然自己如果想在已有 flash 的工程中再次添加一下进行实验话,可按如下图移除 flash,然后按上面添加 flash 的步骤进行即可:
-
在 Add configruation Memory Device 的配置界面里选择正确的 FLASH 型号,如下图所示:
-
提示是否对 SPI FLASH 进行编程, 点击 OK;
-
在弹出的 Program Configuration Memory Device 窗口中, Configration file 项选择 Vivado 生成的flow_led.bin 文件(此文件默认在 impl_1 目录下) 。 PRM File 项不用选。另外在这个窗口用户还可以配置 I/O 为上拉,下拉或者无上下拉。 配置操作选项保留默认就可以;
-
点击 OK 开始编程 FLASH;
-
FLASH 编程完毕后, 会弹出如下成功的界面。
-
至此, SPI FLASH 烧写完毕, flow_led 程序已经固化到 SPI FLASH 中了。我们来验证一下,关电重新启动开发板, 等待一会儿你就可以看到开发板上的 LED 灯已经在做跑马运动了。