>o>>
,.cout (cout) // >>o>>
);
initial begin
in = 3'b0;
#100;
for (integer i = 0; i < 8; i = i + 1) begin
in = in + 1;
#100;
end
$stop;
end
endmodule
相信你理解这个程序并不会感到困难,在 Logisim 中,你可以把一个画布中的电路引出输入输出口,封装成模块,然后在另一个画布中放置这个模块。
其中高亮的一大段,代表对 ``ref_fa`` 进行实例化,即在另一个模块中使用这个模块,然后将这个模块的信号与
对应的信号相连。 ``.a`` 代表 ref_fa 中的 a 端口,后面括号中的 in[0] 是 ref_tb 中的信号,这样就完成了相连的操作。
Testbench 中一定会使用到 \`timescale [timeunit]/[timeprecision] 。用来表示仿真时间的基本单位和时间精度。
``#100`` 也就是延迟 100个时间单位之后,再接着执行后面的语句。而时间精度代表最小的仿真尺度,对于 \`timescale 1ns/1ps 你写 ``#1.1111`` ,
则延迟 1.1111 ns,但由于 0.0001 是 0.1 ps,时间精度没这么高,因此会四舍五入变成延迟 1.111 ns。
$stop 系统任务则是将仿真暂停,有点像是 Debug 打断点,你运行就会发现箭头指向 $stop ,仿真停下来了,可以手动继续运行仿真。
然后我们对 in[2:0] 信号进行驱动,也就是对它进行赋值操作,我们直接使用 for 循环进行遍历所有的输入情况,然后你可以对比
所有的输入是否会得到正确的输出,即可完成 Testbench 仿真测试。
Vivado 使用教程
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Vivado 是 Xilinx 公司的 FPGA 集成设计环境,本次实验我们将使用 Vivado 对 Verilog 设计进行仿真。
我们推荐使用 Vivado 2018.3 ,安装 WebPACK 版本。
该版本安装体积约 20 GB,而近几年的版本安装体积已经约 100 GB,WebPACK 是免费的,不需要许可证的版本,并且已经能够满足我们的实验需求。
如果你此前已经安装 2015 年之后的版本,都是能够满足完成实验的。Vivado 不同的版本之间功能差异也比较小,在学习和使用上也不会造成困难。
创建 adder 工程
-------------------------------
打开 Vivado 软件,会来到 Vivado 软件初始界面
.. figure:: ../picture/lab2/vivado_home.png
:alt: vivado_home
:align: center
Vivado 软件很复杂,我们一点点来了解它。创建一个名为 adder 的新项目,并保存在合适的位置, **一定要是全英文的目录** 。
勾选 ``Create project subdirectory`` 则会在保存新项目的地方创建一个项目名称的文件夹,用于存放项目的文件。
你也可以手动创建一个项目名称的文件夹,作为保存项目的位置,就不勾选该选项了。
.. figure:: ../picture/lab2/vivado_genprj.png
:alt: vivado_genprj
:align: center
来到器件选择页面, ``Family`` 系列选择 ``Artix-7`` , ``Package`` 封装方式选择 ``fgg484`` ,
然后选择 ``xc7a100tfgg484`` ,完成器件选择,其余的步骤直接下一步即可。
.. figure:: ../picture/lab2/vivado_device.png
:alt: vivado_device
:align: center
完成创建项目,来到该项目初始页面,本次实验内容我们只需要关心红色方框标记出来的区域。
左侧是 ``Flow Navigator`` 流程导航,我们完整的实验整个流程就是依次从上往下进行的。
左上角是 ``项目管理`` ,目前由于正处于项目管理界面,因此 ``PROJECT MANAGER`` 是蓝色的。
.. figure:: ../picture/lab2/vivado_prj.png
:alt: vivado_prj
:align: center
完成了加法器和 Testbench 程序的编写,我们就可以进行仿真验证了。
首先需要将编写好的源代码添加到工程中,我们可以通过这两个地方添加源文件。
.. figure:: ../picture/lab2/add_source.png
:alt: add_source
:align: center
有三种类型的源文件,如下图所示,有 ``design source`` 设计文件、 ``simulation source`` 仿真文件和 ``constraints`` 约束文件。
设计文件就是我们描述的电路,仿真文件就是 Testbench,约束文件下一次课才会使用。
.. figure:: ../picture/lab2/source_type.png
:alt: source_type
:align: center
依次添加设计文件和约束文件,添加完成之后,会自动更新源代码的结构,如下图所示。
.. figure:: ../picture/lab2/source_struct.png
:alt: source_struct
:align: center
源文件自动更新了顶层文件,如果你想修改顶层文件,可以对源文件右键 ``Set as Top`` 修改为顶层文件。点击行为仿真,即可进行仿真操作。
.. figure:: ../picture/lab2/behavioral_simulation.png
:alt: behavioral_simulation
:align: center
打开仿真界面后,我们可以看到仿真产生的信号波形,如下图所示。
序号1的播放键按钮用于运行仿真,序号2的按钮用于重启仿真。
序号3的方框可以选择模块,序号4的方框可以添加模块中的信号显示波形。
序号5的方框用以显示信号波形,序号6的两个放大镜按钮可以放大和缩小波形显示范围,序号7的按钮将显示完整的仿真波形信号。
.. figure:: ../picture/lab2/vcd.png
:alt: vcd
:align: center
观察波形,每个输入信号是否都对应着正确的输出信号。
如果你有 ``$display`` 或者 ``$monitor`` 等函数,输出内容会显示在 Vivado 下方的 Tcl Console 中。
.. figure:: ../picture/lab2/Hello_World.png
:alt: Hello_World
:align: center
Carry-look-ahead 超前进位加法器
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
超前进位加法器是一种进位链延迟更短的加法器,我们已经在理论课上学习了4位超前进位加法器的原理。
4位超前进位加法器
------------------------
.. raw:: html
4位超前进位加法器的实现
按照逻辑公式或者电路,完成4位超前进位加法器的代码实现。
下面给出了代码框架,在代码框架的基础上完成代码的编写。
.. code-block:: v
:caption: 4位超前进位加法器代码框架
:emphasize-lines: 9-11
:linenos:
module cla_4bit(a, b, cin, sum, cout);
input a, b, cin;
output sum, cout;
wire [3:0] a, b, sum;
wire cin, cout;
// Your codes should start from here.
// End of your codes.
endmodule
全加器你写好了,你很自信,这真的还需要测试吗?4位超前进位加法器你也写好了,这个也不难,只需要非常的细心。
那这个需要写 Testbench 测试吗,你现在有信心认为代码一定是正确的吗?
那该怎么写呢,还是和参考的 Testbench 一样吗,不过这次的信号数量特别多,真值表一共有512列,我需要依次去看
512次的波形,然后检查是否符合预期吗,这看起来很蠢 : (
.. raw:: html
一种 Testbench 测试思路
我们能否改造一下 ref_fa ,其使用 + 号运算符,看起来会得到正确的结果。那我们能否同时测试我们写的4位超前进位加法器模块
和改造好的4位 ref_fa 模块,每次对比一下输出的值是否相同。如果结果不相同则说明4位超前进位加法器模块有问题,
我们把参考机称为 ref (Reference) ,而我们的待测电路称为 dut (Device Under Test) ,原理如下图所示。
可以打印一句提示信息,这样就不用我们去一个个看波形了。打印信息可以使用 $display() 函数,Vivado 会将信息显示在下方的 Tcl Console 中。
使用方法很像 printf 函数,具体可以 STFW。
.. figure:: ../picture/lab2/Testbench.png
:alt: Testbench
:align: center
.. raw:: html
测试4位超前进位加法器
你编写的这个4位超前进位加法器后续会用于组成更大位宽的加法器,
以及之后的实验中,请你好好测试你的代码,不要留下Bug。
编写一个 Testbench 用于测试你的4位超前进位加法器,
命名规则最好类似于 tb_cla_4bit ,这样很清晰的能够看得出这是用于测试什么模块的激励文件。
层次化超前进位加法器
------------------------
理论课上已经讲过层次化超前进位加法器,这样可以显著提升加法器的性能,但是随着加法器位宽的提升,进位的计算会花费指数级增加的电路开销。
因此对于32位、64位等更大位宽的加法器,可以将低位宽的加法器块之间用行波进位等方式连接。
.. raw:: html
16位层次化超前进位加法器
我们可以改造一下之前的4位超前进位加法器代码,将 generate 进位生成信号 g 和 propagate 进位传递信号 p 输出,
给第二级超前进位电路使用,组成16位层次化超前进位加法器。下面给出了代码框架。
.. code-block:: v
:caption: 16位层次化超前进位加法器
:emphasize-lines: 9-11
:linenos:
module cla_16bit(a, b, cin, sum, cout);
input a, b, cin;
output sum, cout;
wire [15:0] a, b, sum;
wire cin, cout;
// Your codes should start from here.
// End of your codes.
endmodule
选择进位加法器
------------------------
选择进位加法器可以由3个16位加法器组成,低16位加法计算不变,另外两个加法器对高16位进行计算。一个进位假设为0,另一个假设为1,
最后由低16位加法器实际计算出来的进位值输入 2-1 多路选通器(多路复用器),得到高16位的结果。这样可以有效减少了进位传播延迟。
这种设计与超前进位加法器相比,所需的电路数量并非指数级增长,而是大约多花50%的电路开销,也是一种常见的加法器设计方法,可以用于组成位宽很大的加法器。
.. figure:: ../picture/lab2/32bit_sel.png
:alt: 32bit_sel
:align: center
.. raw:: html
设计32位层次化选择进位加法器
按照上图所示的选择进位加法器结构,将16位层次化超前进位加法器作为模块,
使用选择进位的方式,完成最终的32位的加法器模块。
.. raw:: html
验证16位、32位加法器
电路复杂度又上升了,你不验证还有信心保证你的电路一定是正确的吗?
那么问题又来了,怎么验证呢?对于64位的加法器验证,难道将所有的情况都穷举,然后与64位的 ref_fa 比较结果吗?
我已经算不清输入有多少种情况了, 2^64 * 2^64 * 2 种情况,即便是使用无比强大的计算机仿真,
也不能轻松搞定,这看起来真的很蠢。
.. raw:: html
另一种 Testbench 测试思路
在其他编程语言中有生成随机数的函数, Verilog 也不例外,我们可以利用 $random 这个函数帮我们生成一些随机数,
帮助我们随机测试一些样例,然后循环10000次,或者100000次,或者更多。此外,对于一些特殊的情况,我们还可以手动地去测试,
以确保我们的电路符合要求。比如输入全0,全1;除法器我们可能需要对除0进行测试等。
.. code-block:: v
:caption: 测试激励示例
:emphasize-lines: 2, 9-12
:linenos:
initial begin
for (integer i = 0; i < 100000; i = i + 1) begin
a = $random;
b = $random;
cin = $random;
#100;
if ((ref_sum != dut_sum) || (ref_cout != dut_cout)) begin
$display("Print tests failed information");
$stop; // 如果有错误暂停仿真
end
end
end
.. raw:: html
有错误怎么去 Debug
更重要的可能是有错误怎么去 Debug,但毕竟这次加法器的原理和结构并不难。只要你仔仔细细对照公式或者电路,
就能得到正确的功能。但 Bug 是在所难免的,那么遇到错误了,怎么去 Debug 呢。
我们的改造的 ref_fa 可以认为是参考机、标准答案,当然前提是你设计正确。那么与参考机不同的输出结果就认为是有问题的,
这时候我们就可以查看我们打印的错误信息,或者查看错误的波形,手动验算一下,核对哪些信号有问题,就去检查相应的代码。
假如你觉得信号数量太多了,根本无从下手,对于你搭建的32位层次化选择进位加法器,我们建议你先把16位层次化超前进位加法器模块验证正确,
毕竟选择进位加法器的结构是很简单的,而16位层次化超前进位加法器模块稍微复杂一点。你直接验证32位层次化选择进位加法器,
可能没办法定位出问题出在32位的选择进位加法器结构,还是16位层次化超前进位加法器结构,甚至是4位的超前进位加法器。
所以好的办法当然是写一个模块验证一个,写一点代码验证一点,这样可以快速定位问题,也可能更早的暴露代码的Bug,
而不是写了1000行代码了,再去验证。
因此我们16位层次化超前进位加法器、32位层次化选择进位加法器并没有显式地让你设计完就做验证,是希望你能够一开始就养成好的习惯,自己设计好后主动去验证所有的代码模块,
而不是最后只能求助于老师、助教或者同学。