SV-路科-V0实验:lab3-设置Monitor和Checker

本文详细介绍了如何创建一个从路由器输出端取样的监视器,包括接收和处理数据的`recv()`和`get_payload()`任务。同时,讨论了如何实现数据比较器,以及在`check()`任务中验证数据的一致性。在实现过程中,提到了队列管理、下降沿检测和错误处理策略。文章还涉及到了Verilog语言中的任务和函数实现,以及在遇到问题时的调试方法。
摘要由CSDN通过智能技术生成

学习目标:

1.构造一个从路由器输出端进行取样的监视器

2.构造一个可以验证路由器输出的比较器

3.运行Driver和Monitor程序,检验Checker能否正确比数 


check()用来比较输入端发送的数据和输出的得到的数据

recv()接收从输出端拿到的数据,后续还会填充很多功能。

get_payload()实现从输出端拿到数据的功能


 任务一:创建顶层测试环境

  1. 在test.sv中声明一个8bit的队列pkt2cmp_payload。
  2. 在发送数据的同时核查数据,使用并行线程用来接收输出端的数据的任务recv()和发送数据的任务send()并行进行。并在后边调用比较二者的任务check()。

 任务二:实现一个监视器

  1. 声明recv()任务,并在其中调用get_payload()任务以获取输出端数据。后续还需要往里面添加内容。
  2. 声明get_payload()任务,在该任务中删除队列pkt2cmp_payload[$]的内容(删除之前的数据包)
  3. 等待输出端frame的下降沿。

4. 保持循环直到frameo_n[da]被检测到,采样输出端数据包,使用循环体,每8个时钟周期打包一个字节byte并将于他们存入队列pkt2cmp_payload[$]

5.如果payload没有连成一个byte,则打印错误信息。


Tips :

对于步骤3:

 意思是需要将等待下降沿的语句放入并行线程fork-join中。防止在类中提到的中断线程fork的问题。

步骤4:

a.将数据存放进队列,一般采用queue.push_back(data)从队列尾部排队放入。要求8bit一个字节的数据存入队列,因此需要声明一个8bit的变量,使用循环为这个变量的每一位赋值,即每一时钟周期的输出端的数据dout[da]。

b.下降沿触发保持frameo_n[da] = 0,当valido=0时接收数据,当frameo_n[da]=1且数据接收到最后一位时,8bit数据接收完毕,就可以将其存入到用于监测dout的队列中了。


任务三:发展比较器

  1. 创建compare函数,返回1个bit的值并进行数据比较
  2. 在compare()函数中比较队列payload[$]和队列pkt2cmp_payload[$]中存储的数据。

【注:可以直接用==来比较两个队列】

如果两个队列的长度不匹配,设置描述该错误的语句,并返回0结束该子程序。

如果两个队列的数据不匹配,设置描述该错误的语句,并且返回0结束该子程序。

如果两个队列的数据一致,设置compare success的语句,并且返回1结束该子程序。

     3.创建check()任务

     4.在check任务中,声明一个字符串string和一个计数器变量count

     5.在check()中调用compare函数,检查接收的的数据

如果有步骤2中的错误产生,则打印错误message并终止仿真

如果success,则打印语句包含message with compare success and packet numbers

 

对比参考代码

函数返回:参考代码使用return(bit)返回,不知道compare=bit是否可以作为返回值?

函数调用:在task check()中调用函数时,我使用的是if条件判断,与参考代码一致。

队列比较:参考代码的队列比较payload == pkt2cmp_payload,这里需要修改。

字符串信息:太简陋了,参考代码给出了比较失败时的两个队列长度,以及内容。(使用$sformat整合字符串)

  • 可以使用%p来打印队列内容(用于打印聚合表达式,例如解压缩结构,数组和联合(unpacked structure,array,unions),结构体等等)。
  • %m和%l分别可以显示打印语句所在当前模块的模块名,和该模块所在的仿真所构建的运行库路径信息,对于调试跟踪来说非常有用,其效果参见以下代码例。

修改后的代码

monitor

 task recv();
  	get_payload();
endtask: recv
	
	task get_payload();
		logic [7:0] datum;
		
		pkt2cmp_payload.delete();
		fork		
			@(negedge rtr_io.cb.frameo_n[da]);
		join
		
		foreach(pkt2cmp_payload[id]) begin
			for(int i = 0; i < 8; i++)begin
					if(!rtr_io.cb.valido_n[da])
						datum[i] = rtr_io.cb.dout[da];
					if(rtr_io.cb.frameo_n[da]==1'b1 && i==7)
						pkt2cmp_payload.push_back(datum);
					else
						begin
							$display("%m \n[Error]:payload not a byte ",$realtime);
							$finish;
						end
						@(rtr_io.cb);
				end
		end
	endtask: get_payload

checker

task check();
		string msg;
		int count;
		
		if(!compare(msg))begin
		$display("%m [Error] Packet: \n %s",$realtime,msg);
		$finish;
	end
		else	
		$display("%m [Accomplish] Packet: \n %s",$realtime,msg);
endtask
	
function bit compare(string msg);
		if(payload.size()!=pkt2cmp_payload.size()) begin
			compare = 0;
			msg = "Payload size mismatch:";
            msg = {msg , $sformatf("Cause of failure : payload.size()=%0d,pkt2cmp_payload.size()=%0d queues size is inconsistent",payload.size(),pkt2cmp_payload.size())};
		end
		else
			if(payload==pkt2cmp_payload) begin
				compare = 0;
				msg = "Payload content mismatch:";
				msg = {msg ,$sformatf("Cause of failure : payload = %p pkt2cmp_payload = %p  queues data is inconsistent", payload,pkt2cmp_payload)};
			end
				else
				begin
					compare = 1;
					msg = "Two queues data compare succeslly";
				end
endfunction

 编译仿真没有问题,但是打印结果与波形错误。


  Question Summary:学习参考代码

  • monitor步骤3的区别

 1.参考代码开辟了一个fork...join_any,用于防止设计出现问题即frame_n的下降沿等不到仿真一直运行的情况,那么它给出了1000个时钟周期的等待时间,如果等不到就停止仿真。

等到下降沿或者等不到,这两个线程任意一个完成就可以执行下一个语句即终止另一个线程。实际上这里的终止线程也终止了最外面的fork,可以明确disable frameo_wd_timer。

  • monitor步骤4的区别:    

  • 使用了forever,没看懂,有必要吗而且它怎么退出。 
  • 还有这个for循环(这个循环有点深奥。。。),i=i?,datum[i++]?,我理解的是手动递增i,不把递增的步骤卸载for循环的括号里,正常代码应该是我的方式,可以试着修改参考代码跑一下仿真。for-loop实现的功能就是每8bit为一个包装进datum。 

我的代码push_back的条件有问题,这个条件和上边payload阶段的不一样,payload是当i=7且queue的长度为size()-1,是用来拉高frame_n的。这里的条件应该是要在frameo_n拉高的并且dout最后一位已经装入datum内,才能push_back。那么这个时候i应该是7还是8?

  • 两个push.back[datum]没看懂

正常字节的写入时下边的push.back[datum],for-loop里面的push.back[datum]是用来写入最后一个字节。也就是说在完成8个for-loop后由于还没有等待frameo_n拉高所以不满足第二个if条件,这样会结束循环,执行下边的push.back[datum]写入操作。这是第一个byte,但整个pay_load过程不止发送一个byte,所以还需要继续执行for-loop。

怎么执行?参考代码使用了forever,这样for-loop就可以一直执行下去。那么如何结束呢?当frameo_n为高并且恰好是第8位时就能通过return结束task但这个时候没有将存有最后一个byte的datum写入队列因此在return之前有个push.back[datum],如果不是第8位呢?即datum没有填满一个byte,不满足要求则打印相关信息并通过系统函数$finish结束仿真。

  • 输入是在payload期间以8bit即一个字节byte发送数据,在这个试验中get_payload也是以8bit接收数据,如果在激励改为12bit的数据发送两次,那么在get_payload中还能否在一个frameo_n期间先接收8bit的数据?还是说在一个frameo_n期间接收整数倍的byte(即24bit)的数据。

设计要求会有说明能否实现前者,但首先应该验证设计而非看设计说明


  • checker步骤2区别

 1.函数的参数列表我没有声明端口方向。参考代码使用了ref类型;如果使用input类型会如何?

修改input类型后,不影响仿真结果,但是打印结果会有变化,在compare修改的msg不会打印出来。

 2.定长数组,队列,动态数组的比较;函数返回值return();

  • checker步骤3区别

 1.参考代码计数变量声明了static,全局变量,这样pkts_checked的值可以被下一次比较所共享,pkts_checked++后的值可以累积。


二次修改后代码

monitor

task recv();
  	get_payload();
	endtask: recv
	
	task get_payload();
		logic [7:0] datum;
		
		pkt2cmp_payload.delete();
		fork
			begin		
				fork 
				 @(negedge rtr_io.cb.frameo_n[da]);
				 begin
				 repeat(1000) @(rtr_io.cb);
			   $display("\n%m\n[ERROR]%t Frame signal timed out!\n", $realtime);
			   $finish;
			 	 end
				join_any
				disable fork;
			end
		join
		
		forever begin
			for(int i = 0; i < 8; i++)begin
					if(!rtr_io.cb.valido_n[da])
						datum[i] = rtr_io.cb.dout[da];
					if(rtr_io.cb.frameo_n[da]==1'b1 && i==7)begin
						pkt2cmp_payload.push_back(datum);
						return;
					end
					else begin
							$display("%m \n[Error]:payload not a byte ",$realtime);
							$finish;
						end
					@(rtr_io.cb);
				end
			pkt2cmp_payload.push_back(datum);
	end
	endtask: get_payload

 checker

	task check();
		string msg;
		static int count;
		
		if(!compare(msg))begin
		$display("%m [Error] Packet: #%0d \n %s",$realtime,count,msg);
		$finish;
	end
		else	
		$display("%m [Accomplish] Packet: #%0d \n %s",$realtime,count++,msg);
endtask
	
	function bit compare(ref string msg);
		if(payload.size()!=pkt2cmp_payload.size()) begin
			compare = 0;
			msg = "Payload size mismatch:";
			msg = {msg , $sformatf("Cause of failure : payload.size()=%0d, pkt2cmp_payload.size()=%0d queues size is inconsistent",payload.size(),pkt2cmp_payload.size())};
		end
		else
			if(payload!=pkt2cmp_payload) begin
				compare = 0;
				msg = "Payload content mismatch:";
				msg = {msg ,$sformatf("Cause of failure : payload = %p pkt2cmp_payload = %p  queues data is inconsistent", payload,pkt2cmp_payload)};
			end
				else
				begin
					compare = 1;
					msg = "Two queues data compare succeslly";
				end
endfunction

 仿真在[Error]:payload not a byte处停止。没有等到frameo_n拉高且datum最后一位。

调试

  • if(rtr_io.cb.frameo_n[da]==1'b1 && i==7)两个条件放一起,会出现以下情况:

即当满足第一个条件第二个条件不满足时会执行else语句这是我们期望的情况,

当第一个条件不满足,二个条件满足,也会执行else语句,这是正常写入数据的情况。

当两个条件都不满足时,也会执行else语句,这也是正常写入数据的情况。 

因此应该按照参考代码的方式将两个条件分开

 再次仿真,发送了数据后就结束了仿真。

调试:108和109,110,120设置断点,发现在发送两次数据后,get_padload有工作,两次数据都写入了pkt2cmp_payload队列。但是第二次存入的数据并没有等到frameo_n拉高且i==7。使得foever没有return,重复了4次for-loop后等到了frameo_n拉高但是i不等于7,因此执行了报错。实际上在查看波形时发现在第二次数据存入pkt2cmp_payload前frameo_n已经翻转了,但不知道为什么没有执行条件判断。

 


调试:使用参考代码i=idatum[i++]和i==8,打印结果正常。说明compare=[bit]是可以作为返回值的。但是为什么使用常用的for循环不行?

 

【思考】为什么for(int i=0,;i<8;i++)不可以?

通过设置断点,整个for循环的执行过程如下:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值