monitor是验证环境自动检查环节中不可缺少的一个组件,当前DUT功能的对错不能总是依靠波形确认,而是需要由验证环境自行检查,这就需要monitor和checker的加入,这一次我们先尝试加入monitor。
在之前写代码的目录中新建pkt_mon.sv,之后我们逐步键入如下代码。monitor稍微复杂一些,我们一段一段的看下。
建立pkt_mon类,类属性包括虚接口mif(pkt_if中定义);信箱mon2chk为之后monitor发送数据到checker预留;rec_status为状态标记,==0表示当前正等待包到来(即等待包头),==1表示正在接收包,等待接收完成(即等待包尾);wait_pkt_time是为了终止监测收包设置的辅助值,一旦在wait_pkt_time个周期内没有收到任何有效数据,就跳出run()函数;之后新建三个task/funciton,关于new就不多说了。
`ifndef PKT_MON_SV
`define PKT_MON_SV
`include "pkt_data.sv"
`include "pkt_if.sv"
class pkt_mon;
vmon mif;
mailbox mon2chk;
bit rec_status;//0:wait sop, 1:wait eop
int wait_pkt_time = 1000;
extern function new(input vmon mif,
input mailbox mon2chk,
input int wait_pkt_time);
extern virtual task run();
extern virtual task send_chk(pkt_data pkt);
endclass
function pkt_mon::new(input vmon mif,
input mailbox mon2chk,
input int wait_pkt_time);
this.mif = mif;
this.mon2chk = mon2chk;
this.wait_pkt_time = wait_pkt_time;
this.rec_status = 0;
endfunction : new
run()为主函数,其中包含两个while(1)循环,第一级的循环会把所有的pkt收全后才跳出,第二级的循环收齐一个完整报文并且发送到至checker的通道上后就会跳出循环。
在第二级循环中,一旦mif.vld==1就会开始工作,rec_status进行跳转,每一拍的mif.data都会进入payload_q[$]队列中,直到检测到eop置起,表示当前pkt结束了,那么将刚刚收集的pkt发送给checker,并将rec_status归零等待下一个pkt到来。
每次进入二级循环并且mif.vld==1时,均会把wait_time归零,否则会+1;这意味着长时间vld==1未能出现时,wait_time会>预定的阈值wait_pkt_time,两次break跳出run();
同时代码中加入了异常检查,即接收pkt时sop不能置起。
task pkt_mon::run();
pkt_data rec_pkt;
int wait_time;
int i = 0;
$display("mon run start!");
while(1) begin//get all pkt
while(1) begin//get a pkt
@(posedge top.clk);
if(mif.vld == 1)begin
wait_time = 0;
if(rec_status == 0) begin//wait sop
rec_pkt = new();
if(mif.sop == 0) begin
$display("ERROR! The first pkt cycle is not sop!");
break;
end
else begin
rec_pkt.payload_q.push_back(mif.data);
if(mif.eop == 1) begin
rec_status = 0;
$display("get no.%0d pkt!", i++);
send_chk(rec_pkt);
end
else
rec_status = 1;
end
end
else if(rec_status == 1) begin//wait eop
if(mif.sop == 1) begin
$display("ERROR! SOP??");
break;
end
else begin
rec_pkt.payload_q.push_back(mif.data);
if(mif.eop == 1) begin
rec_status = 0;
$display("get no.%0d pkt!", i++);
send_chk(rec_pkt);
end
else
rec_status = 1;
end
end
end
else begin
wait_time++;
if(wait_time <= wait_pkt_time) begin
wait_time++;
//$display("%0d", wait_time);
end
else break;
end
end
$display("mon run over!");
break;
end
endtask : run
一旦一个pkt数据收集好了,那么我们调用task send_chk(pkt)将其发出去。在发送前先调用下unpack()函数解析出pkt的各种属性如pkt_len,err等,不过对于我们这个简单的数据类型,只得到pkt_len就够了,具体见“修正增改”一节pkt_data。调用unpack()后,发送pkt到信箱中,由checker进行进一步处理。信箱mon2chk是一个默认的无限容量的信箱,之所以用无限容量的信箱是因为,不管chk取不取出数据,monitor只要采到pkt了就要仍进去,否则可能会漏采。
task pkt_mon::send_chk(pkt_data pkt);
pkt.unpack();
pkt.psprintf();
mon2chk.put(pkt);
endtask : send_chk
`endif
OK,monitor的代码全部完成,之后要修改环境把mon加进去,同时适配下。
对之前环境做出了一定修改,具体如下。
修正后代码如下。
`ifndef PKT_DATA_SV
`define PKT_DATA_SV
class pkt_data;
rand bit [7:0] payload_q[$];
rand int interval;
rand int pkt_len;
bit send_over;
bit [10:0] data[$];
constraint data_size_cons{
payload_q.size() == pkt_len;
};
constraint pkt_len_cons{
pkt_len inside {[1:20]};
//pkt_len == 5;
};
constraint interval_cons{
interval inside {[3:6]};
};
extern function new();
extern virtual function void psprintf();
extern virtual function void pack();
extern virtual function void unpack();
extern virtual function pkt_data copy(input pkt_data to=null);
endclass
function pkt_data::new();
endfunction
function void pkt_data::pack();
foreach (this.payload_q[i]) begin
if (i==0)
this.data.push_back({1'b1, 1'b1, 1'b0, payload_q[i]});
if (i==pkt_len-1)
this.data.push_back({1'b1, 1'b0, 1'b1, payload_q[i]});
else
this.data.push_back({1'b1, 1'b0, 1'b0, payload_q[i]});
end
for(int i=0; i
`ifndef PKT_IF_SV
`define PKT_IF_SV
interface pkt_if(input clk, rst_n);
logic [7:0] data;
logic sop;
logic eop;
logic vld;
clocking drv @(posedge clk);
output data;
output sop, eop, vld;
endclocking : drv
modport pkt_drv (clocking drv);
clocking mon @(posedge clk);
input data;
input sop, eop, vld;
endclocking : mon
modport pkt_mon (clocking mon);
endinterface
typedef virtual pkt_if.drv vdrv;
typedef virtual pkt_if.mon vmon;
`endif
`ifndef ENV_SV
`define ENV_SV
`include "pkt_gen.sv"
`include "pkt_drv.sv"
`include "pkt_mon.sv"
`include "pkt_if.sv"
class environment;
pkt_gen gen;
pkt_drv drv;
pkt_mon mon;
mailbox gen2drv;
mailbox mon2chk;
vdrv dif;
vmon mif;
int send_pkt_num;
extern function new(input vdrv dif,
input vmon mif
);
extern virtual task build();
extern virtual task run();
extern virtual task report();
endclass
function environment::new(input vdrv dif,
input vmon mif
);
this.dif = dif;
this.mif = mif;
endfunction
task environment::build();
$display("environment::build() start!");
gen2drv = new(1);
mon2chk = new();
gen = new(gen2drv);
drv = new(dif, gen2drv);
mon = new(mif, mon2chk, 100);
$display("environment::build() over!");
endtask
task environment::run();
fork
drv.run();
gen.run();
mon.run();
join
$display("send pkt over");
endtask
task environment::report();
repeat(100) @top.clk;
endtask
`endif
`include "pkt_if.sv"
`include "environment.sv"
program automatic test(
pkt_if.pkt_drv dif,
pkt_if.pkt_mon mif,
input clk, rst_n
);
environment env;
initial begin
env = new(dif, mif);
env.build();
env.gen.send_num = 5;
env.run();
$display("env run over at %d!", $time);
env.report();
$finish;
end
endprogram
将pkt_mon.sv加入工程,编译运行,得到log信息如下,可知pkt_mon功能基本正确。
run 10000ns
# environment::build() start!
# environment::build() over!
# pkt_drv run()!
# send_num = 5
# gen send a pkt to drv
# mon run start!
# 225, let's go
# after rst_n at 235
# drv get no.0 pkt from gen
# drv pkt_send begin!
# gen send a pkt to drv
# get no.0 pkt!
# pkt_data.pkt_len=15
# pkt_data.payload_q[0]=d0
# pkt_data.payload_q[1]=d0
# pkt_data.payload_q[2]=7f
# pkt_data.payload_q[3]=f7
# pkt_data.payload_q[4]=bc
# pkt_data.payload_q[5]=f
# pkt_data.payload_q[6]=ef
# pkt_data.payload_q[7]=c7
# pkt_data.payload_q[8]=1b
# pkt_data.payload_q[9]=19
# pkt_data.payload_q[10]=23
# pkt_data.payload_q[11]=22
# pkt_data.payload_q[12]=c
# pkt_data.payload_q[13]=4
# pkt_data.payload_q[14]=9e
# drv get no.1 pkt from gen
# drv pkt_send begin!
# gen send a pkt to drv
# get no.1 pkt!
# pkt_data.pkt_len=9
# pkt_data.payload_q[0]=4e
# pkt_data.payload_q[1]=4e
# pkt_data.payload_q[2]=5c
# pkt_data.payload_q[3]=f4
# pkt_data.payload_q[4]=d5
# pkt_data.payload_q[5]=70
# pkt_data.payload_q[6]=2
# pkt_data.payload_q[7]=30
# pkt_data.payload_q[8]=b6
# drv get no.2 pkt from gen
# drv pkt_send begin!
# gen send a pkt to drv
# get no.2 pkt!
# pkt_data.pkt_len=2
# pkt_data.payload_q[0]=a9
# pkt_data.payload_q[1]=a9
# drv get no.3 pkt from gen
# drv pkt_send begin!
# gen send a pkt to drv
# get no.3 pkt!
# pkt_data.pkt_len=19
# pkt_data.payload_q[0]=7f
# pkt_data.payload_q[1]=7f
# pkt_data.payload_q[2]=b8
# pkt_data.payload_q[3]=ae
# pkt_data.payload_q[4]=5e
# pkt_data.payload_q[5]=46
# pkt_data.payload_q[6]=3a
# pkt_data.payload_q[7]=cf
# pkt_data.payload_q[8]=7c
# pkt_data.payload_q[9]=1
# pkt_data.payload_q[10]=35
# pkt_data.payload_q[11]=8b
# pkt_data.payload_q[12]=33
# pkt_data.payload_q[13]=3a
# pkt_data.payload_q[14]=a8
# pkt_data.payload_q[15]=9a
# pkt_data.payload_q[16]=79
# pkt_data.payload_q[17]=3f
# pkt_data.payload_q[18]=12
# drv get no.4 pkt from gen
# drv pkt_send begin!
# gen over pkt
# get no.4 pkt!
# pkt_data.pkt_len=14
# pkt_data.payload_q[0]=96
# pkt_data.payload_q[1]=96
# pkt_data.payload_q[2]=af
# pkt_data.payload_q[3]=2f
# pkt_data.payload_q[4]=aa
# pkt_data.payload_q[5]=5e
# pkt_data.payload_q[6]=6e
# pkt_data.payload_q[7]=5b
# pkt_data.payload_q[8]=e
# pkt_data.payload_q[9]=c2
# pkt_data.payload_q[10]=f8
# pkt_data.payload_q[11]=ee
# pkt_data.payload_q[12]=2d
# pkt_data.payload_q[13]=37
# get over pkt
# mon run over!
# send pkt over
# env run over at 1575!
# ** Note: $finish : C:/Users/gaoji/Desktop/sv test/test.sv(18)
# Time: 2075 ns Iteration: 1 Instance: /top/u_test