Systemverilog中数组、队列的用法
- 前言
- 数据类型
- 定宽数组
- 动态数组
- 关联数组
- 队列
- 常用的数组操作方法
- 注意事项
前言
在数字电路验证与硬件设计中,SystemVerilog(SV)提供了丰富且灵活的数据结构,其中数组与队列是最常用、也最基础的数据组织方式。无论是编写测试平台(testbench)、搭建验证环境,还是进行数据建模,都离不开对它们的熟练运用。
本文将从定宽数组、动态数组、关联数组和队列四种核心数据结构入手,逐一介绍它们的声明方式、使用场景与典型操作,并对比它们之间的区别。
数据类型
- 无符号数据类型:
- 双状态类型:
- bit b :双状态单bit数;
- bit [15:0] b:双状态16bit数;
- int unsigned b:双状态32bit无符号整数;
- 四状态类型:
- time b:四状态64bit无符号整数;
- logic b:四状态1bit无符号整数;
- 双状态类型:
- 有符号数据类型:
- 双状态类型:
- int b:双状态32bit有符号整数;
- byte b:双状态8bit有符号整数;
- shortint b:双状态16bit有符号整数;
- longint b:双状态64bit有符号整数;
- 四状态类型:
- integer b:四状态32bit有符号整数;
- 双状态类型:
定宽数组
顾名思义,定宽数组是指其元素个数在编译时即确定,不可改变。
定宽数组的声明:
// 定宽单维数组inta[0:15];// 16个整数inta[16];// 16个整数intb[6]='{6,2,9,8,6,5};// 对数组进行初始化//定宽多维数组inta[0:15][0:3];// 16*4个整数inta[16][4];// 16*4个整数动态数组
动态数组是指其大小在运行时决定,可以动态调整其大小,这样就不会造成存储空间的浪费,因此动态数组在声明的时候使用的是空下标,使用时必须调用new[]构造符来分配空间。
intdyn[];dyn=new[6];//分配6个元素dyn[0]=8;//第一个值赋值为8dyn=new[20](dyn);// 首先分配20个新元素,并将原本的dyn数组复制给开始的6个元素,最终指向一个20个元素的数组dyn=new[100];//分配100个新元素- 只要基本数据类型相同,定宽数组和动态数组之间就可以互相赋值,在元素数目相同的情况下,就可以把动态数组的值复制到定宽数组中;
关联数组
关联数组使用一个索引(键)来访问数组中的元素,其索引可以是整数、字符串或其他数据类型。与定宽数组和动态数组不同,关联数组在声明时不需要指定大小,其存储空间是在使用时按需动态分配的,适合用于稀疏存储或按名字查找的场景。
// 关联数组的声明:使用 [索引类型] 指定键的类型intassoc[string];// 以字符串为键,值为整数// 赋值:通过键来写入元素assoc["apple"]=3;assoc["banana"]=5;assoc["cherry"]=8;// 遍历:foreach 会遍历关联数组中的所有键值对foreach(assoc[key])begin $display("assoc[%s] = %0d",key,assoc[key]);end// 删除:删除指定键对应的元素assoc.delete("banana");// 检查某个键是否存在if(assoc.exists("apple"))begin $display("apple 存在,值为 %0d",assoc["apple"]);end// 删除整个关联数组assoc.delete();关联数组与动态数组的区别:
- 索引类型:关联数组的索引(键)可以是整数、字符串等任意数据类型;动态数组的索引只能是整数。
- 存储方式:关联数组按需分配存储空间,适合稀疏存储;动态数组在声明后需用
new[]一次性分配连续空间。 - 大小控制:关联数组无需预先指定大小,元素随赋值自动增加;动态数组的大小由
new[]显式分配,且空间连续。 - 使用场景:关联数组适合按键名查找、稀疏数据等场景;动态数组适合需要连续存储、按序号访问的场景。
队列
队列可以在任何地方增加或删除元素,其在性能上的损失比动态数组小的多。其与数组类似,可以通过索引实现对任一元素的访问。
q[$]={0,1,6,9};// 对队列进行初始化,队列初始化时不需要'q.delete(1);// 删除队列中的#1元素,执行后的结果为{1,6,9}q.push_front(2);// {2,1,6,9} 在队列前面插入q.push_back(7);// {2,1,6,9,7} 在队列后面插入q.pop_front();// {1,6,9,7} 将队列前面的数据pop出q.pop_back();// {1,6,9} 将队列后面的数据pop出q=q[1:$];// {6,9} ,$放在左边就表示最小值,放在右边就表示最大值z=q[$];// 从队列末尾取出数据,z=9q.delete();//删除队列q={};//删除队列队列与关联数组的区别:
- 索引类型:关联数组的索引(键)可以是整数、字符串等任意数据类型,通过键直接访问元素;队列的索引只能是整数,且元素按插入顺序排列,通过序号访问。
- 存储方式:关联数组按需分配存储空间,适合稀疏存储;队列在物理上是一段连续空间,元素紧密排列。
- 插入/删除位置:关联数组通过键删除任意元素,删除后不影响其他元素;队列支持在头部(
push_front/pop_front)和尾部(push_back/pop_back)高效地插入或删除元素,中间位置的插入删除性能较差。 - 遍历顺序:关联数组的遍历顺序不固定(由内部哈希决定);队列的遍历顺序与元素插入顺序一致。
- 使用场景:关联数组适合按键名查找、稀疏数据等场景;队列适合需要按顺序处理数据、频繁在两端增删元素的场景,如 FIFO 缓冲。
常用的数组操作方法
- for循环
bit[31:0]aaa[6];for(inti=0;i<$size(aaa);i++)begin//采用$size返回数组宽度aaa[i]=i;end- foreach循环
foreach(md[i,j])md[i][j]=0;//多维数组的下标用逗号隔开放到同一个方括号中- 返回最大最小值
q.min();q.max();- 去掉重复值
q.unique();- 数组的排序
q.reverse();// 翻转q.sort();// 顺序排序q.rsort();// 倒序排序q.shuffle();// 乱序- 数组中元素的计算
q.sum();// 计算数字内元素的和q.product();// 计算数字内元素的积q.and();// 数组内元素与q.or();// 数组内元素或q.xor();// 数组内元素异或- 元素的查找
intlzz[]='{6,7,8,6,1,2,9},zz[$],cc;// 下述例子中的item是缺省的名字,也可以指定为其他名字zz=lzz.findwith(item>3);// {6,7,8,6,9},找出大于3的元素zz=lzz.find_indexwith(item>3);// {0,1,2,3,6},找出大于3的元素的索引zz=lzz.find_firstwith(item%6==0);// {6},找出第一个被6整除的元素zz=lzz.find_first_indexwith(item%6==0);// {0},找出第一个被6整除的元素的索引zz=lzz.find_lastwith(item%6==0);// {6},找出最后一个被6整除的元素zz=lzz.find_last_indexwith(item%6==0);// {3},找出最后一个被6整除的元素的索引cc=lzz.sum(x)with(x>6);// {0,1,1,0,0,0,1} , c=3 , sum函数计算时用的是数组元素的位宽注意事项
- 可以采用
$isunknown()来检测数据内部是否有X态或者Z态; - 若代码中从一个越界的地址中读取数据,那么SV将返回数组元素类型的缺省值。四状态类型的返回
X,双状态类型的返回0; - 用
%p可以打印数组、结构和类等; - 定宽数组:声明后大小不可改变,访问时需注意下标范围,越界访问会返回缺省值(四状态返回
X,双状态返回0); - 动态数组:使用前必须调用
new[]分配空间,否则访问会报错;重新分配时若未使用(dyn)复制旧数据,原数据会丢失; - 关联数组:遍历顺序不固定(由内部哈希决定),不要依赖遍历顺序;删除元素用
delete(键),删除整个数组用delete();访问不存在的键会返回缺省值,建议先用exists()检查; - 队列:
push_front/pop_front在头部操作,push_back/pop_back在尾部操作,性能最优;中间位置插入删除性能较差;$表示队列边界,q[$]取末尾元素,q[0]取头部元素; - 数组方法:
sort/rsort/reverse/shuffle会直接修改原数组;sum/product/and/or/xor等归约方法返回结果但不修改原数组;find系列方法返回新数组,不修改原数组; - foreach遍历:多维数组的下标用逗号隔开放在同一个方括号中,如
foreach(md[i,j]);遍历关联数组时,key为键名,assoc[key]为对应值; - 数组复制:定宽数组和动态数组之间赋值时,要求基本数据类型相同且元素数目一致,否则可能产生截断或填充;
- 队列初始化:队列初始化时不需要
',直接使用q[$] = {0,1,6,9};即可;