SV验证-3Scoreboad验证结果
本文采用system verilog语言对一个router.v模块进行验证。参考文档为Synopsys公司2012年出版的《SystemVerilog Testbench Lab Guide》
其顶层文件如下图,本节中出现的组件全在test.sv文件中调用。
1、Scoreboard类
新建一个check类来比较输入输出包,check类的主要功能是采用mailbox在Drivers和Recivers之间通信。该类中新增covergroup方法,使用covergroup功能验证能知道为了测试所有的输入输出端口需要发多少包到router里面,在该Scoreboard中加入功能验证去验证testbanch过程并且能在所有输入输出组合端口都测试完毕后结束仿真。
class Scoreboard;
string name; // unique identifier
event DONE; // flag to indicate goal reached
Packet refPkt[$]; // reference Packet array
Packet pkt2send; // Packet object from Drivers
Packet pkt2cmp; // Packet object from Receivers
pkt_mbox driver_mbox; // mailbox for Packet objects from Drivers
pkt_mbox receiver_mbox; // mailbox for Packet objects from Receivers
bit[3:0] sa, da; // functional coverage properties
covergroup router_cov;
coverpoint sa;
coverpoint da;
cross sa, da;
endgroup
extern function new(string name = "Scoreboard", pkt_mbox driver_mbox = null, receiver_mbox = null);
extern virtual task start();
extern virtual function void check();
endclass: Scoreboard
在start()任务中,当从receiver_mbox中发现一个Packet包时,会将该包赋值给pkt2cmp。然后将driver_mbox中的所有包装到refPkt[$]队列中。随后在check()中将pkt2cmp包与该包中da指定的存在于refPkt中的输出端口参考包进行比较,如果在refPkt中没有发现参考包,则打印错误信息。
function Scoreboard::new(string name, pkt_mbox driver_mbox, receiver_mbox);
if (TRACE_ON) $display("[TRACE]%0t %s:%m", $time, name);
this.name = name;
if (driver_mbox == null) driver_mbox = new();
this.driver_mbox = driver_mbox;
if (receiver_mbox == null) receiver_mbox = new();
this.receiver_mbox = receiver_mbox;
router_cov = new();
endfunction: new
task Scoreboard::start();
if (TRACE_ON) $display("[TRACE]%0t %s:%m", $time, name);
fork
forever begin
receiver_mbox.get(pkt2cmp);
while (driver_mbox.num()) begin
Packet pkt;
driver_mbox.get(pkt);
refPkt.push_back(pkt);
end
check();
end
join_none
endtask: start
在check()任务中,使用real型变量coverage_result来保存验证覆盖率结果,使用compare()方法来比较收发包的数据,使用display()方法打印调试信息。
在start()任务driver_mbox中所有包赋值到refPkt[ ] 队 列 后 , 找 到 r e f P k t [ ]队列后,找到refPkt[ ]队列后,找到refPkt[]中起始数据,并赋值给pkt2send,之后将pkt2cmp与pkt2cmp包进行比较。之后将pkt2cmp包中的变量sa、da赋值给类变量,并触发router_cov.sample()功能验证函数。最后采用$get_coverage () 得到验证覆盖率,当接收包的数量达到全局变量run_for_n_packets的值或验证覆盖率达到100%时,event信号DONE触发,使仿真停止。
function void Scoreboard::check();
int index[$];
string message;
static int pkts_checked = 0;
real coverage_result = 0.0;
if (TRACE_ON) $display("[TRACE]%0t %s:%m", $time, name);
index = refPkt.find_first_index() with (item.da == pkt2cmp.da);
if (index.size() <= 0) begin
$display("\n%m\n[ERROR]%0t %s not found in Reference Queue\n", $time, pkt2cmp.name);
pkt2cmp.display("ERROR");
$finish;
end
pkt2send = refPkt[index[0]];
refPkt.delete(index[0]);
if (!pkt2send.compare(pkt2cmp, message)) begin
$display("\n%m\n[ERROR]%0t Packet #%0d %s\n", $time, pkts_checked, message);
pkt2send.display("ERROR");
pkt2cmp.display("ERROR");
$finish;
end
this.sa = pkt2send.sa;
this.da = pkt2send.da;
router_cov.sample();
coverage_result = $get_coverage();
$display("[NOTE]%0t Packet #%0d %s Cvrg = %3.2f", $time, pkts_checked++, message, coverage_result);
if ((pkts_checked >= run_for_n_packets) || ( coverage_result == 100.0))
->DONE;
endfunction: check
2、代码运行结果
在仿真开始时会先执行reset()任务,可看到reset_n信号复位的15个时钟周期后,frame_n才有拉低的信号,而只有frame_n拉低对应的通道其din才会有随机化数据。
每个通道会发送地址、打拍、数据三种信号,如下图所示,din[0]通道前四个时钟周期对应的信号是4‘b1111,表明其输出地址为15豪通道。然后等待四个时钟节拍后发现dout[15]通道与din[0]通道的数据一致。
没添加功能验证的仿真结果,发送2000次包均验证正确。
添加验证功能的仿真结果,功能验证率达到100%时验证自动结束。