文章目录
2.5.数据驱动模块driver:driver.sv
generator与driver之间属于异步通信,二者之间要实现同步通信需要使用“握手机制”,即event事件。
需要注意的是,由generator产生进过agent发送来的数据并不具备时序,因此driver再将数据发送至DUT时,需要进行一定的时序处理。
`ifndef DRIVER_SV
`define DRIVER_SV
class driver;
mailbox agt2drv_mbx = new(); //创建邮箱,以采集由agent发送至driver的数据包(无时序)
transaction tr;
virtaul ahb_alv_if slv_if; //driver会将接收到的数据按照AHB时序处理后,通过接口interface发送至DUT
int tr_num; //发包数量
logic [31:0] hwdata_ld; //做时序用途,因为数据阶段会比地址阶段晚一拍发送
extern function new(mailbox agt2drv_mbx,virtual ahb_slv_if slv_if,int tr_num);
extern function build(); //new函数主要将driver内部成员(从generator发送到agent,再到driver的
extern task run(); //tr数据包)与DUT的外部接口DUT成员连接起来,以便数据发送
endclass
function driver::new(mailbox agt2drv_mbx,virtual ahb_slv_if slv_if,int tr_num);
this.agt2drv_mbx = agt2drv_mbx; //new函数主要起连接作用,连接agent的邮箱,以获取agent发来的数据包
this.slv_if = slv_if;
this.tr_num = tr_num;
endfunction
function driver::build();
endfunction
task driver::run(); //run将对agent发来的数据进行时序处理,再进过接口发送进入DUT
@(posedge slv_if.hresetn); //在run开始之前,先等待DUT的复位信号失效,有低变高
@slv_if.drv_cb;
@slv_if.drv_cb; //等待两个时钟周期
repeat(tr_num)begin
tr = new(); //创建数据包对象
agt2drv_mbx.get(tr); //将由agent发送来的数据从邮箱mailbox中取出
wait(slv_if.hready_resp); //master需要看到slave ready信号是才会发送数据,
slv_if.drv_cb.hsel <= tr.hsel; //driver是去模拟master的行为时序,等slave ready为高时,才将数据发出
slv_if.drv_cb.haddr <= tr.haddr;
slv_if.drv_cb.htrans <= tr.htrans;
slv_if.drv_cb.hwrite <= tr.hwrite;
slv_if.drv_cb.hsize <= tr.hsize; //右侧数据为generator进过agent发送来driver的数据
slv_if.drv_cb.hready <= tr.hready; //地址和控制信号相对于写数据信号寄存一拍
hwdata_ld <= tr.hwdata;
@slv_if.drv_cb; //等待一个时钟周期,再发送写数据信号
slv_if.drv_cb.hwdata <= tr.hwdata;
end
repeat(10)begin
@slv_if.drv_cb; //后续处理,等待几个周期,以防止尾巴上的时序数据丢失
end
endtask
`endif
2.6.结果采样模块monitor:monitor.sv
monitor采集模块需要将采集的数据做时序还原处理,使其不再具备时序,然后再将其打包放入邮箱发送至scb。
`ifndef MONITOR_SV
`define MONITOR_SV
class monitor;
mailbox mon2scb_mbx = new(); //创建邮箱,采集DUT输出的数据,并发送至scb
transaction tr; //数据包对象句柄,其中数据无时序概念
virtaul ahb_slv_if slv_if //monitor与DUT之间通过接口相连
int tr_num; //发包个数
logic [31:0] haddr_ld; //从接口中采集的数据是按照AHB时序采集的
logic hwrite_ld; //在将这些数据发送至scb之前需要将地址和控制信号放置一拍,与数据对齐
logic [1:0] htrans_ld; //然后一起打包成tr,放入邮箱,发送至scb
logic [1:0] hsize_ld;
logic hsel_ld;
extern function new(mailbox mon2scb_mbx,virtaul ahb_slv_if slv_if,int tr_num);
extern function build();
extern task run();
endclass
function monitor::new(mailbox mon2scb_mbx,virtaul ahb_slv_if slv_if,int tr_num);
this.mon2scb_mbx = mon2scb_mbx; //new函数主要起连接作用,将顶层邮箱与本地邮箱连接起来
this.slv_if = slv_if; //将顶层DUT的interface与本地相连
this.tr_num = tr_num;
endfunction
function driver::build(); //初始化配置
endfunction
task monitor::run(); //实时采集slv_if的数据,并将采集的数据进行时序处理,打包放入邮箱送至scb
@(posedge slv_if.hresetn); //在run开始之前,先等待DUT的复位信号失效,有低变高
@slv_if.mon_cb;
@slv_if.mon_cb; //等待两个时钟周期
repeat(tr_num)begin
tr = new(); //创建数据包对象
haddr_ld <= slv_if.mon_cb.haddr;
htrans_ld <= slv_if.mon_cb.htrans;
hwrite_ld <= slv_if.mon_cb.hwrite;
hsize_ld <= slv_if.mon_cb.hsize;
hsel_ld <= slv_if.mon_cb.hsel;
@slv_if.mon_cb; //等待一个时钟周期,即将monitor采集的接口地址阶段信号寄存一拍
tr.haddr = haddr_ld;
tr.htrans = htrans_ld; //由于tr对象里的数据都为随机化数据,故只能使用阻塞赋值
tr.hwrite = hwrite_ld;
tr.hsize = hsize_ld;
tr.hsel = hsel_ld; //地址和控制信号,slave选择信号寄存一拍,到来需等待数据信号
tr.hwdata = slv_if.mon_cb.hwdata;
tr.hrdata = slv_if.mon_cb.hrdata;
mon2scb_mbx.put(tr);
end
repeat(10) begin
@slv_if.mon_cb; //后续时钟延长处理,防止数据丢失
end
endtask
`endif
2.7.结果比对模块scoreboard:scoreboard.sv
计分板scoreboard会将从agent发来的数据与由monitor收集的DUT输出数据进行一一比对。具体比对方法可参考文章:简单全加器验证—Testbench。
`ifndef SCOREBOARD_SV
`define SCOREBOARD_SV
class scoreboard;
int tr_num;
mailbox agt2scb_mbx = new(); //创建邮箱,接收由agent发来的数据,激励
mailbox mon2scb_mbx = new(); //创建邮箱,接收由monitor发来的数据,DUT输出
transaction tr;
parameter ADDR_WIDTH = 16; //系统地址空间64K=2^6×2^10=2^16
parameter SRAM_ADDR_WIDTH = ADDR_WIDTH -2; //SRAM地址空间16K=2^4×2^10=2^14
parameter SRAM_DEPTH = 1 << (SRAM_ADDR_WIDTH); //1左移14位,即为2^14
logic [31:0] sram_gld[SRAM_DEPTH]; //golden sram, 初始值为x,未知态
//sram_gld是一个由2^14个32位寄存器组成的存储器,即该存储器深度为2^14,数据位宽为32bit,地址位宽为14bit
logic [31:0] sram_gld_rdata; //临时变量,用于存储golden model单个数据,便于一一比对
logic [31:0] sram_cmp_rdata; //临时变量,用于存储DUT输出的单个数据,便于一一比对
int err_cnt = 0; //记录比较的数据不一致的错误次数
extern function new(mailbox agt2scb_mbx,mailbx mon2scb_mbx,int tr_num);
extern function build();
extern task check();
extern task run();
endclass
function scoreboard::new(mailbox agt2scb_mbx,mailbx mon2scb_mbx,int tr_num);
this.agt2scb_mbx = agt2scb_mbx; //将env的mailbox与tr_num传进来,建立连接
this.mon2scb_mbx = mon2scb_mbx;
this.tr_num = tr_num;
endfunction
function scoreboard::build();
endfunction
task scoreboard::check();
repeat(tr_num)begin
mon2scb_mbx.get(tr); //从monitor到scoreboard的邮箱中取出一个数据包,用于做数据比对
$display("**@%0t:SCB::tr.haddr(%0h),tr.hwdata(%0h)",$time,tr.haddr,tr.hwdata); //打印,用于检查错误
if(tr.hsel && tr.htrans[1]) begin //检查数据是否为有效传输
if(tr.hwrite) begin //如果为写数据命令
case({tr.hsize[1:0],tr.haddr[1:0]})begin //根据地址低两位来判断传输带宽8/16/32bit
4'b00_00: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][7:0] = tr.hwdata[7:0]; //sram_gld地址位宽14bit,写8bit
4'b00_01: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:8] = tr.hwdata[15:8]; //写8bit
4'b00_10: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][23:16] = tr.hwdata[23:16]; //写8bit
4'b00_11: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:24] = tr.hwdata[31:24]; //写8bit
4'b01_00: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:0] = tr.hwdata[15:0]; //写低16bit
4'b01_10: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:16] = tr.hwdata[31:16]; //写高16bit
4'b10_00: sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:0] = tr.hwdata[31:0]; //写32bit
default: begin
sram_gld[tr.haddr[ADDR_WIDTH-1:2]] = 32'hx;
$display("**@%0t:ERROR::WRONG hsize (%0h) and haddr[1:0](%0h)",$time,tr.hsize,tr.haddr[1:0]);
err_cnt++;
end
endcase
end
else begin //读数据命令,进行比较
sram_gld_rdata = 32'h0000_0000; //初始化临时变量,用于存储DUT输出的读数据
sram_cmp_rdata = 32'h0000_0000; //初始化临时变量,用于存储agent发来的数据
case({tr.hsize[1:0],tr.haddr[1:0]})
4'b00_00: begin
sram_cmp_rdata[7:0] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][7:0]; //DUT输出的比对数据
sram_gld_rdata[7:0] = tr.hrdata[7:0]; //agent发来的比对数据
4'b00_01: begin
sram_cmp_rdata[15:8] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:8]; //DUT输出的比对数据
sram_gld_rdata[15:8] = tr.hrdata[15:8]; //agent发来的比对数据
4'b00_10: begin
sram_cmp_rdata[23:16] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][23:16]; //DUT输出的比对数据
sram_gld_rdata[23:16] = tr.hrdata[23:16]; //agent发来的比对数据
4'b00_11: begin
sram_cmp_rdata[31:24] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:24]; //DUT输出的比对数据
sram_gld_rdata[31:24] = tr.hrdata[31:24]; //agent发来的比对数据
4'b01_00: begin
sram_cmp_rdata[15:0] = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:0]; //DUT输出的比对数据
sram_gld_rdata[15:0] = tr.hrdata[15:0]; //agent发来的比对数据
4'b01_10: begin
sram_cmp_rdata = sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:24]; //DUT输出的比对数据
sram_gld_rdata = tr.hrdata; //agent发来的比对数据 ,32bit可省略bit位
default: begin
sram_gld[tr.haddr[ADDR_WIDTH-1:2]] = 32'hx;
$display("**@%0t:ERROR::WRONG hsize (%0h) and haddr[1:0](%0h)",$time,tr.hsize,tr.haddr[1:0]);
err_cnt++;
end
endcase
if(sram_gld_rdata !== sram_cmp_rdata)begin //golden model定义为logic类型,存在未知态x,故采用!==
$display("**@%0t:ERROR::sram_gld_hrdata(%0h) !== sram_cmp_hrdata(%0h) in haddr(%0h)", \
$time,sram_gld_rdata,sram_cmp_rdata,tr.haddr[ADDR_WIDTH-1:0]);
err_cnt++; //地址不进行移位处理,便于观察
end
end //else begin
end //if(tr.hsel && tr.htrans[1]) begin
end // repeat(tr_num)begin
endtask
task scoreboard::run();
check(); //检查错误
if(err_cnt)begin
$display("**********************************************");
$display("**********************************************");
$display("******************TEST PASS******************");
$display("**********************************************");
$display("**********************************************");
else begin
$display("**********************************************");
$display("**********************************************");
$display("**************TEST FAILED with %d errors******",err_cnt);
$display("**********************************************");
$display("**********************************************");
end
endtask
`endif
2.8.验证环境environment:environment.sv
Environment验证环境通过定义公共邮箱,将其内部的各个子组件进行连接,从而实现数据在各组件之间的传输;同时需要定义接口slv_if,将内部子组件与DUT进行连接;此外还需传递具体的发包数目tr_num。
class environment;
generator gen; //environment中包含了generator、agent、driver、monitor和scoreboard
agent agt //等子组件,并且会对其进行调用
driver drv;
monitor mon;
scoreboard scb;
int tr_num; //定义发包数目,发挥指挥作用
mailbox gen2agt_mbx = new(); //environment顶层会定义公共邮箱,进行邮箱连接,传递数据
mailbox agt2drv_mbx = new();
mailbox agt2scb_mbx = new();
mailbox mon2scb_mbx = new();
virtaul ahb_slv_if slv_if; //env顶层通过接口interface与子组件之间进行连接
extern function new(virtaul ahb_slv_if slv_if,int tr_num);
extern task build();
extern task run();
endclass
function environment::new(virtaul ahb_slv_if slv_if,int tr_num);
this.slv_if = slv_if; //new函数执行时,会将env顶层外部(DUT传递来的)接口信号与其子组件连接
this.tr_num = tr_num;
endfunction
task environment::build();
gen = new(gen2agt_mbx, tr_num); //env层公共邮箱的建立是为了实现数据包tr在组件之间的传递
agt = new(gen2agt_mbx,agt2drv_mbx,agt2scb_mbx,tr_num);
drv = new(agt2drv_mbx,slv_if,tr_num); //slv_if负责连接DUT
mon = new(mon2scb_mbx,slv_if,tr_num); //tr_num的具体数值有testcase层给出
scb = new(agt2scb_mbx,mon2scb_mbx,tr_num);
endtask
task environment::task();
fork
gen.run(); //产生数据,给到agent
agt.run(); //将接受自generator的数据通过邮箱发送至drv,scb
drv.run(); //将接受自generator的数据,按照AHB时序协议通过接口slv_if送至DUT
mon.run(); //采集DUT输出,将其转化为无时序状态,打包发送至scb
scb.run(); //将golden model与无时序状态的DUT输出进行一一比较
join
endtask
`endif