对SystemVerilog语言数据类型做一个总结,详细全面的介绍可以翻阅绿皮书
一、内建数据类型
- 相对于Verilog将寄存器类型reg和线网(net)类型,如wire,SV中引入了logic数据类型。
- Verilog便硬件描述,所以注重的是声明的变量是寄存器类型还是线网类型。
- SV偏验证,不关心logic对应逻辑应该被综合成为寄存器类型还是线网类型,只会作为单纯的变量进行赋值。
- logic类型为四值逻辑,分别是0、1、x、z。x代表不确定,可能为0也可能为1;z代表高阻态,表示没有被驱动。四值逻辑可以看作是硬件的世界。
- bit类型为二值逻辑,分别是0、1。二值逻辑可以看作是软件的世界。
四值逻辑
- integer
- logic
- reg
- net-type(wire、tri)
二值逻辑
- byte
- short int
- int
- long int
- bit
有无符号划分
- 有符号:byte、short int、int、long int、integer
- 无符号:bit、logic、reg、net-type(wire、tri)
在编码时操作符左右两侧符号类型要保持一致
显式转换
- 静态转换:在转换的表达式前面加上单引号,该方式不会对转换值做检查
- 动态转换:$cast(src,dst), 转换失败会在仿真的时候报错
隐式转换
logic[3:0] a = 4'b111x;
bit[2:0] b;
b = a; //b的值应该为110,四值赋值给二值逻辑类型时,x变成0,z也会变成0
在不同类型进行操作时要注意:
- 逻辑数值类型:是四值还是二值
- 符号的类型
- 矢量的位宽
二、定宽数组
数组声明
int lo_hi[0:15]; //16个int类型元素
int c_style[16]; //C语言风格
多维数组声明和使用
int array2[0:7][0:3]; //完整声明
int array3[8][4]; //紧凑声明
array[7][3] = 1; //给最后一个元素赋值为1
初始化和赋值
//声明的同时,对4个元素初始化
int ascend[4] = '{0,1,2,3};
//声明和赋值分开操作
int descend[5];
descend = '{4,3,2,1,0};
//4个值全部赋值为8
ascend = '{4{8}};
//部分元素使用默认赋值
descend = '{9,8,default:-1}; //{9,8,-1,-1,-1}
存储空间考量
二值逻辑
bit[3][7:0] b_pack; //合并型
bit[7:0] b_unpack[3]; //非合并型
对于bit[3][7:0] b_pack;
每一个元素都是8bit,一共有3个元素,3*8=24bit,因为是合并型,所以占用一个WORD空间就够了。
对于bit[7:0] b_unpack[3];
因为是非合并型,每一个元素占用一个WORD空间,但是有效空间只有8bit。
四值逻辑类型
logic[3][7:0] l_pack; //合并型
logic[7:0] l_unpack[3]; //非合并型
四值逻辑类型有四种值,所以需要2bit来表示。
对于logic[3][7:0] l_pack;
因为是合并型,每一个元素有8bit,一共有3个元素,每种值得用2bit来表示,所以832=48位,用2个WORD空间足以。
对于logic[7:0] l_unpack[3];
因为是非合并型,每个元素占用8bit,而四值逻辑得用2bit表示,所以一个元素8*2=16位,用一个WORD足以放下一个元素,所以需要3 * WORD=3WORD的空间。
基本数组操作for和foreach循环
initial begin
bit[31:0] src[5], dst[5];
for(int i = 0; i < $size(src); i++) //for循环进行赋值
src[i] = i;
foreach(dst[i]) //foreach循环遍历,所有值都乘2
dst[j] = src[j] * 2;
end
赋值和比较
- 赋值:阻塞赋值“=”,非阻塞赋值“<=”
- 比较:等于“==”, 不等于“!=”
三、动态数组
- 定宽数组:宽度编译时确定
- 动态数组:运行时再确定宽度
动态数组声明时用[],之后使用new[]来分配空间
四、队列
- 队列可在任何地方添加、删除元素,并且通过索引实现对任一元素的访问,这一点和C/C++、java语言的队列不相同,其他语言的队列只能是先进先出,从队列头取元素,在队尾添加元素。
- 队列的声明使用[$],队列元素的标号从0到 $ 。
- 队列不需要new[]来创建空间。
- 可用push_back()和pop_front()二者结合实现FIFO的用法。
五、关联数组
当使用超大容量的数组时,使用关联数组会更好一些。动态数组面临超大容量的情景,可能存在着浪费的情况,因为很有可能该大容量数组中有相当多的数据不会被存储和访问。而关联数组可以用来保存稀疏矩阵的元素,该数组只为实际写入的元素分配空间。
bit[63:0] assoc[int], idx = 1;
repeat(64) begin
assoc[idx] = idx;
idx = idx << 1; //1,2,4,8,···
end
六、结构体
Verilog没有数据结构,而在SV中可以使用struct语句创建结构。struct只是一个数据的集合,伴随typedef可以用来创建新的类型。
typedef struct{bit[7:0] r,s,b} pixel_s
pixel_s my_pixel;
my_pixel = '{'h10, 'h10, 'h10};
七、枚举类型
enum经常和typedef搭配使用
typedef enum{INIT,DECODE,IDLE} fsmstate_e;
fsmstate_e pstate,nstate;
case(pstate)
IDLE: nstate = INIT; //枚举类型可复制给整型,但是整型赋值给枚举类型时需要做转化
INIT: nstate = DECODE;
default: nstate = IDLE;
endcase
八、字符串
使用string来保存和处理与字符串相关的处理,字符串格式化函数$sformatf().
string s为空是指的“ ”,而不是c语言中的“\0”.
string s = "IEEE ";
s.getc(0); // I
s.tolower() // ieee
s.putc(s.len()-1, "-") // 将末尾的空格替换成了“-”
s = {s, "P1800"}; // 拼接字符串 IEEE-P1800
s.substr(2,5); // EE-P
$sformatf("%s %5d", s, 42) //格式化,生成临时字符串返回