SV组件实现篇之七:监测器的采样(上)

 

SV组件实现篇之七:监测器的采样(上)...



当verifier梅在实现slave channel验证环境的时候,她也绘制了一幅slave channel验证结构图:



verifier梅借鉴了verifier董在验证模块registers时的方法,为DUT slave channel创建了2个stimulator,即上行发送数据的initiator和下行接收数据的responder。在实现了这两个组件之后,由initiator发送的激励,经过slave的数据通路可以最终送出至responder。在stimulator实现之后,就需要将必要的信号采集下来,以用作日后的数据比较。

就之前的介绍,slave的initiator和responder应该采取各自的interface,即ini_if和rsp_if,而从不同接口上可以分别采集到送入slave的数据和从slave送出的数据。因此,我们倾向于也分别实现两个内部结构类似的monitor,即ini_monitor和rsp_monitor(别怕麻烦,将monitor按照接口和功能分离为两个的优势在后面的验证集成中会显现)。待monitor采集完数据之后,我们也准备将其进一步发送至checker。

对于monitor的功能而言,它的核心部分就是从interface做数据采样(sampling)和打包(packaging)送给checker。我们在之前的《SV的环境构建篇之四:程序和模块》中已经提到了,设计部分(硬件)和验证部分(软件)之间可能存在竞争的现象,除了program可以消除竞争之外,我们也可以通过interface的clocking块来消除竞争。所以,我们先在这里引入interface clocking块的介绍,再利用clocking来实现monitor的数据采样功能。

interface clocking介绍

在之前《SV的环境构建篇之三:接口》中,可以看到硬件和软件世界的连接可以通过灵活的interface来连接,也可以通过modport来进一步限定信号传输的方向,避免端口连接的错误。同时,我们也可以在接口中声明clocking(时序块)来通过显式地指出时钟信号,用其来对信号做信号的同步和采样。clocking块基于时钟周期来对信号在进行驱动或者采样的方式,使得testbench不需要再关注于如何准确及时地对信号传输或者采样,也消除了信号竞争的问题。通过clocking,testbench可以利用其进行:

  • 事件的同步
  • 输入的采样
  • 输出的驱动


我们来看看,一个典型的clocking定义:



在上面这个例子中,第一行定义了一个clocking块bus,由clock1的上升沿来驱动。第二行指出了在clocking块中所有的信号,默认情况下会在clocking事件(clock1上升沿)的前10ns来对其进行输入采样,在其事件的后2ns对其进行输出驱动。下一行是声明了要对其采样的三个输入信号,data,ready和指向top.mem1.enable的enable信号,这三个信号作为输入,它们的采样时间即采用了默认输入事件(clock1上升沿前的10ns)。第四行则声明了要驱动的ack信号,而驱动该信号的时间则是时钟clock1的下降沿,即覆盖了原有的默认输出事件(clock1上升沿后的2ns)。接下来的addr,也采用了自身定义的采样事件,即clock1上升沿前的1step。这里的1step会使得采样发生在clock1上升沿的上一个time slot的postponed区域,即可以保证采样到的数据是上一个时钟周期的数据。

从上面这个例子可以看到,关于定义clocking块需要注意的几个地方

  • clocking块不但可以定义在interface中,也可以定义在module和program中。
  • clocking中列举的信号不是自己定义的,而是应该由interface或者其它声明clocking的模块定义的。
  • clocking在声明完名字之后,应该伴随着定义默认的采样事件,即“default input/output event”。如果没有定义,则会采用默认的在clocking采样事件前的1step对输入进行采样,在采样事件后的#0对输入进行驱动。
  • 除了定义默认的采样和驱动事件,也可以在其后定义信号方向时,用新的采样事件对默认采样和驱动事件做覆盖。


这里,我们再深入一些,讨论关于所谓定义采样和驱动事件的规定,例如,上面例子中第二行:

default input #10ns output #2ns;

再来看看相对的采样事件是如何定义的呢?



从这张关于clocking事件的说明图能够得知,输入信号的采样,会在关于时钟事件(clock1上升沿)前的10ns做采样,所以,规定的“input clocking_skew”中clocking skew(时钟偏移量)采取的是负值,即相对clocking事件的前10ns;而输出驱动则采用的是正值,会在clocking事件后的2ns时刻做输出驱动。

所以,从上面的图中可以发现,这种在时钟事件前后的采样或者驱动方式可以有效地避免竞争的情况,因为可以通过在不同的time slot中做信号的采样或驱动。例如input #1step,可以使得在上一个时钟postponed区域做时钟采样,而#1ps,利用一个很小的输出延迟,使得输出可以在clocking事件(时钟变化沿)后的time slot内发生变化。这样就使得在clocking块中罗列的信号事件不会造成竞争的情况。

利用clocking事件同步

关于clocking功能的第一特性,使用起来同一般@或者wait事件方式没有明显差别,只是需要注意添加信号所定义的clocking块名称。为了使得读者对于包括clocking事件同步在内的clocking三个功能特性有更好的体会,我们拿出下面这个例子来看看:



无论是上面利用@ck(即@(posedge clk))还是@ck.vld,可以肯定的一点是,这些事件发生的时刻都是在clk的上升沿,因为无论是clocking块本身,还是通过ck采样的vld或者驱动的grant,都是基于时钟上升沿的。所以,我们仍然可以通过使用@或者wait等耗时的操作符来基于信号变化的事件进行同步。

利用clocking采样数据

依然还是上面的clocking1的例子,其中虽然在clocking1::drv_vld中并没有在clk时钟沿驱动vld,但采样时,仍然还是基于时钟沿和采样偏移量进行基于固定采样时刻的数据采样;如果没有利用clocking块进行采样,那么采样只会基于时钟沿(没有叠加采样偏移量)进行。

从上面的例子输出结果可以看到:



vld的驱动和采样,也可以从波形图实例中有更直观的体现:



从波形图上也可以看到,如果只是基于clk上升沿,那么采样的结果同基于clk上升沿叠加采样偏移量的结果是不相同的。如果,我们将采样偏移量抽象为物理中的建立时间(setup time),则其要求更明确为时钟上升沿之前的某一段时间内,数据不能有变化,否则数据采样会有不一样的结果;如果只是基于时钟上升沿采样,那么上图中的“采样vld”则表示了采样出的结果。而无论是“采样ck.vld”还是“采样vld”,值得变化均发生在了时钟上升沿,只是背后的采样点不同罢了。

利用clocking产生激励

同样地,如果采样clocking块的驱动,也可以实现一种类似于“物理保持时间”的驱动方式,实现了驱动时钟沿叠加驱动偏移量的延迟效果。例如下面的例子:



上面的例子中,对于ck块中的grt赋值,都是通过clocking块ck.grt来引用进而赋值的。所以,虽然赋值的时间点都是在clk上升沿触发的,而真正grt值发生变化的时刻都是在clk上升沿叠加驱动偏移量的时间点。关于这一点,可以从下面的输出结果以及驱动时序波形图中印证。

输出结果:



驱动时序波形图:



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


    关注 路科验证


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册