提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
实验内容:
本次实验需要升级实验2中的genenrator和initiator之间的数据生成和数据传输的处理。
同时将何时结束测试的主动权讲给generator而非test。
添加monitor和checker组件。
提示:以下是本篇文章正文内容,下面案例可供参考
一、随机约束——chnl_pkg1.sv、tb1.sv
验证文件独立放置,即将chnl_pkg1.sv与tb1.sv文件独立开
1.为chnl_basic_test的数据包添加约束
需要修改数据包的约束,在chnl_trans中实现,要求如下:
在chnl_trans中添加约束
2.在generator中结束仿真
将chnl_root_rest中的$finish移到generator中,在数据全部发送完后结束仿真。
仿真结果不对
- 应该是1200个对象;req600个,rsp600个,每个chnl是200个对象。
- 下图数将种子数改为随机的仿真结果,一个是req的数据包内容,另一个是rsp的。可以从pkt_id看出chnl0完整发送了ntrans = 200个packet,但是其他通道明显没有发完200个。因此obj_id没有达到1200,所以错误原因明显是因为$finish的位置放的不对,它在完成了一个通道的任务后就停止了
vsim -novopt -solvefaildebug -sv_seed random work.tb1
- $finish应该放在generator何处?
仿真结果是正确的,但是仅针对ntrans = 200,不够灵活。
$finish不能直接放入generator中(我是没想出来咋样能够放入generator来控制结束),用来控制仿真结束的应该是ntrans,这是数据包发送的个数,但是三个通道都要例化所以不能在generator中结束,这样只会在完成一个通道的任务后结束,当repeat了200次后结束gen,从而结束了agent[x].run,而要满足三个通道的agent都结束才可以。(ntrans->generator->agent[0,1,2])
二、灵活的测试控制——chnl_pkg2.sv tb2.sv
我们要实现三种测试chnl_basic_test、chnl_burst_test、chnl_fifo_full_test,同时对于不同的测试需要对generator的随机变量做出不同控制,进而控制内部随机的chnl_trans对象。
即对随机化进行分层,在test层随机化generator层,依靠gen中随机化的成员变量,在进一步随机化generator中的chnl_trans对象,由此达到顶层到底层的随机化控制。
综上,我们需要将generator从agent中迁移出来,放置在test层中。
- chnl_generator
class chnl_generator;
rand int pkt_id = -1;
rand int ch_id = -1;
rand int data_nidles = -1;
rand int pkt_nidles = -1;
rand int data_size = -1;
rand int ntrans = 10;
mailbox #(chnl_trans) req_mb;
mailbox #(chnl_trans) rsp_mb;
constraint cstr{
soft ch_id == -1;
soft pkt_id == -1;
soft data_size == -1;
soft data_nidles == -1;
soft pkt_nidles == -1;
soft ntrans == 10;
}
function new();
this.req_mb = new();
this.rsp_mb = new();
endfunction
task run();
repeat(ntrans) send_trans();
endtask
// generate transaction and put into local mailbox
task send_trans();
chnl_trans req, rsp;
req = new();
assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id;
local::pkt_id >= 0 -> pkt_id == local::pkt_id;
local::data_nidles >= 0 -> data_nidles == local::data_nidles;
local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;
local::data_size >0 -> data.size() == local::data_size;
})
else $fatal("[RNDFAIL] channel packet randomization failure!");
this.pkt_id++;
$display(req.sprint());
this.req_mb.put(req);
this.rsp_mb.get(rsp);
$display(rsp.sprint());
assert(rsp.rsp)
else $error("[RSPERR] %0t error response received!", $time);
endtask
function string sprint();
string s;
s = {s, $sformatf("=======================================\n")};
s = {s, $sformatf("chnl_generator object content is as below: \n")};
s = {s, $sformatf("ntrans = %0d: \n", this.ntrans)};
s = {s, $sformatf("ch_id = %0d: \n", this.ch_id)};
s = {s, $sformatf("pkt_id = %0d: \n", this.pkt_id)};
s = {s, $sformatf("data_nidles = %0d: \n", this.data_nidles)};
s = {s, $sformatf("pkt_nidles = %0d: \n", this.pkt_nidles)};
s = {s, $sformatf("data_size = %0d: \n", this.data_size)};
s = {s, $sformatf("=======================================\n")};
return s;
endfunction
function void post_randomize();
string s;
s = {"AFTER RANDOMIZATION \n", this.sprint()};
$display(s);
endfunction
endclass: chnl_generator
1.在root_test中连接gen和agent.init的信箱
连接信箱
2.在root_test中对gen进行随机化配置
- chnl_root_test
- chnl_burst_test
- chnl_fifo_full_test
要求等到每个通道的fifo余量同时全满,停止仿真,但是对发送的数据包有要求,否则无法达到要求(上个实验一样),因此随机的ntrans个数应该大一些,我们将它定在1000-2000之间。
在root_test中定义run_stop_callback()虚方法停止仿真,这样在运行fifo_full_test时就能执行子类的同名方法。
3.测试
tb2.sv对于测试的选择可以通过仿真时的参数传递来完成。
系统函数
v
a
l
u
e
value
valueplusarges
- basic_test
vsim -novopt -solvefaildebug -sv_seed 0 work.tb2
仿真波形没问题,但打印语句有问题,ch_id一直打印的是0.
以下操作将id传递给gen[x]
还是不行,因为gen[x]在new以后调用了randomize(),而gen中定义的软约束使得ch_id=0。
所以应该在调用randomize()时的with约束块中添加约束条件 ch_id=x。后续的测试类也应该加上。
- burst_test
vsim -novopt -solvefaildebug -sv_seed 0 +TESTNAME=chnl_burst_test work.tb2
gen[0].ntrtans=58,gen[1].ntrtans=5a,gen[2].ntrtans=51,合计259,打印结果obj_id=518
ch_id也是对应的。 - fifo_full_test
gen[0].ntrans=69b,gen[1].ntrtans=64e,gen[2].ntrtans=7b2,合计5275,打印结果obj_id=10550
三、添加monitor、checker——chnl_pkg3.sv、tb3.sv
将checker置于顶层环境,思考什么不放在agent中?
1.采样数据并存入信箱
在chnl_monitor和chnl_checker中的任务mon_trans内采样正确的数据包,并且将其写入mailbox。然后将采样的数据打印出来方便调试。
1)输入端数据监测chnl_monitor
- 首先创建结构体类型合并型结构体,存放data和id
- chnl_monitor类
利用时钟块实现在mon_trans内采样
- 在接口内新建一个时钟块mon_ck
- 采样data并写入monitor信箱
打印语句缺少参数$time
2)输出端数据监测mcdt_monitor
- 新建接口,因为采样的信号是mcdt的输出端信号,由实验二可以知道输出端有三个信号mcdt_data_o,mcdt_val_o,mcdt_id_o,新建接口在顶层环境将这三个信号与接口连接,这有区别于tb2.sv直接将它们在端口处声明
- 采样data并写入monitor信箱
不能使用非阻塞赋值
2.在agent中例化chnl_monitor,并运行run
3.比较数据
在chnl_checker的任务do_compare()中取出输入端和输出端monitor中信箱里的数据,然后进行比较。
- 【注意】:输入端有三个agent,即有三个chnl_monitor,那么就有三个缓存的数据,取出哪一个和输出端进行比较?
不能使用foreach来取出输入端的数据,因为do_compare中只有一个结构体im,每次.get(im)都会覆盖上一次的。
输出端有一信号为mcdt_id_o它对应的是输入端口的id,因此输出id与输入id相同的数据进行比较,使用case语句可以实现。
少了endcase
4.在root_test测试环境中连接信箱
- 例化mcdt_monitor,chnl_checker - 连接信箱
mon中的信箱只有一个this.agents[i].mon.mon_mb = this.chker.in_mbs[i]
- 传递虚接口
另外在top环境内传递接口时需要加上mcdt_if
5.测试
- basic_test
vsim -novopt -solvefaildebug -sv_seed 0 work.tb3
报错1:
这里是因为chker的例化应该放在使用这个句柄之前
报错2:
约束冲突,在chnl_trans的约束cstr中data.size没有声明软约束soft
仿真结果与参考代码不一致,对比波形
ref:
ready信号不匹配,chnl_write任务中缺少@(negedge intf.clk);修改后仿真如下:
可以看到ch1和ch2的ready信号有误。
仔细检查代码后,发现data.size设置的是6,应该是发送的数据太少,没办法使fifo到达空状态。
正确的仿真波形:
-
burst_test
vsim -novopt -solvefaildebug -sv_seed 0 +TESTNAME=chnl_burst_test work.tb3
参考代码使用了旗语来控制仿真停止,每个通道的gen.run结束后放入钥匙,在得到三把钥匙后停止仿真。
(不用旗语也可以) -
fifo_full_test
vsim -novopt -solvefaildebug -sv_seed 0 +TESTNAME=chnl_fifo_full_test work.tb3
仿真波形与参考波形不同
原因:
当至少两个slave_fifo全满时(即chx_ready_o=0),强行停止产生数据gen[x].run线程,等待数据在initiator发送完毕(即fifo全空==>margin=20),停止仿真。
而我是直接等待gen_threads自己结束即全部数据产生完,再等待数据发送完即余量为20,,然后停止仿真。
如何实现至少两个slave_fifo容量停下仿真是fifo_full_test测试的难点。
- fifo全满从设计代码中可以看到对应与ready = 0,即要求至少两个chnl的ready = 0
- 方法get_chnl_ready,用来得到该时刻三个chnl的ready值,如何得到三个chnl?返回3bit值,每一位代表一个chnl_ready即可
- 任务gen_stop_callback用来停止generator产生数据
得到了三个ready值后还需要判定至少两个ready都为0,然后停止gen_threads线程。
1.这个条件判定是从仿真开始就一直在进行的,因此放入forever中。
2.需要触发事件gen_stop_e以执行disable来结束线程,而事件通过满足结束条件来触发。
- root_test内开辟fork…join_none线程,调用任务,等待事件执行disable
在root_test内也需要空的virtual gen_stop_callback()任务
总结
1.$finish(),没有找到如何在generator中结束仿真的办法。
2.约束块中的条件语句可以使用{staement1 -> statement2;}
3.%x格式是以16进制打印
4.使用case语句可以取出相同id的数据
5.function可以利用return返回向量
6.class内不能使用initial begin…end
7.initiator中的两个信箱没有例化,req_mb与rsp_mb仅表示句柄,通过在agent中进行句柄赋值,从而指向generator中的信箱
8.chnl_generator.send_trans中的断言处约束块内local不能换成this,local::指向gen实例,this指向req