【验证小白】只有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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尼德兰的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值