Skip to content

Commit 9e611dc

Browse files
committed
add simple testbench demo
1 parent 28f4458 commit 9e611dc

File tree

15 files changed

+958
-1
lines changed

15 files changed

+958
-1
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
transcript
66

77
# waveform file
8-
*.wlf
8+
*.wlf
9+
10+
# waveform output
11+
*.vcd

SystemVerilogTutorial.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,36 @@
3737
包括所有的测试环境
3838
![testbench](./code/testbenchComponent.png)
3939

40+
## 什么是DUT?
41+
* DUT全称"Design Under Test",是被测的硬件电路。在仿真测试中,DUT也称为DUV,即"Design Under Verification"。
42+
* [例子 dut.v](./code/testbench/dut.v)
43+
44+
## 什么是interface?
45+
* 当DUT的端口非常复杂时,可通过定义interface抽象出测试关心的输入输出端口。
46+
* [例子 interface.sv](./code/testbench/interface.sv)
47+
48+
## 什么是driver?
49+
* driver定义了一个task用于驱动输入信号到被测电路,可被其他模块调用。
50+
* [例子 driver.sv](./code/testbench/driver.sv)
51+
52+
## driver如何实现驱动?
53+
* generator生成有效的数据([data transaction](./code/testbench/data.sv))给driver,driver再将数据传输到被测电路。
54+
* 例子"testbench"中并没有generator,generator的行为被放入了test组件。
55+
56+
## 为什么需要monitor?
57+
* driver会将数据输入给DUT执行,monitor会获取DUT的输出信号,并将其传给scoreboard和期望数据做比较。
58+
* [例子 monitor.sv](./code/testbench/scoreboard.sv)
59+
60+
## scoreboard的目的是什么?
61+
* scoreboard内部有一个和DUT行为一样的`reference model`。driver输入给DUT的信号同时会输入给scoreboard,此输入经过scoreboard中的`reference model`会产生DUT的期望输出。`reference model`的输出应该和monitor传过来的数据一致,否则测试失败。
62+
* [例子 scoreboard.sv](./code/testbench/scoreboard.sv)
63+
64+
## 为什么需要environment?
65+
* environment组件是为了增加testbench的可扩展性,可在其中加入更过其他的组件。
66+
* [例子 environment.sv](./code/testbench/environment.sv)
67+
68+
## test组件是做什么的?
69+
* test会实例化一个environment组件,并实施配置。
70+
71+
## testbench流程是如何的?
72+
* Generator -> Driver -> Interface -> Design -> Interface -> Monitor -> Scoreboard

code/testbench/data.sv

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This is the base transaction object that will be used
2+
// in the environment to initiate new transactions and
3+
// capture transactions at DUT interface
4+
class reg_item;
5+
rand bit [7:0] addr;
6+
rand bit [15:0] wdata;
7+
bit [15:0] rdata;
8+
rand bit wr;
9+
10+
// This function allows us to print contents of the data packet
11+
// so that it is easier to track in a logfile
12+
function void print(string tag="");
13+
$display ("T=%0t [%s] addr=0x%0h wr=%0d wdata=0x%0h rdata=0x%0h",
14+
$time, tag, addr, wr, wdata, rdata);
15+
endfunction
16+
endclass

code/testbench/demo/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
TARGET=testbench.sv
2+
SCRIPT=compile.do
3+
4+
all : $(TARGET) $(SCRIPT)
5+
vsim -do $(SCRIPT)
6+
7+
.PHONY: clean
8+
9+
clean:
10+
rm -f transcript vsim.wlf
11+
rm -rf work

code/testbench/demo/compile.do

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
set UVM_DPI_HOME C:/modeltech64_10.1c/uvm-1.1/win64
2+
vlib work
3+
vlog -L mtiAvm -L mtiOvm -L mtiUvm -L mtiUPF design.sv testbench.sv
4+
vsim -c -novopt -sv_lib $UVM_DPI_HOME/uvm_dpi work.tb
5+
run

code/testbench/demo/design.sv

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Note that in this protocol, write data is provided
2+
// in a single clock along with the address while read
3+
// data is received on the next clock, and no transactions
4+
// can be started during that time indicated by "ready"
5+
// signal.
6+
7+
module reg_ctrl
8+
# (
9+
parameter ADDR_WIDTH = 8,
10+
parameter DATA_WIDTH = 16,
11+
parameter DEPTH = 256,
12+
parameter RESET_VAL = 16'h1234
13+
)
14+
( input clk,
15+
input rstn,
16+
input [ADDR_WIDTH-1:0] addr,
17+
input sel,
18+
input wr,
19+
input [DATA_WIDTH-1:0] wdata,
20+
output reg [DATA_WIDTH-1:0] rdata,
21+
output reg ready);
22+
23+
// Some memory element to store data for each addr
24+
reg [DATA_WIDTH-1:0] ctrl [DEPTH];
25+
26+
reg ready_dly;
27+
wire ready_pe;
28+
29+
// If reset is asserted, clear the memory element
30+
// Else store data to addr for valid writes
31+
// For reads, provide read data back
32+
always @ (posedge clk) begin
33+
if (!rstn) begin
34+
for (int i = 0; i < DEPTH; i += 1) begin
35+
ctrl[i] <= RESET_VAL;
36+
end
37+
end else begin
38+
if (sel & ready & wr) begin
39+
ctrl[addr] <= wdata;
40+
end
41+
42+
if (sel & ready & !wr) begin
43+
rdata <= ctrl[addr];
44+
end else begin
45+
rdata <= 0;
46+
end
47+
end
48+
end
49+
50+
// Ready is driven using this always block
51+
// During reset, drive ready as 1
52+
// Else drive ready low for a clock low
53+
// for a read until the data is given back
54+
always @ (posedge clk) begin
55+
if (!rstn) begin
56+
ready <= 1;
57+
end else begin
58+
if (sel & ready_pe) begin
59+
ready <= 1;
60+
end
61+
if (sel & ready & !wr) begin
62+
ready <= 0;
63+
end
64+
end
65+
end
66+
67+
// Drive internal signal accordingly
68+
always @ (posedge clk) begin
69+
if (!rstn) ready_dly <= 1;
70+
else ready_dly <= ready;
71+
end
72+
73+
assign ready_pe = ~ready & ready_dly;
74+
endmodule

0 commit comments

Comments
 (0)