四状态类型
逻辑类型
整数类型
integer
:带符号的四值整数,不指定明确位宽
位宽由实现定义,通常是32位
主要用于循环控制、通用计算,不用于硬件建模
time t
:存储仿真时间值的无符号四值整数,不指定明确位宽
timeunit 1ns;
用于指定仿真中#
延迟
timeprecision 1ps;
用于指定$time
单位
双状态类型
相比于四值类型,引入双值类型有利于提高仿真器性能并减少内存的使用量
整数类型
bit
:最常用的二值类型,无符号整数
bit [31:0]
:32比特无符号整数
unsigned int
:32比特无符号整数
int
:32比特有符号整数
byte
:8比特有符号整数,范围是 [ − 128 , 127 ] [-128, 127] [ − 128 , 127 ]
shortint
:16比特有符号整数
longint
:64比特有符号整数
注意:当把双状态变量连接到被测设计输出时,若输出X或Z态会被转换成双状态值而测试代码无法察觉。使用$isunknown
操作符,在表达式任意位出现X或Z态时返回1
1 2 if ($isunknown (iport) == 1 ) $display ("@%t: 4-state value detected on iport %b" , $time , iport);
类型转换
静态转换
语法:目标类型'(表达式)
强制改变表达式类型,不进行越界检查
1 2 3 int i = int '(10 .0 - 0 .1 ); bit [7 :0 ] byte_val = unsigned '(-1 ); void '(function_call());
动态转换
语法:$cast(目标变量, 源表达式)
返回值:1表示转换成功,0表示转换失败
应用场景
非枚举值赋给枚举变量时需显式检查,避免引入非法值
当父类句柄指向子类对象时,将父类句柄转换为子类句柄
1 2 3 4 5 6 7 8 9 10 11 12 typedef enum {RED, BLUE} color_e; int c = 2 ;color_e color; if (!$cast (color, c)) $error ("越界" ); class Parent; endclass class Child extends Parent; endclass Parent p = new Child(); Child c; $cast (c, p);
隐式转换
由编译器自动完成,无需额外语法
不同位宽/符号类型:自动扩展/截断、补零或符号扩展
可能引入风险
1 2 logic [3 :0 ] x_vec = 'b11x0 ; bit [2 :0 ] b_vec = x_vec;
特殊类型
浮点数类型
real
:双精度浮点数,等效于IEEE 754标准的64位浮点
shortreal
:单精度浮点数,32位
空类型
句柄类型
chandle
存储由C/C++通过DPI传递的指针
事件类型
event
用于声明同步时间,无数据,仅作为同步信号标识
通过操作符->
触发事件,@
或wait()
等待事件
字符串
字符串
特性
动态长度:运行时自动调整长度,无需预定义大小
ASCII存储:每个字符以字节形式存储 (ASCII 编码)
空终止符:自动维护结尾的\0
字符,与C语言兼容
索引访问:支持str[i]
访问字符
支持转移序列:\n
\t
\\
\"
\0
\xHH
(十六进制)
1 2 3 4 string msg = "Hello SV!" ; string empty_str; string path = "dir/file.txt" ; string hex_str = "A\x42C" ;
内建方法
长度操作
len()
或length()
:返回字符串长度
putc(int i, byte c)
:替换位置i
的的字符为c
,索引从0开始
getc(int i)
:获取位置i
的字符
1 2 3 4 string word = "test" ;int length = word.len (); word.putc (length-1 , '!'); byte last_char = word.getc (3 );
大小写转换
toupper()
:转为全大写
tolower()
:转为全小写
大写:Uppercase Letter;小写:Lowercase Letter
子串操作
substr(int s, int e)
:提取子串[s,e]
delete(int s, int e)
:删除子串
1 2 string full = "SystemVerilog" ;string sub = full.substr (6 , 8 );
比较与搜索
compare(string s)
:区分大小写比较,相等时返回0,大于时返回1,小于时返回-1
icompare(string s)
:不区分大小写比较
1 2 3 string a = "apple" , b = "Apple" ;int cmp1 = a.compare (b); int cmp2 = a.icompare (b);
find(string substr)
:正向(从左往右)查找字符串位置
大小写敏感
返回子串第一次出现的起始索引或-1
用于提取前缀、解析命令
rfind(string substr)
:反向(从右往左)查找子串位置
大小写敏感
返回子串最后一次出现的起始索引或-1
用于提取后缀、处理文件扩展名或路径
1 2 3 4 5 6 7 8 9 10 11 string s = "apple,orange,apple" ;string file_path = "/home/user/report_2023.pdf" ;int pos1 = s.find ("apple" ); int pos2 = s.rfind ("apple" ); int dot_pos = file_path.rfind ("." ); if (dot_pos != -1 ) { string extension = file_path.substr (dot_pos, file_path.len ()-1 ); $display ("Extension: %s" , extension); }
类型转换
atoi()
:字符串->十进制整数
atohex()
:字符串->十六进制整数
atobin()
:字符串->二进制整数
atoreal()
:字符串->实数
hextoa(int i)
:十六进制整数->字符串
SV不直接提供itoa()
,需先使用$sformatf
bintoa(int i)
:二进制整数->字符串
1 2 int value = "7F" .atohex (); real pi = "3.1416" .atoreal ();
格式化
$sformatf
:格式化字符串生成器
优先使用此函数代替拼接
SV不直接提供itoa()
,需使用$sformatf
尽管大多数仿真支持$psprintf
函数,功能也和$sformatf
相同,但并不是SV的原生函数
1 2 3 4 5 6 int data = 255 ;string hex_str = $sformatf ("%h" , data); string hex_fmt = $sformatf ("0x%h" , data); string hex_auto = $sformatf ("%0h" , data); string hex_4bit = $sformatf ("%4h" , value); string hex_04bit = $sformatf ("%04h" , value);
结构体
类型重命名
typedef
语句为现有的数据类型创建新名称(别名)
提高代码的可读性、复用性和可维护性
约定俗成:一般所有用户自定义类型都带后缀_t
typedef
定义的类型与原始类型完全等效
定义数组类型的语法:typedef <元素类型> <新类型> [<维度>];
1 2 3 4 5 6 7 8 9 10 11 12 13 `define OPSIZE 8 `define OPREG reg [`OPSIZE-1:0] `OPREG op_a, op_b; parameter OPSIZE = 8 ;typedef reg [OPSIZE-1 :0 ] opreg_t;opreg_t op_a op_b; typedef bit [7 :0 ] byte_array [64 ];byte_array buffer;
包
可以把parameter
和typedef
语句放到一个程序包(package)里,使之能被整个设计和测试平台使用
使用import
语句从包里导入符号
引用的模块优先使用自己索引路径中的符号,若没有才去包里寻找
使用范围操作符::
导入指定的符号
包是完全独立的,可以被放到任何需要的地方
包只能看到包内部定义的符号,或者包自己导入的包
不能层次化引用来自包外部的符号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package ABC; parameter int abc_data_width = 32 ; typedef logic [abc_data_width-1 :0 ] abc_data_t; parameter time timeout = 100 ns; string message = "ABC done" ; endpackage module test; import ABC::*; abc_data_t data; string message = "Test timed out" ; initial begin #(timeout) ; $display ("Timeout - %s" , message); $finish ; end endmodule
创建结构体
struct
语句把若干变量组合到一个结构中
封装数据,便于模块间传递(可综合)
约定俗成:一般用户自定义类型带后缀_s
1 2 3 4 5 6 typedef struct { bit [7 :0 ] r, g, b; } pixel_s; pixel_s my_pixel; my_pixel = '{'h10 , 'h20 , 'h30 };
packed
关键字将结构体中的数据合并到尽可能小的空间中
若需经常对整个结构体进行复制,使用合并结构效率更高
若需经常对结构内的个体进行操作,使用非合并结构效率更高
1 2 3 4 typedef struct packed { bit [7 :0 ] r, g, b; } pixel_s_p;
流操作符
流操作符用于数据打包和解包,简化数据的序列化操作
>>
(从左至右打包或解包):将数据打包成比特流,或从比特流中解包提取数据
<<
(从右至左打包或解包):反序打包数据,或反序解包比特流
1 2 3 4 5 6 7 type_packed = { >> {var1, var2, ...} }; type_packed = { << {var1, var2, ...} }; { >> {var1, var2, ...} } = type_packed; { << {var1, var2, ...} } = type_packed;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 typedef struct { logic [7 :0 ] addr; logic [31 :0 ] data; } packet_t; packet_t tx_packet; logic [39 :0 ] packed_stream; tx_packet.addr = 8'hA5 ; tx_packet.data = 32'h1234_5678 ; packed_stream = { >> {tx_packet.addr , tx_packet.data } }; packet_t rx_packet; { >> {rx_packet.addr , rx_packet.data } } = packed_stream;
可指定片段宽度,再将数据按照指定宽度分段后再打包或解包
比特流结果不能直接赋值给非合并数组
这是因为非合并数组相邻元素间可能存在间隙,如定宽数组使用字边界
需在赋值表达式的左边使用流操作符把比特流拆分到非合并数组中
1 2 3 4 5 6 7 8 bit [31 :0 ] stream = 32'hA5A5_A5A5 ;int arr [0 :3 ]; arr = stream; { >> {arr} } = stream; { >> byte {arr} } = stream;
枚举类型
定义与特性
定义
枚举类型enum
定义一组命名常量集合
增强可读性和安全性,避免手动赋值繁琐易出错
特性
默认首标签为0,后续标签自动递增
也可以显式指定任意数值,未赋值的标签继承上一个标签值+1
默认基类为int
,支持自定义基类,需显式声明宽度
例如enum bit {TRUE=1, FALSE=0} Boolean;
标签值在同一枚举类型中必须唯一
标签名在同一模块中必须唯一,并且不能以数字开头
支持范围语法批量生成标签
1 2 3 4 5 6 7 8 9 10 11 enum {RED, YELLOW, GREEN} light; enum {R, Y, G} color; enum {R = 10 , Y, G} color_e; enum {RESET, S[5 ], W[6 :9 ]} state;
自定义枚举类型
使用typedef
定义可复用的枚举类型
支持包导入,需显式导入包import ABC::*;
,*
也可以是指定的枚举类型
1 2 3 4 5 6 7 8 9 10 11 typedef enum {INIT, DECODE, IDLE} fsm_state_e; fsm_state_e st, nst; initial begin case (st) IDLE: nst = INIT; INIT: nst = DECODE; default : nst = IDLE; endcase $display ("Next state is %s" , nst.name ()); end
枚举类型子程序
first()
或first
返回第一个枚举常量
last()
返回最后一个枚举常量
next()
返回下一个枚举常量
next(N)
返回第N个枚举常量
prev()
返回前一个枚举常量
prev(N)
返回前第N个枚举常量
当到达枚举常量列表的头或尾时,函数next
和prev
会自动以环形方式绕回
1 2 3 4 5 6 7 8 9 10 typedef enum {RED, BLUE, GREEN} color_e;color_e color; color = color.first ; do begin $display ("Color = %0d/%s" , color, color.name ()); color = color.next ; end while (color != color.first )