一、 内建数据类型
下图展示常用的数据类型,包含位宽,符号,2/4态等区别
简单总结下(加粗的为常用):
按四值逻辑和二值逻辑的类型划分:
四值逻辑类型:integer、logic、reg、net-type(例如wire、tri)
二值逻辑类型:byte、shortint、int、longint、bit
按有无符号的类型划分:
有符号类型:byte、shortint、int、longint、integer
无符号类型:bit、logic、reg、net-type(例如wire、tri)
SV中引入新的数据类型logic,可以被连续赋值、门单元和模块所驱动,但是不能用在双向总线和多驱动的情况下,此时只能使用网线类型,例如wire。
二、 数组
1 定宽数组
定宽数组,宽度再编译时就已经确定
1.1 定宽数组的声明:
int lo_hi [0:15]; // 16 ints [0]..[15]
int c_style [16]; // 16 ints [0]..[15], 可以理解为16行1列的数组,每个数据都是int型,即32位的整数
1.2 多维数组声明:
int array2 [0:7][0:3]; // 完整声明,声明了一个8行4列的数组,每个位置都是int型
int array3 [8][4]; // 紧凑声明
array2[7][3] = 1; // 设置最后一个元素
1.3 初始化和赋值:
int ascend[4] = '{
0, 1, 2, 3}; // 对4个元素初始化
int descend[5];
descend = '{
4, 3, 2, 1, 0}; // 为5个元素赋值
descend[0:2] = '{
5, 6, 7}; // 为前三个元素赋值
sacend = '{
4{
8}}; // 4个值全部为8
descend = '{
0:9, 1:8, default:-1}; // {9, 8, -1, -1, -1}
注意:如果从越界的地址中读数据,那么将会返回数组元素类型的缺省值,如logic,返回X;int或者bit,返回0;
1.4 压缩数组和非压缩数组:
压缩数组指的是维数的定义在变量标识符之前,非压缩数组指的是维数的定义在变量标识符之后
bit [7:0] cl; // 压缩数组(比特类型)
real u [7:0]; // 非压缩数组(实型)
存储空间考量
下面的两个变量,都可以表示24bit的数据容量,那么从实际硬件容量的角度出发,那种方式的存储方式更小呢?
bit [3][7:0] b_pack; // 3*8 合并型packed
bit [7:0] b_unpack[3]; // 3*8 非合并型unpacked
bit [3:0] [7:0] mix_array[3]
写法:维度(即b_pack的[3],b_unpack的[3],mix_array的[3]),写在左边称为合并型数组,写在右边那就是非合并数组
存储:b_unpack实际会占据3个WORD的存储空间,但是b_pack指挥占据一个WORD的存储空间,如下图:
如果使用logic类型来存储上面的数据即24bit,分别声明变量为下面情况,它们占据的实际存储空间应该是多少?
logic [3][7:0] 1_pack;
logic [7:0] 1_unpack[3];
【解】:logic是四值逻辑,想要表示4中状态,就得需要2bit,所以24位的logic需要连续48bit,就是2WORD,非合并型需要 3* (8*2) 大小的空间,需要3WORD
压缩数组的维数只能通过[hi, lo]方式来定义。如下,变量reg_32是一个 由四个8bit特组成的压缩数组,作为一个32位的长字存储。
// array_example
module test_array ( );
bit [3:0] [7:0] reg_32; // 4个字节压缩为32bit的向量
bit [3:0] [7:0] mix_array[3];
initial begin
reg_32 = 32'hdead_beef;
$display ("%b", reg_32); // 打印所有的32bit
$display ("%h", reg_32[3]); // 打印最高位的字节“de”
$display ("%b", reg_32[3][7]); // 打印最高位比特“1”
mix_array[0] = 32'h0123_4567;
mix_array[1] = mix_array[0];
mix_array[2] = mix_array[1];
if (mix_array[1][3] == 8'h01) //1:第1行;3:第3组
$display ("Access mix_array[1][3]");
if (mix_array[2][1][6] == 1'b1) //2:第2行;1:第1组;6:第6bit
$display ("Access mix_array[2][1][6]");
#50
//mix_array[0] = 64 mix_array[1] = 63, mix_array[2] = 62;
mix_array = '{
64, 63, 62}; // 对于非压缩数组初始化,在声明时可以使用’{}内的值序列进行初始化
end
endmodule
1.5 基本数组操作for和foreach循环
可以用for或者foreach来给数组赋值,$size函数可以返回数组的宽度,foreach只需要指定数组名并在其后面的方括号中给出索引变量,sv便会自动遍历数组中的元素,如下代码所示:
initial begin
bit [31: 0] src[5], dst[5];
for (int i = 0; i < $size(src); i++) //$size默认是最高的维度,这里就是5
src[i] = i;
foreach (dst[j])
dst[j] = src[j]*2; // dst doubles src values
end
foreach多维数组是这样的:
int md[2][3] = '{ '{
0, 1, 2}, '{
3, 4, 5} };
initial begin
foreach (md[i, j])
$display("md[%0d][%0d] = %0d", i, j, md[i][j]);
end
1.5 基本数组操作复制和比较
对于复制,可以利用复制符号“ = ”直接进行数组的复制;
对于比较,在不适用循环的情况下,也可以利用” == “或者” != “来比较数组的内容,不过结果仅限于内容相同或者不同;
bit [31: 0] src[5] = '{
0, 1, 2, 3, 4},
dst[5] = '{
5, 4, 3, 2, 1};
if (src == dst) // 比较数组
$display ("src == dst");
else
$display ("src != dst");
dst = src; // 数组复制
src[0] = 5; // 修改数组中的一个元素
2. 动态数组
定宽数组类型给的宽度在编译时就确定了,但如果像在程序运行时再确定数据的宽度就需要使用动态数组;SV提供动态数组,可以在仿真的过程动态分配大小。动态数组最大的特点就是可以在仿真运行时灵活调节数组的大小即存储量;
动态数组在一开始声明时,就需要利用” [ ] “来声明,而数组此时是空的,即0容量。其后,需要使用” new [ ] “来分配空间,在方括号中传递数组的宽度;
此外,也可以在调用 new [ ] 时将数组名也一并传递,将已有数组的值复制到新数组中;
int dyn[], d2[]; // 声明动态数组
initial begin
dyn = new[5]; // 分配5个元素
foreach (dyn[j]) dyn[j] = j; // 动态数组初始化
d2 = dyn; // 复制一个动态数组
d2[0] = 5; // 修改复制值
$display (dyn[0], d2[0]); // 显示数值0和数值5
dyn = new[20](dyn); // 分配20个数值并进行复制,{0, 1, 2, 3, 4, 0, 0, ..., 0};
dyn = new[100]; // 重新分配100个数值,而旧值不复存在,{0, 0, ..., 0};
dyn.delete(); // 删除所有元素
// dyn = new[0];
// dyn = '{};
end
sv增加了foreach循环,它可用来对一维或多维数组中的元素进行迭代。foreach循环的自变量是数组名,它后面是方括号内用逗号隔开的循环变量列表。每个循环变量对应于数组的一个维度。
// dy_array_example
module test_dy_array ( );
int dyn1[ ], dyn2[ ]; // 动态数组
initial begin
dyn1 = new[100]; // 分配100个成员
foreach (dyn1[i])
dyn1[i] = i; // 初始化成员
dyn2 = new[100](dyn1); // 复制一个动态数组
dyn2[0] = 5;
// 检查dyn1[0]和dyn2[0]的数值
$display ("dyn1[0] = %2d, dyn2[0] = %2d", dyn1[0], dyn2[0]);
// 扩展大小,并赋值初值
dyn1 = new[200](dyn1);
$display ("The size of dyn1 is %0d", dyn1.size());
// foreach (dyn1[i])
// $display ("dyn1[%2d] = %3d", i, dyn1[i]);
// 改变dyn1的大小
dyn1 = new[50];
$display ("The size of dyn1 is %0d", dyn1.size());
dyn1.delete;
$display ("The size of dyn1 is %0d", dyn1.size());
// 复制一个动态数组
dyn1 = dyn2;
$display ("dyn1[0] = %2d, dyn2[0] = %2d", dyn1[0], dyn2[0]);
end
endmodule
3. 关联数组
关联数组可以用来保存稀疏矩阵的元素,数据没有连续存储。当你对一个非常大的地址空间寻址时,该数组只为实际写入的元素分配空间,这种实现方法所需要的空间比定宽或动态数组所占用的空间要小的多;
关联数组在使用之前不会分配任何存储空间,并且索引表达式不仅仅是整型表达式,而且可以是任何数据类型。
此外,关联数组有其它灵活的应用,在其他软件语言也有类似的数据存储结构,被称之为哈希(Hash)或者词典(Dictionary),可以灵活赋予键值(key)和数值(valueÿ