综合与实现
我们将通过一个完整的项目带大家体验 Vivado 开发流程。还记得我们在第一次课上演示的猜数字游戏吗?从现在开始,我们就能够运用所学的知识逐步实现这个游戏了。在这个项目里,我们将使用开发板上的 LED 制作一个简易流水灯。
2.1 代码设计
要如何实现上面的效果呢?我们可以思考下面的问题:
-
如何让指定的 LED 灯亮起?
很简单,只需要令
led
变量为我们期望的 8bits 数值即可。 -
如何实现流水的效果?
在我们的设计里,亮起的 LED 灯以一定的速度向左平移,如果已经到了最左侧的 7 号灯则会循环回到最右侧的 0 号灯。我们可以使用一个信号
shift
作为标志:当shift
信号发出时,就让亮起的 LED 灯向左移动一格。怎么实现循环移动的效果呢?或许我们可以这样做:让
led
信号的最高位挪到最低位,其余位顺次左移。这样就可以写出下面的代码:1 2 3
if (shift) begin led <= {led[6:0], led[7]}; end
思考
如果让 LED 灯一次向左循环移动两格,对应的代码是什么样子呢?
-
如何控制流水灯的速度?
现在我们只剩下
shift
信号没有确定了。我们可以每间隔一定的时间就发出一次shift
信号,改变时间间隔就可以实现不同速度的流水灯。『每间隔一定的时间就发出一次信号』?这个模块我们好像已经在这里设计过了!
考虑完这三个问题,流水灯模块的 Verilog 代码也就呼之欲出了。
LED.v | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
这里我们令计数器的上限值为 \(5\times10^7\),是因为开发板上的时钟频率是 100MHz。这样计数器每间隔 0.5s 就会发出一次信号,对应流水灯也向左移动一格。
接下来,请打开 Vivado,创建一个新的项目,随后创建一个新的设计文件 LED.v,写入上面的 Verilog 代码。你可以选择 Open Elaborated Design,以查看流水灯的 RTL 电路图。这些操作的具体细节可以参考 Lab2 的教程,这里就不再展开介绍。
2.2 ★ 综合与实现
上一次实验中,我们已经介绍了如何使用 Vivado 进行 RTL 分析与仿真。接下来我们将进入综合与实现的环节。
单击左侧的 Run Synthesis
即可开始综合(Synthesis)。在这一步,我们可以找到更多 warnings、critical warnings 和 errors,例如逻辑环路(自己的输出直接接到自己的输入)、多驱动(一个变量由多个 always 块或多个输入修改)等。
综合过程中也可以查看电路图,但这时的电路图大部分以查找表的形式出现,其结构与规模都和 RTL 电路有着较大的不同,因此很难找出问题。此外,为了更优的电路性能,综合出的电路也有可能把某一模块的元件安置到另一个模块里,所以在使用综合出的电路图查找问题时要关注这些细节。
在实现(Implementation)之前,我们需要一个额外的约束文件,用来指示模块输入输出端口和开发板端口之间的对应关系。由于开发板型号、配置不同,因此对应的约束文件也不同。本课程使用的约束文件可以在这里下载。
得到约束文件后,将其放入项目文件夹中,并在 Vivado 中添加设计文件 Constraints。
使用文件时,将需要连接接口的注释符号 #
去掉,改为对应模块接口的名字即可。例如,我们这次使用的约束文件如下:
## This file is a general .xdc for FPGAOL_BOARD (adopted from Nexys4 DDR Rev. C)
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project
## Clock signal
set_property -dict { PACKAGE_PIN E3 IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_35 Sch=clk100mhz
# create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports {CLK100MHZ}];
## FPGAOL LED (signle-digit-SEGPLAY)
set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports { led[0] }];
set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 } [get_ports { led[1] }];
set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports { led[2] }];
set_property -dict { PACKAGE_PIN G17 IOSTANDARD LVCMOS33 } [get_ports { led[3] }];
set_property -dict { PACKAGE_PIN D17 IOSTANDARD LVCMOS33 } [get_ports { led[4] }];
set_property -dict { PACKAGE_PIN E17 IOSTANDARD LVCMOS33 } [get_ports { led[5] }];
set_property -dict { PACKAGE_PIN F18 IOSTANDARD LVCMOS33 } [get_ports { led[6] }];
set_property -dict { PACKAGE_PIN G18 IOSTANDARD LVCMOS33 } [get_ports { led[7] }];
## FPGAOL SWITCH
#set_property -dict { PACKAGE_PIN D14 IOSTANDARD LVCMOS33 } [get_ports { sw[0] }];
#set_property -dict { PACKAGE_PIN F16 IOSTANDARD LVCMOS33 } [get_ports { sw[1] }];
#set_property -dict { PACKAGE_PIN G16 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }];
#set_property -dict { PACKAGE_PIN H14 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }];
#set_property -dict { PACKAGE_PIN E16 IOSTANDARD LVCMOS33 } [get_ports { sw[4] }];
#set_property -dict { PACKAGE_PIN F13 IOSTANDARD LVCMOS33 } [get_ports { sw[5] }];
#set_property -dict { PACKAGE_PIN G13 IOSTANDARD LVCMOS33 } [get_ports { sw[6] }];
#set_property -dict { PACKAGE_PIN H16 IOSTANDARD LVCMOS33 } [get_ports { sw[7] }];
## FPGAOL HEXPLAY
#set_property -dict { PACKAGE_PIN A14 IOSTANDARD LVCMOS33 } [get_ports { hexplay_data[0] }];
#set_property -dict { PACKAGE_PIN A13 IOSTANDARD LVCMOS33 } [get_ports { hexplay_data[1] }];
#set_property -dict { PACKAGE_PIN A16 IOSTANDARD LVCMOS33 } [get_ports { hexplay_data[2] }];
#set_property -dict { PACKAGE_PIN A15 IOSTANDARD LVCMOS33 } [get_ports { hexplay_data[3] }];
#set_property -dict { PACKAGE_PIN B17 IOSTANDARD LVCMOS33 } [get_ports { hexplay_an[0] }];
#set_property -dict { PACKAGE_PIN B16 IOSTANDARD LVCMOS33 } [get_ports { hexplay_an[1] }];
#set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports { hexplay_an[2] }];
## FPGAOL BUTTON & SOFT_CLOCK
set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports { btn }];
##USB-RS232 Interface
#set_property -dict { PACKAGE_PIN C4 IOSTANDARD LVCMOS33 } [get_ports { UART_TXD_IN }]; #IO_L7P_T1_AD6P_35 Sch=uart_txd_in
#set_property -dict { PACKAGE_PIN D4 IOSTANDARD LVCMOS33 } [get_ports { UART_RXD_OUT }]; #IO_L11N_T1_SRCC_35 Sch=uart_rxd_out
#set_property -dict { PACKAGE_PIN D3 IOSTANDARD LVCMOS33 } [get_ports { UART_CTS }]; #IO_L12N_T1_MRCC_35 Sch=uart_cts
#set_property -dict { PACKAGE_PIN E5 IOSTANDARD LVCMOS33 } [get_ports { UART_RTS }]; #IO_L5N_T0_AD13N_35 Sch=uart_rts
提醒
不要在约束文件中自行添加空格。这可能会导致语法问题。
完成添加后,单击 Run Implementation
即可开始实现。此时可能会出现更多更难以解决的问题,比如时序不满足(逻辑电路过长)等。但同时我们也可以看到更多资源使用(utilization)、功耗(power)、时序(timing)等方面的信息。
2.3 ★ 在 FPGAOL 上运行
到这里,我们开发的过程就基本结束了,接下来需要在 FPGAOL 平台上进行验证。
点击左侧最下方的 Generate Bitstream
即可生成比特流文件。在生成比特流文件之前,你可以不进行仿真、综合与实现的步骤(假定你的设计没有问题),Vivado 工具会自动完成综合、实现、布局布线等过程,并最终生成比特流文件。
生成比特流文件的过程一般较为漫长,且取决于所用设备的性能。助教在自己电脑上生成一次比特流耗时大约在三分钟,如果使用 VLAB 平台则可能是五分钟。你可以在开始生成前选择本次生成所使用的核心数,进而加速生成过程。
注意
如果你使用的是 Vlab 虚拟机上的 Vivado,可能会出现因为内存不足而无法生成 bit 流的问题。如果出现了这种情况,请改用 2019.1 版本的 Vivado 完成实验。
生成的文件一般存放在 工程目录/工程名.runs/impl_1/
目录下,为 顶层模块名.bit
。我们建议大家每次生成后将其拷贝到特定的目录,因为下次综合时会清除掉原先的比特流文件。完成后,点击 Cancel
按钮关闭弹出的对话框。
得到比特流文件后,在申请的在线开发板中单击 Select File
,将文件上传至服务器。上传完成后点击 Program!
就可以在线进行测试了。
Tips:物理开发板用户
如果你是开发板用户,可以在 Generate Bitstream
下面点击 Open HardWare Manager
,将开发板连接上电脑,点击 auto connect,连接完成后点击 Program device
即可烧板。
休息一会儿!
本部分内容到此结束,你理解了多少呢?
现在,你可以将自己的代码烧写成 bit 流并在 FPGAOL 平台上运行了。当然,你也可以自行为模块添加更多更为有趣的功能。
参考资料