****以下总结为个人归纳总结,欢迎讨论****
1 几点概念
1.1 仿真单位(timeunit):
意思:当我们的代码中写延时语句时,若不指定时间单位,则使用此单位; 例如: `timescale 1ns/1ps 则 #10 语句表示delay10ns;
*细节点:若指定单位,则仿真工具会转化为当前仿真单位的数值。 此行为可能导致不同timescale作用域之间传参数时,产生预期之外的错误。
例:如下代码,module a 的timescale是1ns/1ps, module b 是1ps/1ps; module b中的clk,频率是由输入参数 t 决定的,在module a中例化 b 时,输入参数接 t1,而t1的值是5us。
那么,我们期望的是inst_b中的clk 是每隔5us 翻转一次,也就是5000 ns翻转一次,然而事实真的如此吗?
答:否,实际上inst_b中的clk 每隔5ns翻转一次。 为何?
因为“变量” 是没有单位这个概念的,在module a中定义的变量 t1=5us ,在a的timescale 1ns/1ps下,会被解析成5000,原因就是*细节点中提到的,“若指定单位,则仿真工具会转化为当前仿真单位的数值”,当前仿真单位是ns,所以"5us"会被解析成"5000"这个“数”。
然后,会把5000这个数,传给inst_b, 相当于module b中的forever语句变成: forever #5000 clk = ~clk; 然而,module b的仿真单位是1ps,故最终结果是b中的clk,每隔5000ps 翻转一次,也就是5ns 翻转一次。
1.2 仿真精度(timeprecision)
意思:延时语句中小于此精度的数,采取四舍五入的方式。 例如:`timescale 1ns\100ps ,则 #1.46 语句 表示delay 1.5ns
*细节点1:与timeunit结合,
*细节点2:谨慎使用除法!!!
例:如下写法: initial begin clk = 0 ; forever #(500/500ns) clk = ~clk;end
在timescale 1ns/1ps中,结果是clk每隔1ns 翻转一次;
在timescale 1ps/1ps中,结果是仿真进入死循环,在0时刻挂死。
在timescale 1ps/1fs中,结果是clk每隔1fs 翻转一次。
原因:1. 先涉及一个表达式的问题,上面这个除法表达式,仿真器会翻译成 #(500/(500ns)),ns单位紧跟前面的数字运算优先级最高。
2.与timeunit 结合来看,分子的500由于没带单位,那单位就是timeunit,分母则会按进制转换为当前timeunit的数字,在timeunit=1ps时,500ns就是500000。 (这一条可以解释 1ps/1fs时clk的情况:500/500000 = 0.001ps=1fs)
3.结合精度来看,若timescale为1ps/1ps,500/500000=0.001ps ,但是精度只有1ps,所以相当于0ps; 原表达式结果为: initial begin clk = 0 ; forever #0 clk = ~clk;end ,forever后面延时0,仿真进入死循环
2 timescale的作用域
2.1 定义timescale的4种方式与其作用域
2.1.1 定义timescale的4种方式:
(1)在编译命令中定义默认timescale:
(2)在module内部定义timescale:
(3)使用`timescale宏,写在module外部:
(4)使用`timescale宏,写在module内部:
2.1.2 以上4种写法的作用域
先说结论:
- 第(1)种写法定义仿真默认timescale,作用域按编译顺序,作用于所有module,直到遇见新的timescale;
- 第(2)种写法定义的timescale只作用于当前module,对其他module无影响;
- 第(3)种写法定义的timescale作用于此语句下面的所有module,直到遇见新的timescale;
- 第(4)种写法定义的timescale不作用于当前module,作用于按编译顺序的下一个module以及之后的module,直到遇见新的timescale;
*其中第(4)种写法为易错点,需注意这种写法定义的timescale对当前module是不生效的,而会对下一个编译的module生效;