关于常量和参数的定义
1.0 Verilog常量
定义常量的方法有两种:
- parameter xx = yy;
parameter 作用于声明的那个文件; - `define XX YY
define 从编译器读到这条指令开始到编译结束都有效,或者遇到undef命令使之失效。
如果想让parameter或define作用于整个项目,可以将如下声明写于单独文件,并用include让每个文件都包含声明文件:
`ifndef xx
`define xx yy // or parameter xx = yy;
`endif
状态机的定义可以用parameter 定义,但是不推荐使用`define 宏定义的方式,因为’define 宏定义在编译时自动替换整个设计中所定义的宏,而parameter 仅仅定义模块内部的参数,定义的参数不会与模块外的其他状态机混淆。
parameter 和 local param
parameter可用作在顶层模块中例化底层模块时传递参数的接口,localparam的作用域仅仅限于当前module,不能作为参数传递的接口。
define,是宏定义,全局有效。则在整个工程都是有效
parameter,参数,可以由调用者修改参数值。
localparam,本地参数,调用者不可修改。
2.0 parameter
如例1和例2所示,是Parameter在Verilog-1995和Verilog-2001版的使用方法。
3.0 Parameter 重定义
3.1 Verilog-1995
有两种方法可以更改部分或全部实例化模块的参数;
- 实例化本身中的参数重定义,
- 单独的defparam语句。
3.1.1 使用#重新定义参数(1)
在示例3中,将来自示例1的寄存器的两个副本实例化到two_regs1模块中。 两个实例的SIZE参数由与寄存器实例化本身相同的行上的#(16)参数重定义值设置为16。
多年来,所有综合工具都支持这种形式的参数重新定义。
此类参数重新定义的最大问题是参数必须按照它们在实例化的模块中出现的顺序传递给实例化模块。
3.1.2 使用#重新定义参数(2)
示例4的myreg模块有四个参数,如果模块在实例化时要求只更改第三个参数(例如SIZE参数),则不能使用一系列逗号后跟新值来实例化模块 对于SIZE参数,如例5所示。这将是语法错误。
为了在实例化模块时使用参数重定义语法,必须在实例化中列出所有参数值(包括所有已更改的值)。 对于示例4的myreg模块,必须列出前两个参数值,即使它们没有更改,然后是SIZE参数的新值,如例6所示。
意识到这种限制,工程师经常重新排列参数的顺序,以确保最常用的参数首先放在模块中,类似于Thomas和Moorby [4]描述的技术。
尽管Verilog-1995参数重新定义存在局限性,但它仍然是修改实例化模块参数的最佳支持和最干净的方法。
3.1.3 Defparam语句
语法:defparam path_name = value ;
defparam语句在编译时可重新定义参数值.
但是一般情况下这个语句是不可综合的.
所以不要使用defparam语句!
在模块的实例引用时可用"#"号后跟参数的语法来重新定义参数.
3.2 Verilog-2001
Verilog-2001通过在实例化本身中使用命名参数传递,添加了第三种更好的方法来更改实例化模块的参数。
Verilog-2001标准中增加的增强功能是能够在实例化本身中实例化具有命名参数的模块。
如例10.
这种新技术提供了明确指示修改哪个参数(如defparam语句)的优点,并且还将参数值方便地放入实例化语法中,如Verilog-1995#parameter redefinition。
这是从任何供应商实例化模型的最简洁方法,这是一种应该由可重用模型的设计者和供应商鼓励的技术。
因为所有参数信息都包含在模型的实例化中,所以这种编码风格也最容易被供应商和内部工具解析。
使用新的Verilog-2001命名参数重定义技术完成所有参数传递!
4.0 `define
`define指令用于执行“全局”宏替换,类似于C语言#define指令。
define 从编译器读到这条指令开始到编译结束都有效,或者遇到`undef编译器指令然后停止。
宏定义可以存在于模块声明的内部或外部,并且两者都被视为相同。
参数声明只能在模块边界内进行。
由于宏是为宏定义之后读取的所有文件定义的,因此使用宏定义通常会使编译依赖于设计文件顺序。
与使用宏定义相关的典型问题是另一个文件也可能对同一宏名称进行宏定义。发生这种情况时,Verilog编译器会发出与“macro redefinition”相关的警告,但是未经注意的警告对设计或调试工作来说可能代价高昂。
为什么重新定义宏是不好的? Verilog语言允许对标识符进行分层引用。
这证明对于探测和调试设计非常有价值。 如果在设计中为同一个宏名称提供了多个定义,则只有最后一个定义可供测试平台用于探测和调试。
所以当需要对一个宏名称进行多个定义时,可以考虑使用Parameter进行定义。
4.1 用法
准则1:仅对这明确要求在设计的其他地方不会修改的标识符使用宏定义.
准则2:不要使用宏定义来定义模块本地的常量。
指南:在可能的情况下,将所有宏定义放入一个“definitions.vh”文件中,并在编译设计时首先读取文件。
备用指南:将所有宏定义放在顶级测试平台模块中,并在编译设计时首先阅读此模块。
在编译设计时首先读取所有宏定义,确保宏在需要时存在,并且它们对设计中编译的所有文件全局可用。
注意有关宏重新定义的警告。
4.2 `define Inclusion
在使用之前确保宏定义存在的一种流行技术是使用
`ifdef
`ifndef(新的Verilog-2001)
编译器指令来查询带有
`define
`include
的全局宏定义。
4.3`undef编译器指令
`undef是用来删除全局定义的
使用`define compiler指令在适当的地方创建全局宏非常有用。
对于重新定义宏可能有意义的罕见情况,请在同一文件中以及定义宏文件末尾使用`undef。
确保最后编译的宏定义可能是您可能希望在testbench访问的宏,因为在运行时调试期间只能存在一个宏定义。
同样,使用define-`undef对应该被认为是问题的最后手段,可能使用更好的方法更好地处理。
4.4时钟周期使用宏定义
准则:使用`define编译器指令进行时钟周期定义。
准则:将时钟周期定义放在“definitions.vh”文件或testbench的顶层模块中。
原因:时钟周期是设计和testbench的基本常量。公共时钟信号的周期不应该从一个模块改变到另一个模块;周期应该是恒定的!
工程师在testbench的时钟边缘上进行大多数刺激生成和验证测试。
通常,这种类型的testbench可以很好地适应全局时钟周期定义的变化。
4.5 状态机不使用`define
限状态机(FSM)设计应使用参数来定义状态名称,因为状态名称是仅适用于FSM模块的常量。
如果将多个状态机添加到大型设计中,则需要在多个FSM设计中重用某些状态名称并不罕见。多个设计共有的示例状态名称包括:RESET,IDLE,READY,READ,WRITE,ERROR和DONE。
使用`define来分配状态名称会阻止重用状态名称,因为名称已经在全局名称空间中使用,或者必须在模块之间取消地址名称并在新FSM中重新定义状态名称模块。
准则:不要使用`定义状态名称的宏定义来进行状态分配。
指南:使用带符号状态名称的参数进行状态分配。
5.0 Verilog-2001 localparam
Verilog-2001标准的增强功能是本地参数。
与parameter不同,localparam不能通过参数重定义(位置或命名重定义)来修改,也不能通过defparam语句重新定义localparam。
localparam可以根据参数来定义,这些参数可以通过参数的位置重定义,命名参数重定义(首选)或defparam语句重新定义。
localparam背后的想法是允许基于其他参数生成一些本地参数值,同时保护本地参数不被最终用户意外或不正确地重新定义。
在例13中,存储器阵列mem的大小应该从地址总线的大小产生。通过将MEM_DEPTH放在localparam声明中,可以“保护”内存深度大小MEM_DEPTH不受错误设置的影响。 MEM_DEPTH参数仅在修改ASIZE参数时才会更改。
我们希望保护本地MEM_DEPTH参数,并根据地址总线的大小参数值进行计算。
注意:Verilog-2001标准不会将localparam功能扩展到模块头参数列表。 具体来说,localparam当前无法添加到ANSI样式的参数列表中,如例14所示。
6.0 `timescale 定义
`timescale指令给出了可能出现在Verilog模型中的延迟的含义。 时间刻度位于模块标题上方,采用以下形式:
`timescale指令会对大多数Verilog仿真器的性能产生巨大影响。 选择time_precision为1ps是一个常见的新用户错误,以便考虑设计中的每个最后皮秒。
将1ps精度添加到使用1ns或100ps time_precisions充分建模的模型可以将模拟时间增加100%以上,模拟内存使用量增加150%以上。
例子15表明了`timescale 的用法。
全局更改设计中每个时间刻度的time_units会对整个设计的完整性产生负面影响。
任何包含#delays的设计都依赖于`timescale指令中指定的time_units的准确性
在示例16中,模型要求`时间刻度的time_units以100ps为单位。若将time_units更改为1ns,会将延迟从160ps更改为1.6ns,从而在模型中引入错误。
由于time_precision必须始终等于或小于timescale指令中的time_unit,因此如果采用全局timescale,则应该遵循其他指导原则:
准则:使用户定义的`timecales的所有time_units等于或大于1ns。
原因:如果在任何模型中使用较小的time_unit,则将所有时间精度全局更改为1ns将破坏现有设计。
注意:如果模拟中包含供应商模型,并且供应商在其模型中使用了非常小的时间精度,则整个模拟将减慢,并且通过全局更改用户模型的时间精度将完成很少的操作。
为了提高模拟器性能,使用单位延迟模拟模式或使用基于循环的模拟器比宏观生成设计中的所有“timescale”更好。
7.0 总结
指南:不要在任何Verilog设计中使用defparams。
准则:要求使用新的Verilog-2001命名参数重定义技术完成所有参数传递。
准则:仅对标识符使用宏定义,这些标识符明确要求在设计的其他地方不会修改的标识符的全局定义。
指南:在可能的情况下,将所有宏定义放入一个“definitions.vh”文件中,并在编译设计时首先读取文件。
备用指南:将所有宏定义放在testbench的顶层模块中,并在编译设计时首先阅读此模块。
准则:不要使用宏定义来定义模块本地的常量。
准则:使用define compiler指令进行时钟周期定义。
准则:将时钟周期定义放在“definitions.vh”文件或testbench的顶层模块中。
准则:不要使用`定义状态名称的宏定义来进行状态分配。
指南:使用带符号状态名称的参数进行状态分配。
指南:为了提高仿真效率,使用户定义的所有time_units的时间尺度等于或等于1ns。