# Testbench 介紹
[TOC]
## 更新進程
<font color=green>3/21 新增 clk 介紹。</font>
## 前言
在測試電路的時候,會需要用到`Testbench`來驗證所寫出來的電路是否正確。而`Testbench`也是由`Verilog`語言寫成,基本上可以把它視為另一個 module,把需要測試的 module 接進去,並藉由餵進去不同 input 資料,來觀察 output 是否正確。
|  |
| -------- |
## 先備知識
在寫`Testbench`的時候,由於只是模擬資料輸入,可以使用較高階語言的方式來描述所想使用的功能(像是`for`、`repeat`等等),此外時間也可以由自己控制(像是幾個 cycle 後做什麼事)。
<font color=#bf2222>**宣告變數:**</font>
通常會將`input port`接到`reg`型別的變數當中以利<font color=#bf2222>提供不同測試資料</font>。
```verilog=
reg [2:0]a;
```
通常會將`output port`接到`wire`型別的變數當中,以利<font color=#bf2222>接收電路測試的結果</font>。
```verilog=
wire [2:0]f;
```
<font color=#bf2222>**呼叫 module:**</font>
把寫好的 module 接進來。這部分跟呼叫 AND、OR 等 logic gate 有些類似,包括`module 的名稱`、`命名`、`input port 及 output port`。
```verilog=
Mux_3bits m_0(.a (a), .b (b), .sel (sel), .f (f));
```
<font color=#bf2222>**設置 delay:**</font>
時間是模擬的基礎,換句話說,沒有時間看不出資料出入的關係。使用方式是`#`後面加上 delay 的時間刻度。
```verilog=
parameter delay = 5;
#delay
```
另外,`timescale`是用來告訴模擬軟體時間刻度,以下方為例 ,1ns 為時間單位,以 10ps 為單位察看一次電路的行為。
```verilog=
`timescale 1ns/10ps //<--定義時間刻度
```
通常在寫sequential circuit 會先定義一個 cycle 的時間長短,並使用`always`來控制 clock 信號的變化(Combinational circuit 則不一定要使用規律的 clock 信號)。
```verilog=
always #(CYC/2) clk = ~clk; //<--每半個cycle反向
```
<font color=#bf2222>**測試程序:**</font>
這一步要設計測試的順序,通常使用`initial block`來安排。而`initial`即是指程式一開始會先從此處開始執行,並依序向下進行。
```verilog=
initial begin
a = 3'b000; //<--給予a初值
...
end
```
<font color=#bf2222>**迴圈:**</font>
一些重複性的測試在`Testbench`中可藉由迴圈達成,常用的語法像是`for`或者`repeat`。
以`repeat`舉例,() 當中填入要執行的次數,並在 begin 跟 end 之間加入需要執行的項目。
```verilog=
repeat(2**3)begin //<--執行8次
#delay //<--隔一個delay
a = a + 1'b1; //<--a就會+1
end
```
而`for`其實用法類似,也蠻直覺的,要注意的是這裡不像`C語言`可以寫`i++`,要寫成`i=i+1`。
```verilog=
for(i=0;i<8;i=i+1)begin //<--執行8次
#delay //<--隔一個delay
a = a + 1'b1; //<--a就會+1
end
```
<font color=#bf2222>**其他語法:**</font>
- `$display`類似於`C語言`當中的`printf`,用於顯示一些資訊。
- `$finish`用於結束檢驗時。
## 範例
就以一個 3-bit 的多工器(MUX)為例:
```verilog=
module Mux_3bits (a, b, sel, f);
input [2:0]a, b;
input sel;
output [2:0]f;
Mux_1bit m0(a[0],b[0],sel,f[0]);
Mux_1bit m1(a[1],b[1],sel,f[1]);
Mux_1bit m2(a[2],b[2],sel,f[2]);
endmodule
module Mux_1bit(a, b, sel, f);
input a, b;
input sel;
output f;
wire w0;
wire w1, w2;
not not_0(w0, sel);
and and_0(w1, sel, a);
and and_1(w2, w0, b);
or or_0(f, w1, w2);
endmodule
```
接下來要測試此電路,接上寫好的`Testbench`:
```verilog=
`timescale 1ns/1ps
module Mux_3bit_tb();
parameter delay=5;
reg [2:0]a;
reg [2:0]b;
reg sel;
wire [2:0]f;
Mux_3bits m_0(.a (a), .b (b), .sel (sel), .f (f));
initial begin
a = 3'b000;
b = 3'b000;
sel = 1'b0;
repeat (2 ** 7) begin
#delay
$display("a=%b , b=%b , sel=%b , f=%b", a, b, sel, f);
if((sel == 1'b1 && a != f)||(sel == 1'b0 && b != f)) begin
$display("Wrong!");
$finish;
end
{a, b, sel} = {a, b, sel} + 1'b1;
end
$display("Complete.");
end
$finish;
endmodule
```
## 補充
用到 Design Version 合成的電路會有 .sdf(Standard Delay Format)檔的產生,裡面描述了關於 gate delay 的資訊,因此在檢查合成後的電路時會需要加入這個檔案。而 .fsdb 檔則是描述波形圖,在使用 nWave 工具的時候會需要使用到。
因此在助教提供的`Testbench`當中可能會看到像是下方範例的結構:
```verilog=
initial begin
`ifdef SDF
$sdf_annotate(`SDFFILE, xxx);
$fsdbDumpfile("xxx_syn.fsdb");
`else
$fsdbDumpfile("xxx.fsdb");
`endif
$fsdbDumpvars;
end
```
# [HOMEPAGE](https://hackmd.io/s/HJdaLPTQV)