SV组件实现篇之十一:比较器和参考模型(上)

 

SV组件实现篇之十一:比较器和参考模型(上)...



在同之前的verifier梅、尤、董完成了slave、arbiter和registers的模块验证之后,我们需要看看最后一位verifier娄是如何完成arbiter验证的。Verifier娄也依照之前的验证步骤,给出了arbiter的验证框图:



在实现了stimulator和monitor之后,就进入了数据比对和功能检查的环节了。在开始考虑实现checker之前,我们先来看看,如果要完成formatter的功能验证,需要考虑的功能点检查,即验证计划是什么?

  1. 复位检查。检查在复位以后,formatter各个输出信号的值。考虑到verifier娄是一位新手的情况下,他尽量将formatter作为一个黑盒去检查,而很少关注其内部的信号状态。
  2. 数据完整性。即数据的输入到输出,没有数据发生丢失,而且数据按照协议要求进行传输。
  3. 功能配置情况。即在寄存器功能配置下,formatter可以将来自不同slave的数据打包为不同长度的数据包。
  4. 协议检查。从arbiter到formatter,以及formatter到外部下行数据段的两个协议部分是否符合时序要求。


上面的验证功能点检查,从列出的功能点,到考虑给出的激励场景,都是互相对应的。而面对更复杂的设计,要列出的功能点检查项要更多呢!那么,我们就上面要检查的功能点,来看看verifier娄准备如何实现它的checker。实际中,我们可以就要检查的场景,分为异常检查、常规检查和协议检查:

  • 异常检查:由于某一异常事件触发,进而使得设计做出的响应。
  • 常规检查:指的是设计在正常工作状态下的表现,一般可以伴随长时间的稳定工作。
  • 时序检查:检查DUT的内部或者外部信号之间的时序是否符合设计要求。


接下来,我们将上面的功能点检查,与上面三种检查种类进行划分的话,它们的类属关系是,复位检查由于是复位信号的事件使得设计发生相应的变化,我们可以视为异常检查;数据完整性和功能配置情况都是在formatter稳定工作时的检查,可以归类为常规检查协议检查则因为特别注重时序性的要求,而应当归类为时序检查。而就这种检查的场景而言,实现功能检查的方式也不相同:异常检查主要就事件触发来检查设计的响应;而常规检查则通过查看设计的配置,来判断设计的工作状态是否符合预期;对于时序检查,则会通过捕捉信号间的时序关系来进行检查。对于这三种检查方式,本文也将依次详细论述。

异常检查

在设计异常的时候,通过设计外部或者内部的异常信号,我们可以检查设计的响应(也分内部和外部)是否正确。如果检查得更细致,我们还应该考虑:

  • 异常事件的触发是否合理,符合设计要求。
  • 异常事件的处理是否正确
  • 异常事件恢复的条件在满足之后,设计是否能够再次回到稳定的工作状态


下面的示例代码就是用来检查“设计复位”的。通过这个例子,读者可以知道,如何定义一些事件、已经如何针对事件作出响应和如何对事件检查的功能做出控制。



首先来看看fmt_ini_mon,在其内部定义了成员事件reset_e,通过方法mon_reset()来捕捉复位事件,进而触发reset_e。那么这个reset_e用来通知谁呢?我们在本文最后的代码全览中可以看到,这个事件是用来通知比较器fmt_checker。在fmt_checker中,它对reset_e这个事件时刻保持关注,一旦等到了事件被触发,它便会执行功能检查chk_rst()。在下面的代码中,fmt_checker声明的事件reset_e是等同于fmt_ini_mon中的同名事件,而这两个事件的等价传递发生在了顶层的testbench环境中。而类似的,关于虚拟接口在chk_rst()中的引用也需要首先保证它们在顶层环境被连接到真正的物理接口上。在事件chk_rst()的检查中,会检查formatter的所有输出接口,且要求在复位触发时,输出信号被置为期望的值,否则通过系统函数$error()报错。



常规检查

对于常规检查而言,它是一个长期监测并且时刻检查的行为,这就要求观察三个方面:

  • DUT的配置情况
  • DUT的输入数据
  • DUT的输出数据


对于这三方面的数据监视和最终的比较,这部分提出了两种可行的解决的方式:

  • 拆分检查:是将DUT要检查的功能点有机的剥离开,并且就每个功能点做独立的检查。
  • 整体检查:是将DUT的所有输入包括配置情况作为“参考模型”的输入,而参考模型会按照设计要求,将最终期望的数据输出。


下面是对两种检查方式的具体实现代码。

拆分检查





上面的fmt_ini_mon会在每次有效的数据输入时采集好数据,装入新创建的数据对象trans。这里我们会要求每次创建新的对象引来装入新的数据。fmt_ini_mon通过mon_trans()和put_trans()两个成员方法可以时刻监视和传输捕捉到的有效数据包。由于输入数据没有包的首和尾,因此定义的数据传输类fmt_ini_trans的数据成员也只装载每一个时钟周期的采样。另外需要注意的是,由于从寄存器register到formatter传递的包长配置信号slvX_len需要解码,因此我们在fmt_ini_trans中定义了解码的方法。而该方法dec_length()被声明为了静态方法是由于其它外部的函数也需要单独调用该方法(并不需要例化对象)。





从fmt_rsp_mon的定义来看,主要做了两件事情。

首先,它同fmt_ini_mon类似的是,也捕捉了输出的数据,通过观察vif.mon.fmt_start信号来捕捉包首,进而逐次采样数据。在这里,因为formatter输出协议有明显的包的含义,所以,我们将输出数据包fmt_rsp_trans定义为内部包含多个数据的类,这方便与fmt_rsp_mon可以将一个完整的数据包存入其中。

其次,fmt_rsp_mon也单独捕捉了一个事件,即数据包要发送时的请求事件。当vif.mon_fmt_req从0跳转到1时,我们将这个数据包请求发送的事件捕捉下来,触发事件req_trans_e。而该事件又等价于在fmt_checker中同名的事件req_trans_e。fmt_checker也将利用这个事件,检查功能配置是否生效。

有一些读者在参考上面fmt_rsp_mon::mon_trans时对于数据采样为什么直接从vif.mon.fmt_start上升沿开始而不是从vif.mon.fmt_req开始可能有疑虑,因为按照协议的要求fmt_req需要先等待fmt_grant,再而才能出发fmt_start。那么这个时序部分的检查,在fmt_rsp_mon::mon_trans()采集数据包时可以省略吗?为什么?

答案是,我们当然很关注边界信号协议时序的检查,而这部分的检查我们则会交给第三部分的检查,即“时序检查”,在接下来的示例代码中读者可以看到。所以,我们在fmt_rsp_mon::mon_trans()中省略了检查,而使得代码变得更为清爽,不是吗?

此外,对于formatter输出端的数据类型fmt_rsp_trans定义中,也添加了其用来做数据比较的方法fmt_rsp_trans(fmt_rsp_trans t),这个方法会在后面的fmt_checker中用来比较两部分的数据,即实际采样到的输出数据和期望的输出数据进行比较,如果两个数据对象相同则返回1,否则返回0。

在定义了上面两个monitor即fmt_ini_mon和fmt_rsp_mon之后,将由fmt_checker做最终的功能检查:







在fmt_checker中声明了用来使能检查功能的控制信号en_chk_data和en_chk_len,这便于后期在顶层环境的集中控制。除了声明虚接口ini_vif和rsp_vif以外,fmt_checker还声明并且例化了三个用来装载数据的mailbox:ini_mb、rsp_mb和exp_mb。

由于从ini_mb收集到的数据类型是从formatter输入端采集到了fmt_ini_trans类型,而rsp_mb是从formatter输出端采集到的数据类型fmt_rsp_trans,这两种数据类型有不小的差别,前者存储单周期的数据,后者存储多个周期的数据包。如果要对这两种数据包做比较,我们需要将它们统一到同样地格式,这里fmt_checker选择了利用fmt_checker::ini2rsp_fmt()方法,将从ini_mb得到的数据利用其所含的信息包装成为fmt_rsp_trans类型,进而再存储到第三个mailbox exp_mb。

因为,我们认为exp_mb存储的数据是期望得到的数据,因为其内部的数据是我们通过formatter数据打包的逻辑组装而成的,并非是直接采样得到的数据。而rsp_mb中存储的采样到的输出数据,同exp_mb的数据成员均为fmt_rsp_trans,这就使得比较这两个FIFO之中的数据变得容易多了。fmt_checker::chk_data()做的就是先从这两个mailbox中取到数据对象,进而利用fmt_rsp_trans::comapre()做数据比较,将数据比对结果报告出来。

所以,对于功能检查点“数据完整性”的检查就可以通过上面两个方法chk_data()和ini2rsp_fmt()完成。那么,再来看看,对于“功能配置情况”应该如何检查呢?

fmt_checker::chk_len()通过等待事件req_trans_e来观察formatter在何时发起一个新的数据包。而检查registers到formatter配置是否成功就在于formatter输入端slvX_len的配置,是否可以控制输出包的长度。因此,通过比对slvX_len与fmt_length是否匹配,就可以完成“功能配置的检查“

因此,上面的检查方式,是将两个不同的功能点检查独立到不同的检查方法中完成,互相不影响,并且有独立的使能信号en_chk_data和en_chk_len可以控制这两种检查。

谢谢你对路科验证的关注,也欢迎你分享和转发真正的技术价值,你的支持是我们保持前行的动力。


    关注 路科验证


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册