关于Systemverilog语法学习的专栏博客已经告一段落,现在结合 chipverify 官网给出的几个testbench 案例,利用 QuestaSim 平台实做一些练习。
// ---- ---- Design module
// 简述:数据地址选通
module switch #(
parameter ADDR_WIDTH = 8,
parameter DATA_WIDTH = 16,
parameter ADDR_DIV = 8'h3f
)(
input clk,
input rstn,
input vld,
input [ADDR_WIDTH-1:0] addr,
input [DATA_WIDTH-1:0] data,
output reg [ADDR_WIDTH-1:0] addr_a,
output reg [DATA_WIDTH-1:0] data_a,
output reg [ADDR_WIDTH-1:0] addr_b,
output reg [DATA_WIDTH-1:0] data_b
);
always@(posedge clk)
begin
if(~rstn)
begin
addr_a <= 0;
data_a <= 0;
addr_b <= 0;
data_b <= 0;
end
else
begin
if(vld)
begin
if(addr >=0 & addr <= ADDR_DIV)
begin
addr_a <= addr;
data_a <= data;
addr_b <= 0;
data_b <= 0;
end
else
begin
addr_a <= 0;
data_a <= 0;
addr_b <= addr;
data_b <= data;
end
end
end
end
endmodule
这个案例的设计模块其实逻辑很简单,就是一个地址数据选择赋值功能,当地址在前半区间时,将地址和数据一并赋值给 a 通道;否则赋值给 b 通道。
// ---- ---- Transaction Object
// 简述:事务对象
class switch_item;
rand bit [7:0] addr;
rand bit [15:0] data;
bit [7:0] addr_a;
bit [15:0] data_a;
bit [7:0] addr_b;
bit [15:0] data_b;
function void print(string tag="");
$display("T = %0t %s addr = 0x%0h data = 0x%0h addr_a = 0x%0h data_a = 0x%0h addr_b = 0x%0h data_b = 0x%0h",
$time,tag,addr,data,addr_a,data_a,addr_b,data_b);
endfunction
endclass
事务对象基本作为对象给驱动器提供被驱动事务,输入(激励)信号一般用 rand 关键字声明,因为在每次激励时产生随机数进行测试验证。输出(响应信号) 则不必用 rand 关键字。
// ---- ---- Generator
// 简述:测试数据生成
class generator;
mailbox drv_mbx;
event drv_done;
int num = 20;
task run();
for(int i = 0; i < num; i++)
begin
switch_item item = new;
item.randomize();
$display("T = %0t [Generator] Loop : %0d/%0d creat next item",$time,i+1,num);
drv_mbx.put(item);
@(drv_done);
end
$display("T = %0t [Generator] Done generation of %0d items",$time,num);
endtask
endclass
此部分代码主要是实现了一个数据生成器的类,类内的任务 run 每次创建一个新的事务对象,产生随即激励数据,通过 mailbox 传入驱动器。
// ---- ---- Interface
interface switch_if (input bit clk);
logic rstn;
logic vld;
logic [7:0] addr;
logic [15:0] data;
logic [7:0] addr_a;
logic [15:0] data_a;
logic [7:0] addr_b;
logic [15:0] data_b;
endinterface
此处接口定义的地方也可以用 virtual 关键字,声明为虚接口;
// ---- ---- Driver
// 简述:驱动器
class driver;
virtual switch_if vif;//虚拟接口:每个接口实例是独立的,互不影响
event drv_done;
mailbox drv_mbx;
task run();
$display("T = %0t [Driver] starting ...",$time);
@(posedge vif.clk);
forever
begin
switch_item item;
$display("T = %0t [Driver] waiting for item ...",$time);
drv_mbx.get(item);
item.print("Driver");
vif.vld <= 1;
vif.addr <= item.addr;
vif.data <= item.data;
@(posedge vif.clk);
vif.vld <= 0; ->drv_done;
end
endtask
endclass
注意此处代码内对接口的实例化使用了 virtual 关键字;虚接口的实例彼此之间独立互不影响。
drv_mbx 通过 mailbox 获取事务对象,然后将 vld 有效信号拉高,地址和数据各自赋值。
最后将 vld 信号拉低 触发 drv_done 事件。
// ---- ---- Monitor
// 简述:
class monitor;
virtual switch_if vif;
mailbox scb_mbx;
semaphore sema4;
function new ();
sema4 = new(1);
endfunction
task run();
$display("T = %0t [Monitor] starting ...",$time);
fork
sample_port("Thread0");
sample_port("Thread1");
join
endtask
task sample_port(string tag="");
forever
begin
@(posedge vif.clk);
if(vif.rstn & vif.vld)
begin
switch_item item = new;
sema4.get();
item.addr = vif.addr;
item.data = vif.data;
$display("T = %0t [Monitor] %s First part over",$time,tag);
@(posedge vif.clk);
sema4.put();
item.addr_a = vif.addr_a;
item.data_a = vif.data_a;
item.addr_b = vif.addr_b;
item.data_b = vif.data_b;
$display("T = %0t [Monitor] %s Second part over",$time,tag);
scb_mbx.put(item);
item.print({"Monitor_",tag});
end
end
endtask
endclass
监视器部分的代码涉及到两个线程对同一个内存区域的读写操作,为避免冲突,此处引入 semaphore 。
// ---- ---- Scoreboard
// 简介:
class scoreboard;
mailbox scb_mbx;
task run();
forever
begin
switch_item item;
scb_mbx.get(item);
if(item.addr inside{[0:'h3f]})
begin
if(item.addr_a != item.addr | item.data_a != item.data)
$display("T = %0t [Scoreboard] ERRROR! Mismatch addr=0x%0h data=0x%0h addr_a=0x%0h data_a=0x%0h",$time,item.addr ,item.data ,item.addr_a ,item.data_a);
else
$display("T = %0t [Scoreboard] PASS! Match addr=0x%0h data=0x%0h addr_a=0x%0h data_a=0x%0h",$time,item.addr ,item.data ,item.addr_a ,item.data_a);
end
else
begin
if(item.addr_b != item.addr | item.data_b != item.data)
$display("T = %0t [Scoreboard] ERRROR! Mismatch addr=0x%0h data=0x%0h addr_b=0x%0h data_b=0x%0h",$time,item.addr ,item.data ,item.addr_b ,item.data_b);
else
$display("T = %0t [Scoreboard] PASS! Match addr=0x%0h data=0x%0h addr_b=0x%0h data_b=0x%0h",$time,item.addr ,item.data ,item.addr_b ,item.data_b);
end
end
endtask
endclass
主要就是对设计的功能作判断,对比输入输出是否一致。给出判断信息。
// ---- ---- Environment
// 简介:
class env;
driver d0;
monitor m0;
generator g0;
scoreboard s0;
mailbox drv_mbx;
mailbox scb_mbx;
event drv_done;
virtual switch_if vif;
function new ();
d0 = new;
m0 = new;
g0 = new;
s0 = new;
drv_mbx = new();
scb_mbx = new();
d0.drv_mbx = drv_mbx;
g0.drv_mbx = drv_mbx;
m0.scb_mbx = scb_mbx;
s0.scb_mbx = scb_mbx;
d0.drv_done = drv_done;
g0.drv_done = drv_done;
endfunction
virtual task run();
d0.vif = vif;
m0.vif = vif;
fork
d0.run();
m0.run();
g0.run();
s0.run();
join_any
endtask
endclass
环境主要负责构建 testbench 的基本对象、事件、任务等。
// ---- ---- Test
class test;
env e0;
function new ();
e0 = new;
endfunction
task run();
e0.run();
endtask
endclass
测试可以看作是,执行环境中对应的任务。
// ---- ---- TB_TOP
module TB_TOP ();
reg clk;
always #10 clk=~clk;
switch_if _if(clk);
parameter ADDR_WIDTH = 8;
parameter DATA_WIDTH = 16;
parameter ADDR_DIV = 8'h3f ;
switch #(
.ADDR_WIDTH(ADDR_WIDTH),
.DATA_WIDTH(DATA_WIDTH),
.ADDR_DIV(ADDR_DIV)
) inst_switch (
.clk (clk),
.rstn (_if.rstn),
.vld (_if.vld),
.addr (_if.addr),
.data (_if.data),
.addr_a (_if.addr_a),
.data_a (_if.data_a),
.addr_b (_if.addr_b),
.data_b (_if.data_b)
);
test t0;
initial
begin
{clk,_if.rstn} <= 0;
#20 _if.rstn <= 1;
t0 = new;
t0.e0.vif = _if;
t0.run();
#50 $finish;
end
endmodule
顶层需要例化设计文件,给时钟激励,执行测试。
为方便编译,建议将上述所有代码放在一个 sv 文件中。
QuestaSim 给出的仿真结果:
# T = 20 [Driver] starting ...
# T = 20 [Monitor] starting ...
# T = 20 [Generator] Loop : 1/20 creat next item
# T = 30 [Driver] waiting for item ...
# T = 30 Driver addr = 0xfb data = 0x78ad addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 50 [Driver] waiting for item ...
# T = 50 [Monitor] Thread0 First part over
# T = 50 [Generator] Loop : 2/20 creat next item
# T = 50 Driver addr = 0xce data = 0x6676 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 70 [Driver] waiting for item ...
# T = 70 [Monitor] Thread0 Second part over
# T = 70 Monitor_Thread0 addr = 0xfb data = 0x78ad addr_a = 0x0 data_a = 0x0 addr_b = 0xfb data_b = 0x78ad
# T = 70 [Generator] Loop : 3/20 creat next item
# T = 70 [Monitor] Thread1 First part over
# T = 70 [Scoreboard] PASS! Match addr=0xfb data=0x78ad addr_b=0xfb data_b=0x78ad
# T = 70 Driver addr = 0xc0 data = 0x69be addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 90 [Driver] waiting for item ...
# T = 90 [Monitor] Thread1 Second part over
# T = 90 Monitor_Thread1 addr = 0xce data = 0x6676 addr_a = 0x0 data_a = 0x0 addr_b = 0xce data_b = 0x6676
# T = 90 [Monitor] Thread0 First part over
# T = 90 [Generator] Loop : 4/20 creat next item
# T = 90 [Scoreboard] PASS! Match addr=0xce data=0x6676 addr_b=0xce data_b=0x6676
# T = 90 Driver addr = 0x36 data = 0x317 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 110 [Driver] waiting for item ...
# T = 110 [Monitor] Thread0 Second part over
# T = 110 Monitor_Thread0 addr = 0xc0 data = 0x69be addr_a = 0x0 data_a = 0x0 addr_b = 0xc0 data_b = 0x69be
# T = 110 [Monitor] Thread1 First part over
# T = 110 [Generator] Loop : 5/20 creat next item
# T = 110 [Scoreboard] PASS! Match addr=0xc0 data=0x69be addr_b=0xc0 data_b=0x69be
# T = 110 Driver addr = 0x71 data = 0x16ba addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 130 [Driver] waiting for item ...
# T = 130 [Monitor] Thread1 Second part over
# T = 130 Monitor_Thread1 addr = 0x36 data = 0x317 addr_a = 0x36 data_a = 0x317 addr_b = 0x0 data_b = 0x0
# T = 130 [Monitor] Thread0 First part over
# T = 130 [Generator] Loop : 6/20 creat next item
# T = 130 [Scoreboard] PASS! Match addr=0x36 data=0x317 addr_a=0x36 data_a=0x317
# T = 130 Driver addr = 0x6e data = 0x23f4 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 150 [Driver] waiting for item ...
# T = 150 [Monitor] Thread0 Second part over
# T = 150 Monitor_Thread0 addr = 0x71 data = 0x16ba addr_a = 0x0 data_a = 0x0 addr_b = 0x71 data_b = 0x16ba
# T = 150 [Monitor] Thread1 First part over
# T = 150 [Generator] Loop : 7/20 creat next item
# T = 150 [Scoreboard] PASS! Match addr=0x71 data=0x16ba addr_b=0x71 data_b=0x16ba
# T = 150 Driver addr = 0x47 data = 0x4fb2 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 170 [Driver] waiting for item ...
# T = 170 [Monitor] Thread1 Second part over
# T = 170 Monitor_Thread1 addr = 0x6e data = 0x23f4 addr_a = 0x0 data_a = 0x0 addr_b = 0x6e data_b = 0x23f4
# T = 170 [Monitor] Thread0 First part over
# T = 170 [Generator] Loop : 8/20 creat next item
# T = 170 [Scoreboard] PASS! Match addr=0x6e data=0x23f4 addr_b=0x6e data_b=0x23f4
# T = 170 Driver addr = 0xbb data = 0xcd69 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 190 [Driver] waiting for item ...
# T = 190 [Monitor] Thread0 Second part over
# T = 190 Monitor_Thread0 addr = 0x47 data = 0x4fb2 addr_a = 0x0 data_a = 0x0 addr_b = 0x47 data_b = 0x4fb2
# T = 190 [Monitor] Thread1 First part over
# T = 190 [Generator] Loop : 9/20 creat next item
# T = 190 [Scoreboard] PASS! Match addr=0x47 data=0x4fb2 addr_b=0x47 data_b=0x4fb2
# T = 190 Driver addr = 0x35 data = 0x9e58 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 210 [Driver] waiting for item ...
# T = 210 [Monitor] Thread1 Second part over
# T = 210 Monitor_Thread1 addr = 0xbb data = 0xcd69 addr_a = 0x0 data_a = 0x0 addr_b = 0xbb data_b = 0xcd69
# T = 210 [Monitor] Thread0 First part over
# T = 210 [Generator] Loop : 10/20 creat next item
# T = 210 [Scoreboard] PASS! Match addr=0xbb data=0xcd69 addr_b=0xbb data_b=0xcd69
# T = 210 Driver addr = 0x49 data = 0xd196 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 230 [Driver] waiting for item ...
# T = 230 [Monitor] Thread0 Second part over
# T = 230 Monitor_Thread0 addr = 0x35 data = 0x9e58 addr_a = 0x35 data_a = 0x9e58 addr_b = 0x0 data_b = 0x0
# T = 230 [Monitor] Thread1 First part over
# T = 230 [Generator] Loop : 11/20 creat next item
# T = 230 [Scoreboard] PASS! Match addr=0x35 data=0x9e58 addr_a=0x35 data_a=0x9e58
# T = 230 Driver addr = 0xca data = 0x1207 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 250 [Driver] waiting for item ...
# T = 250 [Monitor] Thread1 Second part over
# T = 250 Monitor_Thread1 addr = 0x49 data = 0xd196 addr_a = 0x0 data_a = 0x0 addr_b = 0x49 data_b = 0xd196
# T = 250 [Monitor] Thread0 First part over
# T = 250 [Generator] Loop : 12/20 creat next item
# T = 250 [Scoreboard] PASS! Match addr=0x49 data=0xd196 addr_b=0x49 data_b=0xd196
# T = 250 Driver addr = 0x5b data = 0x1d5b addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 270 [Driver] waiting for item ...
# T = 270 [Monitor] Thread0 Second part over
# T = 270 Monitor_Thread0 addr = 0xca data = 0x1207 addr_a = 0x0 data_a = 0x0 addr_b = 0xca data_b = 0x1207
# T = 270 [Monitor] Thread1 First part over
# T = 270 [Generator] Loop : 13/20 creat next item
# T = 270 [Scoreboard] PASS! Match addr=0xca data=0x1207 addr_b=0xca data_b=0x1207
# T = 270 Driver addr = 0xbc data = 0x2688 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 290 [Driver] waiting for item ...
# T = 290 [Monitor] Thread1 Second part over
# T = 290 Monitor_Thread1 addr = 0x5b data = 0x1d5b addr_a = 0x0 data_a = 0x0 addr_b = 0x5b data_b = 0x1d5b
# T = 290 [Monitor] Thread0 First part over
# T = 290 [Generator] Loop : 14/20 creat next item
# T = 290 [Scoreboard] PASS! Match addr=0x5b data=0x1d5b addr_b=0x5b data_b=0x1d5b
# T = 290 Driver addr = 0x97 data = 0x559d addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 310 [Driver] waiting for item ...
# T = 310 [Monitor] Thread0 Second part over
# T = 310 Monitor_Thread0 addr = 0xbc data = 0x2688 addr_a = 0x0 data_a = 0x0 addr_b = 0xbc data_b = 0x2688
# T = 310 [Monitor] Thread1 First part over
# T = 310 [Generator] Loop : 15/20 creat next item
# T = 310 [Scoreboard] PASS! Match addr=0xbc data=0x2688 addr_b=0xbc data_b=0x2688
# T = 310 Driver addr = 0x3c data = 0xe700 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 330 [Driver] waiting for item ...
# T = 330 [Monitor] Thread1 Second part over
# T = 330 Monitor_Thread1 addr = 0x97 data = 0x559d addr_a = 0x0 data_a = 0x0 addr_b = 0x97 data_b = 0x559d
# T = 330 [Monitor] Thread0 First part over
# T = 330 [Generator] Loop : 16/20 creat next item
# T = 330 [Scoreboard] PASS! Match addr=0x97 data=0x559d addr_b=0x97 data_b=0x559d
# T = 330 Driver addr = 0x79 data = 0x5f5f addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 350 [Driver] waiting for item ...
# T = 350 [Monitor] Thread0 Second part over
# T = 350 Monitor_Thread0 addr = 0x3c data = 0xe700 addr_a = 0x3c data_a = 0xe700 addr_b = 0x0 data_b = 0x0
# T = 350 [Monitor] Thread1 First part over
# T = 350 [Generator] Loop : 17/20 creat next item
# T = 350 [Scoreboard] PASS! Match addr=0x3c data=0xe700 addr_a=0x3c data_a=0xe700
# T = 350 Driver addr = 0x63 data = 0xd727 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 370 [Driver] waiting for item ...
# T = 370 [Monitor] Thread1 Second part over
# T = 370 Monitor_Thread1 addr = 0x79 data = 0x5f5f addr_a = 0x0 data_a = 0x0 addr_b = 0x79 data_b = 0x5f5f
# T = 370 [Monitor] Thread0 First part over
# T = 370 [Generator] Loop : 18/20 creat next item
# T = 370 [Scoreboard] PASS! Match addr=0x79 data=0x5f5f addr_b=0x79 data_b=0x5f5f
# T = 370 Driver addr = 0x3 data = 0x770e addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 390 [Driver] waiting for item ...
# T = 390 [Monitor] Thread0 Second part over
# T = 390 Monitor_Thread0 addr = 0x63 data = 0xd727 addr_a = 0x0 data_a = 0x0 addr_b = 0x63 data_b = 0xd727
# T = 390 [Monitor] Thread1 First part over
# T = 390 [Generator] Loop : 19/20 creat next item
# T = 390 [Scoreboard] PASS! Match addr=0x63 data=0xd727 addr_b=0x63 data_b=0xd727
# T = 390 Driver addr = 0xab data = 0x489a addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 410 [Driver] waiting for item ...
# T = 410 [Monitor] Thread1 Second part over
# T = 410 Monitor_Thread1 addr = 0x3 data = 0x770e addr_a = 0x3 data_a = 0x770e addr_b = 0x0 data_b = 0x0
# T = 410 [Monitor] Thread0 First part over
# T = 410 [Generator] Loop : 20/20 creat next item
# T = 410 [Scoreboard] PASS! Match addr=0x3 data=0x770e addr_a=0x3 data_a=0x770e
# T = 410 Driver addr = 0x63 data = 0x2527 addr_a = 0x0 data_a = 0x0 addr_b = 0x0 data_b = 0x0
# T = 430 [Driver] waiting for item ...
# T = 430 [Monitor] Thread0 Second part over
# T = 430 Monitor_Thread0 addr = 0xab data = 0x489a addr_a = 0x0 data_a = 0x0 addr_b = 0xab data_b = 0x489a
# T = 430 [Monitor] Thread1 First part over
# T = 430 [Generator] Done generation of 20 items
# T = 430 [Scoreboard] PASS! Match addr=0xab data=0x489a addr_b=0xab data_b=0x489a
# T = 450 [Monitor] Thread1 Second part over
# T = 450 Monitor_Thread1 addr = 0x63 data = 0x2527 addr_a = 0x0 data_a = 0x0 addr_b = 0x63 data_b = 0x2527
# T = 450 [Scoreboard] PASS! Match addr=0x63 data=0x2527 addr_b=0x63 data_b=0x2527