SystemVerilog学习之路(3)— 定宽数组
一、前言
数组在数字验证当中是经常用到的,而且对于不同类型的数组其适用的应用场景也各不相同。
二、数组声明
在Verilog中对于数组的声明必须给出数组的上下界,但是在SystemVerilog中因为几乎所有数组都使用0作为索引下界,所以可以和C语言一样只给出数组宽度的便携式声明方式,如下为两种声明方式:
int lo_hi[0:15]; // 16个int类型整数[0]...[15]
int c_style[16]; // 16个int类型整数[0]...[15]
然后还可以通过在变量名后面指定维度的方式来创建多维的数组,当维度声明在变量名的右边时,靠右的为低维度,靠左的为为高维度;如下所示两个二位数组都是8行4列的,第1维度(低维度)为4个元素,第2维度(高维度)为8个元素,然后通过其对应的维度设置对应的元素,这和C语言中是一样的。
int array2[0:7][0:3]; // 完整的声明
int array3[8][4]; // 紧凑的声明
array2[7][3] = 1; // 设置最后一个元素
另外我们需要知道的是,在很多的SystemVerilog仿真器中,存放数组元素是使用32bit的字边界(WOED),所以byte,shortint和int都是存放在一个字中,而longint则存放到2个字中。
三、初始化和赋值
在对数组初始化时,使用一个单引号加大括号来初始化数组,可以一次性地为数组的部分或所有元素赋值。如下所示,可以在大括号前标上重复次数来对多个元素重复赋值,还可以为那些没有显示赋值的元素指定一个缺省值。
int ascend[4] = '{0, 1, 2, 3}; // 对4个元素进行初始化
int descend[5]; // 声明5个元素的数组
descend = `{4, 3, 2, 1, 0}; // 为5个元素赋值
descend[0:2] = '{5, 6, 7}; // 为前3个元素赋值
ascend = '{4{8}}; // 四个值全部赋为8
descend = '{9, 8, default:1}; // 指定缺省值,{9, 8, 1, 1, 1}
四、合并数组
合并数组既可以用作数组,也可以当成单独的数据,即可以把它作为一个整体来访问,也可以把他分解成小的单元,合并数组的存放方式是连续的比特合集,中间没有任何的闲置空间。
在外面声明合并数组时,合并的位和数组大小作为数据类型的 一部分必须在变量名的前面指定,数组大小定义的格式必须是[msb:lsb]
,而不能是[size]
。编写代码如下所示,变量bytes
是一个有4个字节的合并数组,使用单独的32比特的1个字来存放,我们对其赋值一个32位的数据,然后$displayh()
函数(默认输出十六进制)将其打印出来,另外我们使用两个,
做分隔是为了插入一个空格,避免所有打印连在一起,
module array;
initial begin
bit[3:0][7:0]bytes; // 四个字节组装成32比特
bytes = 32'hcafe_dada;
$displayh(bytes,, // 显示所有的32比特
bytes[3],, // 显示最高字节
bytes[3][7]); // 显示最高bit位
end
endmodule
仿真结果如下所示
五、非合并数组
当我们将数组大小写在了变量名右边时,即成为了非合并数组,在非合并数组中,字的低位用来存放数据,高位则不使用,如下所示,数组b_unpack
被存放到三个字的空间里,
仿真器通常是使用两个或两个以上连续的字来存放logic和integer等四状态类型,这就会比存放双状态变量多占用一倍的空间。如下所示,我们使用logic类型来存储24bit的数据,变量声明如下,他们实际占据的实际存储空间应为2WORD
和3WORD
,因为logic为四值逻辑类型,使用其每一位都需要两个bit来存储(0,1,x,z),所以l_pack
需要连续的48bit来存储,则需要占据2WORD
,而对于l_unpack
来说,其第一维为8位的合并数组,则需要占据16bit存储空间,使用1WORD
即可,然后第二维有3个元素,则总共需要3WORD
。
logic [3][7:0] l_pack;
logic [7:0]l_unpack[3];
六、数组操作
使用for
和foreach
循环可以遍历整个数组,编写代码如下所示,声明i
为for循环内的局部变量,通过$size
函数返回数组的宽度,这样就可以遍历整个数组并对其初始化;在foreach
循环中,只需要指定数组名并在其后面的方括号中给出索引变量,SystemVerilog便会自动遍历数组中的元素,索引变量将自动声明,并只在循环内有效。
module arr;
initial begin
bit [31:0]src[5],dst[5];
for(int i=0;i<$size(src);i++)
src[i] = i;
foreach(dst[j])
dst[j] = src[j]*2; // dst的值是src的两倍
foreach(src[j])
$display("@1: src[%1d] = 'h%h", j, src[j]);
foreach(src[j])
$display("@2: dst[%1d] = 'h%h", j, dst[j]);
end
endmodule
仿真运行如下所示
一般来说我们会使用foreach
循环来更简单的遍历数组,但是对多维数组使用foreach时并不是像[i][j]
这样把每个下标分别放在不同的方括号里,而是用逗号隔开后放在同一个方括号里[i, j]
,如下所示,使用foreach来遍历多维数组,
module arr;
int md[2][3] = '{'{0,1,2}, '{3,4,5}};
initial begin
$display("Initial value:");
foreach(md[i, j])
$display("@1: md[%0d][%0d] = %h", i, j, md[i][j]);
$display("New value:");
md = '{'{9,8,7}, '{3{32'd5}}}; // 重新赋值
foreach(md[i, j])
$display("@1: md[%0d][%0d] = %h", i, j, md[i][j]);
end
endmodule
仿真运行如下所示
七、复制和比较
对于数组的赋值,可以利用赋值符号=
直接进行数组的复制;
对于数组的比较,可以在不使用循环的情况下,利用==
或!=
来对数组进行聚合比较,不过比较只限于内容相同或不相同。编写代码如下所示
module arr;
initial begin
bit [31:0]src[5] = '{0, 1, 2, 3, 4},
dst[5] = '{5, 4, 3, 2, 1};
if(src == dst) // 两个数组的聚合比较
$display("@1: src == dst");
else
$display("@1: src != dst");
dst = src; // 把src所有元素复制给dst
src[0] = 5; // 改变一个元素的值
// 判断所有元素的值是否相等
$display("@2: src %s dst", (src==dst) ? "==" : "!=");
// 使用数组片段对第1-4个元素进行比较
$display("@3: src[1:4] %s dst[1:4]", (src[1:4]==dst[1:4]) ? "==" : "!=");
end
endmodule
仿真运行结果如下所示