跳转至

Vivado 使用入门

计算机组成原理实验使用的是 Vivado 开发环境 2019.2 版本。Vivado 开发环境非常庞大,组成原理实验只需要安装最基础的 WebPack 免费版本即可。免费版本包含了生成实验板所需要的比特流 bit 文件的生成工具。安装过程可以参考Vivado 安装

需要注意的是 Vivado 不支持中文目录(机器名或用户名为中文也可能在路径中引入中文),务必不要安装到中文目录下使用,也不要将 Vivado 的项目放到中文目录下面,否则会综合不过。实验平台后台的自动测试工具只支持 Vivado 2019.2 版本,不支持旧的版本,也不支持新的版本,请仅使用 2019.2 的版本实验。

创建项目

注意

这里创建项目的流程是给同学们演示一个 Vivado 项目的各个环节,包括创建项目,编写代码,综合,实现等的过程。理解整个流程可以帮助理解硬件设计的过程。但是,实际上在后续的实验中我们已经创建了一个专门针对实验环境的顶层项目 thinpad_top。后面的文档中有关于顶层项目的详细讨论和介绍。实验只需要依赖这个顶层项目工作即可。

这里是 Vivado 使用入门,让大家熟悉一下环境,以及开发的各个流程。真正的实验需要复制顶层项目,针对不同的实验在顶层项目中进行增删代码。

首次进入 Vivado 的界面:

创建新的一个项目:

输入项目名称:

选择项目的性质:

选择型号芯片 xc7a100tfgg676-2L 型号芯片,这个芯片是实验平台上的 FPGA 芯片:

项目情况简介:

创建完成空白的项目:

编写代码

添加源文件:

创建新的文件:

输入 clock 为创建文件的名称,注意文件类型选择为 SystemVerilog:

创建完文件,点击 finish:

设置模块的输入输出:

添加完成,并且可以查看源代码:

在源代码部分输入以下源代码,这是一段计数器的代码:

module clock (
    input wire clock,
    input wire rst,
    output reg[6:0] seg1,
    output reg[6:0] seg2
    );

    reg[3:0] cnt_L = 4'b0000;
    reg[3:0] cnt_H = 4'b0000;
    reg[25:0] cnt = 26'b00000000000000000000000000;
    reg clk_out;
    reg[3:0] tmp_H = 4'b0000;
    reg[3:0] tmp_L = 4'b0000;

    always_ff @ (posedge clock) begin
        cnt <= cnt + 1;
        if(cnt ==  26'b00_0000_0000_0000_0000_0000_0000)
            clk_out <= 0;
        if(cnt == 26'b10_1111_1010_1111_0000_1000_0000)   // 计数 50000000 次,1 秒钟
            begin
                cnt <= 26'b00_0000_0000_0000_0000_0000_0000;
                clk_out <= 1;
            end
    end

    always_ff @ (posedge clk_out or posedge rst) begin  // 将秒数转换成输出的格式
        if(rst == 1)
            begin
                tmp_H = 4'b0000;
                tmp_L = 4'b0000;
            end
        else
            begin
                tmp_L = cnt_L + 1;
                if(tmp_L > 4'b1001)
                begin
                    tmp_L = 4'b0000;
                    tmp_H = cnt_H + 1;
                    if (tmp_H > 4'b1001)
                        tmp_H = 4'b0000;
                end
            end
        cnt_L = tmp_L;
        cnt_H = tmp_H;
    end

    always_comb begin // 将秒表的个位数输出到数码管
        case(cnt_L)
            4'b0000: seg1 = 7'b0111111;
            4'b0001: seg1 = 7'b0000110;
            4'b0010: seg1 = 7'b1011011;
            4'b0011: seg1 = 7'b1001111;
            4'b0100: seg1 = 7'b1100110;
            4'b0101: seg1 = 7'b1101101;
            4'b0110: seg1 = 7'b1111101;
            4'b0111: seg1 = 7'b0000111;
            4'b1000: seg1 = 7'b1111111;
            4'b1001: seg1 = 7'b1101111;
            default: seg1 = 7'b0000000;
        endcase
    end

    always_comb begin // 将秒表的十位数输出到数码管
        case(cnt_H)
            4'b0000: seg2 = 7'b0111111;
            4'b0001: seg2 = 7'b0000110;
            4'b0010: seg2 = 7'b1011011;
            4'b0011: seg2 = 7'b1001111;
            4'b0100: seg2 = 7'b1100110;
            4'b0101: seg2 = 7'b1101101;
            4'b0110: seg2 = 7'b1111101;
            4'b0111: seg2 = 7'b0000111;
            4'b1000: seg2 = 7'b1111111;
            4'b1001: seg2 = 7'b1101111;
            default: seg2 = 7'b0000000;
        endcase
    end
endmodule

输入代码之后的情况:

上述的代码就是一个秒表计数器的代码。这个秒表计数器会在每秒计数一次,99 秒后回到 0 开始重新计数。系统的输入为外部时钟的输入,系统的输出为两个七段数码管。由于外部的时钟频率非常高,在代码中依据 1 秒的时间要求进行延迟。

代码综合

写完代码之后,将经过综合,实现,生成比特流的过程,最终生成可以加载到 FPGA 上面能用的比特流文件。下面是各个流程的含义。

综合 (Synthesis):Verilog 语言翻译为门电路的表示,使用 LUT,FF(flip-flop,寄存器)方式来完成门电路的构成等。

实现 (Implementation):考虑连线,各个元器件的放置,连线优化等。

生成 Bitstream:生成最终的可以使用的比特流文件,可以被加载到 FPGA 上使用。

下面是代码经过综合和实现和生成比特流的各个过程。实际工作中,可以直接点击 Generate Bitstream 选项来直接生成比特流。Vivado 软件会自动调用上面的三个过程。

选择综合选项:

开始综合:

综合进行中:

综合完成:

点击 ok 继续完成实现:

实现完成:

Open Implemented Design 打开实现设计,用于进行添加约束等操作。

Generate Bitstream 生成用于下载到 FPGA 的比特流文件,没有添加约束的情况下直接生成比特流文件可能会报错,因此需要先添加约束。

View Report 查看综合报告。

到这一步,在生成比特流的时候,Vivado 会报告类似下面的错误:

[DRC NSTD-1] Unspecified I/O Standard: 16 out of 16 logical ports use I/O
standard (IOSTANDARD)... Problem ports: seg1[6:0], seg2[6:0], clock and rst.

这是因为目前项目中缺少约束文件,本文接下来在介绍仿真后,就会介绍如何添加约束,那时就可以成功地生成比特流文件。

增加仿真代码与前仿真

在生成比特流之前我们可以对设计进行仿真,用来初步验证设计是否正确,右键点击 Simulation Sources,可以增加仿真的代码:

通过上述的向导工具,创建一个 test.sv 的测试文件。(创建过程和上述创建 clock.sv 的过程类似,可以先不用指定输入输出,只需要创建出文件即可。)

将 test.sv 中的源代码修改如下:

`timescale 1ns / 1ps
module test ();
    reg clock;
    reg rst;
    wire [6:0] seg1, seg2;

    initial begin
        clock = 0;
        rst = 1;
        #100;
        rst = 0;
        forever #10 clock = ~clock;
    end

    clock test_clock (
        .clock(clock),
        .rst(rst),
        .seg1(seg1),
        .seg2(seg2)
    );
endmodule

上述代码的主要工作是为模块加入输入信号,创建了一个 50MHz 的时钟并加入了一个复位信号。实际的硬件电路也需要这样的两个信号作为输入。

点击 Run Simulation 选择 Run Behavioral Simulation,开始行为仿真:

默认情况下,仿真到 1us 暂停,仿真波形会显示在右边窗口中:

仿真波形的上方的菜单栏中上有控制仿真开始和暂停的按钮,仿真到 20s 的波形如下(仿真时间会比较长)。

行为仿真与时序仿真

上面的仿真都是行为仿真,也称为前仿真(简称前仿),这种仿真没有将信号延时加入,离实际的硬件还有一定的差距。功能仿真可以用来验证设计的功能,但不能用来检查严格的时序,而时序仿真(后仿真,简称后仿)则是将延时加入,更接近实际硬件运行,但距离硬件仍然有一定的距离。前仿真比较快,后仿真更费时间。在开发的过程中,可以先利用前仿真来查找功能上的错误,再用后仿真调时序。

但无论是前仿真还是后仿真,仿真行为都与硬件有一定的区别。为了让代码、仿真和硬件一致,需要在编写代码的时候注意代码规范,避免一些常见的容易引起不一致的问题。

在 Vivado 中,进行行为仿真的时候,直接点击行为仿真就可以直接反映修改后的代码仿真情况。但是,如果需要综合后仿真,或者实现后仿真,则需要进行综合或者实现后才有正确的仿真结果。这一点需要注意,即在进行后仿真的时候需要先进行综合和实现。

后仿真的信号名可能具有误导性

在后仿真的时候,硬件逻辑都已经被综合为 FPGA 上的原语(如常见的 LUT,FDRE 等等),同时信号名称也会发生变化,有时候这个变化会带来误导,例如在下面的例子中:

always_ff @ (posedge clock) begin
    a <= a + 1;
end

综合器会生成一系列的寄存器来保存 a,寄存器的输出端口 Q 经过加法运算,得到的加法结果会被连接到寄存器的输入端口 D。此时 Vivado 可能会把寄存器的输入端口 D 的信号叫做 a,把寄存器的输出端口 Q 的信号叫做 a_reg,这时候你在后仿真中看波形,如果认为看到的 a 就是寄存器当前的输出值,就会被误导,认为仿真行为与代码不一致。

添加约束

约束是指定 Vivado 软件一些行为的 Tcl 语言语句,在本实验的范围内,主要用到的就是 IO 约束,即将已经完成的顶层模块中的信号,与 FPGA 芯片实际的输入输出管脚对应起来,这样生成的最后的比特流文件才能够在板子上真正执行。

通过Open Implemented Design 中的 Constraint Wizard 来定义约束,点击 Define Target 按钮:

创建约束文件:

选择 Create File 新建文件:

选择新文件为设计的约束文件:

编辑 clock.xdc 文件:

将下面的约束内容写入到 clock.xdc 中:

# Clock
set_property -dict {PACKAGE_PIN D18 IOSTANDARD LVCMOS33} [get_ports clock]; #50MHz main clock in
set_property -dict {PACKAGE_PIN F22 IOSTANDARD LVCMOS33} [get_ports rst]; #BTN6

# DPY0
set_property IOSTANDARD LVCMOS33 [get_ports seg1[*]]
set_property PACKAGE_PIN F15 [get_ports {seg1[2]}]
set_property PACKAGE_PIN H15 [get_ports {seg1[3]}]
set_property PACKAGE_PIN G15 [get_ports {seg1[4]}]
set_property PACKAGE_PIN H16 [get_ports {seg1[1]}]
set_property PACKAGE_PIN H14 [get_ports {seg1[0]}]
set_property PACKAGE_PIN G19 [get_ports {seg1[5]}]
set_property PACKAGE_PIN J8 [get_ports {seg1[6]}]

# DPY2
set_property IOSTANDARD LVCMOS33 [get_ports seg2[*]]
set_property PACKAGE_PIN G8 [get_ports {seg2[2]}]
set_property PACKAGE_PIN G7 [get_ports {seg2[3]}]
set_property PACKAGE_PIN G6 [get_ports {seg2[4]}]
set_property PACKAGE_PIN D6 [get_ports {seg2[1]}]
set_property PACKAGE_PIN E5 [get_ports {seg2[0]}]
set_property PACKAGE_PIN F4 [get_ports {seg2[5]}]
set_property PACKAGE_PIN G5 [get_ports {seg2[6]}]

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

其中 set_property PACKAGE_PIN 命令表示设置管脚约束,这类约束是与电路板连线相关的。实验平台上有各种外围电路连接到 FPGA 不同的管脚上,进行实验的时候需要选择不同的信号与 FPGA 哪些管脚进行绑定。

完整的管脚绑定关系可以参考 thinpad_top 的项目模板。

最后两条命令 set_property CFGBVSset_property CONFIG_VOLTAGE 设置了 FPGA 配置电路的供电方式,它们由电路板硬件设计得出,在各个实验中不需要更改。

运行实现并生成比特流 bit 文件

重新运行实现(Run Implementatuion)后,选择生成比特流(bitstream)文件:

成功生成 bit 文件,为最终可以装载到 FPGA 中的文件:

在 Vivado 的右上角可以看到当前 Vivado 后台综合,实现,生成 bitstream 的进度情况。

clock.bit

装载 FPGA

有了比特流文件之后就可以将文件装载到 FPGA 中,使其工作,可以使用线下的 ThinPAD 实验板,或者通过在线实验平台使用线上的实验板。

首先,将电源线及 USB 线连接好后,打开电源,此时实验板会进行自检,同时连接的计算机中也会发现设备 Thinpad

此时通过浏览器打开 192.168.8.8 就可以打开 Thinpad 控制面板

上传设计文件中通过选择文件选择比特流文件:

通过写入实验 FPGA就可以完成对 FPGA 的下载:

此时在实验板上可以观察到两个七段数码管显示的秒表数字正在计数:

首先,访问在线实验平台 https://lab.cs.tsinghua.edu.cn/thinpad/,登录后通过选择文件选择比特流文件:

点击上传并开始将比特流文件装载到线上平台中分配到的实验板上的 FPGA 中:

进入实验平台工作区域,可以看到页面中的实验板上的七段数码管显示的秒表数字正在计数:

注意

每一个开发板都会把特定的 FPGA 管脚与特定的片外元器件相连接在一起,因此约束文件反映的是开发板的硬件实现情况。组成原理开发板的硬件连接情况是固定的,教学小组也直接提供了匹配硬件平台连接情况的约束文件,这个文件被包含在 thinpad_top 的项目文件中。

在实验中,程序可以基于 thinpad_top 的项目文件开始编写,项目里面已经有了所有的约束,但是要注意约束和程序中的变量名对应。建议可以直接用 thinpad_top 的项目文件中的变量名作为程序的输入输出的变量名。


最后更新: 2023年9月27日
作者:Jiajie Chen (14.69%), Youyou Lu (28.35%), gaoyichuan (37.37%), Ziang Liu (0.52%), Kang Chen (1.55%), kusile (17.53%)