SV 数据类型
前言
SV新特性
对比VERILOG
- 2值逻辑:提高性能,减少内存使用;
- 队列,动态数组,关联数组:减少内存使用,内建搜索和排序函数;
- unions 和 packed;
- class 和 structures
- string
- enumerated
赋值
- SV 赋全值(全0,全1,全x,全z)时,可以忽略位宽
- verilog 赋全 1 时,不能忽略位宽,也不能忽略进制。
// verilog parameter SIZE=64; logic [SIZE-1:0] data; data=`b0; data=`bz; data=`bx; data=64'hFFFFFFFFFFFFFFFF; //sv parameter SIZE=64; logic [SIZE-1:0] data; data=`0; data=`z; data=`x; data=`1;
基本数据类型
类型列表
type | 逻辑值 | 符号类型 | 位宽 | 特点 |
wire | 4 值逻辑 | 无符号 | 1 bit | |
reg | 4 值逻辑 | 无符号 | 1 bit | |
logic | 4 值逻辑 | 无符号 |
1 bit |
|
bit | 2 值逻辑 | 无符号 | 1 bit | |
byte | 2 值逻辑 | 有符号 | 8 bit | |
shortint | 2 值逻辑 | 有符号 | 16 bit | |
integer | 4 值逻辑 | 有符号 | 32 bit | |
int | 2 值逻辑 | 有符号 | 32 bit | |
longint | 2 值逻辑 | 有符号 | 64 bit | |
time | 2 值逻辑 | 无符号 | 64 bit | 64位整数,默认单位为:秒 |
real | 2 值逻辑 | 有符号 | 64 bit | 类似 C 的 double 类型 |
shortreal | 2 值逻辑 | 有符号 | 32 bit | 类似 C 的 float 类型 |
string | 可变长度的字符数组 | |||
void | 空,用于函数返回 |
使用示例
var logic [63: 0] addr; // a 64-bit wide variable wire logic [63: 0] data; // a 64-bit wide net bit b; // 两态,单比特 bit [31: 0] b32; // 两态,32比特无符号 int i32; // 两态,32比特有符号 byte b8; // 两态,8比特有符号 shortint s16; // 两态,16比特有符号 longint l64; // 两态,64比特有符号
逻辑类型
4值逻辑
- 逻辑值:0,1,x, z
- 常用类型:integer, logic, reg, net_type (例如wire, tri)
- 仿真时,4值逻辑默认为 x,2值逻辑默认为 0
- 4值逻辑可以赋值给2值逻辑变量,x 和 z 会转化成 0
- 使用 $isunknown() check任何bit是x或z,如果表达式任何一个bit是z或x,返回1;
if($isunknown(iport)==1);
2值逻辑
- 逻辑值:0和1;z和x 会转换成0
- 常用类型:bit, byte, shortint, int, longint
- 2值逻辑变量不能代表一个未初始化的状态
- 提升仿真性能和减少内存使用
- 不适用于DUT,因为DUT识别z和x,但是2值逻辑将会转换为0
数据类型转换
静态转换
- 在转换的表达式前加上单引号即可
- 不会対转换值做检查,如果转换失败,无从得知
- 在编译的时候做检查
- 有符号转换成无符号: unsigned'(signed_vec)
动态转换
在仿真的时候做检查,通常使用函数:$cast(tgt, src)
显示转换
静态转换和动态转化都需要操作符号或者系统函数介入
隐式转换
不需要进行转换的一些操作
tips
- Verilog没有严格区分信号的类型,变量和线网类型均是四值逻辑;
- SV中将硬件信号分为:变量类型 和 线网类型;
- 线网类型赋值只能使用连续赋值语句(assign),不能出现在过程块(initial / always);
- 对于变量类型赋值可以使用连续赋值、或者过程赋值;
定宽数组
- 类型的宽度在编译时就确定了。
- 支持多维数组
- 超过边界写将会被忽略,超过边界读将会返回x,2值逻辑也是返回x;
- byte,int, shortint,都是存储在一个32位的空间中
- longint存储在两个32位的空间中
- 默认为unpacked数组
- 将维度都写在左边称为合并型数组,高纬度在左边
- 只要有一个维度是写在右边,那就是非合并数组,高纬度从变量名右边开始
语法
type name [constant]; type name [0: constant];
int array[20]; // 元素:0-19 int array[64:83]; // 元素:64-83 logic [31:0] data [1024]; // 元素: 0-1023, 32位宽 int array1 [4]= '{3,2,4,1}; array1[0:2] = '{5,6,7}; array1 = '{4{8}}; array1 = '{1,2, default:-1}; array1 = '{{3}, '{3{8}}}; int md [2][3] = '{'{0,1,2}, '{3,4,5}};
pack vs unpack
// 两个变量都可以表示24bit的数据容量 // b_pack只会占据 1 个 WORD 的存储空间 // b_unpack占据 3 个 WORD 的存储空间 // 如果变量前后都有容量定义,右边的维度高,即b_unpack的3的维度高 bit [3][7:0] b_pack bit [7:0] b_unpack[3]; // logic是四值逻辑,即需要2bit logic [3][7:0] b_pack; // 2 个WORD 2*8*3 logic [7:0] b_unpack[3]; // 3 个WORD 2*8=16, 1个WORD
数组复制和比较
-
利用赋值符号 "=" 直接进行数组的复制
- 直接利用 "==" 或者 "!=" 来比较数组的内容,结果仅限于内容相同或者不相同
动态数组
-
定宽数组的宽度在编译时就确定了;
- 动态数组可以在仿真运行时灵活调节数组大小,即存储量
// 动态数组在声明时,需要使用 [] 声明,此时数组数组是空的,即0容量 // 使用new[] 来分配空间,在方括号中传递数组的宽度 // 也可以在调用 new[] 时将数组名传递,将已有的数组的值复制到新的数组中 initial begin: dynamic_array int dyn1[], dyn2[]; dyn1 = new[5]; // 分配5个元素 dyn1 = '{1, 2, 3, 4}; $display("dyn1 = %p", dyn1); // copp method option-1 dyn2 = dyn1; // 重新复制一份数据到dyn2,相当于深拷贝 $display("dyn2 = %p", dyn2); $display("dyn2 size is %0d", dyn2.size()); // copp method option-2 dyn2 = new[dyn1.size()](dyn1); $display("dyn2 = %p", dyn2); $display("dyn2 size is %0d", dyn2.size()); dyn2.delete(); // 删除所有元素 $display("dyn2 size is %0d", dyn2.size()); end
队列
- 可以在任何地方添加或删除元素,并且通过索引实现队任一元素的访问
- 使用美元符号声明:[$],元素标号从0到$
- 不需要使用new[] 去创建空间,一开始其空间为0
- 使用 push_back 和 pop_front 结合实现FIFO的用法,还有push_front 和 pop_back
initial begin: queue_use int que1[$], que2[$]; que1 = {10, 30, 40}; // 不需要使用单引号赋值 $display("que1 = %p", que1); que2 = que1; $display("que2 = %p", que1); que1.insert(1, 20); $display("que1 = %p", que1); // {10, 20,30,40} que1.delete(3); // delete que1[3]==40 void'(que1.pop_front()); // pop que[0]==10 $display("que1 = %p", que1); foreach(que1[i]) $display(que1[i]); que1.insert(3, que2); que1.push_front(6); // 队首插入6 j = que1.pop_back(); // 队尾弹出,并赋值给 j que1.push_bach(7); // 队尾插入7 que.pop_front(); // 队头弹出 que1.delete(); $display("que1 = %p", que1); end
关联数组
- 用来保存稀疏矩阵的元素
initial begin: associate_array int id_score1[int], id_score2[int]; // key ID, value SCORE id_score1[101] = 111; id_score1[102] = 222; id_score1[103] = 333; // associate array copy id_score2 = id_score1; id_score2[101] = 101; id_score2[102] = 102; id_score2[103] = 103; foreach(id_score1[id]) begin $display("id_score1[%0d] = %0d", id, id_score1[id]); end foreach(id_score2[id]) begin $display("id_score2[%0d] = %0d", id, id_score2[id]); end if(id_score.first(idx)) begin do $display("id_score[%d]=%d", idx, id_score[idx]); while(id_score.next(idx)); end // 找到并删除第一元素 id_score2.first(idx); id_score2.delete(idx); end
关联数组内建函数
function int num ( ) / function int size ( ) // 返回关联数组中成员的数目。如果数组是空数组,那么返回0; function void delete ([input index]) // index是一个可选的适当类型的索引,删除特定索引的成员,如果没有指定索引,则删除数组的所有成员; function int exists (input index) // index是一个适当类型的索引,如果成员存在,则返回1,否则返回0; function int first (ref index) // index是一个适当类型的索引,将指定的索引变量赋值为关联数组中第一个(最小的索引的值);如果数组是空的,则返回0,否则返回1; function int last (ref index) // index是一个适当类型的索引,将指定的索引变量赋值为关联数组中最后一个(最大的索引的值);如果数组是空的,则返回0,否则返回1; function int next (ref index) // index是一个适当类型的索引,查找索引值大于指定索引的条目。如果存在下一个成员,索引变量被赋值为下一个成员的索引,并且函数返回1。否则,索引不会发生变化,函数返回0; function int prev (ref index) // index是一个适当类型的索引,查找索引小于指定索引的成员。如果存在前一个成员,索引变量被赋值为前一个成员的索引,并且函数返回1 。否则,索引不会发生变化,并且函数返回0;
结构体
- 数据的集合
- 使用struct语句创建
- 结合typedef可以用来创建新的类型,并利用新类型来声明更多的变量
struct { bit [7:0] r,g,b; } pixel; // 创建一个pixel结构体 typedef struct { bit [7:0] r,g,b; } pixel_s; pixel_s my_pixel; // 声明变量 my_pixel = '{'h10, 'h10, 'h10}; // 赋值
枚举
- 可读性和可维护性更好
- 结合typedef使用
- 枚举类型可以直接赋值给整型: int a = INIT;
- 整型不能直接赋值给枚举类型,需要进行转换
typedef enum {INIT,DECODE,IDLE} fsmstate_e; fsmstate_e pstate, nstate; // 声明自定义类型变量 case(pstate) IDLE: nstate = INIT INIT : nstate = DECODE; default: nstate = IDLE; endcase
$display("Next state is %s", nstate.name());
字符串
- SystemVerilog 包含一个string数据类型,它是一个可变尺寸、动态分配的字节数组。
- SystemVerilog 还包含许多特殊的方法来对字符串进行操作。
- string类型的变量可以从0到N-1(数组的最后一个元素)进行索引
- 可以作用于一个特殊的空字符串:""
- 从一个字符串读取一个元素会产生一个字节
- string类型变量的索引从字符串的左侧开始排列,例如:对字符串"Hello World!",索引0对应"H",索引1对应"e",依此类推...
- 如果在声明中没有指定初始值,变量会被初始化成空字符串("")
- $sformatf() 函数: 格式化函数
- $display() 函数: 打印函数
string s; initial begin s = "IEEE"; $display (s.getc(0)); //显示第0个字符: I $display (s.tolower()); //显示小写字符: ieee s.putc(s.len() - 1,"-"); //putc是替换,len() - 1是字符串最后一位,即将最后一位替换成“ -” s = {s,"P1800"}; //字符串拼接 ->IEE-P1800 $display(s.substr(2,5)); // 显示第2-5位字符 ->E-P1 //创建一个临时字符串并将其打印 my_log ($sformatf("%s,%5d",s,42)); end
task my_log (string message); //打印信息 $display ("@%0t:%s",$time,message); endtask
循环结构
for循环
// $size 默认获得最高的维度 initial begin bit [31:0] src[5], dst[5];
for(int i=0;i<$size(src); i++) src[i] = i; end
foreach循环
// 自动创建变量j, 默认为最高维度 initial begin bit [31:0] src[5], dst[5];
foreach(dst[j]) dst[j] = j; end
有符号