【验证小白】只有SV+modelsim学验证(3)——加checker到环境中

前言

之前把环境搭起来,写好了gen、drv、mon后就感觉自己理解的还是有点问题,歇了好长一段时间。最近听大神讲了好多细节,感觉理解加深了很多,于是乎继续开动把checker加入环境顺便构建一个合理的结束仿真机制(之前的结束仿真太简单粗暴了)。

相关博文:

【验证小白】只有SV+modelsim学验证(1)——把平台搭起来_尼德兰的喵的博客-CSDN博客_modelsim sv

【验证小白】只有SV+modelsim学验证(2)——加monitor到环境中_尼德兰的喵的博客-CSDN博客_sv中的monitor

修理下pkt_data

既然要加入checker,那么必然涉及到数据对比以及对比失败后数据打印,因此上来就要把pkt_data修理一下,主要修改如下(最后会附完整代码)。

compare函数主要对比了两个属性(实际上我们的数据也没有更多的属性了。。),数据长度和每一拍数据值:

function bit pkt_data::compare(pkt_data to);
	bit match = 1;
	if(this.pkt_len != to.pkt_len) match = 0;
	foreach(payload_q[i]) if(this.payload_q[i] != to.payload_q[i])
		match = 0;
	return match;
endfunction:compare

psprintf函数把所有需要打印的信息加入到string中,方便之后调用:

function string pkt_data::psprintf(string preset = "");
	psprintf = {preset, $psprintf("pkt_len = %0d", pkt_len)};
	foreach(payload_q[i])
		psprintf = {psprintf, ",", $psprintf("payload[%0d] = 'h%0h", i, payload_q[i])};
endfunction

?,pkt_data修理完成。

pkt_chk

接下来就可以添加愉快的添加checker了,在这里我就把checker和scoreboard两个组建合二为一,毕竟结构这么简单没必要搞两个了。关于checker和scoreboard,有兴趣可以看一下SV绿皮书320页的一些代码,其中使用了回调方法(关于回调方法请参考237页),我就不用了就搞一个最最最简单的好了。

checker顾名思义是要将两个来源的数据进行对比,如果如果对比不通过就要报error提醒环境或者DUT做错了。因此定于checker类如下。

`ifndef PKT_CHK_SV
`define PKT_CHK_SV

`include "pkt_data.sv"
`include "env_cfg.sv"

class pkt_chk;
	env_cfg cfg;
	pkt_data expect_q[$];
	int in_expect, in_actual;
	int match, not_match;
	mailbox gen2chk_chan;
	mailbox mon2chk_chan;
	
	//for finish simu
	int idle_cnt;
	
	extern function new(env_cfg cfg, 
						mailbox gen2chk_chan, 
						mailbox mon2chk_chan);
	extern virtual task run();
	extern virtual task expect_gain();
	extern virtual task actual_gain();
	extern virtual task set_idle();
	extern virtual function report();

endclass:pkt_chk

两个信箱分别用来接收环境预期和DUT输出,当然了这个乞丐版环境没有DUT,于是乎gen2chk_chan接收由gen发来的信元作为预期数据,mon2chk_chan接收由mon采集来的信元作为实际数据(具体见1中的结构),如果可以对比通过则基本保证组建是没问题的(后面一仿真直接报error了。。真打脸,发现pkt_data中的pack函数写错了。。。)。四个int类型数据分别用来记录预期数据总量,实际数据总量,对比通过个数和对比不过个数。

这个checker实现简单的顺序对比功能。

这里还加了一个env_cfg类,主要是为了结束仿真用的,回头再说。

函数个功能介绍如下。

run()

task pkt_chk::run();
	fork
		expect_gain();
		actual_gain();
		set_idle();
	join_none
endtask:run

run()很简单,就是把所有的方法调用起来。

expect_gain()

task pkt_chk::expect_gain();
	pkt_data expect_data;
	while(1) begin
		gen2chk_chan.get(expect_data);
		this.expect_q.push_back(expect_data);
		in_expect++;
		idle_cnt = 0;
		//$display("At %0t, [CHK NOTE]: get a expect pkt", $time);
	end
endtask:expect_gain

expect_gain()方法用来从gen2chk_chan中获取数据,一旦chan为空则会阻塞在get语句。获取数据后放入队列expect_q中等待对比。

actual_gain()

task pkt_chk::actual_gain();
	pkt_data expect_data;
	pkt_data actual_data;
	while(1) begin
		mon2chk_chan.get(actual_data);
		in_actual++;
		idle_cnt = 0;
		if(this.expect_q.size == 0) $display("At %0t, [CHK ERROR]: expect_q==0???", $time);
		else begin
			expect_data = expect_q[0];
			if(!expect_data.compare(actual_data)) begin
				$display("At %0t, [CHK ERROR]: no match, \nexpect data:%s \nactual data:%s ", $time, expect_data.psprintf(), actual_data.psprintf());
				expect_q.pop_front();
				not_match++;
			end
			else begin
				expect_q.pop_front();
				//$display("At %0t, [CHK NOTE]: match, \nexpect data:%s \nactual data:%s ", $time, expect_data.psprintf(), actual_data.psprintf());
				match++;
			end
		end
	end
endtask:actual_gain

actual_gain()方法把获取mon2chk_chan数据和数据对比合在了一起。思路就是从chan获取mon实际采集的数据后,与expect_q[$]中最顶头的数据进行对比,如果对比不通过则报[CHK ERROR]。而无论对比是否通过,均将expect_q[$]顶头数据取走。

在对比的同时,进行相关的计数。核心task!!!!!!

report()

function pkt_chk::report();
	$display("----------------------------------------------------------------------------------------------------");
	$display("[CHECKER REPORT]expect pkt_num=%0d, actual pkt_num=%0d, match pkt_num=%0d, not match pkt_num=%0d", in_expect, in_actual, match, not_match);
	$display("----------------------------------------------------------------------------------------------------");
endfunction:report

仿真结束后的打印,报告匹配数据的个数等信息。

set_idle()

与结束仿真相关,下次再说吧。

修理ENV

?,checker添加完成,我们需要将其添加到环境当中,作为组件运转起来。env的修改还包括了添加env_cfg以及调整反正结束机制,在下个博客中说明。

env中新添加了gen2chk_chan,连接gen和chk。也就是说gen在给drv发送数据时候也会把同样一份数据复制一下发给chk,作为chk中数据比对的依据,具体代码也见下一篇博客吧。

`ifndef ENV_SV
`define ENV_SV

`include "pkt_gen.sv"
`include "pkt_drv.sv"
`include "pkt_mon.sv"
`include "pkt_chk.sv"
`include "pkt_if.sv"
`include "env_cfg.sv"

class environment;
	pkt_gen gen;
	pkt_drv drv;
	pkt_mon mon;
	pkt_chk chk;
	env_cfg cfg;
	mailbox gen2drv_chan;
	mailbox gen2chk_chan;
	mailbox mon2chk_chan;
	vdrv dif;	
	vmon mif;
	int send_pkt_num;
	
	//for finish fimu
	int wait_time;

	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;
	this.cfg = new();
endfunction

task environment::build();
	$display("At %0t, [ENV NOTE]: environment::build() start!", $time);
	gen2drv_chan = new();
	gen2chk_chan = new();
	mon2chk_chan = new();
	gen = new(cfg, gen2drv_chan, gen2chk_chan);
	drv = new(cfg, dif, gen2drv_chan);
	mon = new(cfg, mif, mon2chk_chan);
	chk = new(cfg, gen2chk_chan, mon2chk_chan);
	$display("At %0t, [ENV NOTE]: environment::build() over!", $time);
endtask
	
task environment::run();
	
	fork
		drv.run();
		gen.run();
		mon.run();
		chk.run();
	join_none
	
	#100;
	fork
		$display("At %0t, [ENV NOTE]: wait for end............", $time);
		begin
			wait(cfg.gen_idle);
			wait(cfg.drv_idle);
			wait(cfg.mon_idle);
			wait(cfg.chk_idle);
			$display("At %0t, [ENV NOTE]: normal finish", $time);
		end
		begin
			while(1)begin
				@(negedge top.clk);
				if(this.dif.vld) wait_time = 0;
				else wait_time++;
				if(wait_time > this.cfg.env_wait_pkt_time) break;
			end
			$display("At %0t, [ENV ERROR]: time out!!!!!", $time);
		end		
	join_any
	
	#1000;	
	report();
endtask

task environment::report();	
	$display("At %0t, [ENV NOTE]: report start", $time);
	repeat(100) @top.clk;
	chk.report();
	$display("At %0t, [ENV NOTE]: report over", $time);
endtask
`endif

其他

附上pkt_data的完整代码,pack函数也有修改(之前做错了。。),其他组件的完整代码下一篇附上。

`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:50]};
		//pkt_len == 5;
	};
	
	constraint interval_cons{
		interval inside {[3:6]};
	};
	
	extern function new();
	extern virtual function string psprintf(string preset = "");
	extern virtual function bit compare(pkt_data to);
	extern virtual function void pack();
	extern virtual function void unpack();
	extern virtual function pkt_data copy(pkt_data to=null);
	
endclass

function pkt_data::new();

endfunction

function bit pkt_data::compare(pkt_data to);
	bit match = 1;
	if(this.pkt_len != to.pkt_len) match = 0;
	foreach(payload_q[i]) if(this.payload_q[i] != to.payload_q[i])
		match = 0;
	return match;
endfunction:compare

function void pkt_data::pack();
	foreach (this.payload_q[i]) begin
		if (i==0 & i==pkt_len-1) //modify!!!!!
			this.data.push_back({1'b1, 1'b1, 1'b1, payload_q[i]});
		else if (i==0)
			this.data.push_back({1'b1, 1'b1, 1'b0, payload_q[i]});		
		else 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 string pkt_data::psprintf(string preset = "");
	psprintf = {preset, $psprintf("pkt_len = %0d", pkt_len)};
	foreach(payload_q[i])
		psprintf = {psprintf, ",", $psprintf("payload[%0d] = 'h%0h", i, payload_q[i])};
endfunction

function pkt_data pkt_data::copy(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

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

尼德兰的喵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值