我是靠谱客的博主 飘逸母鸡,最近开发中收集的这篇文章主要介绍SystemVerilog系列实验3SYNOPSYS—SystemVerilog入门实验3前言一、Monitor是什么?二、Checker是什么?三、 总结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

SYNOPSYS—SystemVerilog入门实验3

文章目录

  • SYNOPSYS---SystemVerilog入门实验3
  • 前言
  • 一、Monitor是什么?
    • 1. 实验思路
    • 2. 实验步骤
      • 1)声明与调用recv()
      • 2)读懂时序图
  • 二、Checker是什么?
    • 1. 实验思路
    • 2. 实验步骤
      • 1)声明与调用check()
      • 2)创建compare()
  • 三、 总结


前言

在实验1与实验2中,我们已将Stimulator(激励发生器)对应的四部分(Configure、Generator、Transactor、Driver)初步搭建完成。在本实验中,我们则需要对剩下的环境组件:Monitor(监视器)、Checker(比较器)完成从无到有的构造。


一、Monitor是什么?

如实验1中所描述的:Monitor主要是用来观察DUT的边界或者内部信号,并且经过打包整理传送至其他验证平台的组件。一句话概括:它就是观察DUT输出信号的一个容器,以便在Checker中与DUT输入端口的信号进行比较。
检测信号 的角度来划分Monitor的功能,可以分为以下两部分:

观察DUT边界信号: 对于系统信号如时钟,可以监测其频率变化;对于总线信号,可以监测总线的传输类型和数据内容,以及检查总线时序是否符合协议。
观察DUT内部信号: 灰盒验证往往需要探视DUT内部信号,以指导激励发生器的激励发送,或者完成覆盖率收集,或者完成内部功能的检查。

在初步了解Monitor的大概功能后,针对它的问题也随之而来。


1. 实验思路

  • Monitor如何接收DUT输出的数据?
  • 从DUT输出的数据包格式与内容是什么?

2. 实验步骤

上述两条思路较实验2中如何产生发送激励的方法大体一致,依旧采用:按照设计师提供的输出数据包时序图,将输出的数据存储至队列中即可。

1)声明与调用recv()

在编写代码前,首先需想到应声明一个 接收 task,因为通过DUT的输出端口发送至外部(这里为Monitor)的数据相对于外部(Monitor)来说则是 接收 DUT发来的数据,one 定义一个存放接收数据的队列,名称为pkt2cmp_payload。因此在实验2的基础上增加如下test.sv的代码:

program automatic test(router_io.TB router_io);
//---one---
	logic	[7:0] pkt2cmp_payload[$];
  	initial begin
  		 reset();
	 	 gen();	 	 
//---two---
			fork
			send();
			recv();
			join
	end	
//---three---
	task recv();
		
	endtask: recv

代码分析: two 声明 recv() 【 receive接收简写为recv】,为了使发送与接收能够并行工作,故采用fork…join语句将send()与recv()包裹起来。three 定义接收recv()的一个task。

fork…join、fork…join_any、fork…join_none用法
Verilog中与顺序线程begin…end相对的是并行线程fork…join
fork…join :需要所有并行的线程都结束以后才会继续执行。
fork…join_any :会等到任何一个线程结束以后就继续执行。
fork…join_none :不会等待其子线程而继续执行。

为了更好理解,故绘制fork…join、fork…join_none、fork…join_any图示如下
在这里插入图片描述


2)读懂时序图

Monitor在接收DUT发来的数据时,也应当按照一定的 时序关系 才能够将数据正确接收!
在这里插入图片描述上图中,dout[da] 为DUT的数据输出信号,即Monitor的数据输入信号,该信号规定,数据须组装为一个字节(8bits)才能进行输出,输出至Monitor时则会进入已定义的pkt2cmp_payload[$]队列中。valido_n[da] 为数据有效信号,当该信号由高变低时,第一个数据开始输出(数据的最低位),直到一个字节输出完毕后,该信号由低变高,第二个字节同理。frameo_n[da] 为帧使能信号(一帧信号包含一字节或多字节的数据),在第一个数据发送前,应将该信号置为低电平,并且在最后一个字节的最后一个bit的数据到来时,将该信号由低变高。


	task get_payload();
//---one---
		pkt2cmp_payload.delete();		
//---two---
		fork
			begin: wd_timer_fork	
//***************************************************
				fork: frameo_wd_timer			
//---线程1---
					@(negedge router_io.cb.frameo_n[da]);		
//---线程2---
					begin
						repeat(1000) @(router_io.cb);
						$display("n%mn[ERROR]%t Frame signal timed out!n", $realtime);
						$finish;
					end
				join_any: frameo_wd_timer			
//***************************************************
				disable fork;
			end: wd_timer_fork
		join
//---three---
    forever begin
      logic[7:0] datum;
      for(int i=0; i<8; i=i)  begin
        if(!rtr_io.cb.valido_n[da])
          datum[i++] = rtr_io.cb.dout[da];    
//---four---
       	 	if(rtr_io.cb.frameo_n[da])begin
          		if(i==8) begin //byte alligned
      	  			pkt2cmp_payload.push_back(datum);
      	 	 		return;      //done with payload
      			end
      			else begin
      	 	 		$display("n%mn[ERROR]%t Packet payload not byte aligned!n", $realtime);
      	 	 		$finish;
      			end 	
			end
       	 @(rtr_io.cb);
      end     
//---five---
      pkt2cmp_payload.push_back(datum);
    end
  endtask: get_payload

代码分析: one 在进行队列操作前,须将队列进行清空操作,以防原有队列中的数据影响实验结果。two 此处fork…join的目的是若程序出现bug,可使仿真退出,不会造成持续运行的情况(即超时退出)。 fork…join_any 表示只要出现一个子线程执行结束的情况,即可退出操作,在这里有两个子线程:1、@(negedge router_io.cb.frameo_n[da]);2、begin…end块。disable fork 表示若一个线程执行完毕后,应将没有执行完的线程关闭。

超时退出: 正常模式下,DUT输出信号frameo_n[da]的下降沿(数据输出的开始标志)是会随着数据同时出现的,从而进入下面的forever begin…end块中。但为了防止程序存在bug,须设置超时退出机制(在这里延时1000个cycle,若没有等到输出信号frameo_n[da]的下降沿,即报错([ERROR])并停止运行($finish))。

three 若程序正确,即frameo_n[da]的下降沿已出现。则跳转至forever begin…end块中。首先定义一个缓存数据的datum,若valido_n[da]低有效,就将DUT输出的数据dout[da]按照低位在前、高位在后的形式依次存放至datum中,前7bits由于rtr_io.cb.frameo_n[da]信号始终为低电平,将不会执行 four,而会直接跳转至 five (此时前7bit已由队列pkt2cmp_payload依次从datum中读出)。根据时序图可知,若frameo_n[da]在第8bit(最后1bit)到来时由低变高,则代表整帧数据已传输完毕,目前只需要将已缓存的第8bit(最后1bit)从datum读走即可,至此,发送部分的程序将会停止(return的作用)。

可以通过内建方法push_back()、push_front()、pop_back()、pop_front()来顺序添加或移除并且获得数据成员。
push_back(): 在尾部加入数据。
push_front(): 在首部加入数据。
pop_back(): 从尾部拿出数据。
pop_front(): 从首部拿出数据

return 语法:作用是从当前函数退出,并从这个函数返回一个值。
retrun[() [expression] [] ]
可选项 expression 参数是要从函数返回的值。如果省略,则该函数不返回值。


二、Checker是什么?

1. 实验思路

  • 将发送的数据与接收的数据进行对比

2. 实验步骤

1)声明与调用check()

program automatic test(router_io.TB router_io);
	logic	[7:0] pkt2cmp_payload[$];
  	initial begin
  		 reset();
	 	 gen();	 	 
			fork
				send();
				recv();
			join		
//---one---
		check();
	end

代码分析: 当发送与接收的任务都执行完毕后,one声明check()。


2)创建compare()

在编写check()的任务时,需想到:在进行发送的数据与接收的数据比较时,无非相同或相异,相同时 打印 出包中的数据(第几包也可以考虑在内),相异时 打印 出第几包数据出错,因此在test.sv的代码中编写如下代码:
针对SV中有关 打印 的知识点可以参考该链接。

//----------------------error--------------------------
task check();
//---one---
    if (payload != pkt2cmp_payload) begin
      $display("n%mn[ERROR]%t Packet #%0d %sn", $realtime, pkts_checked, message);
      $finish;
    end
    $display("[NOTE]%t Packet #%0d %s", $realtime, pkts_checked++, message);
  endtask: check

代码分析: one 在此处本人忽略了在进行队列中的数据比较时,不仅要比较队列中的数据,而且还要比较队列的宽度,即size!!! ,为了增加代码可读性,用return返回值(0或1)的思路来代替if的判断条件(payload != pkt2cmp_payload),若数据与队列宽度一致时,返回1,否则返回0。


因此创建一个compare()函数如下:

  function bit compare(ref string message);
    if(payload.size() != pkt2cmp_payload.size()) begin
      message = "Payload size Mismatch:n";
      message = { message, $sformatf("payload.size() = %0d, pkt2cmp_payload.size() = %0dn", payload.size(),pkt2cmp_payload.size()) };
      return (0);
    end
    if(payload == pkt2cmp_payload) ;
    	else begin
     	 	message = "Payload Content Mismatch:n";
      		message = { message, $sformatf("Packet Sent:   %pnPkt Received:   %p", payload, pkt2cmp_payload) };
      		return (0);
    	end
    message = "Successfully Compared";
    return(1);
  endfunction: compare

代码分析: 在上述代码中,如果发送的队列与接收的队列宽度不同,则将各自的宽度打印并从函数function返回数值0;如果宽度相同,则进行队列中数据的比较,若相同,打印出Successfully Compared并从函数function返回数值1,反之打印出各自的数据并返回数值0。


于是将check()更改如下:

task check();
    string message;
    static int pkts_checked = 0;
    if (!compare(message)) begin
      $display("n%mn[ERROR]%t Packet #%0d %sn", $realtime, pkts_checked, message);
      $finish;
    end
    $display("[NOTE]%t Packet #%0d %s", $realtime, pkts_checked++, message);
  endtask: check

ref 用法
task和function的参数列表的类型有四种:input,output,inout,ref。其中,ref类似于软件的指针,调用时不会复制,而是直接引用或修改外部传入的数据对象。
inout和ref的区别是:inout只有当方法结束之后才可以传递到外部,而ref可以在方法执行过程中就修改数据对象,无须等到方法结束。如果保护数据对象只被读取不被写入,则通过const的方法来限定ref声明的参数。

针对 ref 的理解亦可以参考该链接。


三、 总结

在这里插入图片描述

该部分实验主要将红色框内的大致结构(DUT输出数据的接收与比对)搭建了起来,至此,一个基本完整的验证环境已搭建完毕。后续会继续优此验证环境,并引入功能覆盖率的测试,感谢大家支持。

最后

以上就是飘逸母鸡为你收集整理的SystemVerilog系列实验3SYNOPSYS—SystemVerilog入门实验3前言一、Monitor是什么?二、Checker是什么?三、 总结的全部内容,希望文章能够帮你解决SystemVerilog系列实验3SYNOPSYS—SystemVerilog入门实验3前言一、Monitor是什么?二、Checker是什么?三、 总结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部