书接上回:
第14章 clocking block
14.1 总览
-clocking block声明
-input skew和output skew
-clocking block的信号事件
-cycle delay
-同步事件
-同步驱动
14.2概述
Clocking block的作用:识别时钟信号并实现同步。
14.3 clocking block声明
声明格式:
解释说明:
delay_control:可以是具体数值,也可以是常量表达式
clocking_identifier:clocking block的名字,只有默认的clocking block可以没有名字,但是没有名字的clocking block里的声明是不能被引用的。
Signal_identifier:clocking block里声明的一个信号
Clocking_event:clocking block里扮演时钟角色的事件,用于驱动和采样
当clocking block里的变量方向是input时,不能做写操作;当clocking block里的变量方向是output时,不能做读操作。
Clocking_skew:指定驱动和采样信号距离时钟事件的时间,input skew是采样时间,在时钟事件之前,ouput skew是驱动时间,在时钟事件之后。
举例:
上面这个例子中,第一个ck1表示clk上升沿的前#1step会做数据采用,clk上升沿之后的下降沿会做数据驱动。第二个ck2表示如果clk在上升沿,那么采样在上升沿前#1step,驱动在该上升沿后面的下降沿;如果clk在下降沿,那么采样在下降沿前#1step,驱动在该下降沿。
注意:clocking block里声明的变量可以和rtl里面的某个hierarchical信号关联。举例:
注意:上面这个例子第四行的意思是ack这个output信号驱动时会override掉默认的#2ns skew,而选择在clock1的下降沿进行驱动。第五行的作用类似,也是override掉默认的#10ns,而换成#1step。
默认情况下,default input skew是#1step,default output skew是0。#1step指的就是一个时间单位。
14.4 input skew和output skew
input skew和output skew分别控制着采样数据和驱动数据的时间。如下图:
Skew可以是常量表达式,也可以是参数。
当input skew是#0时,会在当前time step的observed域进行采样,当output skew是#0时,会在当前time step的re-NBA域进行采样。
14.5 hierarchical表达式
前面已经有例子说明了clocking block里声明的信号可以和hierarchical信号关联,不仅如此,还可以和hierarchical信号的slice或者通过{}连接符将多个信号拼接起来和clocking block里声明的信号关联上。
需要注意的是关联信号时要注意信号的方向:input/output/inout。
14.6 在多个clocking block里使用的同一个信号
需要注意的是,多个clocking block可以使用同一个信号,比如时钟信号,那么就表示同一个时钟实现了多个同步。
14.7 clocking block的作用范围和生命周期
注意:clocking block和always一样声明就是例化,在调用里面的信号时直接clocking_block_name.signal_name即可。
Clocking block不能在function\task\package里面声明,也不能在编译单元之外声明,只能在module、interface、checker、program里声明,它有着静态生命周期,和module、interface、program一样。
14.8 多个clocking block使用举例
14.9 interface和clocking block联合使用
Clocking block经常和interface搭配使用,这是因为interface封装了一系列信号,能够帮助减少测试平台的代码量。对于testbench而言,信号的方向和rtl是不相同的,那么在使用clocking block时,可以带上modport关键字来声明信号的方向。因此使用modport声明的信号方向是站在clocking block的角度而言的,也即interface的角度,也是testbench的角度。和dut的角度是完全相反的。举例:
14.10 clocking block事件
我们可以直接使用clocking block的名字来当做一个事件,它的作用就相当于使用它的同步时钟事件。举例,定义了这样的一个clocking block:
当我们这样等待一个事件时:
它的作用就相当于:
14.11 ## cycle delay
作用:延迟指定数量个default clocking event。Default Clocking event的概念见下一小节。
语法:##后面跟一个正整数
使用##的前提是当前scope下有定义default clocking event,没有定义就使用##会报错。
##0比较特殊:如果当前time step有default clocking event发生,那么不做任何delay;如果当前time step没有default clocking event发生,那么会一直delay到下一个default clocking event发生。
##不能用在intra-assignment里。
14.12 default clocking event
定义:直接在clocking block定义的关键字前面加上default关键字即可,如下:
一个scope下只能定义一个default clocking event,多于一个会报错,并且只对当前scope起作用。举例:
14.13 Input sampling
当input skew非0时,会在当前clocking event之前的某个time step的postponed region进行采样;当input skew为0时,会在当前clocking event的observed region进行采样。
当表达式中有clocking event时,该event是在observed region触发。举例:
虽然@(cb)和@(negedge clk)等效,但两者触发的region不一样,前者在observed region触发,后者在active region触发,因此前者能避免很多竞争冒险。
14.14 global clocking
作用:当指定了global clocking时,可以调用系统函数$global_clock。
语法:在clocking block关键字前面加上global关键字即可,如下:
可以出现的位置:module、interface、program、checker
不能出现的位置:generate block
一个scope里最多只能声明一个global clocking。
当调用$global_clock系统函数时,有一套搜寻global clock的规则如下:
- 首先在当前scope下搜寻,找到了就退出搜寻,找不到且当前scope是top level时,报error,找不到但是当前scope不是top level时,执行第(2)步;
- 在parent level scope下继续搜寻,找到了就退出搜寻,找不到则继续在parent level scope下搜寻,直到top level还找不到就报error。
举三个经典的例子:
例子一:
这个例子中,sub1.common里调用的$global_clock是sub1.sub_sys1;sub2.common里调用的$global_clock是sub2.sub_sys2。
例子二:
这个例子中,sub1.checks里的断言p的$global_clock调用的是sub1.sub_sys1;sub2.checks里的断言p的$global_clock调用的是sub2.sub_sys2
注意这里property里的global clock引用比较特殊,引用的不是property声明时scope下的global clock,而是property被引用的assert所在的scope下的global clock。
例子三:
这个例子中,无论是sub1还是sub2,里面的another_module.t里的$global block引用的都是another_clocking。
14.15 synchronous events
完成同步操作需要一个关键符号:@。同步事件语句有下面几种形式:
14.16 synchronous drives
同步驱动发生在re-NBA region。
可以有intra-delay。