前言:Lab5主要完成如何定义覆盖率,如何从验证计划到测试用例的实现,最后再到覆盖率的量化。
种一棵树最好的时间是十年前,其次是现在。不是吗?
关于实验代码、验证相关的资料,你找的到的找不到的都可以从公众号"袁小丑"免费获取!
代码分析
在mcdf_pkg.sv新添加了一个组件mcdf_coverage,来定义之前实验定义的测试点。
class mcdf_coverage;
local virtual chnl_intf chnl_vifs[3];
local virtual arb_intf arb_vif;
local virtual mcdf_intf mcdf_vif;
local virtual reg_intf reg_vif;
local virtual fmt_intf fmt_vif;
local string name;
local int delay_req_to_grant;
covergroup cg_mcdf_reg_write_read;
addr: coverpoint reg_vif.mon_ck.cmd_addr {
type_option.weight = 0;
bins slv0_rw_addr = {`SLV0_RW_ADDR};
bins slv1_rw_addr = {`SLV1_RW_ADDR};
bins slv2_rw_addr = {`SLV2_RW_ADDR};
bins slv0_r_addr = {`SLV0_R_ADDR };
bins slv1_r_addr = {`SLV1_R_ADDR };
bins slv2_r_addr = {`SLV2_R_ADDR };
}
cmd: coverpoint reg_vif.mon_ck.cmd {
type_option.weight = 0;
bins write = {`WRITE};
bins read = {`READ};
bins idle = {`IDLE};
}
cmdXaddr: cross cmd, addr {
bins slv0_rw_addr = binsof(addr.slv0_rw_addr);
bins slv1_rw_addr = binsof(addr.slv1_rw_addr);
bins slv2_rw_addr = binsof(addr.slv2_rw_addr);
bins slv0_r_addr = binsof(addr.slv0_r_addr );
bins slv1_r_addr = binsof(addr.slv1_r_addr );
bins slv2_r_addr = binsof(addr.slv2_r_addr );
bins write = binsof(cmd.write);
bins read = binsof(cmd.read );
bins idle = binsof(cmd.idle );
bins write_slv0_rw_addr = binsof(cmd.write) && binsof(addr.slv0_rw_addr);
bins write_slv1_rw_addr = binsof(cmd.write) && binsof(addr.slv1_rw_addr);
bins write_slv2_rw_addr = binsof(cmd.write) && binsof(addr.slv2_rw_addr);
bins read_slv0_rw_addr = binsof(cmd.read) && binsof(addr.slv0_rw_addr);
bins read_slv1_rw_addr = binsof(cmd.read) && binsof(addr.slv1_rw_addr);
bins read_slv2_rw_addr = binsof(cmd.read) && binsof(addr.slv2_rw_addr);
bins read_slv0_r_addr = binsof(cmd.read) && binsof(addr.slv0_r_addr);
bins read_slv1_r_addr = binsof(cmd.read) && binsof(addr.slv1_r_addr);
bins read_slv2_r_addr = binsof(cmd.read) && binsof(addr.slv2_r_addr);
}
endgroup
covergroup cg_mcdf_reg_illegal_access;
addr: coverpoint reg_vif.mon_ck.cmd_addr {
type_option.weight = 0;
bins legal_rw = {`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR};
bins legal_r = {`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR};
bins illegal = {[8'h20:$], 8'hC, 8'h1C};
}
cmd: coverpoint reg_vif.mon_ck.cmd {
type_option.weight = 0;
bins write = {`WRITE};
bins read = {`READ};
}
wdata: coverpoint reg_vif.mon_ck.cmd_data_m2s {
type_option.weight = 0;
bins legal = {[0:'h3F]};
bins illegal = {['h40:$]};
}
rdata: coverpoint reg_vif.mon_ck.cmd_data_s2m {
type_option.weight = 0;
bins legal = {[0:'hFF]};
illegal_bins illegal = default;
}
cmdXaddrXdata: cross cmd, addr, wdata, rdata {
bins addr_legal_rw = binsof(addr.legal_rw);
bins addr_legal_r = binsof(addr.legal_r);
bins addr_illegal = binsof(addr.illegal);
bins cmd_write = binsof(cmd.write);
bins cmd_read = binsof(cmd.read);
bins wdata_legal = binsof(wdata.legal);
bins wdata_illegal = binsof(wdata.illegal);
bins rdata_legal = binsof(rdata.legal);
bins write_illegal_addr = binsof(cmd.write) && binsof(addr.illegal);
bins read_illegal_addr = binsof(cmd.read) && binsof(addr.illegal);
bins write_illegal_rw_data = binsof(cmd.write) && binsof(addr.legal_rw) && binsof(wdata.illegal);
bins write_illegal_r_data = binsof(cmd.write) && binsof(addr.legal_r) && binsof(wdata.illegal);
}
endgroup
covergroup cg_channel_disable;
ch0_en: coverpoint mcdf_vif.mon_ck.chnl_en[0] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch1_en: coverpoint mcdf_vif.mon_ck.chnl_en[1] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch2_en: coverpoint mcdf_vif.mon_ck.chnl_en[2] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch0_vld: coverpoint chnl_vifs[0].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
ch1_vld: coverpoint chnl_vifs[1].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
ch2_vld: coverpoint chnl_vifs[2].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
chenXchvld: cross ch0_en, ch1_en, ch2_en, ch0_vld, ch1_vld, ch2_vld {
bins ch0_en = binsof(ch0_en.en);
bins ch0_dis = binsof(ch0_en.dis);
bins ch1_en = binsof(ch1_en.en);
bins ch1_dis = binsof(ch1_en.dis);
bins ch2_en = binsof(ch2_en.en);
bins ch2_dis = binsof(ch2_en.dis);
bins ch0_hi = binsof(ch0_vld.hi);
bins ch0_lo = binsof(ch0_vld.lo);
bins ch1_hi = binsof(ch1_vld.hi);
bins ch1_lo = binsof(ch1_vld.lo);
bins ch2_hi = binsof(ch2_vld.hi);
bins ch2_lo = binsof(ch2_vld.lo);
bins ch0_en_vld = binsof(ch0_en.en) && binsof(ch0_vld.hi);
bins ch0_dis_vld = binsof(ch0_en.dis) && binsof(ch0_vld.hi);
bins ch1_en_vld = binsof(ch1_en.en) && binsof(ch1_vld.hi);
bins ch1_dis_vld = binsof(ch1_en.dis) && binsof(ch1_vld.hi);
bins ch2_en_vld = binsof(ch2_en.en) && binsof(ch2_vld.hi);
bins ch2_dis_vld = binsof(ch2_en.dis) && binsof(ch2_vld.hi);
}
endgroup
covergroup cg_arbiter_priority;
ch0_prio: coverpoint arb_vif.mon_ck.slv_prios[0] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
ch1_prio: coverpoint arb_vif.mon_ck.slv_prios[1] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
ch2_prio: coverpoint arb_vif.mon_ck.slv_prios[2] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
endgroup
covergroup cg_formatter_length;
id: coverpoint fmt_vif.mon_ck.fmt_chid {
bins ch0 = {0};
bins ch1 = {1};
bins ch2 = {2};
illegal_bins illegal = default;
}
length: coverpoint fmt_vif.mon_ck.fmt_length {
bins len4 = {4};
bins len8 = {8};
bins len16 = {16};
bins len32 = {32};
illegal_bins illegal = default;
}
endgroup
covergroup cg_formatter_grant();
delay_req_to_grant: coverpoint this.delay_req_to_grant {
bins delay1 = {1};
bins delay2 = {2};
bins delay3_or_more = {[3:10]};
illegal_bins illegal = {0};
}
endgroup
function new(string name="mcdf_coverage");
this.name = name;
this.cg_mcdf_reg_write_read = new();
this.cg_mcdf_reg_illegal_access = new();
this.cg_channel_disable = new();
this.cg_arbiter_priority = new();
this.cg_formatter_length = new();
this.cg_formatter_grant = new();
endfunction
task run();
fork
this.do_reg_sample();
this.do_channel_sample();
this.do_arbiter_sample();
this.do_formater_sample();
join
endtask
task do_reg_sample();
forever begin
@(posedge reg_vif.clk iff reg_vif.rstn);
this.cg_mcdf_reg_write_read.sample();
this.cg_mcdf_reg_illegal_access.sample();
end
endtask
task do_channel_sample();
forever begin
@(posedge mcdf_vif.clk iff mcdf_vif.rstn);
if(chnl_vifs[0].mon_ck.ch_valid===1
|| chnl_vifs[1].mon_ck.ch_valid===1
|| chnl_vifs[2].mon_ck.ch_valid===1)
this.cg_channel_disable.sample();
end
endtask
task do_arbiter_sample();
forever begin
@(posedge arb_vif.clk iff arb_vif.rstn);
if(arb_vif.slv_reqs[0]!==0 || arb_vif.slv_reqs[1]!==0 || arb_vif.slv_reqs[2]!==0)
this.cg_arbiter_priority.sample();
end
endtask
task do_formater_sample();
fork
forever begin
@(posedge fmt_vif.clk iff fmt_vif.rstn);
if(fmt_vif.mon_ck.fmt_req === 1)
this.cg_formatter_length.sample();
end
forever begin
@(posedge fmt_vif.mon_ck.fmt_req);
this.delay_req_to_grant = 0;
forever begin
if(fmt_vif.fmt_grant === 1) begin
this.cg_formatter_grant.sample();
break;
end
else begin
@(posedge fmt_vif.clk);
this.delay_req_to_grant++;
end
end
end
join
endtask
function void do_report();
string s;
s = "\n---------------------------------------------------------------\n";
s = {s, "COVERAGE SUMMARY \n"};
s = {s, $sformatf("total coverage: %.1f \n", $get_coverage())};
s = {s, $sformatf(" cg_mcdf_reg_write_read coverage: %.1f \n", this.cg_mcdf_reg_write_read.get_coverage())};
s = {s, $sformatf(" cg_mcdf_reg_illegal_access coverage: %.1f \n", this.cg_mcdf_reg_illegal_access.get_coverage())};
s = {s, $sformatf(" cg_channel_disable_test coverage: %.1f \n", this.cg_channel_disable.get_coverage())};
s = {s, $sformatf(" cg_arbiter_priority_test coverage: %.1f \n", this.cg_arbiter_priority.get_coverage())};
s = {s, $sformatf(" cg_formatter_length_test coverage: %.1f \n", this.cg_formatter_length.get_coverage())};
s = {s, $sformatf(" cg_formatter_grant_test coverage: %.1f \n", this.cg_formatter_grant.get_coverage())};
s = {s, "---------------------------------------------------------------\n"};
rpt_pkg::rpt_msg($sformatf("[%s]",this.name), s, rpt_pkg::INFO, rpt_pkg::TOP);
endfunction
virtual function void set_interface(virtual chnl_intf ch_vifs[3]
,virtual reg_intf reg_vif
,virtual arb_intf arb_vif
,virtual fmt_intf fmt_vif
,virtual mcdf_intf mcdf_vif
);
this.chnl_vifs = ch_vifs;
this.arb_vif = arb_vif;
this.reg_vif = reg_vif;
this.fmt_vif = fmt_vif;
this.mcdf_vif = mcdf_vif;
if(chnl_vifs[0] == null || chnl_vifs[1] == null || chnl_vifs[2] == null)
$error("chnl interface handle is NULL, please check if target interface has been intantiated");
if(arb_vif == null)
$error("arb interface handle is NULL, please check if target interface has been intantiated");
if(reg_vif == null)
$error("reg interface handle is NULL, please check if target interface has been intantiated");
if(fmt_vif == null)
$error("fmt interface handle is NULL, please check if target interface has been intantiated");
if(mcdf_vif == null)
$error("mcdf interface handle is NULL, please check if target interface has been intantiated");
endfunction
endclass
上述代码定义了6个covergroup(覆盖组)
covergroup cg_mcdf_reg_write_read;
covergroup cg_mcdf_reg_illegal_access;
covergroup cg_channel_disable;
covergroup cg_arbiter_priority;
covergroup cg_formatter_length;
covergroup cg_formatter_grant();
然后例化这6个covergroup
function new(string name="mcdf_coverage");
this.name = name;
this.cg_mcdf_reg_write_read = new();
this.cg_mcdf_reg_illegal_access = new();
this.cg_channel_disable = new();
this.cg_arbiter_priority = new();
this.cg_formatter_length = new();
this.cg_formatter_grant = new();
endfunction
然后通过4个采样任务进行采样,分别采样了6个covergroup。
task run();
fork
this.do_reg_sample();
this.do_channel_sample();
this.do_arbiter_sample();
this.do_formater_sample();
join
endtask
//do_reg_sample()
cg_mcdf_reg_write_read.sample();
cg_mcdf_reg_illegal_access.sample();
//do_channel_sample()
cg_channel_disable.sample();
//do_arbiter_sample()
cg_arbiter_priority.sample();
//do_formater_sample()
cg_formatter_length.sample();
cg_formatter_grant.sample();
在mcdf_env中对mcdf_coverage例化,并且调用cvrg.run()和cvrg.do_report()运行并生成测试报告。
6个covergroup分别对应之前的6个测试。
- cg_mcdf_reg_write_read:寄存器读写测试对所有控制、状态寄存器的读写进行测试。
covergroup cg_mcdf_reg_write_read;
//要覆盖到6个寄存器地址,3个读写寄存器地址和3个只读寄存器地址
addr: coverpoint reg_vif.mon_ck.cmd_addr {
type_option.weight = 0; //权重设置为0,表示不关心此覆盖点的覆盖率,否则cross会出现很多的bin
bins slv0_rw_addr = {`SLV0_RW_ADDR};
bins slv1_rw_addr = {`SLV1_RW_ADDR};
bins slv2_rw_addr = {`SLV2_RW_ADDR};
bins slv0_r_addr = {`SLV0_R_ADDR };
bins slv1_r_addr = {`SLV1_R_ADDR };
bins slv2_r_addr = {`SLV2_R_ADDR };
//8bit地址实际上会生成256个bin,256-6=250会被平均分配到这6个bin中,
//但默认max为64个bin,所以剩下的250要分配到64-6=58个bin里面
}
//覆盖读写指令
cmd: coverpoint reg_vif.mon_ck.cmd {
type_option.weight = 0;
bins write = {`WRITE};
bins read = {`READ};
bins idle = {`IDLE};
//cmd是2bit的,所以还有一个默认的bin
}
//交叉覆盖,哪些寄存器可以读写,哪些寄存器只读,通过不同指令对不同地址的寄存器的操作来进行覆盖
cmdXaddr: cross cmd, addr {
bins slv0_rw_addr = binsof(addr.slv0_rw_addr); //因为权重为0,所以需要再一次的声明,这样可以只关心所需要的bin即可,不会生成没用的bin
bins slv1_rw_addr = binsof(addr.slv1_rw_addr);
bins slv2_rw_addr = binsof(addr.slv2_rw_addr);
bins slv0_r_addr = binsof(addr.slv0_r_addr );
bins slv1_r_addr = binsof(addr.slv1_r_addr );
bins slv2_r_addr = binsof(addr.slv2_r_addr );
bins write = binsof(cmd.write);
bins read = binsof(cmd.read );
bins idle = binsof(cmd.idle );
bins write_slv0_rw_addr = binsof(cmd.write) && binsof(addr.slv0_rw_addr);
bins write_slv1_rw_addr = binsof(cmd.write) && binsof(addr.slv1_rw_addr);
bins write_slv2_rw_addr = binsof(cmd.write) && binsof(addr.slv2_rw_addr);
bins read_slv0_rw_addr = binsof(cmd.read) && binsof(addr.slv0_rw_addr);
bins read_slv1_rw_addr = binsof(cmd.read) && binsof(addr.slv1_rw_addr);
bins read_slv2_rw_addr = binsof(cmd.read) && binsof(addr.slv2_rw_addr);
bins read_slv0_r_addr = binsof(cmd.read) && binsof(addr.slv0_r_addr);
bins read_slv1_r_addr = binsof(cmd.read) && binsof(addr.slv1_r_addr);
bins read_slv2_r_addr = binsof(cmd.read) && binsof(addr.slv2_r_addr);
}
endgroup
- cg_mcdf_reg_illegal_access:寄存器稳定性测试,对非法地址进行读写测试,对控制寄存器的保留域进行读写测试,对状态寄存器进行写操作测试。
covergroup cg_mcdf_reg_illegal_access;
addr: coverpoint reg_vif.mon_ck.cmd_addr {
type_option.weight = 0;
bins legal_rw = {`SLV0_RW_ADDR, `SLV1_RW_ADDR, `SLV2_RW_ADDR}; //合法读写地址范围
bins legal_r = {`SLV0_R_ADDR, `SLV1_R_ADDR, `SLV2_R_ADDR}; //合法读地址范围
bins illegal = {[8'h20:$], 8'hC, 8'h1C}; //非法地址范围
}
//覆盖读写指令
cmd: coverpoint reg_vif.mon_ck.cmd {
type_option.weight = 0;
bins write = {`WRITE};
bins read = {`READ};
}
//写数据覆盖点
wdata: coverpoint reg_vif.mon_ck.cmd_data_m2s {
type_option.weight = 0;
//数据合法范围,0~111111,即bit(0)使能信号、bit(2:1)优先级、bit(5:3)数据包长度都可以写
bins legal = {[0:'h3F]};
//数据非法范围,bit(31:6)保留位,无法写入
bins illegal = {['h40:$]};
}
//读数据覆盖点
rdata: coverpoint reg_vif.mon_ck.cmd_data_s2m {
type_option.weight = 0;
//读数据合法范围,0~11111111,即bit(7:0)上行数据从端FIFO的可写余量
bins legal = {[0:'hFF]};
illegal_bins illegal = default;
}
//交叉覆盖组合
cmdXaddrXdata: cross cmd, addr, wdata, rdata {
bins addr_legal_rw = binsof(addr.legal_rw);
bins addr_legal_r = binsof(addr.legal_r);
bins addr_illegal = binsof(addr.illegal);
bins cmd_write = binsof(cmd.write);
bins cmd_read = binsof(cmd.read);
bins wdata_legal = binsof(wdata.legal);
bins wdata_illegal = binsof(wdata.illegal);
bins rdata_legal = binsof(rdata.legal);
//只关注这几个重点组合
bins write_illegal_addr = binsof(cmd.write) && binsof(addr.illegal); //对非法地址的写操作
bins read_illegal_addr = binsof(cmd.read) && binsof(addr.illegal); //对非法地址的读操作
bins write_illegal_rw_data = binsof(cmd.write) && binsof(addr.legal_rw) && binsof(wdata.illegal); //对读写寄存器的合法地址写入非法数据
bins write_illegal_r_data = binsof(cmd.write) && binsof(addr.legal_r) && binsof(wdata.illegal); //对只读寄存器的合法地址写入非法数据
}
endgroup
- cg_channel_disable:数据通道开关测试,对每一个数据通道对应的控制寄存器域en配置为0,在关闭状态下测试数据写入是否通过。
covergroup cg_channel_disable;
//3个数据通道的en信号覆盖点
ch0_en: coverpoint mcdf_vif.mon_ck.chnl_en[0] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch1_en: coverpoint mcdf_vif.mon_ck.chnl_en[1] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
ch2_en: coverpoint mcdf_vif.mon_ck.chnl_en[2] {
type_option.weight = 0;
wildcard bins en = {1'b1};
wildcard bins dis = {1'b0};
}
//3个数据通道的valid信号的覆盖点
ch0_vld: coverpoint chnl_vifs[0].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
ch1_vld: coverpoint chnl_vifs[1].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
ch2_vld: coverpoint chnl_vifs[2].mon_ck.ch_valid {
type_option.weight = 0;
bins hi = {1'b1};
bins lo = {1'b0};
}
//交叉覆盖组合
chenXchvld: cross ch0_en, ch1_en, ch2_en, ch0_vld, ch1_vld, ch2_vld {
bins ch0_en = binsof(ch0_en.en);
bins ch0_dis = binsof(ch0_en.dis);
bins ch1_en = binsof(ch1_en.en);
bins ch1_dis = binsof(ch1_en.dis);
bins ch2_en = binsof(ch2_en.en);
bins ch2_dis = binsof(ch2_en.dis);
bins ch0_hi = binsof(ch0_vld.hi);
bins ch0_lo = binsof(ch0_vld.lo);
bins ch1_hi = binsof(ch1_vld.hi);
bins ch1_lo = binsof(ch1_vld.lo);
bins ch2_hi = binsof(ch2_vld.hi);
bins ch2_lo = binsof(ch2_vld.lo);
//采集当valid拉高时,en拉高和拉低的情况数据
bins ch0_en_vld = binsof(ch0_en.en) && binsof(ch0_vld.hi);
bins ch0_dis_vld = binsof(ch0_en.dis) && binsof(ch0_vld.hi);
bins ch1_en_vld = binsof(ch1_en.en) && binsof(ch1_vld.hi);
bins ch1_dis_vld = binsof(ch1_en.dis) && binsof(ch1_vld.hi);
bins ch2_en_vld = binsof(ch2_en.en) && binsof(ch2_vld.hi);
bins ch2_dis_vld = binsof(ch2_en.dis) && binsof(ch2_vld.hi);
}
endgroup
- cg_arbiter_priority:优先级测试,将不同数据通道配置为相同或者不同的优先级,在数据通道使能的情况下进行测试。
covergroup cg_arbiter_priority;
//把3个数据通道的优先级分别配置为0,1,2,3
ch0_prio: coverpoint arb_vif.mon_ck.slv_prios[0] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
ch1_prio: coverpoint arb_vif.mon_ck.slv_prios[1] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
ch2_prio: coverpoint arb_vif.mon_ck.slv_prios[2] {
bins ch_prio0 = {0};
bins ch_prio1 = {1};
bins ch_prio2 = {2};
bins ch_prio3 = {3};
}
endgroup
- cg_formatter_length:发包长度测试,将不同数据通道随机配置为各自的长度,在数据通道使能的情况下进行测试。
covergroup cg_formatter_length;
//不同的数据通道是否都能发送数据
id: coverpoint fmt_vif.mon_ck.fmt_chid {
bins ch0 = {0};
bins ch1 = {1};
bins ch2 = {2};
illegal_bins illegal = default;
}
//发送不同的数据长度
length: coverpoint fmt_vif.mon_ck.fmt_length {
bins len4 = {4};
bins len8 = {8};
bins len16 = {16};
bins len32 = {32};
illegal_bins illegal = default;
}
endgroup
- cg_formatter_grant:下行从端低带宽测试,将MCDF下行数据接收端设置为小存储量,低带宽的类型,由此使得在由formatter发送出数据之后,下行从端有更多的机会延迟grant信号的置位,用来模拟真实场景。
covergroup cg_formatter_grant();
delay_req_to_grant: coverpoint this.delay_req_to_grant { //delay_req_to_grant是一个软件信号,即是mcdf_coverage中的一个成员变量,是计算出来的
bins delay1 = {1};
bins delay2 = {2};
bins delay3_or_more = {[3:10]};
//req和grant之间的间隔不允许为0
illegal_bins illegal = {0};
}
endgroup
4个sample采样
- do_reg_sample():对寄存器进行采样
task do_reg_sample();
forever begin
//对于每一个时钟上升沿,就要对寄存器的读写、寄存器的非法操作做采样
@(posedge reg_vif.clk iff reg_vif.rstn);
this.cg_mcdf_reg_write_read.sample();
this.cg_mcdf_reg_illegal_access.sample();
end
endtask
- do_channel_sample():对数据通道做采样
task do_channel_sample();
forever begin
//对于每一个时钟上升沿,至少有一个channel的valid信号为1时,即有数据发送进来时,才对disable做采样
@(posedge mcdf_vif.clk iff mcdf_vif.rstn);
if(chnl_vifs[0].mon_ck.ch_valid===1
|| chnl_vifs[1].mon_ck.ch_valid===1
|| chnl_vifs[2].mon_ck.ch_valid===1)
this.cg_channel_disable.sample();
end
endtask
- do_arbiter_sample():对优先级做采样
task do_arbiter_sample();
//对于每一个时钟的上升沿,至少有一个channel的req为1,即至少有一个channel要发送数据了,才对其优先级做采样
forever begin
@(posedge arb_vif.clk iff arb_vif.rstn);
if(arb_vif.slv_reqs[0]!==0 || arb_vif.slv_reqs[1]!==0 || arb_vif.slv_reqs[2]!==0)
this.cg_arbiter_priority.sample();
end
endtask
- do_formater_sample():对grant信号做采样
task do_formater_sample();
fork
forever begin
//对于每一个时钟上升沿,当req为1时,对数据的长度做采样
@(posedge fmt_vif.clk iff fmt_vif.rstn);
if(fmt_vif.mon_ck.fmt_req === 1)
this.cg_formatter_length.sample();
end
forever begin
//当req拉高时,要通过变量delay_req_to_grant来计算延迟,对grant做采样
@(posedge fmt_vif.mon_ck.fmt_req);
this.delay_req_to_grant = 0; //初始化为0
forever begin
//只有grant拉高时,才对grant做采样,进而通过delay_req_to_grant的值得到延迟
if(fmt_vif.fmt_grant === 1) begin
this.cg_formatter_grant.sample();
break;
end
else begin
@(posedge fmt_vif.clk);
this.delay_req_to_grant++;
end
end
end
join
endtask
对于覆盖率组件mcdf_coverage,首先定义covergroup并指点coverpoint,并例化covergroup,例化之后做采样,接着在run()中调用采样任务。
1. 编译
先编译所有的设计文件,再通过“Compile All”编译全部文件,。
2. 仿真
编译完成在仿真窗口(transcript)中,输入仿真命令
vsim -i -classdebug -solvefaildebug -coverage -coverstore D:/questasim64_10.6c/Project/lab5/lab5 -testname mcdf_full_random_test -sv_seed random +TESTNAME=mcdf_full_random_test -l mcdf_full_random_test.log work.tb1
-
coverage:会在仿真时产生代码覆盖率数据,功能覆盖率数据则默认会生成,与此选项无关。
-
coverstore
COVERAGE_STORAGE_PATH:这个命令是用来在仿真在最后结束时,生成覆盖率数据并且存储到COVERAGE_STORAGE_PATH。 -
testname
TESTNAME:这个选项是你需要添加本次仿真的test名称,可以使用同+TESTNAME选项一样的test名称。这样在仿真结束后,将在COVERAGE_STORAGE_PATH下产生一个覆盖率数据文件“{TESTNAME}_{SV_SEED}.data”。由于仿真时传入的种子是随机值,因此每次提交测试,在测试结束后都将产生一个独一无二的覆盖率数据。
接着输入run -all,等待跑完仿真。执行quit -sim命令之后,可以发现文件下中多了一个data文件。仿真结束以后,可以查看代码覆盖率、功能覆盖率。
3. 合并覆盖率
运行不同的仿真,或者运行同一个test,都会生成独一无二的数据库,可以将生成的.data覆盖率数据文件做合并。
vcover merge -out merged_coverage.ucdb D:/questasim64_10.6c/Project/lab5/lab5
4、分析覆盖率
选择Tools->Coverage Report->HTML生成报告。