前言
monitor是驗證環境自動檢查環節中不可缺少的一個組件,當前DUT功能的對錯不能總是依靠波形確認,而是需要由驗證環境自行檢查,這就需要monitor和checker的加入,這一次我們先嚐試加入monitor。
pkt_mon
在之前寫代碼的目錄中新建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加進去,同時適配下。
修正增改
對之前環境做出了一定修改,具體如下。
pkt_data
修正後代碼如下。
`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<interval; i++)begin
this.data.push_front({'0});
end
endfunction
function void pkt_data::psprintf();
$display("pkt_data.pkt_len=%0d", this.pkt_len);
foreach(this.payload_q[i])begin
$display("pkt_data.payload_q[%0d]=%0h", i, this.payload_q[i]);
end
endfunction
function pkt_data pkt_data::copy(input pkt_data to=null);
pkt_data tmp;
if (to == null)
tmp = new();
else
$cast(tmp, to);
tmp.interval = this.interval;
tmp.pkt_len = this.pkt_len;
tmp.send_over= this.send_over;
foreach(this.payload_q[i])begin
tmp.payload_q.push_back(this.payload_q[i]);
end
return tmp;
endfunction
function void pkt_data::unpack();
this.pkt_len = payload_q.size();
endfunction
`endif
pkt_if
`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
environment
`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
top
`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