Verilog中延时和时序检查
- 1. 延时模型
- 1.1 分布延迟
- 1.2 集总延迟
- 1.3 路径延迟
- 2. specify 语法
- 2.1 指定路径延时
- 基本路径延时
- 边沿敏感路径延时
- 状态依赖路径延时
- 2.2 时序检查
- $setup, $hold, $setuphold
- $recovery, $removal, $recrem
- $width, $period
- notifier
1. 延时模型
真实的逻辑元器件和它们之间的互连线上都会有延时的存在。虽然 Verilog 设计主要考虑的是逻辑功能的正确性,但是 Verilog 语法是支持定义延时的。
Verilog 中延时模型有三种:分布延迟、集总延迟(lumped) 和路径延迟。
1.1 分布延迟
分布延迟指的是给电路中每个独立的元件进行延迟定义,不同的路径有不同的延时,如下图所示。
对应的 verilog 描述为:
// 例化逻辑门单元的时候指定延时
module and4(output out,input a, b, c, d);wire an1, an2 ;and #1 (an1, a, b);and #2 (an2, c, d);and #1.5 (out, an1, an2);
endmodule// assign 语句中指定延时
module and4(output out,input a, b, c, d);wire an1, an2 ;assign #1 an1 = a & b ;assign #2 an2 = c & d ;assign #1.5 out = an1 & an2 ;
endmodule
1.2 集总延迟
集总延迟是将全部路径累计的延时集中到最后一个门单元上。
到最后一个门单元上的延迟会因路径的不同而不同,此时取最大延时作为最后一个门单元的延时。
将上述分布延迟图转化为集总延迟图,如下所示。
对应的 verilog 描述如下:
module and4(output out,input a, b, c, d);wire an1, an2 ;and (an1, a, b);and (an2, c, d);and #3.5 (out, an1, an2); //set the max delay at the last gate
endmodule
1.3 路径延迟
路径延迟是对每个输入引脚到每个输出引脚的所有路径指定延迟时间。
路径延迟模型需要使用 specify
关键字来定义,上图对应的 verilog 描述如下所示:
module and4(output out,input a, b, c, d);specify(a => out) = 2.5 ;(b => out) = 2.5 ;(c => out) = 3.5 ;(d => out) = 3.5 ;endspecifywire an1, an2 ;and (an1, a, b);and (an2, c, d);and (out, an1, an2);
endmodule
2. specify 语法
Verilog 中的路径延迟使用 specify 块语句来描述,从 specify
为开始,到 endspecify
结束。
specify
是 module 中独立的一部分,不能出现在其他语句块(initial, always 等)中。
specify
块语句的主要功能是:指定所有路径中引脚到引脚的延迟、在电路中设置时序检查。
2.1 指定路径延时
基本路径延时
specify
块语句有两种基本的语法来定义延时:
方法一: 并行连接,每条路径都有一个源引脚和目的引脚,将这些路径的延迟依次用 specify 语句描述出来。
基本语法为:
(<source_io> => <destination_io>) = <delay_value>;
一个带有路径延时的 4 输入与门的 verilog 描述如下:
module and4(output out,input a, b, c, d);specify(a => out) = 2.5 ;(b => out) = 2.5 ;(c => out) = 3.5 ;(d => out) = 3.5 ;endspecifywire an1, an2 ;and (an1, a, b);and (an2, c, d);and (out, an1, an2);
endmodule
在使用 specify
定义路径延时的时候,也可以定义参数,如下所示:
specifyspecparam ab_2_out = 2.5 ;specparam cd_2_out = 3.5 ;(a => out) = ab_2_out ;(b => out) = ab_2_out ;(c => out) = cd_2_out ;(d => out) = cd_2_out ;
endspecify
需要注意的是,specparam
只能在 specify
内部声明及使用,而 parameter
只能在 specify
语句块的外部声明及使用。
在并行连接中,源引脚和目的引脚是一一对应的。并行连接也支持多位宽信号间的路径延迟描述,但是位宽必须保持一致。
module paral_conn(input [3:0] d,output [3:0] q);specify(d => q) = 3 ;endspecifyassign q = d & 0101 ;
endmodule
上例的路径延时定义等价于:
specify(d[0] => q[0]) = 3 ;(d[1] => q[1]) = 3 ;(d[2] => q[2]) = 3 ;(d[3] => q[3]) = 3 ;
endspecify
方法二: 全连接,源引脚中的每一位与目标引脚的每一位相连接。源引脚和目的引脚的连接是组合遍历的,且不要求位宽对应。
基本语法为:
(<multiple_source_io> *> <multiple_destination_io>) = <delay_value> ;
如下所示,4 输入的与逻辑模块的路径延时可以为:
module and4(output out,input a, b, c, d);specify(a,b *> out) = 2.5 ;(c,d *> out) = 3.5 ;endspecifywire an1, an2 ;and (an1, a, b);and (an2, c, d);and (out, an1, an2);
endmodule
边沿敏感路径延时
边沿敏感路径延时用于对时序电路的输入到输出延迟进行建模,需要使用边缘标识符指明触发条件。如果没有指明的话,任何变化都会触发源引脚到目的引脚的延迟值的变化。
示例1:
(posedge clk => (out +: in)) = (1,2);
在 clk 的上升沿,对于从 clk 到 out 的路径,其上升延时是 1,下降延时是 2。+:
的意思是 in 到 out 的数据路径是同向传输,即 out = in。
示例2:
(negedge clk => (out -: in)) = (1,2);
在 clk 的下降沿,对于从 clk 到 out 的路径,其上升延时是 1,下降延时是 2。-:
的意思是 in 到 out 的数据路径是反向传输,即 out = ~in。
示例3:
(negedge clk => (out : in)) = (1,2);
clk 的任何变化,从clk到out的模块路径,其上升延时是1,下降延时是2,从in到out的数据路径的传输是不可预知的,同向或者反向或者不变。
状态依赖路径延时
Verilog 也允许模型中根据信号值的不同,有条件的给路径延迟进行不同的赋值。
一个简单的示例如下所示:
specifyif (a) (a => out) = 2.5 ;if (~a) (a => out) = 1.5 ;if (b & c) (b => out) = 2.5 ;if (!(b & c)) (b => out) = 1.5 ;if ({c, d} == 2'b01)(c,d *> out) = 3.5 ;ifnone (c,d *> out) = 3 ;
endspecify
需要注意的是:
if
语句的操作数可以是标量,也可以是向量,条件表达式也可以包含任意操作符;- 所有输入状态都应该说明,否则没有说明的路径使用分布延时,如果也没有声明分布延时的话,那么使用零延时(zero delay)。如果路径延时和分布延时同时声明的话,则选择最大的延时作为路径延时;
- 可以使用
ifnone
语句,在其它所有条件都不满足的情况下,说明一个缺省的状态依赖路径延时。
2.2 时序检查
使用 specify
指定路径延迟之后,可以让仿真的时序更加接近实际数字电路的时序。
除此之外,specify
还可以定义一些系统任务,用来进行时序检查。
Verilog 中常用的用于时序检查的系统任务包括:$setup, $hold, $recovery, $removal, $widt, $period
,这些系统任务只能在 specify
块中调用。
$setup, $hold, $setuphold
$setup
用来进行建立时间检查,$hold
用来进行保持时间检查,基本语法格式如下:
$setup(data_event, reference_event, limit, notifier);
data_event:被检查的信号,判断它是否违反约束
reference_event:用于检查的参考信号,一般为时钟信号的跳变沿
limit:最小建立时间
当 reference_event time - limit < data_event time < reference_event time 的时候,仿真的时候会打印出 Timing Violation 的报告。
$hold (reference_event, data_event, limit, notifier);
当 reference_event time < data_event time < reference_event time + limit 的时候,仿真的时候会报告 Hold Timing Violation。
这里需要注意的是,$setup
和 $hold
中输入端口的位置是不一样的。setup 检查中,数据要先到,hold 检查中,数据要晚走,所以可以按照事件的事件顺序来记忆。
此外,Verilog 还提供了同时检查setup 和 hold 的系统任务:
$setuphold (reference_event, data_event, setup_limit, hold_limit, notifier)
该系统函数等价于:
$setup(data_event, reference_event, setup_limit, notifier);
$hold (reference_event, data_event, hold_limit, notifier);
当 reference_event time - setup_limit < data_event time < reference_event time + hold_limit 的时候,仿真的时候会报时序违例。
$recovery, $removal, $recrem
对于异步复位的触发器来说,异步复位信号也需要满足 recovery time(恢复时间)和 removal time(去除时间),才能有效的复位和释放复位,防止出现亚稳态。
$recovery, $removal, $recrem
的基于语法如下:
$recovery (reference_event, data_event, limit, notifier);
reference_event:用于检查的参考信号,一般为清零或复位信号跳变沿;
data_event:被检查的信号,一般为时钟信号跳变沿。
limit:设置的最小 removal time。
当data_event time - limit(clk) < reference_event time(async rst) < data_event time(clk) 时,就会报告recovery time violations。
$removal (reference_event, data_event, limit, notifier);
当 data_event time < reference_event time < data_event time + limit时,就会报告removal time violations。
$recrem (reference_event, data_event, recovery_limit, removal_limit, notifier)
$width, $period
有些数字设计,例如 flash 存储器,还需要对脉冲宽度或周期进行检查,为此 Verilog 分别提供了系统任务 $width
和 $period
。用法如下:
$width (ref_event, time_limit, notifier);$period(ref_event, time_limit, notifier);
ref_event:边沿触发事件
time_limit:脉冲的最小宽度
这里data_event是隐含的,它等于reference_event的相反边沿。
$width
用于检查边沿触发事件 ref_event 到下一个反向跳变沿之间的时间,常用于脉冲宽度的检查。如果两次相反跳边沿之间的时间小于 time_limit,则会报告 violation。
$period
用于检查边沿触发事件 ref_event 到下一个同向跳变沿之间的时间,常用于时钟周期的检查。如果两次同向跳边沿之间的时间小于 time_limit,则报告中会打印 violation。
notifier
任意一条 timing check 语句检测到timing violation发生时,对应的 timing check 语句就会把 notifier 的值做一次 toggle。
notifier 的初始默认值是 x,第1次 timing violation 时,notifier 的值会从x变为0或1。后续每发生一次 timing violation,notifier 的值也会被做一次toggle。如果旧值为0,则新值为1。如果旧值为1,则新值为0。
notifier 的 toggle,会导致寄存器的Q端变为 x。
如下图所示,notifier 是 dff 的一个输入端口,当前发生任意跳变的时候,dff 的输出都将变为 x 态。