综合与实现
我们将通过一个完整的项目带大家体验 Vivado 开发流程,在这个项目里,我们将使用开发板上的 LED 制作一个简易流水灯。
注意
由于实验平台更新,vivado中创建项目时,需要选择的芯片型号变更为 xc7a35tfgg484-1,相应的,fpga平台应选择ZYNQ,约束文件应使用fpgaol_zynq.xdc。请大家注意!
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。
使用文件时,将需要连接接口的注释符号 #
去掉,改为对应模块接口的名字即可。例如,我们这次使用的约束文件如下:
## FPGAOL ZYNQ XDC v2.1
## device xc7a35tfgg484-1
## CLOCK
set_property -dict { PACKAGE_PIN V18 IOSTANDARD LVCMOS33 } [get_ports {clk}]
create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk]
## LED
set_property -dict { PACKAGE_PIN B15 IOSTANDARD LVCMOS33 } [get_ports {led[0]}]
set_property -dict { PACKAGE_PIN B16 IOSTANDARD LVCMOS33 } [get_ports {led[1]}]
set_property -dict { PACKAGE_PIN C13 IOSTANDARD LVCMOS33 } [get_ports {led[2]}]
set_property -dict { PACKAGE_PIN B13 IOSTANDARD LVCMOS33 } [get_ports {led[3]}]
set_property -dict { PACKAGE_PIN A15 IOSTANDARD LVCMOS33 } [get_ports {led[4]}]
set_property -dict { PACKAGE_PIN A16 IOSTANDARD LVCMOS33 } [get_ports {led[5]}]
set_property -dict { PACKAGE_PIN A13 IOSTANDARD LVCMOS33 } [get_ports {led[6]}]
set_property -dict { PACKAGE_PIN A14 IOSTANDARD LVCMOS33 } [get_ports {led[7]}]
## SWITCH
#set_property -dict { PACKAGE_PIN B17 IOSTANDARD LVCMOS33 } [get_ports {sw[0]}]
#set_property -dict { PACKAGE_PIN B18 IOSTANDARD LVCMOS33 } [get_ports {sw[1]}]
#set_property -dict { PACKAGE_PIN D17 IOSTANDARD LVCMOS33 } [get_ports {sw[2]}]
#set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 } [get_ports {sw[3]}]
#set_property -dict { PACKAGE_PIN C18 IOSTANDARD LVCMOS33 } [get_ports {sw[4]}]
#set_property -dict { PACKAGE_PIN C19 IOSTANDARD LVCMOS33 } [get_ports {sw[5]}]
#set_property -dict { PACKAGE_PIN E19 IOSTANDARD LVCMOS33 } [get_ports {sw[6]}]
#set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports {sw[7]}]
## BUTTON
set_property -dict {PACKAGE_PIN F20 IOSTANDARD LVCMOS33} [get_ports {btn}]
## SEG DATA
#set_property -dict { PACKAGE_PIN F18 IOSTANDARD LVCMOS33 } [get_ports {d[0]}]
#set_property -dict { PACKAGE_PIN E18 IOSTANDARD LVCMOS33 } [get_ports {d[1]}]
#set_property -dict { PACKAGE_PIN B20 IOSTANDARD LVCMOS33 } [get_ports {d[2]}]
#set_property -dict { PACKAGE_PIN A20 IOSTANDARD LVCMOS33 } [get_ports {d[3]}]
## SEG AN
#set_property -dict { PACKAGE_PIN A18 IOSTANDARD LVCMOS33 } [get_ports {an[0]}]
#set_property -dict { PACKAGE_PIN A19 IOSTANDARD LVCMOS33 } [get_ports {an[1]}]
#set_property -dict { PACKAGE_PIN F19 IOSTANDARD LVCMOS33 } [get_ports {an[2]}]
## UART
#set_property -dict { PACKAGE_PIN F15 IOSTANDARD LVCMOS33 } [get_ports {rxd}]
#set_property -dict { PACKAGE_PIN F13 IOSTANDARD LVCMOS33 } [get_ports {txd}]
## FRAMEBUFFER
#set_property -dict {PACKAGE_PIN E17 IOSTANDARD LVCMOS33} [get_ports {sck}]
#set_property -dict {PACKAGE_PIN C14 IOSTANDARD LVCMOS33} [get_ports {mosi}]
#set_property -dict {PACKAGE_PIN C15 IOSTANDARD LVCMOS33} [get_ports {miso}]
提醒
完成添加后,单击 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 平台上运行了。当然,你也可以自行为模块添加更多更为有趣的功能。
参考资料