文章目录
1.覆盖率收集操作(Questa)
http://t.csdn.cn/cxRIM
https://github.com/pzressinno/My
原代码基于vcs+verdi,用于questa时需要部分修改
不要添加tb.v
留下fifo_if.sv
tb.sv原代码存在报错
# UVM_WARNING @ 0: reporter [BDTYP] Cannot create a component of type 'read_fast_test' because it is not registered with the factory.
# UVM_FATAL @ 0: reporter [INVTST] Requested test from command line +UVM_TESTNAME=read_fast_test not found.
在tb中添加
import asyn_fifo_test_pkg::*;
1.1编译
1.new-project
1.逐一添加rtl、sv文件,compile后将除了pkg以外的remove
2.选中所有的.v文件,编译设置

点开【Coverage】即覆盖率一栏,点击选中需要的项目,然后点击【OK】即可

3.调整编译顺序,让设计代码先编译,让底层代码先编译,在空白处点击右键,选择【Compile】->【Compile Order…】
点击【Auto Generate】,让其自动识别
4.最后对其全部编译
1.2仿真
1.使用命令编译
vsim -i -novopt -classdebug -solvefaildebug -coverage -coverstore ./mti_covdb -testname as_fifo_random -sv_seed random
+UVM_TESTNAME=read_fast_test work.asyn_fifo_tb
-novopt:编译时不作优化
-solvefaildebug=,使能随机化失败调试,如果value不给值,则使能基本调试功能
-coverage: 会在仿真时产生代码覆盖率数据
-coverstore ./mti_covdb: 在当前的mti_covdb文件夹下生成一个覆盖率数据文件(.data)
-testname as_fifo_random: 本次仿真的名称,后续生成的覆盖率数据文件名为{testname}_{Sv_Seed}
-sv_seed random,用于指定仿真时的随机种子
+UVM_TESTNAME=read_fast_test:TESTNAME参数,我们会在tb.sv文件中用到,作为传入参数选择不同的测试用例
编译完成后如下所示,并获得随机化的种子 2195086157,这样我们得到的覆盖率数据文件为as_fifo_random_2195086157.data

2.使用run -all命令让其跑完整个测试
然后会弹出询问是否结束测试,我们选择【否】,因为选择是会退出软件
3.使用break
4.再使用quit -sim命令结束仿真
结束仿真才能得到覆盖率数据文件
1.3查看覆盖率
1.已经关闭了仿真,所以没有当前的覆盖率了,我们再次运行仿真,仿真完成后我们不关闭
即编译-run -all-break(不quit)

2.把覆盖率文件.data转换为.ucdb,命令行执行
coverage save mti_covdb/as_fifo_random_2195086157.ucdb
coverage save 文件夹/覆盖率文件名.ucdb
3.直接用仿真器即可打开这个UCDB覆盖率数据库,点击【File】->【Open…】
1.4合并覆盖率
1.多跑几次仿真(不同种子)或者使用不同的测试用例跑仿真,每一次仿真都会得到一个覆盖率文件,最后可以将这些覆盖率都合并
命令行执行
vcover merge -out ucdb1/merged1.ucdb mti_covdb/*.ucdb
输出的merge文件所在文件夹/merge文件名.ucdb 需要被merge的所有文件所在文件夹/*.ucdb
2.可以查看merge中单个覆盖率文件的,点击【View】->【Verification Management】->【Browser】打开【Browser】窗口
然后再其窗口中右击选择【Add File…】,将【merged】添加进来可以看到其下的每个覆盖率文件
1.5生成html文件
1.点击【Tools】->【Coverage Report】->【HTML…】
2.勾选需要的选项

然后就会在浏览器打开如下所示的HTML文件了
3.也可以使用命令行指令
vcover report -html -htmldir covhtmlreport -source -assert -directive -cvg -code bcefst -threshL 50 -threshH 90 merged1.ucdb
vcover report -html -htmldir covhtmlreport -source -assert -directive -cvg -code bcefst -threshL 50 -threshH 90 merge文件名.ucdb
1.6分析覆盖率文件
参考http://t.csdn.cn/r8Ehr
设置忽略的选项
e.g若ctrl_regs_inst中的cmd_data_reg信号的Toggle覆盖率低
重新回到QuestaSim软件,打开【Analysis】窗口,选中【ctrl_regs_inst】打开
然后将关注点转为【Toggle】
可以看到,【cmd_data_reg】的5-31位都是没有覆盖到的,而且实际的设计上【cmd_data_reg】的6-31位就是没有使用的,我们可在覆盖率分析时将其忽略,如下所示,选中6-31位,右键选中【Exclude with Comment…】将其忽略掉
然后输入提交信息【reasonable for register value boundary limit】表示该忽略是合理的
接下来我们再次生成一次网页,可以看到覆盖率是上升了的
2.如何实现功能覆盖率
2.1时钟测试
在tb中初始化时钟
// Configurable wclk
initial begin
wclk = 0;
#10
//if(uvm_config_db#(real)::get(uvm_root::get(),"uvm_test_top","wclk_half_period",wclk_half_period)) //In test config
if(uvm_config_db#(int)::get(uvm_root::get(),"uvm_test_top.m_env.m_wf_agent.m_sequencer","wclk_half_period",wclk_half_period))
`uvm_info("WCLK",$sformatf("Configure the wclk_half_period = [%0d]",wclk_half_period),UVM_NONE)
else begin
`uvm_info("WCLK","Can't configure wclk with config_db correctly,will use default value:10",UVM_MEDIUM)
wclk_half_period = 10;
end
forever begin
#wclk_half_period wclk = ~wclk;
end
end
// Configurable rclk
initial begin
rclk = 0;
#10
if(uvm_config_db#(int)::get(uvm_root::get(),"uvm_test_top.m_env.m_rf_agent.m_sequencer","rclk_half_period",rclk_half_period))
`uvm_info("RCLK",$sformatf("Configure the rclk_half_period = [%0d]",rclk_half_period),UVM_NONE)
else begin
`uvm_info("RCLK","Can't configure rclk with config_db correctly,will use default value:10",UVM_MEDIUM)
rclk_half_period = 10;
end
forever begin
#rclk_half_period rclk = ~rclk;
end
end
如果在test config中对时钟周期wclk_half_period设置,则get该设置,没有设置则读写时钟都为#10翻转一次
2.1.1写快读慢
class write_fast_test extends asyn_fifo_test_base;
task write_fast_test::run_phase(uvm_phase phase);
write_fast_vseq vseq = write_fast_vseq::type_id::create("vseq");
vseq.start(null);
class write_fast_vseq extends asyn_fifo_vseq_base;
int wclk_half_period = 10;
int factor = 7;
task write_fast_vseq::body();
write_seq wr_seq;
read_seq rd_seq;
wrstn_config_seq wrstn_cfg_seq;
rrstn_config_seq rrstn_cfg_seq;
wclk_config_seq wclk_cfg_seq;
rclk_config_seq rclk_cfg_seq;
fork
`uvm_do_on(wrstn_cfg_seq,wf_sqr) //产生1个写复位信号传给写sequencer
`uvm_do_on(rrstn_cfg_seq,rf_sqr)
//产生一个随机的写时钟周期,并约束wclk_half_period =10,rclk_half_period =7*10=70,即写快读慢
`uvm_do_on_with(wclk_cfg_seq,wf_sqr,{wclk_half_period == local::wclk_half_period;})
`uvm_do_on_with(rclk_cfg_seq,rf_sqr,{rclk_half_period == local::wclk_half_period*factor; })
join_none
//重复5次产生写seq,50秒后重复5次读操作
repeat(5)
`uvm_do_on(wr_seq,wf_sqr)
#50
repeat(5)
`uvm_do_on(rd_seq,rf_sqr)
#100
fork
repeat(100)
`uvm_do_on_with(wr_seq,wf_sqr,{pkt_idles == 0;})
repeat(100)
`uvm_do_on(rd_seq,rf_sqr)
join
#500;
endtask : body
在wrstn_config_seq 实现复位
task wrstn_config_seq::body();
super.body();
if(wrst_en)
begin
@(negedge cfg.m_wf_cfg.FIFO.wclk);
cfg.m_wf_cfg.FIFO.wrst_n = 0;
repeat(resetn)
@(negedge cfg.m_wf_cfg.FIFO.wclk);
cfg.m_wf_cfg.FIFO.wrst_n = 1;
end
else begin
cfg.m_wf_cfg.FIFO.wrst_n = 0;
repeat(5)
@(posedge cfg.m_wf_cfg.FIFO.wclk);
cfg.m_wf_cfg.FIFO.wrst_n = 1;
end
endtask : body
在wclk_config_seq 随机时钟周期并传给tb,rclk_config_seq 相同
class wclk_config_seq extends uvm_sequence;
rand int wclk_half_period;
task wclk_config_seq::body();
uvm_config_db#(int)::set(m_sequencer,"","wclk_half_period",wclk_half_period);
`uvm_info("WCLK",$sformatf("Configure the wclk half period = [%0d]",wclk_half_period),UVM_MEDIUM)
endtask : body
在write_seq
class write_seq extends uvm_sequence #(fifo_seq_item);
task write_seq::body();
fifo_seq_item req;
`uvm_do_with(req,{cmd == 1;pkt_idles == local::pkt_idles;}) //cmd在driver中决定write '1' or read '0'
2.1.2读快写慢
2.2异常测试
2.2.1写满
深度为16,连续写20个数据进去,从第16个数之后wfull拉高
2.2.2读空
连续读20个数据,从第16个数之后rempty拉高
2.3 读写带复位
class wr_with_reset_test extends asyn_fifo_test_base;
task wr_with_reset_test::run_phase(uvm_phase phase);
wr_with_reset_vseq vseq = wr_with_reset_vseq::type_id::create("vseq");
vseq.start(null);
endtask : run_phase
class wr_with_reset_vseq extends asyn_fifo_vseq_base;
task wr_with_reset_vseq::body();
write_seq wr_seq;
read_seq rd_seq;
wrstn_config_seq wrstn_cfg_seq;
rrstn_config_seq rrstn_cfg_seq;
wrstn_seq wrstn_seq;
rrstn_seq rrstn_seq;
fork
`uvm_do_on(wrstn_cfg_seq,wf_sqr)
`uvm_do_on(rrstn_cfg_seq,rf_sqr)
join_none
// Continuously write 20 datas
repeat(20)
`uvm_do_on(wr_seq,wf_sqr)
env_cfg.wait_for_wclk(5);
// Continuously read 20 datas
repeat(20)
`uvm_do_on(rd_seq,rf_sqr)
env_cfg.wait_for_rclk(5);
fork
repeat(50)
`uvm_do_on(wr_seq,wf_sqr)
repeat(50)
`uvm_do_on(rd_seq,rf_sqr)
begin #200 `uvm_do_on_with(wrstn_cfg_seq,wf_sqr,{resetn == 5;wrst_en == 1;}) end
begin #200 `uvm_do_on_with(rrstn_cfg_seq,rf_sqr,{resetn == 5;rrst_en == 1;}) end
join
在wrstn_config_seq 实现复位
重复20次产生写seq,后重复20次读操作
在重复读写50次操作开始后间隔#200在在wrstn_config_seq 实现写复位和读复位