【SystemVerilog】数据类型(2)定宽数组和动态数组

目录

一、定宽数组

1.1定宽数组的声明和初始化

1.2常量数组

1.3数组的基本操作——for、foreach

1.4数组的比较和复制

1.5合并数组

二、动态数组


SV对数组分为两类:定宽数组和动态数组。

定宽数组,一般长度始终固定,且不存在重用性的问题时,可考虑使用。

动态数组,用得非常多,所有存在变长的遍历,都可用使用。比如验证平台的组件配置,可用自仿真中根据验证场景的不同动态定义,非常方便。还有网络报文数据存储等。因此,动态数组要重点掌握,包括各种内置函数。

先放张图(from:百无忧)

 

一、定宽数组

1.1定宽数组的声明和初始化

        因为几乎所有数组都是用0 作为索引下界,所以SV允许只给出数组宽度的便捷声明方式。 如下两种声明是等效的:

int arr_a[0:15] ; //16个整数 [0]...[15]

int arr_a[16]   ; //16个整数 [0]...[15]

verilog-1995只支持1维的定宽数组,verilog-2001中对多维数组的定义如下:

reg [31:0] arr_a [31:0];

SV提供了一种紧凑的声明方式(8行4列):

int arr_a[7:0][3:0]; //完整的声明

int arr_a[8][4];     //紧凑的声明

int arr_a[7:0][4];   //这样呢?也是可以的

arr_a[7][3] = 1;     //对其中元素的赋值

注意:如果你的读地址越界了,那么SV将返回数组元素类型的缺省值。如四态的logic返回X,双态的int返回0。

1.2常量数组

        常量数组使用单引号+大括号来初始化操作。可以一次性对部分或全部元素进行赋值。

        举个栗子:

int ascend[4] = '{0,1,2,3};  //对4个元素进行初始化

int  descend[5];             //仅声明

ascend = '{4{8}};            //4个元素都为8

descend = '{4,3,2,1,0};      //对5个元素进行赋值

descend [0:2] = '{5,6,7};    //对前3个元素进行赋值

descend = '{7,8,default:1};  //{7,8,1,1,1}

1.3数组的基本操作——for、foreach

回顾下verilog中for循环的用法:

integer i ;

for(i=0;i<10;i=i+1)begin : for_loop

//...

end

我们需要提前使用integer i ;来定义循环变量,但是这个变量是全局有效的,也就是说如果你后面想继续使用for循环,就得换一个变量名重新定义。

SV提供for/foreach循环来操作数组:

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;

end

可以看到SV对其改进如下:

  1. for循环中变量i定义的变化;
  2. froeach只需指定数组名并给出索引变量,SV会自动遍历数组中的元素,索引变量自动声明,且只在循环内有效。
  3. $size()函数返回数组的宽度;

但,如果是多维数组,使用foreach的语法并非是foreach[i][j],而是foreach[i,j]

举个栗子:

int multi_arr[2][3] = '{'{1,2,3},'{4,5,6}};

foreach(multi_arr[i,j])

$display("multi_arr[%0d][%0d] = %0d",i,j,multi_arr[i][j]);

输出结果如下:

 

        如果你不想遍历数组的所有维度,也可以在foreach中忽略它们。我们对上面数组重新打印输出一下:

foreach(multi_arr[i])begin //遍历第一个维度

$write("%2d:",i);

foreach(multi_arr[,j]) //遍历第二个维度

$write("%3d",multi_arr[i][j]);

$display; //换行

end

输出打印如下:

 

1.4数组的比较和复制

        可以在不使用循环的情况下对数组进行聚合比较和复制。(聚合操作适用于整个数组而非单个元素),其中比较只限于等于/不等于比较。

        举个栗子:

initial begin

int src[5] = '{1,2,3,4,5};

int dst[5] = '{5,4,3,2,1};

if(src == dst)

$display("src == dst");

else

$display("src != dst");

end

运行结果如下:

 

对数组进行复制:

dst = src;

只改变一个元素的值:

dst[0] = 5;

注意,聚合操作只能对数组进行等于/不等于比较和复制,对数组的算术运算应使用循环。而对于逻辑运算,例如异或,只能使用循环或合并数组。

1.5合并数组

合并数组: 既可以用作数组,也可以当成单独的数据。

举个栗子:有一个32bit的寄存器,你既能把它看做4个8bit的数据,也能看做单个32bit的数据。

bit [3:0][7:0] bytes; //4个字节组装成32bit

bytes = 32'hcafe_dada;

$displayh(bytes,,

bytes[3],,

bytes[3][7]);

输出:

 

合并数组和非合并数组能混合使用,再举个栗子:

bit [3:0][7:0] arr_b[3]; //具有3个合并元素的非合并数组 ;合并: 3 * 32bit

bit [31:0] lw = 32'h0123_4567;

bit [7:0][3:0] arr_w; //合并数组

arr_b[0] = lw;

arr_b[0][3] = 8'h01;

arr_b[0][1][6]= 1'b1;

lw = arr_b[0]; //复制合并数组的元素值

可以看到,根据情况可以使用1个下标,2个下标,3个下标对其操作。但是需要注意的是,arr_b[3]这个维度是非合并的,所以在使用该数组时至少要有一个下标。

当你需要和标量(如:字节或字)进行相互转换时,使用合并数组会非常方便。

二、动态数组

        SV提供了动态数组类型,可以在仿真时分配空间或调整宽度,这样在仿真中就可以使用最小的存储量。

        动态数组的声明是使用空的下标[ ]。数组在最开始是空的,所以你必须调用new[ ]操作符来分配空间,同时在方括号中传递数组宽度。

举个栗子:

int	dyn[],d2[];

dyn = new[5];             //分配5个元素

foreach(dyn[j]) dyn[j]=j; //初始化

d2=dyn;                  //复制一个动态数组

d2[0]=5;

$display(dyn[0],d2[0]);

dyn=new[20](dyn);         //分配20个整数值并进行复制

foreach(dyn[j]) $display(dyn[j]); //打印查看这20个元素值

dyn=new[100];

dyn.delete();             //删除数组

打印结果如下:

 

        通过运行结果可以看出,①dyn和d2是独立的;②dyn=new[20](dyn);这个语句,分配了20个元素空间,然后dyn原数组的值复制给了新数组的前面几个元素。

        另外,只要基本元素类型相同,定宽数组和动态数组之间就可以相互赋值。在元素数目相同的情况下,可以把动态数组的值复制给定宽数组。当你把一个定宽数组复制给一个动态数组时,SV会调用构造函数new[ ]来分配空间并复制数值。

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子墨祭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值