参考资料:IEEE Std 1800-2023标准《IEEE Standard for SystemVerilog — Unified Hardware Design, Specification, and Verification Language》
文章目录
5.8 Time literals
时间值(Time)应采用整数或定点格式书写,并紧随其后(不带空格)加上时间单位(fs
、ps
、ns
、us
、ms
、s
)。
2.1ns
40ps
时间字面量被解释为按当前时间单位缩放后的 realtime 值。
5.9 String literals
字符串字面量(string literal)是由一对双引号(""
)括起来的字符序列,称为带引号的字符串;或者由三对双引号("""..."""
)括起来的字符序列,称为三重引号字符串。字符串字面量的长度没有预定义限制。
语法如下:
string_literal ::=
quoted_string
| triple_quoted_string
quoted_string ::= " { quoted_string_item | string_escape_seq } "
triple_quoted_string ::= """ { triple_quoted_string_item | string_escape_seq } """
quoted_string_item ::= any_ASCII_character except \ or newline or "
triple_quoted_string_item ::= any_ASCII_character except \
string_escape_seq ::=
\any_ASCII_character
| \one_to_three_digit_octal_number
| \x one_to_two_digit_hex_number
在字符串字面量中,不可打印字符和其他特殊字符可通过字符串转义序列表示,详见 5.9.1 节。对未转义的不可打印 ASCII 字符的支持取决于具体实现。
带引号的字符串必须包含在单行内,除非换行符前紧跟反斜杠(\)。此时,反斜杠和换行符将被忽略。
例1
$display("Humpty Dumpty sat on a wall. \
Humpty Dumpty had a great fall.");
打印:
Humpty Dumpty sat on a wall. Humpty Dumpty had a great fall.
例2
$display("Humpty Dumpty sat on a wall.\n\
Humpty Dumpty had a great fall.");
打印:
Humpty Dumpty sat on a wall.
Humpty Dumpty had a great fall.
三重引号字符串字面量与带引号字符串字面量的区别主要体现在两方面:
- 三重引号字符串允许直接插入换行符,而无需使用
\n
转义序列。 - 三重引号字符串允许直接插入双引号(")字符,而无需使用
\"
转义序列。
在所有其他方面,这两种构造是相同的。这意味着,三重引号字符串字面量中的转义换行符的处理方式与带引号字符串字面量中的转义换行符完全相同。
例3
$display("""Humpty Dumpty sat on a "wall".
Humpty Dumpty had a great fall. """);
打印:
Humpty Dumpty sat on a "wall".
Humpty Dumpty had a great fall.
例4
$display("""Humpty Dumpty sat on a wall. \
Humpty Dumpty had a great fall. """);
打印:
Humpty Dumpty sat on a wall. Humpty Dumpty had a great fall.
例5
$display("""Humpty Dumpty \n sat on a wall. \n
Humpty Dumpty had a great fall. """);
打印:
Humpty Dumpty
sat on a wall.
Humpty Dumpty had a great fall.
在表达式和赋值中作为操作数的字符串字面量,应被视为无符号整数常量,由8位ASCII值序列表示,每个8位ASCII值对应一个字符。字符串字面量中的转义字符序列同样由单个8位ASCII值表示。
字符串字面量可以赋值给整型类型,例如合并数组。若位宽不同,将进行右对齐处理。为完整存储字符串字面量,整型类型的位宽应声明为等于字符串字符数乘以8。例如:
byte c1 = "A" ;
bit [7:0] d = "\n" ;
若合并数组的位宽与字符数乘以8的结果不匹配,则必须遵循 SystemVerilog 赋值规则。当整型类型的位宽大于待赋值字符串字面量所需的位宽时,数值将进行右对齐,高位(左侧)补零,此处理方式与非字符串数值的赋值规则一致。若字符串字面量的长度超过目标整型类型的位宽,字符串将进行右对齐,高位(左侧)字符将被截断。
例如,要存储12个字符的字符串“Hello world\n”,需要一个8 × 12或96位宽的变量。
bit [8*12:1] stringvar = "Hello world\n";
或者,可以使用多维合并数组,其子字段为8位宽度,如下所示:
bit [0:11] [7:0] stringvar = "Hello world\n" ;
字符串字面量可以赋值给非合并的字节数组。若大小不匹配,将进行左对齐处理。
byte c3 [0:12] = "hello world\n" ;
合并和非合并数组将在7.4节中讨论。
字符串字面量也可以强制类型转换为合并数组或非合并数组类型,此转换应遵循与将字符串字面量赋值给合并数组或非合并数组相同的规则。类型转换在 6.24 节讨论。
SystemVerilog 还包含一种字符串数据类型,字符串字面量可以赋值给该类型。字符串类型的变量具有任意长度;它们会动态调整大小以容纳任何字符串。字符串字面量本质上是合并数组(其位宽为 8 的倍数),当被赋值给字符串类型或用于涉及字符串类型操作数的表达式时,它们会被隐式转换为字符串类型(参见 6.16 节)。
以下示例展示了使用多行三引号起始和终止字符串值,向字符串变量赋值的过程。
例6
string foo;
foo = """
This is one continuous string.
Single ' and double " can
be placed throughout, and
only a triple quote will end it.
""";
存储在向量中的字符串字面量可以使用 SystemVerilog 运算符进行操作。运算符所处理的值是 8 位 ASCII 值的序列。有关字符串字面量的操作说明,请参见第 11.10 节。
5.9.1 Special characters in strings
某些 ASCII 字符只能通过转义序列在字符串字面量中表示。下表列出了这些字符,其中左列为表示该字符的转义序列,右列为对应字符。虽然三重引号字符串字面量支持未转义的双引号和换行符,但与此类字符关联的转义序列同样受支持。
Escape sequence | Characters in string literals |
---|---|
\n | Newline character |
\t | Tab character |
\ | \ character |
" | " character |
\v | Vertical tab |
\f | Form feed |
\a | Bell |
\ddd | 使用 1 到 3 位八进制数字表示的字符。若使用的数字少于三位,其后的字符不得是八进制数字。若表示的字符值超过 \377(十进制 255),具体实现可能会报错。在转义序列中使用 x 值或 z 值作为八进制数字是非法的。 |
\xdd | 使用 1 到 2 位十六进制数字表示的字符。若仅使用一位数字,其后的字符不得是十六进制数字。在转义序列中使用 x 值或 z 值作为十六进制数字是非法的。 |
未在表 5-1 中出现的转义字符,其处理方式与该字符未转义时相同。例如(For example),"\b"
被视为与 "b"
相同。这两个字面量都被视为包含单个字符,即字母 "b"
。
如果换行符前面紧跟着 \\
(双反斜杠),则双反斜杠被解释为表示字符串中单个反斜杠字符的转义序列,而非行续接序列的开头部分。因此,行续接序列需要第三个反斜杠。
$display("Humpty Dumpty sat on a wall. \\\
Humpty Dumpty had a great fall.");
打印:
Humpty Dumpty sat on a wall. \Humpty Dumpty had a great fall.
5.10 Structure literals
结构体字面量(structure literals)是包含常量成员表达式的结构体赋值模式或模式表达式(参见 10.9.2 节)。结构体字面量必须具有类型,该类型可通过前缀显式指定,或在类似赋值的上下文中隐式指定(参见 10.8 节)。
typedef struct {int a; shortreal b;} ab;
ab c;
c = '{0, 0.0}; // structure literal type determined from
// the left-hand context (c)
嵌套的大括号应反映结构。例如:
ab abarr[1:0] = '{'{1, 1.0}, '{2, 2.0}};
前面示例中类似C语言的写法 '{1, 1.0, 2, 2.0} 是不允许的。
结构体字面值也可以使用成员名和值,或者使用数据类型和默认值(见10.9.2):
c = '{a:0, b:0.0}; // member name and value for that member
c = '{default:0}; // all elements of structure c are set to 0
d = ab'{int:1, shortreal:1.0}; // data type and default value for all
// members of that type
当初始化结构体数组时,嵌套花括号应反映数组和结构的层次关系。例如:
ab abarr[1:0] = '{'{1, 1.0}, '{2, 2.0}};
复制运算符(replication operator)可用于设置精确数量的成员值。复制操作中的内层花括号将被移除。
struct {int X,Y,Z;} XYZ = '{3{1}};
typedef struct {int a,b[4];} ab_t;
int a,b,c;
ab_t v1[1:0] [2:0];
v1 = '{2{'{3{'{a,'{2{b,c}}}}}}};
/* expands to '{ '{3{ '{ a, '{2{ b, c }} } }},
'{3{ '{ a, '{2{ b, c }} } }}
} */
/* expands to '{ '{ '{ a, '{2{ b, c }} },
'{ a, '{2{ b, c }} },
'{ a, '{2{ b, c }} }
},
'{ '{ a, '{2{ b, c }} },
'{ a, '{2{ b, c }} },
'{ a, '{2{ b, c }} }
}
} */
/* expands to '{ '{ '{ a, '{ b, c, b, c } },
'{ a, '{ b, c, b, c } },
'{ a, '{ b, c, b, c } }
},
'{ '{ a, '{ b, c, b, c } },
'{ a, '{ b, c, b, c } },
'{ a, '{ b, c, b, c } }
}
} */
5.11 Array literals
数组字面量(array literal)在语法上类似于C语言的初始化器,但允许使用复制运算符({{}}
)。
int n[1:2][1:3] = '{'{0,1,2},'{3{4}}};
花括号的嵌套应严格遵循维度数量,这与C语言不同。然而,复制运算符可以嵌套使用。复制操作中的内层花括号将被移除。复制表达式仅在单一维度内操作。
int n[1:2][1:6] = '{2{'{3{4, 5}}}}; // same as
'{'{4,5,4,5,4,5},'{4,5,4,5,4,5}}
数组字面量是包含常量成员表达式的数组赋值模式或模式表达式(参见 10.9.1 节)。数组字面量必须具有类型,该类型可通过前缀显式指定,或在类似赋值的上下文中隐式指定(参见 10.8 节)。
typedef int triple [1:3];
$mydisplay(triple'{0,1,2});
数组字面量也可以使用其索引或类型作为键,并使用默认键值(参见 10.9.1 节)。
triple b = '{1:1, default:0}; // indices 2 and 3 assigned 0
5.12 Attributes
SystemVerilog 提供了一种机制,用于在源代码中描述对象、语句及语句组的属性。这些属性可被仿真器等各类工具使用,以控制工具的操作或行为。此类属性被称为"属性"(attribute)。本节规定了属性的语法机制,但不对具体属性内容进行标准化。
指定属性的语法如下。
attribute_instance ::= (* attr_spec { , attr_spec } *)
attr_spec ::= attr_name [ = constant_expression ]
attr_name ::= identifier
attribute_instance 可作为前缀附着于声明、模块项、语句或端口连接之前,也可作为后缀出现在表达式中的运算符或函数名之后。
未指定值的属性默认类型为 bit
,其值为 1。若指定值,则属性采用该表达式的类型。
若同一语言元素多次定义相同属性名,则采用最后一次的属性值,工具可发出重复属性指定的警告。
属性实例不允许嵌套。使用包含属性实例的常量表达式来指定属性值是非法的。
关于在特定语言元素上指定属性实例的语法,请参阅附录 A。以下列举若干示例说明。
示例 1:以下示例展示了如何将属性附加至 case 语句
(* full_case, parallel_case *)
case (a)
<rest_of_case_statement>
or
(* full_case=1 *)
(* parallel_case=1 *) // Multiple attribute instances also OK
case (a)
<rest_of_case_statement>
or
(* full_case, // no value assigned
parallel_case=1 *)
case (a)
<rest_of_case_statement>
示例 2:附加 full_case 属性,但不附加 parallel_case 属性
(* full_case *) // parallel_case not specified
case (a)
<rest_of_case_statement>
or
(* full_case=1, parallel_case = 0 *)
case (a)
<rest_of_case_statement>
示例 3:将属性附加至模块定义
(* optimize_power *)
module mod1 (<port_list>);
or
(* optimize_power=1 *)
module mod1 (<port_list>);
示例 4:将属性附加至模块实例化
(* optimize_power=0 *)
mod1 synth1 (<port_list>);
示例 5:将属性附加至变量声明
(* fsm_state *) logic [7:0] state1;
(* fsm_state=1 *) logic [3:0] state2, state3;
logic [3:0] reg1; // reg1 does NOT have fsm_state set
(* fsm_state=0 *) logic [3:0] reg2; // nor does reg2
示例 6:将属性附加至运算符
a = b + (* mode = "cla" *) c; // sets the value for the attribute 'mode'
// to be the string cla.
示例 7:将属性附加至函数调用
a = add (* mode = "cla" *) (b, c);
示例 8:将属性附加至条件运算符
a = b ? (* no_glitch *) c : d;
5.13 Built-in methods
SystemVerilog 采用了类似 C++ 的类方法调用语法,通过点号表示法(.
)调用子程序:
object.task_or_function()
object唯一标识了子程序所操作的数据。因此,方法的概念被自然地扩展到内置类型,以实现传统上需通过系统任务或系统函数完成的功能。与系统任务不同,内置方法无需添加前缀,因为它们不需要特殊前缀来避免与用户定义标识符的冲突。因此,method 语法允许在不新增关键字、也不会因系统任务污染全局命名空间的前提下扩展语言功能。
内置方法(built-in method)与系统任务不同,用户无法通过 PLI 任务重新定义内置方法。因此,只有那些不应允许用户重新定义的功能,才适合作为内置方法调用的候选方案。
通常而言,当特定功能适用于所有数据类型时,或仅适用于特定数据类型时,内置方法优于系统任务。例如:
dynamic_array.size, associative_array.num, and string.len
这些都是相似的概念,但它们代表不同的实体。动态数组拥有大小,关联数组包含特定数量的元素,而字符串具有特定长度。若使用相同的系统任务(如$size
)处理所有这些类型,将降低代码的清晰度和直观性。
内置方法仅能关联特定数据类型。因此,若某功能是纯辅助操作,例如 $stop
或 $reset
,或无需操作特定数据(例如 $random
),则应使用系统任务。
当子程序内置方法调用未指定参数时,方法名后的空括号 () 可省略。此规则同样适用于所有参数均有默认值的带参子程序。对于方法而言,此规则使简单调用可呈现为对象或内置类型的属性。需注意例外情况 —— 当内置方法调用作为隐式变量使用时,即使参数为空也必须保留括号(参见 13.4.1 节)。子程序相关类似规则定义于 13.5.5 节。