跳转至

综合与实现

我们将通过一个完整的项目带大家体验 Vivado 开发流程。还记得我们在第一次课上演示的猜数字游戏吗?从现在开始,我们就能够运用所学的知识逐步实现这个游戏了。在这个项目里,我们将使用开发板上的 LED 制作一个简易流水灯。

Image title

流水灯效果图

2.1 代码设计

要如何实现上面的效果呢?我们可以思考下面的问题:

  1. 如何让指定的 LED 灯亮起?

    很简单,只需要令 led 变量为我们期望的 8bits 数值即可。

  2. 如何实现流水的效果?

    在我们的设计里,亮起的 LED 灯以一定的速度向左平移,如果已经到了最左侧的 7 号灯则会循环回到最右侧的 0 号灯。我们可以使用一个信号 shift 作为标志:当 shift 信号发出时,就让亮起的 LED 灯向左移动一格。

    怎么实现循环移动的效果呢?或许我们可以这样做:让 led 信号的最高位挪到最低位,其余位顺次左移。这样就可以写出下面的代码:

    1
    2
    3
    if (shift) begin
        led <= {led[6:0], led[7]};
    end
    
    思考

    如果让 LED 灯一次向左循环移动两格,对应的代码是什么样子呢?

  3. 如何控制流水灯的速度?

    现在我们只剩下 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
module LED (
    input                   clk,
    input                   btn,
    output reg [7:0]        led
);

reg [31:0] count_1hz;
wire rst;
parameter TIME_CNT = 50_000_000;

assign rst = btn;   // Use button for RESET

always @(posedge clk) begin
    if (rst)
        count_1hz <= 0;
    else if (count_1hz >= TIME_CNT)
        count_1hz <= 0;
    else
        count_1hz <= count_1hz + 1;
end

always @(posedge clk) begin
    if (rst)
        led <= 8'b0000_1111;
    else if (count_1hz == 1) begin
        led <= {led[6:0], led[7]};
    end
end
endmodule

这里我们令计数器的上限值为 \(5\times10^7\),是因为开发板上的时钟频率是 100MHz。这样计数器每间隔 0.5s 就会发出一次信号,对应流水灯也向左移动一格。

接下来,请打开 Vivado,创建一个新的项目,随后创建一个新的设计文件 LED.v,写入上面的 Verilog 代码。你可以选择 Open Elaborated Design,以查看流水灯的 RTL 电路图。这些操作的具体细节可以参考 Lab2 的教程,这里就不再展开介绍。

2.2 ★ 综合与实现

上一次实验中,我们已经介绍了如何使用 Vivado 进行 RTL 分析与仿真。接下来我们将进入综合与实现的环节。

Image title

单击左侧的 Run Synthesis 即可开始综合(Synthesis)。在这一步,我们可以找到更多 warnings、critical warnings 和 errors,例如逻辑环路(自己的输出直接接到自己的输入)、多驱动(一个变量由多个 always 块或多个输入修改)等。

综合过程中也可以查看电路图,但这时的电路图大部分以查找表的形式出现,其结构与规模都和 RTL 电路有着较大的不同,因此很难找出问题。此外,为了更优的电路性能,综合出的电路也有可能把某一模块的元件安置到另一个模块里,所以在使用综合出的电路图查找问题时要关注这些细节。

Image title

在实现(Implementation)之前,我们需要一个额外的约束文件,用来指示模块输入输出端口和开发板端口之间的对应关系。由于开发板型号、配置不同,因此对应的约束文件也不同。本课程使用的约束文件可以在这里下载。

得到约束文件后,将其放入项目文件夹中,并在 Vivado 中添加设计文件 Constraints。

Image title

使用文件时,将需要连接接口的注释符号 # 去掉,改为对应模块接口的名字即可。例如,我们这次使用的约束文件如下:

## 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 平台则可能是五分钟。你可以在开始生成前选择本次生成所使用的核心数,进而加速生成过程。

Image title

注意

如果你使用的是 Vlab 虚拟机上的 Vivado,可能会出现因为内存不足而无法生成 bit 流的问题。如果出现了这种情况,请改用 2019.1 版本的 Vivado 完成实验。

生成的文件一般存放在 工程目录/工程名.runs/impl_1/ 目录下,为 顶层模块名.bit我们建议大家每次生成后将其拷贝到特定的目录,因为下次综合时会清除掉原先的比特流文件。完成后,点击 Cancel 按钮关闭弹出的对话框。

得到比特流文件后,在申请的在线开发板中单击 Select File,将文件上传至服务器。上传完成后点击 Program! 就可以在线进行测试了。

Image title

Tips:物理开发板用户

如果你是开发板用户,可以在 Generate Bitstream 下面点击 Open HardWare Manager,将开发板连接上电脑,点击 auto connect,连接完成后点击 Program device 即可烧板。

Image title


休息一会儿!

本部分内容到此结束,你理解了多少呢?

现在,你可以将自己的代码烧写成 bit 流并在 FPGAOL 平台上运行了。当然,你也可以自行为模块添加更多更为有趣的功能。


最后更新: October 18, 2023

评论

Authors: wintermelon008