前言:在很多博客主文章中看到关于scoreboard的编写思路,他们从master端发送命令(比如读、写命令),同时将接口中的数据通过monitor发送给scoreboard,slave边得到命令后发送或者接受数据(比如读数据、写数据),然后他们认为在scoreboard中,在master方向使用队列去接受数据,因为他们觉得dut需要消耗时间,而refmodel不会(除非人为的延时),所以slave端是延迟一定时间才能接受到数据,他们编写代码的思路如下:
master端使用队列接受先到达的数据,然后slave端接受一个数据后与队列进行比较:
结果的打印信息是:
很明显slave端的数据包先到达scoreboard中,然后发现master端队列中没有数据
if(exp_q.size()>0)begin
...
end
else begin
`uvm_info("NO EXPECT PKT")
end
能够想到如果master端是一个主动发起读请求,那么slave端应该是先有数据,然后经过dut传给master端,那么slave方向是先有数据包传给scoreboard的。
基于这个问题,我们应该考虑到既然找不到两端的谁先谁后问题,那么我们有两种解决思路:
1 从master端的tlm_fifo和slave端的tlm_fifo分别get到数据,直接进行比较。(这种方法的劣势就是阻塞问题,如果是一边发送比较快,一边发送比较慢,那么会在一边一直阻塞,非要等到慢的一边来了数据包才进行比较,浪费仿真资源。)
2 master和slave端在scoreboard中都设置一个队列进行数据包接收
virtual task run_phase(uvm_phase phase);
apb_out_item exp,act,exp_tmp,act_tmp;
bit result;
fork
forever begin
exp_port.get(exp);
`uvm_info(get_name(),$sformatf("expect pkt:wr:%0h,addr:%0h,data:%0h",exp.pwrite,exp.paddr,exp.pdata),UVM_LOW)
exp_q.push_back(exp);
if((exp_q.size()>0) && (act_q.size()>0))begin
exp_tmp = exp_q.pop_front;
act_tmp = act_q.pop_front;
result = exp_tmp.compare(act_tmp);
if(result)begin
`uvm_info(get_name(),"COMPARE SUCCESSFUL",UVM_LOW)
end
else begin
`uvm_info(get_name(),"COMPARE FAILURE",UVM_LOW)
$display("this actual pkt:");
act_tmp.print();
$display("this expect pkt:");
exp_tmp.print();
end
end
end
forever begin
act_port.get(act);
`uvm_info(get_name(),$sformatf("actual pkt:wr:%0h,addr:%0h,data:%0h",act.pwrite,act.paddr,act.pdata),UVM_LOW)
act_q.push_back(act);
end
join
endtask
最后打印消息为:
看起来是成功了!
总结:新手在书写scoreboard的时候,一定要注意发包快慢问题。而且出了问题,也很难定位到问题所在,所以我有很多打印信息,结果一目了然,而且scb是一个forever模块,如果没有打印消息,很容易卡死在里面。