在验证仿真过程中,很多人遇到过timescale的问题,其中典型的场景是这样的:“验证环境里加了一个#200ns的延时,希望通过这样带有明确单位的延时,避免timescale设置不同而导致不同的结果;可是奇妙是就是在设置timescale为1ns/1ps和100ps/1ps时,实际的延迟就成了200ns和2000ns,而把#200ns改为#200后,反而无论怎么改timescale,实际延迟都是200ns。”(此案例使用的是vcs 2013)。
本文将详细分析timescale,复现此案例,同时说明如何正确使用timescale。
1 仿真时间的相对性和绝对性
仿真时间是由一系列延时累积起来的。
1.1 延时
没有单位的延时,比如#100,是相对的,实际延时取决于当前时间单位;有单位的延时,比如#100ns,则是绝对的。假设当前时间单位是1ns,则#100ns等价于#100;假设当前时间单位是100ps,则#100ns等价于#1000。
延时除了单位,还需要精度,它们分别对应time_unit和time_precision。对于1ns/1ps的time_unit/time_precision设置,#100.1234会被四舍五入为#100.123。
1.2 仿真时间
我们可以通过波形测量和log打印两种方式得到仿真时间,波形测量相对简单,这里只讨论log打印。
有三个系统函数可以得到当前仿真时间:$realtime返回64bit real类型时间数值,$time返回64bit整数,$stime返回32bt无符号整数。$time和$stime会对小数进行四舍五入,鉴于现在时钟频率越来越高,验证环境中只建议使用$realtime。
这三个函数都会按照当前的时间单位按比例缩放,这句话不好理解。换种说法,它们返回的都是延时相对值的累积。
比如对于下面代码(module、program、packages或interface中):
realtime vr_t
#100;
.….
#20.123ns;
vr_t=$realtime;
如果当前时间单位/精度是1ns/1ps,则vr_t=100+20.123=120.123(单位为ns);
如果当前时间单位/精度是100ps/10ps,则首先根据时间精度10ps将#20.123ns四舍五入为#20.12ns,再根据时间单位100ps换算为相对值#201.2,最后得到vr_t=100+201.2=301.2(单位为100ps)。
这就是仿真时间的相对性。相对仿真时间对我们来说意义不大,因为不同模块/线程的时间单位/精度可能是不一样的。为得到仿真时间的绝对值,就需要$timeformat和%t出场了;$timeformat影响%t和交互式输入的延时单位,我们只讨论使用%t打印时的情况。
SV标准中$timeformat的定义如图:
$timeformat四个参数分别对应%t的打印单位、精度、后缀和打印的最小位宽。
典型设置如:$timeformat(-9, 3, “”, 5)
那么此后的%t打印,将以ns为单位,小数点后3位的精度打印仿真时间。
回到上面的例子,当我们用:
$display(“time=%f”,vr_t);
打印时,会得到120.123(单位/精度是1ns/1ps)和301.200(时间单位/精度是100ps/10ps),这是相对时间,因为它们的单位不同。而当我们用
$display(“time=%t”,vr_t);
打印时,会得到120.123(单位/精度是1ns/1ps)和30.120(时间单位/精度是100ps/10ps),这是绝对时间,它们的单位都已被换算为ns。
用上述的$timeformat设置,再看两个例子(还是在module、program、package或interface中):
上结果无论对于vcs还是xrun都是一样的。
整个仿真系统中,必须使用单一的时间单位,比如ns,来打印各模块/线程的绝对仿真时间,这样才不会造成时间错乱。因此需要正确设置$timeformat和%t,为仿真时间找一把尺子,而不是松紧带。遇到延时问题,先测试尺子准不准。测试尺子的标准就是绝对延迟,比如#100ns,如果确实延迟了100ns,尺子就没有问题(注是在module、program、package或interface中测试)。
$timeformat通常在公共打印组件中已经被设置好。如果$timeforma没有被调用过,这时%t打印的时间单位是所有timescale中的最小time precision,而不是time unit。
2 正确设置时间单位和精度
前面反复提到了时间单位和精度,在了解如何设置它们之前,首先要说一下compilation-unit scope。
2.1 compilation-unit scope
compilation-unit:一个或多个SV文件编译到一起的合集;compilation-unit scope就是这个合集的范围,通过$unit::访问compilation-unit scope中的元素。要更详细地解释compilation-unit scope有点难,因为SV标准说它是工具决定的。简单的可以认为除了module、program、package、interface、checker、primitive之外的SV文件,都会编译到一个compilation-unit scope中,比如全局的class、task、function和全局变量等,展开Verdi Declaration中的$root,就可以比较清晰的看到这种层次关系。
2.2 关键字timeunit/timeprecision
SV标准要求关键字timeunit/timeprecision只能定义在module、program、package、interface和compilation-unit scope中,并且必须定义在最前面。下面的使用是合法的:
module A (…);
timeunit 1ns;
timeprecision 1ps;
……
endmodule
下面的使用是非法的,不能在class/task/function等中:
class A;
timeunit 1ns;
timeprecision 1ps;
endclass
下面的使用,如果A是除module、program、package、inteface外,第一个编译的SV文件,则是合法的,否则是非法的。
timeunit 1ns;
timeprecision 1ps;
classA
.……
endclass
2.3 编译指令`timescale
按照编译顺序,`timescale指定之后,下一个`timescale之前,不含有关键字timeunit/timeprecision的module、program、package和interface的时间单位和精度。
使用方式是`timescale time_unit/time_precision,例如:
`timescale 1ns/10ps
module A (…)
timeunit 100ps;
timeprecision 1ps;
endmodule
module B (…);
endmodule
`timescale 1ps/lps
module C (…);
endmodule
module A的时间单位/精度是100ps/1ps,module B的时间单位/精度是1ns/10ps,module C的时间单位/精度是1ps/1ps。
`timescale没有作用域的概念,比如:
`timescale 1ns/10ps
module B (…);
`timescale 1ps/1ps
endmodule
module C (…);
endmodule
module B使用的是,在它之前离它最近的`timescale,因此时间单位/精度是1ns/10ps;而module C的时间单位/精度取离它最近的`timescale,即1ps/1ps。
2.4 编译选项-timescale
在vcs编译选项中添加-timescale=time_unit/time_precision(对于xrun是-timescale time_unit/time_precision),该选项指定仿真默认的时间单位/精度,等价于第一个`timescale。此后代码中出现的`timescale将会替换-timescale。
如里没有添加编译选项-timescale,代码中也没有出现任何`timescale,则vcs默认时间单位/精度是1s/1s,xrun默认时间单位/精度是1ns/1ns。
SV不允许有的横块有timescale,有的没有timescale,比如下面的代码:
module B (…)
endmodule
`timescale 1ps/1ps;
module C (…);
endmodule
如果编译选项没有添加-timescale,则是非法的。如果添加了-timescale=1ns/1ps,mooule B的时间单位/精度是1ns/1ps,moodule C的时间单位/精度是1ps/1ps。
2.5 编译选项-unit_timescale(仅vcs)
SV标准要求compilation-unit scope仅受关键字timeunit/timeprecision控制,`timescale不起作用,在没有timeunit/timeprecision时,compilation-unit scope使用默认的时间单位/精度。
为此VCS提供了-unit_timescale编译选项,compilation-unit scope的时间单位/精度优先级为:
a)当-unit_timescale和关键字timeunit/timeprecision同时存在时,使用关键字timeunit/timeprecision指定的时间单位/精度,无论-unit_timescale等于什么;
b)只有-unit_timescale选项时,使用-unit_timescale指定的时间单位/精度;
c)没有-unit_timescale选项时,无论是否存在关键字timeunit/timeprecision,都使用最后一个被使用的`timescale指定的时间单位/精度,这条与SV标准稍有区别。例如:
注意如果最后一个`timescale没有被module、program、package或interface使用,那么它将对compilation-unit scope不起作用,例如无-unit_timescale时:
`timescale 100ps/1ps
class A;
endclass
`timescale 10ps/1ps
module B(…);
endmodule
`timescale 1ns/1ps
class A的时间单位/精度是10ps/1ps,而不是1ns/1ps。
2.6 编译选项-override timescale
该选项强制覆盖所有的timescale。对于使用相对延时#xxx的模块,可以利用`timescale巧妙地增加或减小延迟时间,而不用修改任何代码,库文件和interface中可能会有这样的用法。如果使用了-overide_timescale,即使仿真结果对了,也是不真实的,因此应禁止使用。
2.7 打印timescale
有三种方式打印timescale(后两种仅限vcs),以方便调试:
- 系统任务$printtimescale,打印当前模块的timescale;
- 编译选项-diag timescale,加入该选项后,所有module、program、package和interface的timescale都会打印在编译log中;
- 编译选项-Xman=12或28,加入该选项后,所有代码会被打印到token.v文件中,宏代码会展开:该选项本来为了方便宏调试,但它同时也会在每个module、program、package、interface和compilation-unit scope前,打印它们的timescale对应的文件和代码行号,比-diag timescale更进一步。
特别的,VCS在编译时,编译log也会打印出一个Timescale,它的时间单位/精度,分别对应整个系统中出现过的最小时间单位/精度,而并非具体哪一个`timescale。
3 compilation-unit scope特殊之处(vcs)
前面代码例子,都注明了是“mdule、program、package或interface”中的代码,这是因为vcs(实验较早,当时用的版本是2013.06)对compilation-unit scope的处理有特殊的地方。
如果编译时没有加-unit_timescale,那么compilation-unit scope中,绝对延时(比如#200ns)并不遵守上面的分析;相对延时(比如#200)依然遵守。
3.1 compilation-unit scope中的绝对延时
假设编译选项-timescale=10ns/1ps,下面的代码:
class A
task aa;
$printtimescale;
harness.a=0;
#200ns;
harness.a=1;
$display("%0t”,$realtime);
endtask
endclass
`timescale 100ps/1ps
module harmess(…);
bit a;
endmodule
$printtimescale打印出的timescale是100ps/1ps,没有问题。但是对于#200ns,vcs很神奇的先根据#200ns之前,高它最近的`timescale进行一次转换(如果在此之前没出现过`timescale,则使用-timescale),再使用默认的`timescale进行第二次转换。
比如此例中,编译选项中的-timescale=10ns/1ps,将它换算为#20。然后,再根据前文分析的compilation-unit scope的要默认时间单位/精度100ps/1ps,将#20换算为#2ns。
3.2 复现本文开头的例子
构造代码如下:
class A;
task aa;
#200ns;
$dlisplay("aa: %0t, $realtime);
endtask
task bb;
#200;
$display(“bb: %0t; $realime);
endtask
endclass
`timescale 1ns/1ps
module harness(…);
endmodule
- 对于#200ns:
当-timescale=1ns/1ps时,由于默认时间单位/精度也是1ns/1ps,因此延时200ns;
当-timescale=100ps/1ps时,#200ns首先转换为#2000,再按照1ns/1ps的默认时间单位精度,转换为2000ns,因此无论使用%t打印还是波形直接测量,都是延时了2000ns。
- 对于#200:
遵循前文分析,不受-timescale选项影响,直接使用默认时间单位/精度也是1ns/1ps,因此无论-timescale=1ns/1ps还是100ps/1ps时,都延时200ns。
注:本文初稿写的较早,因此不确定2013以后的vcs行为是否依然如此。由于-unit_timescale已经能够完美解决问题,所以也就没有再重复实验确认了。
4 timescale使用建议
- 关键字timeunit/timeprecision只使用在需要不受`timescale影响的module、program、package或interface的内部;
- 用vcs编译时应同时添加-timescale和-unit_timescale;对于xrun有更多的timescale选项,建议参考手册;但无论vcs还是xrun,都不应使用-override_timescale;
- 验证环境顶层module声明前应设置`timescale,且和编译选项保持一致:
- 如果rtl代码在非阻塞赋值前加了#DELAY延迟,则建议在代码最前面设置`timescale,以防止库文件篡改`timescale
- 项目组统一设置$timeformat,并使用%t打印仿真时间,至于#100和#100ns,如果能够按照上面的建议,将timescale正确设置,比如常用的1ns/1ps,那么#100和#100ns就没有区别。
————————————————
版权声明:本文为CSDN博主「木头坛子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_47782224/article/details/121365738