【驗證小白】只有SV+modelsim學驗證(2)——加monitor到環境中

前言

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章