下图是一个加法器的框图:
输入管脚包括a、b、valid和clk、reset,以及一个输出管脚c。
TestBench Without Monitor, Agent and Scoreboard
不带monitor、agent和scoreboard的结构如下:
(1)定义transaction
transaction的作用主要是定义随机化的输入输出管脚,并输出显示.
class transaction;
//declaring the transaction items
rand bit [3:0] a;
rand bit [3:0] b;
bit [6:0] c;
function void display(string name);
$display("-------------------------");
$display("- %s ",name);
$display("-------------------------");
$display("- a = %0d, b = %0d",a,b);
$display("- c = %0d",c);
$display("-------------------------");
endfunction
endclass
(2)定义generator
generator的作用是实例化transaction,并且随机化trasaction中的随机变量,将随机化后的数据发送到driver.要实现与driver的通信,这就需要用到mailbox.在发送数据完成后,需要触发事件表明发送数据的完成,这需要定义事件.
注意,这里的mailbox是generator和driver之间共有的!
class generator;
//declaring transaction class
rand transaction trans;
//定义信箱
mailbox gen2driv;
//定义transaction的实例化次数,每实例化一次就发送一组随机数
int repeat_count;
//在实例化次数完成后需要触发事件表示发送数据结束
event ended;
//构造方法
function new(mailbox gen2driv);//
//getting the mailbox handle from env
this.gen2driv = gen2driv;
endfunction
//main task, generates(create and randomizes) the repeat_count number of transaction packets and puts into mailbox
task main();
repeat(repeat_count) begin
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
end
-> ended; //实例化次数完成后触发事件
endtask
endclass
(3)定义interface
实现testbench与DUT之间的通信.
interface intf(input logic clk,reset);//这个接口中并没有声明采样时序
//declaring the signals
logic valid;
logic [3:0] a;
logic [3:0] b;
logic [6:0] c;
endinterface
(4)定义driver
driver的作用是接收来自generator的随机数据,并将随机数据发送到DUT.
class driver;
//记录transaction实例化的数目
int no_transactions;
//创建虚接口句柄
virtual intf vif;//只有虚接口才可以在类中实例化
//创建信箱句柄
mailbox gen2driv;
//构造方法
function new(virtual intf vif,mailbox gen2driv);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.gen2driv = gen2driv;
endfunction
//复位任务
task reset;
wait(vif.reset);
$display("[ DRIVER ] ----- Reset Started -----");
vif.a <= 0;
vif.b <= 0;
vif.valid <= 0;
wait(!vif.reset);
$display("[ DRIVER ] ----- Reset Ended -----");
endtask
//主任务,将从信箱获得的数据发送到接口,同时将接口中获得的DUT数据存入到信箱
task main;
forever begin
transaction trans;
gen2driv.get(trans);
@(posedge vif.clk);//顺序执行,第一个时钟上升沿
vif.valid <= 1;
vif.a <= trans.a;
vif.b <= trans.b;
@(posedge vif.clk);//第二个时钟上升沿
vif.valid <= 0;//将接口中的vif.c赋给tran.c,vif.c来自DUT
trans.c <= vif.c;
@(posedge vif.clk);
trans.display("[ Driver ]");
no_transactions++;
end
endtask
endclass
(5)定义environment
environment包括mailbox、generator、diver,它通过实例化信箱实现driver和gennerator之间的通信.
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"//将文件导入
class environment;
//generator and driver instance
generator gen;
driver driv;
//信箱句柄
mailbox gen2driv;
//虚接口
virtual intf vif;
//构造方法
function new(virtual intf vif);
//get the interface from test
this.vif = vif;
//creating the mailbox (Same handle will be shared across generator and driver)
gen2driv = new();
//实例化generator和driver的对象
gen = new(gen2driv);
driv = new(vif,gen2driv);
endfunction
//
task pre_test();//测试前任务,用于复位
driv.reset();
endtask
task test();//测试任务
fork
gen.main();
driv.main();
join_any
endtask
task post_test();//测试后任务
wait(gen.ended.triggered);//等待generator的事件触发
wait(gen.repeat_count == driv.no_transactions);//transaction随机化次数
endtask
//主任务
task run;
pre_test();
test();
post_test();
$finish;
endtask
endclass
(6)定义test
test模块是使用program块编写的,这是与前面的不同.其作用主要是实例化environment、初始化激励、配置testbench如产生多少transaction的对象数量.
`include "environment.sv"//导入environment
program test(intf intf);
//declaring environment instance
environment env;
initial begin
//creating environment
env = new(intf);
//setting the repeat count of generator as 10, means to generate 10 packets
env.gen.repeat_count = 10;
//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram
(7)testbench的顶层模块
主要包括interface、DUT和test.用prgram块编写test可以实现test与DUT之间的无竞争交互!
`include "interface.sv"
`include "random_test.sv"//导入文件
module tbench_top;
//clock and reset signal declaration
bit clk;
bit reset;
//clock generation
always #5 clk = ~clk;
//reset Generation
initial begin
reset = 1;
#5 reset =0;
end
//实例化接口
intf i_intf(clk,reset);
//实例化test,接口句柄作为参数
test t1(i_intf);
//DUT instance, interface signals are connected to the DUT ports
adder DUT (
.clk(i_intf.clk),
.reset(i_intf.reset),
.a(i_intf.a),
.b(i_intf.b),
.valid(i_intf.valid),
.c(i_intf.c)
);
//enabling the wave dump
initial begin
$dumpfile("dump.vcd"); $dumpvars;
end
endmodule
DUT模块:
/*
--------------
valid ---->| |
| |
a -/--->| |
| adder |---/-> c
b -/--->| |
| |
--------------
^ ^
| |
clk reset
*/
module adder(
input clk ,
input reset,
input [3:0] a ,
input [3:0] b ,
input valid,
output [6:0] c );
reg [6:0] tmp_c;
//Reset
always @(posedge reset)
tmp_c <= 0;
// Waddition operation
always @(posedge clk)
if (valid) tmp_c <= a + b;
assign c = tmp_c;
endmodule
运行结果如下:
带monitor、agent和scoreboard的结构如下:
(1)定义monitor
monitor的作用是对来自接口的信号进行采样,并将信号级的活动转换为事务级,将它们发送到scoreboard.
class monitor;
//creating virtual interface handle
virtual intf vif;
//creating mailbox handle
mailbox mon2scb;
//constructor
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
//Samples the interface signal and send the sample packet to scoreboard
task main;
forever begin
transaction trans;
trans = new();
@(posedge vif.clk);
wait(vif.valid);
trans.a = vif.a;//因为输出相对于输入延时为1个周期
trans.b = vif.b;
@(posedge vif.clk);
trans.c = vif.c;
@(posedge vif.clk);
mon2scb.put(trans);//将总线上的数据存入信箱
trans.display("[ Monitor ]");
end
endtask
endclass
(2)定义scoreboard
scoreboard主要是接收来自monitor存入信箱的数据包,并将它们与期望值相比较,如果不匹配则会报错.
由于这个例子中DUT的行为比较简单,所以添加了一行代码以生成预期结果,对于复杂的设计可以采用参考模型的方法来生成预期结果.
class scoreboard;
//creating mailbox handle
mailbox mon2scb;
//used to count the number of transactions
int no_transactions;
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction
//Compares the Actual result with the expected result
task main;
transaction trans;
forever begin
mon2scb.get(trans);
if((trans.a+trans.b) == trans.c)//左边为预期结果,右边为monitor传送过来的结果
$display("Result is as Expected");
else
$error("Wrong Result.\n\tExpeced: %0d Actual: %0d",(trans.a+trans.b),trans.c);
no_transactions++;
trans.display("[ Scoreboard ]");
end
endtask
endclass
(3)定义environment
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;
//generator and driver instance
generator gen;
driver driv;
monitor mon;
scoreboard scb;
//mailbox handle's 两个信箱
mailbox gen2driv;
mailbox mon2scb;
//virtual interface
virtual intf vif;
//constructor
function new(virtual intf vif);
//get the interface from test
this.vif = vif;
//creating the mailbox (Same handle will be shared across generator and driver)
gen2driv = new();
mon2scb = new();
//creating generator and driver
gen = new(gen2driv);
driv = new(vif,gen2driv);
mon = new(vif,mon2scb);
scb = new(mon2scb);
endfunction
//
task pre_test();
driv.reset();
endtask
task test();
fork
gen.main();
driv.main();
mon.main();
scb.main();
join_any
endtask
task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions); //Optional
wait(gen.repeat_count == scb.no_transactions);
endtask
//run task
task run;
pre_test();
test();
post_test();
$finish;
endtask
endclass
其余组件不变,运行结果如下: