SystemVerilog语法之定宽数组
1.2定宽数组
1.2.1定宽数组的声明和初始化
Verilog要求在声明中必须给出数组的上下界。因为几乎所有数组都使用0作为索引下界,所以SystemVerilog允许只给出数组宽度的便捷声明方式。SystemVerilog的$clog2()函数可以计算以2为底的对数向上舍入值。你可以通过在变量名后面制定维度的方式创建多维定宽数组。如果你的代码试图从一个越界的地址中读取数据,那么SystemVerilog将会返回数组元素类型的缺省值。对于一个元素为四状态类型的数组,例如logic,返回的是X,而对于双状态类型,则返回0.许多SystemVerilog仿真器在存放数组元素时使用32比特的字边界,所以byte、shortint和int都是存放在一个字中,而longint则存放在两个字中。
// 定宽数组的声明,下面两种方式等价int lo_hi[0:15];int c_ctyle[16];// 计算内存的地址宽度parameter int MEM_SIZE = 256;parameter in ADDR_WIDTH = $clog2(MEM_SIZE);bit [15:0] mem [MEM_SIZE];bit [ADDR_WIDTH-1:0] addr;// 声明并使用多维数组int array2 [0:7][0:3];int array3 [8][4];array2[7][3] = 1;// 非合并数组的声明bit [7:0] b_unpack[3]; // 共3个元素,每个元素占据32bit空间
1.2.2常量数组
常量数组是用一个单引号加大括号来初始化数组。可以一次性为数组的一部分或者所有元素赋值,也可以在大括号前面标上重复次数,对多个元素重复赋值。%p格式化描述符,可以以赋值的格式打印输出数据对象的内容,可以打印输出任何systemverilog的数据类型,包括数组、结构和类等等。
initial beginstatic int ascend[4] = '{0,1,2,3};int descend[5];descend='{4,3,2,1,0};descend[0:2] = '{7,6,5};ascend = '{4{8}};ascend = '{default:42};$display("%p", ascend); // '{0,1,2,3}end
1.2.3基本的数组操作
最常见的操作数组的方式是使用for或foreach循环。在foreach循环中,只需要指定数组名并在其后面的方括号中给出索引变量,SystemVerilog便会自动遍历数组中的元素。
initial beginbit [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;end
对多维数组使用foreach循环时,并不是像i这样分别书写序号,而是用逗号隔开放在同一个方括号中,例如:[i,j].
int md[2][3] = '{'{1,2,3},'{4,5,6}}; // 二维数组的初始化initial begin$display("Initial Value:");foreach(md[i,j])$display("md[%0d][%0d] = %0d", i, j, md[i][j]);foreach(md[i,j]) beginmd[i][j] = md[i][j] * 2;$display("md[%0d][%0d] = %0d", i, j, md[i][j]);endend
如果不需要遍历数组中所有的维度,可以在foreach循环里忽略掉多余的维度。具体例子如下:
byte twoD[4][6];foreach(twoD[i,j])twoD[i][j] = i*10 + j;foreach(twoD[i])$display("twoD[%0d] = %p", i, twoD[i]);foreach(twoD[i,j])$display("twoD[%0d][%0d] = %0d", i, j, twoD[i][j]);
数组f[5]等同于f[0:4],而foreach(f[i])等同于for(int i=0;i<=4;i++).对于数组rev[6:2]来说,foreach(rev[i])语句等同于for(int i=6; i>=2;i--).
1.2.4基本的数组操作——复制和比较
可以在不适用循环的情况下对数组进行聚合比较和复制,比较只限于比较或不等于比较。长度不同的数组之间的复制会导致编译错误,对数组的算术运算不能使用聚合操作,应该使用foreach。
initial beginbit [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;$display("src %s dst", (src == dst) ? "==" : "!=");$display("src[1:4] %s dst[1:4]", src[1:4] == dst[1:4] ? "==" : "!=");end
1.2.5同时使用数组下标和位下标
Verilog-1995不能同时使用数组下标和位下标,2001版本取消了这个限制。$siaplay语句中的连续两个逗号会产生一个空格。
initial beginbit [31:0] src[5] = '{5{5}};$displayb(src[0], , //'b101或'd5src[0][0], , // 'b1src[0][2:1]); // 'b10
1.2.6合并数组
对于某些数据类型,你可能希望既可以把他作为一个整体来访问,也可以将其分解成更小的单元。SystemVerilog的合并数组就可以实现这个功能,它既可以用作数组,也可以用作单独的数据。与非合并的数组不同的是,合并数组的存放方式是连续的比特集合,中间没有任何闲置的空间。
声明合并数组时,合并的位和数组大小作为数据类型的一部分必须在变量名前面指定。数组大小定义的格式必须是[msb:lsb],而不是[size]。合并数组可以和非合并数组混合使用。当你需要和标量进行相互转换时,使用合并数组会非常方便。任何数组类型都可以进行合并,包括动态数组、队列和关联数组。如果你需要等待数组中的变化,则必须使用合并数组。@操作符,只能用于标量或合并数组。例如@(barray[0] or barray[1]);
bit [3:0][7:0] bytes; //4个字节组装成32比特bytes = 32'hCAFE_DATA;$displayh( bytes,, // 显示所有32比特bytes[3],, // 最高字节"CA"bytes[3][7] // "CA"的最高比特位);//合并与非合并混合数组的声明// 由于操作是以比特为单位进行的,所以即使数组维度不同也可以进行复制bit [3:0][7:0] barray[5];bit [31:0] lw = 32'h0123_4567;bit [7:0][3:0] nibbles;barray[0] = lw;barray[0][3] = 8'h01;barray[0][1][6] = 1'b1;nibbles = barray[2];