下面搭建一个验证内存设计的平台。
Generator用于生成随机化的事务,并发送给Driver。Driver收到事务后,通过Interface发送给DUT。Monitor监控DUT的测试结果,并把Interface信号转换成事务发送给Scoreboard。Environment是一个包含了Generator,Driver,Monitor,Scoreboard的容器,负责验证环境的有序运行。Test则是一个更大的容器,包含了Environment以及其他的初始化配置。Top是最顶层的文件,用于连接DUT以及验证平台。
下面按照上述逻辑顺序给出平台搭建代码。
定义transaction类。
class transaction;
/*定义事务项目*/
rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;
/*由于读写不能同时操作,所以施加约束*/
constraint wr_rd_c {wr_en != rd_en;};
endclass
定义generator类。
class generator;
/* 发生器用于生成随机化的事务;并向驱动器发送这些事务 */
/*定义一个事务句柄*/
rand transaction trans;
/* 定义信箱 */
mailbox gen2driv;
/* 定义最大事务数 */
int repeat_count;
/*生成结束事件*/
event ended;
/* 从evn类获得信箱 */
function new(mailbox gen2driv,event ended);
this.gen2driv = gen2driv;
this.ended = ended;
endfunction
/* main task,用来生成并随机化数据包,放入信箱 */
task main();
repeat (repeat_count) begin
trans = new();
if (!trans.randomize()) $fatal("Gen::trans randomization failed");
gen2driv.put(trans);
end
-> ended;
endtask
endclass
定义interface。
interface mem_intf(input logic clk,reset);
/* 接口用于将信号捆绑,并指定方向,同步信号 */
logic[1:0] addr;
logic wr_en;
logic rd_en;
logic [7:0] wdata;
logic [7:0] rdata;
/* 驱动器时钟块 */
clocking driver_cb @(posedge clk);
default input #1 output #1;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;
endclocking
/* 监视器时钟块 */
clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;
endclocking
/* 驱动器modport */
modport DRIVER (clocking driver_cb, input clk, reset);
/* 监视器modport */
modport MONITOR (clocking monitor_cb, input clk, reset);
endinterface
定义driver类。
`define DRIV_IF mem_vif.DRIVER.driver_cb
class driver;
/* 驱动器用于接受发生器生成的事务,并通过接口驱动给DUT */
/*创建虚接口句柄*/
virtual mem_intf mem_vif;
/* 创建信箱句柄 */
mailbox gen2driv;
/*事务数目*/
int no_transactions;
/* 构造函数 */
function new(virtual mem_intf mem_vif, mailbox gen2driv);
/*获取接口和信箱*/
this.mem_vif = mem_vif;
this.gen2driv = gen2driv;
endfunction
/*清零任务*/
task reset;
wait(mem_vif.reset);
$display("-------[DRIVER] Reset Started-------");
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
`DRIV_IF.addr <= 0;
`DRIV_IF.wdata <= 0;
wait(!mem_vif.reset);
$display("--------[DRIVER] Reset Ended--------");
endtask
/* 将事务项目驱动到接口信号 */
task main;
forever begin
transaction trans;
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
gen2driv.get(trans);
$display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.addr <= trans.addr;
if (trans.wr_en) begin
`DRIV_IF.wr_en <= trans.wr_en;
`DRIV_IF.wdata <= trans.wdata;
$display("\tADDR = %0h \tWDATA = %0h", trans.addr, trans.wdata);
@(posedge mem_vif.DRIVER.clk);
end
if (trans.rd_en) begin
`DRIV_IF.rd_en <= trans.rd_en;
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge mem_vif.DRIVER.clk);
trans.rdata = `DRIV_IF.rdata;
$display("\tADDR = %0h \tRDATA = %0h", trans.addr,`DRIV_IF.rdata);
end
$display("-------------------------");
no_transactions++;
end
endtask
endclass
定义monitor类。
`define MON_IF mem_vif.MONITOR.monitor_cb
class monitor;
/* 采样接口信号,并转化信号级活动为事务级活动;
将采样到的事务通过信箱送往记分板; */
virtual mem_intf mem_vif;
mailbox mon2scb;
function new(virtual mem_intf mem_vif, mailbox mon2scb);
this.mem_vif = mem_vif;
this.mon2scb = mon2scb;
endfunction
/* 主任务,实现采样逻辑,并送往记分板 */
task main;
forever begin
transaction trans;
trans = new();
@(posedge mem_vif.MONITOR.clk);
wait(`MON_IF.rd_en || `MON_IF.wr_en);
trans.addr = `MON_IF.addr;
trans.wr_en = `MON_IF.wr_en;
trans.wdata = `MON_IF.wdata;
if (`MON_IF.rd_en) begin
trans.rd_en = `MON_IF.rd_en;
@(posedge mem_vif.MONITOR.clk);
@(posedge mem_vif.MONITOR.clk);
trans.rdata = `MON_IF.rdata;
end
mon2scb.put(trans);
end
endtask
endclass
定义scoreboard类。
class scoreboard;
/* 接收监视器发送的采样数据;
如果是读事务,比较读到的数据和局部内存数据;
如果是写事务,局部内存则会进行储存 */
mailbox mon2scb;
int no_transactions;
bit [7:0] mem[4];
function new(mailbox mon2scb);
this.mon2scb = mon2scb;
foreach(mem[i]) mem[i] = 8'hFF;
endfunction
task main;
transaction trans;
forever begin
//#50;
mon2scb.get(trans);
/* 读数据 */
if (trans.rd_en) begin
if (mem[trans.addr] != trans.rdata)
$error("[SCB-FAIL] Addr = %0h,\n \t Data :: Expected = %0h Actual = %0h",
trans.addr,mem[trans.addr],trans.rdata);
else
$display("[SCB-PASS] Addr = %0h,\n \t Data :: Expected = %0h Actual = %0h",
trans.addr,mem[trans.addr],trans.rdata);
end
/* 写数据 */
else if (trans.wr_en)
mem[trans.addr] = trans.wdata;
no_transactions++;
end
endtask
endclass
定义environment类。
`include"transaction.sv"
`include"generator.sv"
`include"driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;
/* env是包含了发生器,驱动器和信箱的类 */
generator gen;
driver driv;
monitor mon;
scoreboard scb;
mailbox gen2driv;
mailbox mon2scb;
/* 发生器和测试的同步事件 */
event gen_ended;
virtual mem_intf mem_vif;
function new(virtual mem_intf mem_vif);
/* 从测试类中获取接口 */
this.mem_vif = mem_vif;
/* 创建信箱句柄 */
gen2driv = new();
mon2scb = new();
/* 实例化发生器和驱动器 */
gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
mon = new(mem_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);
wait(gen.repeat_count == scb.no_transactions);
endtask
/* 使用run任务启动上述活动 */
task run;
pre_test();
test();
post_test();
$finish;
endtask
endclass
定义test代码块。
`include"environment.sv"
program test(mem_intf intf);
/* 创建环境,配置验证条件,初始化验证 */
environment env;
initial begin
/* 实例化环境类 */
env = new(intf);
/* 设置事务数目 */
env.gen.repeat_count = 10;
/* 启动环境 */
env.run();
end
endprogram
定义tbench_top模块。
`include "interface.sv"
`include "test.sv"
`include "design.sv"
module tbench_top;
/* 最顶层的文件,用于连接DUT和验证平台;
包含DUT,测试以及接口实例;
接口能将DUT与验证平台相连 */
bit clk;
bit reset;
/* 实例化接口 */
mem_intf intf(clk,reset);
/* 实例化DUT,并配置接口 */
memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);
/* 连接测试接口 */
test t1(intf);
always #5 clk = ~clk;
initial begin
reset = 1;
#5 reset=0;
end
initial begin
$dumpfile("dump.vcd");$dumpvars;
end
endmodule
下面是运行结果。