参考资料:IEEE Std 1800-2023标准《IEEE Standard for SystemVerilog — Unified Hardware Design, Specification, and Verification Language》
对芯片验证感兴趣,可以关注微信公众号:芯片设计验证指南
文章目录
6.20 Constants
常量是永不改变的命名数据对象。SystemVerilog 提供三种编译时常量:parameter
、localparam
和 specparam
;同时提供运行时常量 const
(详见 6.20.6 节)。
parameter
、localparam
和 specparam
常量统称为参数常量。
参数常量可以用字面量初始化。
localparam byte colon1 = ":" ;
specparam delay = 10 ; // specparams are used for specify blocks
parameter logic flag = 1 ;
SystemVerilog 提供了多种设置参数常量值的方法:每个参数在声明时可被赋予默认值;对于已实例化的模块、接口或程序中参数的值,可通过以下方式之一在每个实例中被覆盖:
- 有序列表赋值(例如,
m # (value, value) u1 (…);
)(见23.10.2.1节) - 按名赋值(例如,
m #(.param1(value), .param2(value)) u1 (...);
) (见23.10.2.2节) defparam
语句,使用分层路径名重新定义每个参数(见23.10.1节)
6.20.1 Parameter declaration syntax
local_parameter_declaration ::=
localparam data_type_or_implicit list_of_param_assignments
| localparam type_parameter_declaration
parameter_declaration ::=
parameter data_type_or_implicit list_of_param_assignments
| parameter type_parameter_declaration
type_parameter_declaration ::= type [ forward_type ] list_of_type_assignments
specparam_declaration ::= specparam [ packed_dimension ] list_of_specparam_assignments ;
data_type_or_implicit ::=
data_type
| implicit_data_type
implicit_data_type ::= [ signing ] { packed_dimension }
forward_type ::= enum | struct | union | class | interface class
list_of_param_assignments ::= param_assignment { , param_assignment }
list_of_specparam_assignments ::= specparam_assignment { , specparam_assignment }
list_of_type_assignments ::= type_assignment { , type_assignment }
param_assignment ::=
parameter_identifier { variable_dimension } [ = constant_param_expression ]
specparam_assignment ::=
specparam_identifier = constant_mintypmax_expression
| pulse_control_specparam
type_assignment ::= type_identifier [ = data_type_or_incomplete_class_scoped_type ]
parameter_port_list ::=
# ( list_of_param_assignments { , parameter_port_declaration } )
| # ( parameter_port_declaration { , parameter_port_declaration } )
| #( )
parameter_port_declaration ::=
parameter_declaration
| local_parameter_declaration
| data_type list_of_param_assignments
| type_parameter_declaration
list_of_param_assignments 可出现在模块、接口、程序、类或包中,亦可出现在模块(参见 23.2 节)、接口、程序或类的 parameter_port_list 中。若某设计单元声明使用了参数端口列表(即使为空列表),则在该声明内部直接包含的任何 parameter_declaration 中,parameter 关键字应为 localparam 关键字的同义词(见 6.20.4 节);类体中出现的所有 param_assignments 均成为localparam 声明,无论 parameter_port_list 存在与否;生成块、包或编译单元作用域内出现的所有param_assignments 均成为 localparam 声明。
参数端口列表中可以省略 parameter 关键字。例如:
class vector #(size = 1); // size is a parameter in a parameter port list
logic [size-1:0] v;
endclass
interface simple_bus #(AWIDTH = 64, type T = word) // parameter port list
(input logic clk) ; // port list
...
endinterface
在参数常量列表中,参数可依赖于先前声明的参数。在下列声明中,第二个参数的默认值取决于第一个参数的值,第三个参数为类型声明,而第四个参数则是该类型的值。
module mc #(int N = 5, M = N*16, type T = int, T x = 0)
( ... );
...
endmodule
在参数端口列表中声明参数时可省略其默认值的指定,此时该参数将无默认值。若设计元素的参数未指定默认值,则在该设计元素的每次实例化中必须指定覆盖参数值(参见 23.10 节),且工具不得隐式实例化该设计元素(参见 23.10 节)。此外,若设计元素的参数未指定默认值,工具不得隐式实例化该设计元素(参见 23.3、23.4 和 24.3 节)。若类的参数未指定默认值,则在该类的每次特化中必须指定覆盖参数值(参见 8.25 节)。
class Mem #(type T, int size);
T words[size];
...
endclass
typedef Mem#(byte, 1024) Kbyte;
6.20.2 Value parameters
参数常量可包含类型规格说明和范围规格说明,参数的类别与范围应遵循以下规则:
- 未指定类型或范围的参数声明,其类型和范围应默认为应用任何值覆盖后赋予该参数的最终值的类型和范围:若表达式为实数,则参数为实数类型;若表达式为整型,则参数将默认为与表达式大小相同的逻辑向量,其范围为 [size-1:0]。
- 具有范围说明但无类型说明的参数,其范围应遵循参数声明中指定的范围且为无符号类型;该符号和范围不受值覆盖的影响。
- 具有类型说明但无范围说明的参数应为指定类型;有符号参数在应用任何值覆盖后,其范围应默认为赋予该参数的最终值的范围。
- 具有有符号类型说明及范围说明的参数应为有符号类型,且范围应遵循其声明;该符号和范围不受值覆盖的影响。
- 无范围说明且具有有符号类型说明或未指定类型的参数,其隐含范围的LSB(最低有效位)应为0,MSB(最高有效位)应比赋予该参数的最终值的位数少一。
- 对于无范围说明、具有有符号类型说明或未指定类型,且赋予其的最终值为非定宽的参数,其隐含范围的LSB(最低有效位)应为0,MSB(最高有效位)应为至少31的实现相关值。
在向具有显式类型声明的参数进行赋值或覆盖时,右侧表达式的类型应与声明类型保持赋值兼容性(参见 6.22.3 节)。
6.12.1 节所述的实数与整数值间的转换规则同样适用于参数。
允许对整型参数进行位选(bit-select)和片选(part-select)(参见 6.11.1 节)。
值参数(parameter
、localparam
或 specparam
)仅可被设置为字面量表达式、值参数或局部参数、genvar 变量、枚举名称、或其常量函数。允许包引用,但禁止层次化名称。specparam
还可被设置为包含一个或多个 specparam
的表达式。
parameter msb = 7; // defines msb as a constant value 7
parameter e = 25, f = 9; // defines two constant numbers
parameter r = 5.7; // declares r as a real parameter
parameter byte_size = 8,
byte_mask = byte_size - 1;
parameter average_delay = (r + f) / 2;
parameter signed [3:0] mux_selector = 0;
parameter real r1 = 3.5e17;
parameter p1 = 13'h7e;
parameter [31:0] dec_const = 1'b1; // value converted to 32 bits
parameter newconst = 3'h4; // implied range of [2:0]
parameter newconst = 4
参数亦可声明为聚合类型(如非合并数组或非合并结构体);聚合参数应整体赋值或覆盖,其单个成员不可单独赋值或覆盖;然而,聚合参数的单个成员可用于表达式中。例如:
parameter logic [31:0] P1 [3:0] = '{ 1, 2, 3, 4 } ; // unpacked array
// parameter declaration
initial begin
if (P1[2][7:0]) ... // use part-select of individual element of the array
...
6.20.3 Type parameters
类型参数(type parameter)允许模块(module)、接口(interface)或程序(program)拥有可为每个实例自定义类型的端口或数据对象。
module ma #(parameter p1 = 1, parameter type p2 = shortint)
(input logic [p1:0] i, output logic [p1:0] o);
p2 j = 0; // type of j is set by a parameter, (shortint unless redefined)
always @(i) begin
o = i;
j++;
end
endmodule
module mb;
logic [3:0] i,o;
ma #(.p1(3), .p2(int)) u1(i,o); //redefines p2 to a type of int
endmodule
数据类型参数(parameter type
)只能被设置为某种数据类型。允许引用包,但不允许使用层次化名称。
使用 defparam
语句覆盖类型参数的行为属于非法操作。
类似于前向类型定义声明(参见章节 6.18),类型参数声明可通过在标识符前添加基础数据类型关键字(enum
、struct
、union
、class
或 interface
class
)来限定其有效类型范围:
type enum parameter_identifier
type struct parameter_identifier
type union parameter_identifier
type class parameter_identifier
type interface class parameter_identifier
若为类型参数分配的类型定义不符合指定的基础数据类型,则属于错误。
尽管类型参数可能解析为类类型,但使用类作用域解析运算符(参见 8.23)选择带此前缀的类型应仅限于以下三种情形:类型定义声明(参见 6.18)、类型操作符(参见 6.23)以及类型参数赋值。若此前缀无法解析为类,则属于错误。
class P#(type C);
C::T x; // Illegal, C is an incomplete type
localparam type C_t = C::T; // Legal, reference to C::T is made
C_t y; // by parameter assignment
endclass : P
class X;
typedef int T;
endclass : X
typedef P#(X) P_X;
6.20.4 Local parameters (localparam)
局部参数与参数完全相同,区别在于:局部参数无法直接通过 defparam
语句(参见 23.10.1)或实例参数值赋值(参见 23.10.2)进行修改;局部参数允许被赋予包含参数的常量表达式(参见 11.2.1);这些被引用的参数自身可通过 defparam
语句或实例参数值赋值进行修改。
局部参数与非局部参数不同,其允许在以下作用域中声明:生成块、包、类主体、编译单元作用域。 在这些上下文中,parameter
关键字应视为 localparam
关键字的同义词。
局部参数可在模块的parameter_port_list中声明。在此类列表中,位于 localparam
关键字之后、下一个 parameter
关键字之前(若无后续 parameter
关键字,则至列表结束)的任何参数声明均视为局部参数。列表中所有其他参数声明均属于非局部参数,此类参数可按照章节 23.10 所述方式被覆盖。
6.20.5 Specify parameters
关键字 specparam
声明了一种特殊类型的参数,该参数专门用于提供时序和延迟值,它可以出现在任何未被赋值给参数且不作为声明范围规格组成部分的表达式中。指定参数(也称为 specparams
)既允许出现在指定块(参见第 30 章)内,也允许出现在主模块主体中。
在指定块外部声明的指定参数必须在被引用之前进行声明;分配给指定参数的值可以是任何常量表达式;指定参数可以作为后续指定参数声明的常量表达式的一部分使用;与参数常量不同,指定参数不能从语言内部修改,但可以通过SDF标注进行修改(参见第32章)。
指定参数与参数常量不可互换使用;此外,不允许将参数(parameter)和本地参数(localparam)赋值为包含任何指定参数的常量表达式;表6-11总结了两类参数声明的差异。
指定参数可以包含范围规格;指定参数的范围应遵循以下规则:
- 未声明范围规格的
specparam
声明将默认采用参数最终赋值的范围(在应用所有值覆盖之后); - 带范围规格的
specparam
声明应使用该参数声明的范围,且该范围不受值覆盖的影响。
specify
specparam tRise_clk_q = 150, tFall_clk_q = 200;
specparam tRise_control = 40, tFall_control = 50;
endspecify
在关键字specify
和endspecify
之间的代码行声明了四个指定参数:第一行声明了名为 tRise_clk_q 和 tFall_clk_q 的指定参数,其值分别为150和200;第二行声明了名为 tRise_control 和 tFall_control 的指定参数,其值分别为40和50。
module RAM16GEN ( output [7:0] DOUT,
input [7:0] DIN,
input [5:0] ADR,
input WE, CE);
specparam dhold = 1.0;
specparam ddly = 1.0;
parameter width = 1;
parameter regsize = dhold + 1.0; // Illegal - cannot assign
// specparams to parameters
endmodule
6.20.6 Const constants
关键字 const
形式的常量与 localparam
常量的区别在于:localparam
必须在编译阶段设置,而 const
可以在仿真阶段设置(例如在动态任务中)。
使用 const
关键字声明的静态常量可被赋值为字面值、参数、本地参数、genvars、枚举命名、基于这些元素的常量函数或其他常量的表达式;由于 const
关键字声明的常量在编译阶段后计算,因此允许使用层级路径名。
const logic option = a.b.c;
使用 const
关键字声明的自动常量可被赋值为任何在省略 const
关键字时合法的表达式;类的实例(即对象句柄)也可用 const
关键字声明。
const class_name object = new(5,3);
换句话说,该对象的行为类似于一个无法写入的变量。除声明为const
的成员外,该对象的其余成员仍可被写入。
6.20.7 $ as a constant
在特定上下文中,符号 $
代表一个特殊常量;其含义取决于具体语境。
$
符号仅在以下特定语境中使用,其含义对应如下:
- 在非合并数组声明中,
[$]
表示该数组维度为队列类型(参见 7.10 节)。例如byte q1[$];
声明了一个字节队列; - 在队列选择表达式中,
$
表示队列的末位元素(例如q[$]
,参见 7.10 节); - 在形如
[$:expression]
或[expression:$]
的值域范围中,前者表示小于等于表达式的值集合,后者表示大于等于表达式的值集合。此类用法可出现在范围列表(参见 11.4.13、12.5.4 节)或分布项(参见 18.5.3 节)中。 - 在覆盖组值范围(covergroup_value_range)中,形式为
[$:expression]
或[expression:$]
的表达式(与值范围中的形式相同),其中第一种形式表示小于等于表达式的值集合,而第二种形式表示大于等于表达式的值集合(参见章节 19.5.1、19.5.2、19.6.1)。 - 在交叉覆盖bin select_expression 的整数覆盖组表达式(integer_covergroup_expression)中,
$
要求所有值元组都必须满足该表达式(参见标准章节 19.6.1.2)。 - 在序列(sequence)或属性(property)中,对于形式为
[constant_expression:$]
的周期延迟范围表达式(cycle_delay_const_range_expression),符号$
表示一个有限但无明确上限的最大值。 - 在SystemVerilog中,符号
$
作为序列实际参数(sequence_actual_arg)或属性实际参数(property_actual_arg)时,其作为实际参数值传递给序列、属性或检查器实例,或者用作周期延迟常量范围表达式(cycle_delay_const_range_expression)的上界 $
也可作为值被赋给参数,具体细节如下所述。
在SystemVerilog中,符号 $
必须作为完整独立的表达式使用(不可拆分),唯一例外是在队列选择表达式中允许 $
与操作符组合使用(例如 q[$+1],参见7.10节)。
在SystemVerilog中,符号 $
可被赋值给简单位向量类型的值参数(参见6.11.1节),但被赋值为 $
的参数仅允许出现在 $
可用作字面常量的位置(队列上下文除外,该场景禁止使用 $
参数);例如,将 $
参数赋给其他参数(如 parameter P=Q;)是合法的。
在SystemVerilog中,需注意 $
不代表特定的数值,而是一个符号值,其具体含义取决于所使用的上下文环境。
在以下示例中,$
表示一个无界的范围规格,其中上限索引可为任意大于等于下界的非负整数。
parameter r2 = $;
property inq1(r1,r2);
@(posedge clk) a ##[r1:r2] b ##1 c |=> d;
endproperty
assert property (inq1(3, r2));
系统提供了函数 $isunbounded()
用于检测参数是否为 $
(参见20.6.3节)。该函数的语法格式如下:
function bit $isunbounded( ps_parameter_identifier | hierarchical_parameter_identifier );
以下示例展示了使用 $
符号在参数化范围属性中实现简洁描述的优势:该示例中的检查器用于验证由信号 en 驱动的总线是否在指定的最小静默时间(min_quiet)和最大静默时间(max_quiet)内保持静默状态(即值为0)。函数 $isunbounded()
用于检查实际参数的有效性;若 max_quiet 的值为 $
时,执行 max_quiet == 0 的表达式是非法的,因此通过 && 操作符的短路特性(参见11.3.5节)确保当 $isunbounded(max_quiet) 为真时跳过 max_quiet == 0 的检查。
interface quiet_time_checker #(parameter int min_quiet = 0,
parameter int max_quiet = 0)
(input logic clk, reset_n, logic [1:0] en);
generate
if (!$isunbounded(max_quiet) && (max_quiet == 0)) begin
property quiet_time;
@(posedge clk) reset_n |-> ($countones(en) == 1);
endproperty
a1: assert property (quiet_time);
end
else begin
property quiet_time;
@(posedge clk)
(reset_n && ($past(en) != 0) && en == 0)
|->(en == 0)[*min_quiet:max_quiet]
##1 ($countones(en) == 1);
endproperty
a1: assert property (quiet_time);
end
if ((min_quiet == 0) && $isunbounded(max_quiet))
$warning(warning_msg);
endgenerate
endinterface
quiet_time_checker #(0, 0) quiet_never (clk,1,enables);
quiet_time_checker #(2, 4) quiet_in_window (clk,1,enables);
quiet_time_checker #(0, $) quiet_any (clk,1,enables);
以下示例展示了通过测试 $
符号,可根据需求配置属性:当参数 max_cks 为无界时(即赋值为 $
),无需测试expr是否变为false。
interface width_checker #(parameter min_cks = 1, parameter max_cks = 1)
(input logic clk, reset_n, expr);
generate
if ($isunbounded(max_cks)) begin
property width;
@(posedge clk)
(reset_n && $rose(expr)) |-> (expr [*min_cks]);
endproperty
a2: assert property (width);
end
else begin
property width;
@(posedge clk)
(reset_n && $rose(expr)) |-> (expr[*min_cks:max_cks])
##1 (!expr);
endproperty
a2: assert property (width);
end
endgenerate
endinterface
width_checker #(3, $) max_width_unspecified (clk,1,enables);
width_checker #(2, 4) width_specified (clk,1,enables);