带你了解SystemVerilog中的关联数组

电子说

1.3w人已加入

描述

在SystemVerilog中,我们知道可以使用动态数组实现数组元素个数的动态分配,即随用随分,其中元素在数组中的索引是连续的,但是如果要实现数组元素访问时不采用连续索引的话,采用动态数组和定宽数组就不是很合适,容易造成空间的浪费,为此在SystemVerilog中引入了关联数组(Associative Array),实现了一种查找表,该查找表的索引可以根据用户需要指定,不限于整形,其内存空间直到使用时才会分配,即只针对写入的元素分配存储空间,其使用方式类似于Perl等其他语言中的哈希结构

关联数组与其他数组表面上的不同主要体现在数组的索引上,非关联数组的索引一般都是整型变量,而关联数组的索引可以是任何的数据类型。

下面我们将通过示例说明关联数组是如何定义和常用的方法如何使用。

1 关联数组的声明格式

关联数组采用在方括号中放置数据类型的形式来进行声明,其格式如下:

data_type array_name [index_type];

其中

data_type :指定的是数组成员数据类型,可以是定宽数组允许的任何类型;

array_name:指定关联数组的名字,符合标识符命名规则,当然每个公司命名规范可能不一样;

index_type:关联数组索引的数据类型,如果指定了索引数据类型,那么对于数组元素访问时索引就必须要匹配指定的索引数据类型。同时索引的类型也可以使用通配符,此时对于同一数组中元素索引时,索引的数据类型就可以不一样;

下面主要针对不同的index_type和关联数组中常用到的方法进行示例说明。

2  关联数组不同的索引类型

2.1 index_type为string

【示例】

C语言

【仿真结果】 

C语言

示例中定义了两个数组,arr1数组的索引类型为string,数组中每个元素的类型为int,再给arr1初始化时,通过“键:值对”实现对应元素的初始化,arr1[First]的值为’hAB,arr2[Second]的值为’hCD,在访问arr1中的元素时,也可以如示例中通过索引“First”和“Second”来访问。通过该示例,对于关联数组中特定索引元素的空间分配赋值可以通过{index:value} 方式实现。

2.2 index_type为class

【示例】 

C语言

【仿真结果】 

C语言

示例中,声明的关联数组arr的索引类型为packet(class),packet声明了三个句柄,pkt1和pkt2都指向了创建的对象,pkt3为空句柄null,通过数组名.[句柄]的方式实现了对于特定句柄作为索引的数组元素的赋值操作。示例中,通过foreach遍历数组中所有元素,通过仿真结果可以看到其中所有的赋值操作都赋值成功。这里有一点需要注意,尽管句柄pkt3为null,对其的赋值操作仍然是成功的,但是需要注意的是,如果再声明一个pkt4空句柄,并且在“arr[pkt3] = ‘hC”之后给arr[pkt4]赋值为”’hD”,那么对arr[pkt3]中的值将会被覆盖掉,这是因为pkt3和pkt4的值相同都是null,所以后续如果进行对数组的遍历访问操作,访问的结果还是只能访问到三个元素,而不是四个,即对于索引类型为class的关联数组,关联数组的索引句柄可以为null,且所有句柄值为null的数组元素指向同一个值。同时通过示例注意到,关联数组中元素在使用foreach进行遍历时,并不是按照数组元素初始化的顺序进行输出的,而是根据索引类型排列相对顺序的。

2.3 index_type不能为4值逻辑变量

【示例】 

C语言

【仿真结果】 

C语言

示例中,L4_type为自定义的两位logic类型,因为logic具有(0、1、x、z四种),数组arr的索引类型使用的就是L4_type,在给数组中元素进行初始化时,使用“2’hx”和“2’hz”作为索引,此时进行仿真时会产生提示信息(不同仿真工具产生的信息级别不同),通过foreach遍历数组,数组中并不包含对于索引为“2’hx”和“2’hz”的数组元素,即关联数组的数组索引不能为“x”和“z”

2.4 index_type为*

如果index_type没有指定类型,而是使用通配符“*”时,虽然貌似通配符可以匹配任何类型,但是在SystemVerilog中,此时的“通配符*”只能匹配任何的integral数据类型(参见IEEE1800-2017 6.11),对于其他非integral类型不能用于匹配!

【示例】 

C语言

【仿真结果】 

C语言

示例中,关联数组arr声明索引类型时使用了“*”,在initial块中,将句柄pkt作为数组的索引进行赋值操作。对上述代码编译时仿真器报错,这主要是因为class不属于SystemVerilog中定义的integral类型。那么index_type为“*”的关联数组中索引的类型是不是必须要一致呢?看下例。

【示例】 

C语言

【仿真结果】 

C语言

示例中,关联数组arr在声明时index_type为“*”,并且在通过不同类型的索引对数组arr中相应元素进行了赋值操作,并且将这些元素通过不同类型的索引都打印了出了。虽然这些索引的类型不同,但是这些类型有一个共同点都属于SystemVerilog中的integral类型,可见关联数组的索引如果为“*”的话,在通过不同类型索引给数组元素赋值时,这些索引必须都是integral类型。

其实从一个侧面可以体会到,SystemVerilog中所有的integral类型在某种程度上可以相互转换,类似于C语言中的不同数据类型之间的自动转换一样。另外,可能有人会问,为什么对于这个数组打印消息是不使用foreach结构呢?请看下例。

【示例】不能使用foreach结构 

C语言

【仿真结果】 

C语言

示例中,关联数组arr声明时其中index_type使用了“*”,然后给arr中相关元素通过不同的索引类型进行了赋值初始化操作,最后企图使用foreach遍历arr时报错,正如报错信息显示,当关联数组声明时索引为“*”,那么该数组不能使用使用foreach对该数组进行遍历

3 关联数组常用方法

关联数组中在使用时常会使用一些内建的方法,这些常用的方法如下表所示,

方法名 作用
num()/size() function int num();
function int size();
返回当前数组元素个数
delete(index) function void delete([input index]);
删除指定索引的数组元素
exists(index) function int exists(input index);
检查指定索引的数组元素是否存在,如果存在返回1,否则为0
first(var) function int first(ref index);
将数组第一个元素的索引赋给变量var
last(var) function int last(ref index);
将数组最后一个元素的索引赋给变量var
next(var) function int next(ref index);
将数组当前元素的下一个元素的索引赋给变量var
prev(var) function int prev(ref index);
将数组当前元素的上一个元素的索引赋给变量var

那么,关联数组的这些方法如何使用呢?下面通过示例了解下。

【示例】num(),exists(),delete(index) 

C语言

【仿真结果】 

C语言

示例中,通过“数组名.num()”的方式获取到当前数组中元素的个数。然后通过foreach遍历了数组中所有的元素。通过“数组名.exists(索引号)”的方式,可以检测数组中是否存在指定索引的元素。

当通过“数组名.delete(索引号)”的方式可以删除数组中指定索引号的数组元素,示例中通过arr.delete(1)删除了数组中索引号为“1”的元素后,再次调用arr.exists(1)检查数组中是否还存在索引号为“1”的元素,通过仿真结果可以看到,数组中索引号为“1”的元素已经被删除。

这里需要注意,示例中使用的三个方法的格式分别是:“数组名.num()”、“数组名.exists(索引号)”和“数组名.delete(索引序号)”,其中delete后也可以不带索引号,此时将会将整个数组“清空”!另外,在使用时一定要注意,关联数组如果不对其进行任何有效写入时,数组的大小时为0。、,即关联数组在使用之前是不会耗费存储空间的。

【示例】first(),next()

C语言

【仿真结果】 

C语言

示例中,在initial过程块中,首先通过“数组名.first(变量名)”的方式将数组arr中第一个(最小)索引的值赋给给定的索引变量temp(注意变量类型与数组索引类型一致),但是此时arr中没有任何元素,所以此时通过数组名调用“arr.first(temp)”方法返回0,即输出“1st entry does not exist!”。

通过for循环对数组进行初始化,之后再次“arr.first(temp)”,因为数组中有元素,所以first()方法返回非零,同时将初始化后数组中第一个(最小)索引赋值给temp。

然后通过“arr.next(temp)”获取比给定的索引值(temp)大的最小索引值,如果存在下一项,则将下一项的索引赋给索引变量temp,因为给定的temp此时值为0,所以next()方法调用后,将数组下一个元素的索引赋给了变量temp,所以仿真结果此时输出temp值变为了1.示例的最后do...while()循环将first()和next()配合使用,有效地遍历数组。

这里需要注意,示例中使用的方法的格式分别是:“数组名.first(索引变量)”和“数组名.next(索引变量)”。

【示例】 

C语言

【仿真结果】 

C语言

示例中,在initial过程块中,首先通过for循环对数组进行初始化,之后执行“arr.last(temp)”,因为数组中有元素,所以last()方法返回非零,同时将初始化后数组中最后(最大)索引赋值给temp。

然后通过“arr.prev(temp)”获取比给定的索引值(temp)小的最大索引值,如果存在上一项,则将上一项的索引赋给索引变量temp,因为调用last(temp)方法后给temp的值为3,所以prev()方法调用后,将数组上一个元素的索引赋给了变量temp,所以仿真结果此时输出索引为temp的数组元素的值输出.示例的最后将last()和prev()配合使用可以有效地遍历数组

这里需要注意,示例中使用的方法的格式分别是:“数组名.last(索引变量)”和“数组名.prev(索引变量)”。

上述first()、next()、last()和prev()这些方法在使用的时候一定要注意,如果关联数组索引类型为“*”的话,这些方法不能使用。





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分