1. clocking block的作用
Clocking block可以将timing和synchronization detail从testbench的structural、functional和procedural elements中分离出来,因此sample timming和clocking block信号的驱动会隐含相对于clocking block的clock了,这就使得对一些key operations的操作很方便,不需要显示使用clocks或指定timing。这些操作如下:
Clocking block拥有declaration和instance一体化,也就说在declaration的时候,就已经实例化了,不需要再做一遍了。多个clocking blocks不能嵌套,且clocking block不能声明在function、task、package或compilation unit的所有声明之外。Clocking block只能声明在module、interface、checker或program里。
2. clocking block中signal_identifier的input/output
Clocking block中指定为input方向signal只能被read,不能被write;
Clocking block中指定为output方向signal只能被write,不能被read;
Clocking block中指定为inout方向signal既能被read,也能被write;inout拥有input和output两种属性,它在本质上会同时定义两个相同名字的input和output。
3. clocking skew
Clocking skew决定了一根signal在距离clock event有多少time units后被sampled或driven的。Input skews隐含为负数的,也就意味着总是在clock event之前发生的,output skews总是在clock event之后发生的。
Clocking event可以用edge,而不是一个数字来指定的。
可以采用default来将一个clocking skew应用到整个clocking block。
input指定#0 skew应该在corresponding clocking event时采样,但为了避免冲突,它们会在Observed region采样。相似的,对于output带有#0 skew或没有skew,那它们会在re-NBA region时驱动。
如果input没有显示指定#0,那么采样值会在clocking event之前的postponed region的sample的。这里有个注意点:如果clocking event是在program里的执行程序触发的,那么clocking event和sample value之间存在竞争关系,只有clocking event在module里更新才不会有竞争关系的。
在处理clocking event时,clocking block应先更新自己的sampled values,然后采取trigger与它相关的event事件。event应该在observed region时触发,因此一个正在等clocking block的process可以保证读到updated sampled value。
4. cycle delay: ##
##可以控制delay特定的clocking event或clock cycle的个数之后,才继续执行。例如:
5. Synchronous events
显示的同步可以通过event control operator (@) 来控制的,这样会允许一个process等待一个特别信号值的变化或一个clocking event。例如:
@(negedge dom.sig1 or posedge dom.sig2);
@(posedge ram_bus.enable);
@(edge dom.sig1);
@(ram_bus) // ram_bus is clocking block
6. Delay control
在赋值语句中有两种delay的控制方法,分别为intra-assigment delay和procedural cycle delay。
intra-assignment delay的形式如下:
bus.data <= #4 r; //等价于:temp = r; #4 bus.data <= temp; 也就是说在#4之前就搞好值了,只不过在#4之后才把赋值的
Procedural cycle delay的形式如下:
#2 bus.data <= 2; //也就是说在#2之后,才会计算右边的值,并赋值的
在对clocking block里的signals进行赋值时,intra-assignment只能用cycle delay(##)的,不能用常规的delay(#),如下:
对于clocking block中信号的赋值可能在非clocking event时被执行到,这样的drive statements应该没有blocking的执行,但drive的值应该是在下一个clocking event到来时才生效的。也就是说右边的值在执行到时马上计算评估出来,但是drive的处理是被delay了,直到下一个clocking event的事件到来时。例如:
对于clockvar(clocking block中的signal)的写只能用synchronous drive语法,用其他方式会报错。因此,在任何的continuous assignment、force statement、procedural continuous assignment去写clockvar是非法的。
7. Drives and nonblocking assignments
Clocking block里的信号必须用<=这种赋值符号,否则编译会报错,我猜想跟clocking block里drive的值是在re-NBA生效有关的吧。
尽管clocking block的synchronous drives语法operator和nonblocking variable assignment一样,但它们本质上有点区别的:
区别1:clocking block的赋值不支持intra-assignment的delay syntax,它要求delay syntax必须是cycle delay(##)。
区别2(重要):clocking block的synchronous drives给inout clockvars时不改变clocking block的input,这是因为input总是在最后才会被sampled更新的,而不是在驱动时更新的。例子如下:
用以上例子来比较下clocking block和regular variable在驱动和采样上的区别:
首先是上述的clocking block,a信号在re-NBA时才会被驱动的,b表达式右边的a在上一time step时已经采样了,在当前step还没有被更新,因此b仍然用的是旧值(看input skew,#0则在observed时,非#0则在更之前采样的(如#1step则是在上一个step的postponed采样))。
然后说下如果是regular variable,a和b没有在clocking block里,因为a和b的驱动是在module里的话,那么a和b会在NBA执行,如果在program里驱动的话,那么a和b会在re-NBA里执行。对于b右边表达式a的值,如果在module里的话,会用active时的值,如果在program,会用reactive的值。