第五步、编写数据处理模块
generator与driver之间的通信属于异步通信,要实现通信需要使用握手机制,由generator发送给agent的数据并不具备时序性,所以需要driver将数据发送给DUT的时候偶,进行一定的时序处理。
`ifndef DRIVER_SV
`define DRIVER_SV
class friver
int tr_num;
transaction tr;
mailbox agt2drv_mbx=new();
virtual ahb_slv_if slv_if;
logic [31:0] hwdata_ld;
extern function new(int tr_num,virtual ahb_slv_if slv_if, mailbox agt2drv_mbx);
extern function build();
extern task run();
endclass
function driver::new(int tr_num,virtual ahb_slv_if slv_if, mailbox agt2drv_mbx);
this.tr_num = tr_num;
this.agt2drv_mbx = agt2drv_mbx;
this.slv_if = slv_if;
endfunction
function driver::build();
endfunction
task driver::run();
@(posedge slv_if.hresetn);
@slv_if.drv_cb;
@slv_if.drv_cb;
repeat@(tr_num)
tr = new();
agt2drv_mbx.get(tr);
wait@(slv_if.hready.hresp);
slv_if.drv_cb.hsel <= hsel ;
slv_if.drv_cb.haddr <= haddr ;
slv_if.drv_cb.hwrite <= hwrite;
slv_if.drv_cb.htrans <= htrans ;
slv_if.drv_cb.hsize <= hsize ;
slv_if.drv_cb.hready <= hready ;
hwdata_ld <= hrdata;
@slv_if.drv_cb;
slv_if.drv_cb.hwdata <= hwdata_ld;
repeat@(10) begin
@slv_if.drv_cb;
end
endtask
`endif
第六步、编写采样模块
monitor模块的主要作用是将采集的数据做时需处理,使得地址阶段和数据阶段同步,然后发送到scoreboard里面去。这里面有个较为关键的地方就是,haddr需要先复制给haddr_ld,然后再通过haddr_ld传递给tr,hwdata可以直接传递给tr。这是根据AHB的特点来的,一个操作需要两个周期完成,第一个周期是地址周期,第二个周期是数据周期,时序协议所规定的,地址和控制信号都在数据前面一个时钟周期,从interface过来的信号,肯定按AHB协议来做,但是环境内部是没有时序,tr一个数据包,包含所有内容,是不具备时序的。所以如果把对应的据和命令都放在一个包里面的话,相当于做一个同步的调整,放在同一个周期才行。因为地址要做到和数据同—拍,就要比数据晚—拍才行。
`ifndef MONITOR_SV
`define MONITOR_SV
class monitor;
mailbox mon2scb_mbx = new();
transaction tr;
virtaul ahb_slv_if slv_if
int tr_num;
logic [31:0] haddr_ld;
logic hwrite_ld;
logic [1:0] htrans_ld;
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;
this.slv_if = slv_if;
this.tr_num = tr_num;
endfunction
function monitor::build();
endfunction
task monitor::run();
@(posedge slv_if.hresetn);
@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;
tr.haddr = haddr_ld;
tr.htrans = htrans_ld;
tr.hwrite = hwrite_ld;
tr.hsize = hsize_ld;
tr.hsel = hsel_ld;
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
第七步、编写自动比较模块
scoreboard部分会把agent发送的数据和monitor发送的数据进行比较。比较关键的地方就是构建了一个由寄存器构成的存储器,储存器的数据位宽为32bit,深度为2^14,地址位宽为14,因为我们是gloden模型跟RTL不一样的是,我们没有分bank,没有物理实现,只是实现一个逻辑功能,只要数据能写进去就行。tr.haddr[ADDR_WIDTH-1:2]这一列是/4操作,系统地址到物理地址的转换。
`ifndef SCOREBOARD_SV
`define SCOREBOARD_SV
class scoreboard;
int tr_num;
mailbox agt2scb_mbx = new();
mailbox mon2scb_mbx = new();
transaction tr = new();
//定义golden_model参数
parameter ADDR_WIDTH = 16;
parameter SRAM_ADDR_WIDTH =ADDR_WIDTH -2;
parameter SRAM_DEPTH = 1<<(SRAM_ADDR_WIDTH);
logic [31:0]sram_gld[SRAM_DEPTH ];
int err_cnt = 0;
extern function new(int tr_num,mailbox agt2scb_mbx,mailbox mon2scb_mbx );
extern function build();
extern task check();
extern task run();
endclass
function scoreboard::new(int tr_num,mailbox agt2scb_mbx,mailbox mon2scb_mbx );
this.agt2scb_mbx = agt2scb_mbx ;
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)
if(tr.hsel && tr.htrans[]) begin
if(tr.hwrite) begin
case({tr.hsize[1:0],tr.haddr[1:0]})
4'b00_00:sram_gld[tr.haddr[ADDR_WIDTH-1:2]][7:0] = tr.hwdata[7:0];
4'b00_01:sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:0] = tr.hwdata[15:0];
4'b00_10:sram_gld[tr.haddr[ADDR_WIDTH-1:2]][23:16] =tr.hwdata[23:16];
4'b00_11:sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:24] =tr.hwdata[31:24];
4'b00_00:sram_gld[tr.haddr[ADDR_WIDTH-1:2]][15:0] = tr.hwdata[15:0];
4'b01_01:sram_gld[tr.haddr[ADDR_WIDTH-1:2]][31:16] =tr.hwdata[31:16];
4'b10_00:sram_gld[tr.haddr[ADDR_WIDTH-1:2]]= tr.hwdata[31:0];
default:begin
sram_gld[tr.haddr[ADDR_WIDTH-1:2]]= 32'hx;
$display("@%ot:ERROR::MISMATCH*",$time ,tr.hsize,tr.haddr[1:0]);
err_cnt++;
end
endcase
end
else begin
if(tr.hdata != sram_gld[tr.haddr[ADDR_WIDTH-1:2]])begin
$display("@ot:ERROR::tr.hdata != sram_gld[tr.haddr[ADDR_WIDTH-1:2]]",$time ,tr.hdata,sram_gld[tr.haddr[ADDR_WIDTH-1:2]],tr.haddr);
err_cnt++;
end
end
end
end
endtask
task scoreboard::run();
check();
if(err_cnt == 0)beign
$display("**********************************************");
$display("**********************************************");
$display("*****************TEST PASS*******************");
$display("**********************************************");
$display("**********************************************");
end
else begin
$display("**********************************************");
$display("**********************************************");
$display("*****************TEST FAILD*******************",err_cnt);
$display("**********************************************");
$display("**********************************************");
end
endtask
`endif
第八步、编写环境部分
环境部分通过定义公共邮箱,把各个部分的组件连接起来,实现数据在不同组件中进行传输。new和build都是起连接的作用,只不过就是new的时候跟把env上面一层的相关的接口信号连接到env内部的成员里面来;build阶段就是把new阶段连接的成员信息又连接到drv里面来。environment中所有的数值都是从testcase中得到的
`ifndef ENVIRONMENT_SV
`define ENVIRONMENT_SV
class environment;
generator gen;
agent agt;
driver drv;
scoreboard scb;
monitor mon;
int tr_num;
mailbox gen2agt_mbx = new();
mailbox agt2drv_mbx = new();
mailbox agt2scb_mbx = new();
mailbox mon2scb_mbx = new();
virtual ahb_slv_if slv_if;
extern function new(int tr_num, virtual ahb_slv_if slv_if);
extern function build();
extern task run();
endclass
function environment::new(int tr_num, virtual ahb_slv_if slv_if);
this.tr_num = tr_num ;
this.slv_if = slv_if ;
endfunction
function environment::build();
gen = new(gen2agt_mbx ,tr_num);
agt = new(gen2agt_mbx ,agt2drv_mbx ,agt2scb_mbx,tr_num);
drv = new(agt2drv_mbx,slv_if,tr_num);
scb = new(agt2scb_mbx ,mon2scb_mbx ,tr_num);
mon = new(mon2scb_mbx ,slv_if,tr_num);
endfunction
task environment::run();
fork
gen.run();
agt.run();
drv.run();
mon.run();
scb.run();
join
endtask
`endif
第九步、编写测试用例
测试模块采用program块进行编写,这样可以利用该模块隐式的执行$finish来结束仿真,同时定义覆盖组还可以收集功能覆盖率
`ifndef AHB_SRAM_TEST_SV
`difine AHB_SRAM_TEST_SV
program automatic ahb_sram_test(ahb_slv_if slv_if);
int tr_num= 20;
int tc_num =0;
int rnd_seed;
covergroup ahb_slv_cg();
coverpoint slv_if.hsel{ bins hsel = {0,1};
}
coverpoint slv_if.hwrite{ bins write= {0};
bins read = {1};
}
coverpoint slv_if.hsize{ bins hsize_8= {2'b00};
bins hsize_16= {2'b01};
bins hsize_31= {2'b10};
}
coverpoint slv_if.haddr{ bins haddr_0 = {[32'h0:32'h0000_1FFF]};
bins haddr_1 = {[32'h0000_2000:32'h0000_3FFF]};
bins haddr_2 = {[32'h0000_4000:32'h0000_5FFF]};
bins haddr_3 = {[32'h0000_6000:32'h0000_7FFF]};
bins haddr_4 = {[32'h0000_8000:32'h0000_9FFF]};
bins haddr_5 = {[32'h0000_A000:32'h0000_BFFF]};
bins haddr_6 = {[32'h0000_C000:32'h0000_DFFF]};
bins haddr_7 = {[32'h0000_E000:32'h0000_FFFF]};
}
coverpoint slv_if.hwdata{ { bins hwdata_0byte={[32'h0:32'hFF]};
bins hwdata_1byte={[32'h100:32'hFF_00]};
bins hwdata_2byte={[32'h1_0000:32'hFF_0000]};
bins hwdata_3byte={[32'h100_0000:32'hFF00_0000]};
}
coverpoint slv_if.hrdata{ bins hrdata_0byte={[32'h0:32'hFF]};
bins hrdata_1byte={[32'h100:32'hFF_00]};
bins hrdata_2byte={[32'h1_0000:32'hFF_0000]};
bins hrdata_3byte={[32'h100_0000:32'hFF00_0000]};
}
endgroup
class tc_base;
int tr_num;
encironment ahb_env;
virtual ahb_slv_if slv_if;
ahb_slv_cg slv_cg=new();
extern function new(virtual ahb_slv_if slv_if,int tr_num);
extern function build();
extern virtual task run();
endclass
function tc_base::new(virtual ahb_slv_if slv_if,int tr_num);
this.slv_if = slv_if;
this.tr_num = tr_num;
endfunctiom
function tc_base::build();
ahb_env = new(this.slv_if ,tr_num);
ahb_env.build();
endfunction
task tc_base::run();
fork
ahb_env.run();
begin
@(posedge slv_if.hclk);
@(posedge slv_if.hresetn);
@(posedge slv_if.hclk);
repeat@(tr_num) begin
@(posedge slv_if.hclk);
slv_cg.sample(); 每一次发包,都对其功能覆盖率进行收集
end
end
jion
endtask
/*-------------------------------------------------------------------------------------*/
//测试用例
/*-------------------------------------------------------------------------------------*/
class tc000 extends tc_base;
extern function new(virtual ahb_slv_if slv_if,int tr_num);
extern virtual task run();
endclass
function tc000::new(virtual ahb_slv_if slv_if,int tr_num);
super.new(slv_if,tr_num);
endfunction
task tc000::run()
fork
super.run()
begin
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data32(i*4,i);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data32(i*4);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data16(i*2,i);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data16(i*2);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data8(i,i);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data8(i);
end
end
jion
endtask
class tc001 extends tc_base;
extern function new(virtual ahb_slave_if slv_if, int tr_num);
extern virtual task run();
endclass
function tc001::new(virtual ahb_slave_if slv_if, int tr_num);
super.new(slv_if,tr_num);
endfunction
task tc001::run();
fork
super.run();
begin
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data32(32'h0000_FFFC-i*4,32'h5A5A_5A5A);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data32(32'h0000_FFFC-i*4);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data16(32'h0000_FFFC-i*2,32'h0000_5A5A);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data16(32'h0000_FFFC-i*2);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data8(32'h0000_FFFC-i,32'h0000_005A);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data8(32'h0000_FFFC-i);
end
end
join
endtask
class tc002 extends tc_base;
extern function new(virtual ahb_slave_if slv_if, int tr_num);
extern virtual task run();
endclass
function tc002::new(virtual ahb_slave_if slv_if, int tr_num);
super.new(slv_if,tr_num);
endfunction
task tc002::run();
fork
super.run();
begin
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data32_random(i*4);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data32(i*4);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data16_random(i*2);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data16(i*2);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.write_data8_random(i);
end
for(int i=0;i<(tr_num/6);i++)begin
#1;
ahb_env.gen.read_data8(i);
end
end
join
endtask
class tc003 extends tc_base;
extern function new(virtual ahb_slave_if slv_if, int tr_num);
extern virtual task run();
endclass
function tc003::new(virtual ahb_slave_if slv_if, int tr_num);
super.new(slv_if,tr_num);
endfunction
task tc003::run();
fork
super.run();
begin
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.write_data8(32'h0000_FFFC-i,i);
#1;
ahb_env.gen.no_op();
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.read_data8(32'h0000_FFFC-i);
#1;
ahb_env.gen.no_op();
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.write_data16(32'h0000_FFFC-i*2,i);
#1;
ahb_env.gen.no_op();
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.read_data16(32'h0000_FFFC-i*2);
#1;
ahb_env.gen.no_op();
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.write_data32(32'h0000_FFFC-i*4,i);
#1;
ahb_env.gen.no_op();
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.read_data32(32'h0000_FFFC-i*4);
#1;
ahb_env.gen.no_op();
end
end
join
endtask
class tc004 extends tc_base;
extern function new(virtual ahb_slave_if slv_if, int tr_num);
extern virtual task run();
endclass
function tc004::new(virtual ahb_slave_if slv_if, int tr_num);
super.new(slv_if,tr_num);
endfunction
task tc004::run();
fork
super.run();
begin
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.write_data8_random(i);
#1;
ahb_env.gen.no_op();
#1;
ahb_env.gen.read_data8(i);
#1;
ahb_env.gen.no_op();
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.write_data16_random(i*2);
#1;
ahb_env.gen.no_op();
#1;
ahb_env.gen.read_data16(i*2);
#1;
ahb_env.gen.no_op();
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.write_data32_random(i*4);
#1;
ahb_env.gen.no_op();
#1;
ahb_env.gen.read_data32(i*4);
#1;
ahb_env.gen.no_op();
end
end
join
endtask
class tc005 extends tc_base;
extern function new(virtual ahb_slave_if slv_if, int tr_num);
extern virtual task run();
endclass
function tc005::new(virtual ahb_slave_if slv_if, int tr_num);
super.new(slv_if,tr_num);
endfunction
task tc005::run();
fork
super.run();
begin
for(int i=0;i<(tr_num/2);i++)begin
#1;
ahb_env.gen.write_data32(i*4,100+i);
end
for(int i=0;i<(tr_num/12);i++)begin
#1;
ahb_env.gen.read_data32(i*4);
end
end
join
endtask
class tc006 extends tc_base;
extern function new(virtual ahb_slave_if slv_if, int tr_num);
extern virtual task run();
endclass
function tc006::new(virtual ahb_slave_if slv_if, int tr_num);
super.new(slv_if,tr_num);
endfunction
task tc006::run();
fork
super.run();
begin
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.write_data32(i*4, 32'h0000_0000);
end
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.read_data32(i*4);
end
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.write_data32(i*4, 32'hFFFF_FFFF);
end
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.read_data32(i*4);
end
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.write_data32(i*4, 32'h5555_5555);
end
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.read_data32(i*4);
end
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.write_data32(i*4, 32'hAAAA_AAAA);
end
for(int i=0;i<(tr_num/8);i++)begin
#1;
ahb_env.gen.read_data32(i*4);
end
end
join
endtask
tc000 tc0;
tc001 tc1;
tc002 tc2;
tc003 tc3;
tc004 tc4;
tc005 tc5;
tc006 tc6;
initial begin
$vcdpluson(); //生成波形文件
if(!$vcdpluson$plusargs("tc_num=%d,tc_num")) begin
end
else begin
$display("@%ot::tc_num is : %d",$time,tc_num);
end
end
tc0 = new(slv_if,6000); //传递接口和发包数量
tc1 = new(slv_if,600); //定向用例
tc2 = new(slv_if,6000);
tc3 = new(slv_if,1200);
tc4 = new(slv_if,6000);
tc5 = new(slv_if,3000);
tc6 = new(slv_if,3000); //随即用例
tc7 = new(slv_if,32'h0002_0000); //遍历整个SRAM空间,0,1,5,a
if(tc_num == 0)begin
tc0.build();
tc0.run();
end
else if(tc_num == 1)begin
tc1.build();
tc1.run();
end
else if(tc_num == 2)begin
tc2.build();
tc2.run();
end
else if(tc_num == 3)begin
tc3.build();
tc3.run();
end
else if(tc_num == 4)begin
tc4.build();
tc4.run();
end
else if(tc_num == 5)begin
tc5.build();
tc5.run();
end
else if(tc_num == 6)begin
tc6.build();
tc6.run();
end
else begin
$display("@%0t : ERROR tc_num(%0d) does not exist",$time,tc_num);
end
end
endprogram
`endif
主要部分是覆盖组的的定义,用于收集功能覆盖率;其次是测试用例的编写,主要是八个部分:
测试用例0:从较低地址开始写+读,包含8、16、32bit的数据;
测试用例1:从较高地址开始写+读,包含8、16、32bit的数据,32和16位数据读写固定了数据大小;
测试用例2:随机写读数据,按32、16、8的顺序;
测试用例3:从较高地址开始写+读,包含8、16、32bit的数据;
测试用例4:随机写读数据,中间插入无效操作 no_op();
测试用例5:较低地址写读32位数据;
测试用例6:随机写读数据;
测试用例7:写读数据,地址遍历每个空间,数据固定为四种;
第十步、编写顶层连接文件top
通过interface来建立测试用例和DUT之间的连接。
module ahb_sramc_svtb_top();
bit hclk;
bit sram_clk;
bit hresetn;
ahb_slv_if ahb_sramc_if(hclk);
ahb_sram_test test(ahb_sramc_if);
sramc_top u_sramc_top(
.hclk (hclk),
.sram_clk (sram_clk),
.hresetn (ahb_sramc_if.hresetn),
.hsel (ahb_sramc_if.hsel),
.hwrite (ahb_sramc_if.hwrite),
.htrans (ahb_sramc_if.htrans),
.hsize (ahb_sramc_if.hsize),
.hready (ahb_sramc_if.hready),
.hburst (3'b0),
.haddr (ahb_sramc_if.haddr),
.hwdata (ahb_sramc_if.hwdata),
.hrdata (ahb_sramc_if.hrdata),
.dft_en (1'b0),
.bist_en (1'b0),
.hready_resp (ahb_sramc_if.hready_resp),
.hresp (ahb_sramc_if.hresp),
.bist_done ( ),
.bist_fail ( )
);
initial begin
forever #10 hclk = ~ hclk;
end
always @(*) sram_clk = ~hclk;
initial begin
hresetn = 1;
#1 hresetn = 0;
#10 hresetn = 1;
end
assign ahb_sramc_if.hresetn = hresetn; //开始的时候先进行复位操作
endmodule
代码参考: AHB—SRAMC基于SV的Testbench之二(driver、monitor、scoreboard、environment)_SD.ZHAI的博客-CSDN博客