【Day16】TestBench 的撰写技巧

透过 Verilog 完成一个具有特定功能的电路後,并不代表你的工作已经完成了,TestBench(tb) 在电路设计中也是一个非常重要的环节,往往验证电路所花的时间还会比较开发来的多。

而 TestBench 则是另一个 .v 档,TestBench 的工作就是产生待测模块的输入信号,用这些讯号模拟真实的情况,接着透过波型来观察输出结果是否如自己预期,如果不如预期,也可以再透过把待测电路中的各个讯号叫出来观察,以此 debug 也会比自己一直盯着程序码来除错来的有效率唷!

我们可以先来看[Day15]中的范例程序

module testFSM(
  clkSys, 
  rst_n,
  en,
  out
);
/*--------localparam--------*/
localparam one   = 2'd0;
localparam two   = 2'd1;
localparam three = 2'd2;
localparam four  = 2'd3;
/*--------ports declarations--------*/
input       clkSys;
input       rst_n;
input       en;
output [2:0]out;
reg    [2:0]out;
/*--------variables--------*/
reg    [1:0]fstate;
.
.
.
.
endmodule

可以看到这个模块有三个输入,分别是 clkSys, rst_n, en,那麽我们在 testbench 那边就会需要有对应这三个讯号的 register(通常我名字会取一样),而输出则是一个 3 bit的 out,那麽对应 testbench,就会有一个 3 bit 的 wire 对应它。

再来稍微解释一下为甚麽 testbench 那边一定要模块的输入用 reg,而输出用 wire,因为 reg 属於过程性赋值(Procedural Assignment),意思是值是可以一直随心所欲变动的,而 wire 则属於连续性赋值(Continuous Assignment),通常用於连接讯号用。

所以就会有对应的这区块程序:

`timescale 10ns/1ns
module tb_testFSM();
reg       clkSys;
reg       rst_n;
reg       en;
wire [2:0]out;
testFSM UUT(
  .clkSys(clkSys), 
  .rst_n(rst_n),
  .en(en),
  .out(out)
);

timescale

先来谈谈最上面那行 timescale 10ns/1ns
timescale 是 Verilog 中的一种时间预编译指令,它用来定义模组模拟的时间单位以及时间精度

格式长这样:

`timescale 时间单位 / 时间精度

需要注意到的是:以上两者的数字只能是 1 or 10 or 100,而且时间单位必须大於时间精度。

以下来举个例子:

`timescale 1ns/10ps
reg clk_50M;
initial begin
  clk_50M = 0;
end
always #10 clk_50M = ~clk_50M;

现在时间单位设为 1ns 那麽 #1 就会 delay 1ns,#10 则是 delay 10 个 ns,所以 delay 10 个 ns 则 clk_50M 会反向一次,所以反向两次可以得到 1 周期的 clk,所以一个 clk 总共 delay (1ns)x(delay 10 个)x(反向 2 次) = 20 ns,得到此频率为 50 MHZ。


现在回到 testBench 那个程序,宣告完 reg 以及 wire 後,剩下的就是以 by name 的方式来引用模组了。

注: TestBench 中引用的测试模块通常命名为 UUT(Unit Under Test)

再接着:

initial begin
  clkSys = 0;
  rst_n = 0;
  en = 0;
  repeat(2)@(posedge clkSys)rst_n = 0;//push rst_n
  rst_n = 1;//release rst_n
  en = 1;
  #20  en = 0;
  #40;
  en = 1;
  #20  en = 0;
  #40;
  en = 1;
  #20  en = 0;
  #40;
  en = 1;
  #20  en = 0;
  #100 $stop;
end

always #10 clkSys = ~clkSys;

endmodule

initial begin 是用於 TestBench 中初始化变数值的语法,而且 initial block 中的程序只会执行一遍,并不会像 always 一直重复执行,在 initial block 内初始化讯号後,就要开始对输入讯号的变数赋值了,而 $stop 语法则可以停止波形的模拟(否则会一直跑下去,会一直占用电脑的资源)。


如何在 Quartus 完成对 TestBench 的设定以及跑模拟

Assignment/setting

Simulation/Compile test bench --->点选右侧 Test Bench.. 按钮

New...

上面两行打上 module 名称,下方选择 tb 的 .v 档并 add

接着连串的 ok ok...


设定 modelsim 路径

tools/options

EDA Tool Options,并选择路径

模拟

Tools/Run Simulation Tool/RTL Simulation

这样好好看的波形就会出来罗!!

顺带解释一下 RTL 与 Gate-level Simulation 的差异:
RTL模拟是所谓的功能上的模拟,也就是不考虑硬体电路中实际的 delay,所有情况皆为理想,所以适合拿来纯测功能,而 Gate-level 模拟则是时序上的模拟,此时会考虑到所有的不理想因素,因此会有 delay 的情况发生,所以跑模拟的顺序都会是先跑 RTL 再跑 Gate-level。


<<:  Day 16: 物件导向设计、函数式设计 (待改进中... )

>>:  110/16 - 整合Android 6到Android 11

System Design: 读书心得3

这篇的主题是因为有朋友提到 Database vs Data warehouse 的差别,所以就开始...

成为工具人应有的工具包-03 CredentialsFileView

CredentialsFileView 今天就来认识 CredentialsFileView 这个工...

{DAY 25} Matplotlib 基础操作

前言 今天这篇要进入到资料视觉化 在前几篇的文章里,资料的形式多半是呈现在表格化的资料表上 为了让数...

【Day09】数据输入元件 - Upload

元件介绍 Upload 是一个上传元件。帮助我们能够发布文字、图片、影片、档案到後端服务器上。 参考...

[Day4]-基本串列(list)

串列基本定义 串列可以储存不同的资料型态,如:整数、字串、浮点数,基本格式如下: List = [...