跳转至

使用Vivado构建完整项目

工欲善其事,必先利其器。 ——《论语·卫灵公》

在本门课程中,我们使用Vivado IDE帮助我们完成电路的设计。Vivado系列软件是Xilinx公司开发的数字电路设计集成环境, 主要用于将硬件描述高级语言编译实现为物理电路,并部署到对应的实验平台上,同时也是硬件描述语言代码纠错的利器。

什么?你还没有安装Vivado2023.1?

助教为你准备好了下载速度较快的Vivado睿客网盘链接,安装方法请点击这里!请注意,Vivado2023.1的压缩包大小约为100G(如果在本地解压至少需要200G的空间),安装完成大约50G的空间,因此请务必保证你的电脑有足够的空间或使用外接存储设备进行操作!

Step 1:创建一个新项目

打开Vivado,点击"Create Project":

image-20230805224923680

设置好项目名称后,一路Next,直到遇到“选择开发板型号”的界面,按照下面的配置就可以搜到我们使用的开发板:

image-20230805225128079 之后继续Next下去,就可以创建好一个新的工程项目了。进入Vivado,你就可以看到以下界面了:

image-20230805225450310

Step 2:创建设计文件

点击Add Sources,再点击“create file”,即可创建一个全新的设计文件,可以将文件类型设为System Verilog或者Verilog(对于提高班的同学,建议全部设为System Verilog):

image-20230805225519292

image-20230805225837325

如何把别处的源代码文件加入项目?

在添加文件时,点击“create file”旁边的“add files”,就可以把其他位置的设计文件包含入项目了。

需要注意的是,这里并不能将对应位置的设计文件真正复制入项目文件夹,也就是说,一旦其他项目也包含了这个设计文件,那么如果在本项目中修改这个文件,其他项目中的这个文件也会被修改。

创建后,我们可以双击这个文件,就可以在编辑器中编辑它了:

image-20230805225957404

一个32位2选1多路选择器代码

module mux(
    input       [31:0] din1,
    input       [31:0] din2,
    input              sel,
    output reg  [31:0] dout
    );

    always @(*) begin
        case(sel)
        1'b0: dout = din1;
        1'b1: dout = din2;
        endcase
    end
endmodule

编写完毕后,下面就需要进入纠错、综合、实现的环节了。不过在这之前,请将你的顶层模块或需要进一步操作的模块设为顶层模块(set as top)

image-20230805230302008

Set as Top的意义

由于不同文件的模块存在互相例化的情况,因此Vivado规定:只对手动规定的Top文件进行操作,这样可以保证我们能够一直对我们想要的模块进行分析。

所以,不管你要对那个模块进行分析(画电路图、运行检查器、仿真、综合、实现),都要记得把它设为顶层模块。

Step 3:电路检查与RTL分析

这一步过于重要,请务必认真看完

历年来多少同学都因为忽视这一步而吃了大亏,请一定一定一定要认真看完!

点击左侧“Run Linter”,即可进行一个较高级别的代码检查。检查完毕后,下方的Linter栏目中会给出一些可能的警告:

image-20230805230859187

这里是一片空白,证明没有任何可能的警告,那么恭喜你,这里你已经初步避免了设计的非逻辑失误。

Linter报出的警告不一定会影响设计正确性

例子中的设计比较简单,当使用一个比较复杂的设计时,Linter很有可能报出如下警告:

image-20230808095740621

这里需要关注以下两个警告:

  • 提示信号没有被读:表示这个信号的某几位虽然接进了某个模块,但在这个模块中并没有使用。
  • 提示信号没有被用:表示这个信号虽然被声明了,但是并没有使用。或者说信号的某几位接了常值,而这个常值的位宽小于这个信号的位宽。

以上两个警告很有可能有错,也很有可能是“杞人忧天”。这时一定要仔细核对出错信息,如果发现所有的信息都是“杞人忧天”,那么就可以进行下一步了。

点击左侧“Open Elaborate Design”,画出设计电路图:

image-20230805231452984

这一步并不是完全为了画出电路图,更重要的意义在于打开下方“Message”栏目,解决Elaborate Design文件夹下所有带具体文件行数的Warnings。这些具体的警告是千万不可以被忽视,因为它们是影响硬件设计的重要问题。不过对于一些很冗长且没有标出具体行数的Warnings,可以选择忽视。

image-20230806083502170

Vivado RTL分析的历史遗留问题

Vivado在进行了RTL分析后,如果直接修改源文件,那么Vivado会检测到修改痕迹,并在上方提示可以对RTL分析进行Reload。不过这样做的话会导致新的Warnings不会在Message中报出来!因此,每次修改设计文件后,请一定要关闭Elaborate Design后重新RTL分析,才可以得到新的Warnings!

再次提醒,上面这一步真的很重要!

人们常说“遇事不决RTL”,Message里的Warnings和Critical Warnings在开发的所有阶段都非常非常重要!一定要看!有关这些警告的具体解释,请点击这里

Step 4:对设计进行仿真

仿真的重要性

在工程正式上板之前,应当正确使用仿真来避免烧板的大错误,防止损坏开发板。

首先,我们应当创建仿真资源(也就是仿真文件):一般习惯上,我们在需要测试的模块名后加_tb来表示testbench仿真文件

image-20230805232027662

image-20230805232057140

对于前文的多选器的仿真例子

module mux_tb(

    );
    reg [31:0] din1, din2;
    reg sel;
    wire [31:0] dout;
    mux mux2_1_sim(
        .din1       (din1),
        .din2       (din2),
        .sel        (sel),
        .dout       (dout)
    );
    initial begin
        din1 = 0;
        din2 = 32'd100;
        sel = 0;                        // 对三个信号进行初始化
        repeat(10) begin            // 重复十次
            #1 din1 = din1 + 1; // 每隔一个时间单位,令din1自增1
            #2 din2 = din2 - 1; // 每隔两个时间单位,令din2自减1
            #3 sel = ~sel;        // 每隔三个时间单位,令sel取反
        end
    end
endmodule

点击左侧的Run Simulation,即可进行仿真并查看波形,如下图所示:

image-20230805232443187

值得注意的是,你可以点击左侧Scope选项卡中的模块名选中模块,再在中间的Objects选项卡中选择模块内部信号,将其拖拽到波形图的Name列中,查看内部信号的波形。

例如,当我们对一个用三个二选一选择器组成的四选一选择器进行仿真时,如果要看第一级两个多选器的输出,我们就可以通过上述方法找到内部信号:

image-20230927192003498

上图中的dout1就是其中一个第一级多选器的输出,我们已经在波形的最后一行看到了它的值。

但是,如果不加处理的话,这样操作需要重新运行一次仿真。如果不希望重新运行,那么你需要在Tools——Settings中打开这两个选项:

image-20230805232644104

通过了仿真,证明你的设计在仿真集上已经没有任何逻辑错误了。下面就要将电路进行具体的实现了。

我运行的是什么类型的仿真?

细心的同学可能发现了,在点下Run Simulation的时候,事实上运行的是 behaviour simulation,我们称之为行为仿真。行为仿真是一种在逻辑上近乎正确,而在物理上可能出现问题的仿真,它假定设计的最长组合逻辑通路的延时一定小于时钟周期,因此它并没有考虑到逻辑延迟大于时钟周期的情况。如果想要考虑逻辑延迟,那么就需要等到实现电路后运行时序仿真,但由于Vivado编译器会将信号重命名或元件重排,因此时序仿真很有可能如同”天书”,因此我们一般还是以看行为仿真为主要的纠错手段。

Step 5 综合与实现

综合电路(Synthesis)

在本步骤中,Vivado将会把描述的电路正式编译出来。在这里,我们可以找到更多warnings、critical warnings和errors,例如逻辑环路(自己的输出直接接到自己的输入)、多驱动(一个变量由多个always块或多个输入修改)等。

综合电路也可以查看电路图,但这时的电路图大部分以查找表(LUT)出现。这里我们以第一次实验中的编码-译码单元为例,来观察一下其综合电路图:

image-20230927192905107

这样的电路图我们是很难理解其逻辑的,它的作用主要是帮助我们找到最长的逻辑通路。在电路图中,所有的组合逻辑都是使用查找表来实现的,一条通路上的查找表越多,其组合逻辑延迟就会越大。因此,我们在设计电路时,应当尽量减少逻辑通路的长度,以减少组合逻辑延迟。同时,综合出的电路也有可能把某一模块的电气元件安置到另一个模块里,以获得更优的电路性能。

在综合电路时,我们还可以通过左侧的Open Synthesis Design选项卡的Report Utilization选项中找到该设计使用的资源数量。一个更简单的办法就是直接在下方的Design Run选项卡中找到资源使用量:

image-20230927193919583

synth_1所在行就是本次综合所用的资源量,我们只需要关注LUT(查找表,代表组合电路)、FF(触发器,代表时序电路)、BRAM(块式存储器)三个指标即可。从这里可以看出,我们的设计使用了38个查找表,没有使用触发器和块式存储器。

资源越多越好还是越少越好?

在比较简单的组合逻辑设计中,资源使用量少自然是一件很好的事情,但对于组合——时序综合逻辑设计中,评判一个设计的好坏,不能仅仅看资源使用量,还要看电路可接受的最高时钟频率。因此,我们在设计电路时,如果能够使用更多的资源来减少逻辑通路长度,从而提高时钟频率,那么这样的设计也可以是好的设计。

实现电路(Implementation)

本步骤需要一个全新的文件:限制文件。不同的开发板所需的限制文件不同(本课程实验所需的限制文件请点击这里)。在使用文件时,请将需要连接的接口的注释符号去掉,改好对应接口的名字即可**。

限制文件使用实例

如果希望将输入信号d[15:0]接到开发板的16个拨码开关上,则需要将以下行注释解除,并将接入信号的各个位写入接口即可: image-20230808094633680

在实现电路时,更多更难以解决的问题将浮现,比如时序不满足(逻辑电路过长)等。但同时我们也可以看到更多有关资源使用(utilization)、功耗(power)、时序(timing)的信息。而在这个阶段,电路图也会进一步发生变化:

image-20230927200201190

这个电路就是最终实现在物理开发板上的资源排布情况。由于综合是和具体开发板型号无关的,而实现是落实到具体开发板的,因此实现出的电路和综合的电路会有一定区别,资源使用量也会出现不同:

image-20230927200357144

impl_1所在行就是实现电路所用的资源,可以看到查找表使用量少了一个。在实际情况下,实现的电路资源使用可能大于综合,也可能小于综合。

生成比特流文件(.bit)并烧板

  1. 点击“Generate Bitstream”即可生成比特流文件,建议每次生成后将其拷贝出来,防止下次综合时清除掉原先的比特流文件。

  2. 打开HardWare Manager,将开发板连接上电脑,点击“auto connect”,待开发板连接到电脑后,点击“Program device”即可烧板。 image-20230808100155399