SV学习笔记(八)-数组

数组

非组合型数组

  • 对于verilog,数组经常会被用来做数据存储,例如:

    • reg [15:0] RAM [0:4095];
  • SV将Verilog这种声明数组的方式称之为非组合型声明,即数组中的成员之间存储数据都是相互独立的

    • 例如:wire [7:0] table [3:0];

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ZTYurDG-1692258845709)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230612145855217.png)]

  • SV保留了非组合型数组声明方式,并且拓展了允许的类型,包括eventlogicbitbyteintlongintshortrealreal

  • SV页保留了Verilog索引非组合型数组或者数组片段的能力,这种方式为数组及数组片段的拷贝带来了方便

  • 声明,以下两种皆可

    logic [31:0] data [1024];
    logic [31:0] data [0:1023];
    

组合型数组

  • SV将Verilog的向量作为组合型数组声明方式

    wire [3:0] select;
    reg [63:0] data;
    
  • SV也进一步允许多维组合型数组的声明

    logic [3:0][7:0] data;
    
  • 组合型数组会进一步规范数据的存储方式,而不需要关心编译或者操作系统的区别,数据是连续存放的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jvctUmXu-1692258845710)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20230613142544788.png)]

  • 组合型除了可以运用数组声明,也可以用来定义结构体的存储方式

    typedef struct packed {
        logic [7:0] crc;
        logic [63:0] data;
    } data_word;			// 结构体类型:data_word
    data_word[7:0] darray; // 数组中每个元素都是一个结构体
    
  • 组合型数组和其它数组片段也可以灵活选择,用来拷贝和赋值

混合型数组

  • 维度怎么看
    • 例如:int[1:0][2:0] arr [3:0][4:0]
      • 先看arr的右边[3:0][4:0],维度从左至右,所以是4*5
      • 再看arr的左边[1:0][2:0],维度从左至右,所以是2*3
      • 所以总的维度:4*5*2*3

数组初始化

  • 组合型(packed)数组初始化时,同向量初始化一致

    logic[3:0][7:0] a = 32'h0;
    logic[3:0][7:0] b = {16'hz, 16'h0};
    logic[3:0][7:0] c = {16{2'b01}};
    
  • 非组合型(unpacked)数组初始化时,需要通过'{}来对数组的每个维度进行赋值

    int d [0:1][0:3] = '{'{7, 3, 0, 5}, '{2, 0, 1, 6}};
    // 最终赋值结果:
    d[0][0] == 7
    d[0][1] == 3
    d[0][2] == 0
    d[0][3] == 5
    d[1][0] == 2
    d[1][1] == 0
    d[1][2] == 1
    d[1][3] == 6
    
  • 非组合型数组在初始化时,也可以类似结构体初始化,通过'{}default关键词完成

    int a1 [0:7][1:1023] = '{default:8'h55};
    // 每个元素的值默认为16进制的55
    
  • 非组合型数组的数据成员或数组本身均可以为其赋值

    byte a [0:3][0:3];
    a[1][0] = 8'h5;		// 单个维度赋值
    a[3] = '{'hF, 'hA, 'hC, 'hE};	// 低维集体赋值
    

数组的拷贝

  • 对于组合型数组,由于数组会被视为向量,因此当赋值左右的大小和唯独不同时,也可以做赋值
  • 当尺寸不相同时,会通过截取或者拓展右侧操作数的方式(高位被拓展为0或者高位被截取)来对左侧操作数赋值
  • 对于非组合型数组,在发生数组间拷贝时,要求左右两侧的维度和大小必须严格一致
  • 非组合型数组也无法直接赋值给组合型数组,组合型数组也无法直接赋值给非组合型数组

数组的循环

foreach循环
  • SV添加foreach循环来对一维或者多维数组进行循环索引,而不需要指定该数组的维度大小

    int a[4:0][4:0];
    foreach (a[i, j]) begin;
        a[i][j] = i+j;
        $display("i is %0d, j is %0d, a is %0d", i,j,a[i][j]);
    end
    
  • foreach循环结构中的变量无需声明

  • foreach循环结构中的变量是只读的,其作用域在此循环结构中有效

系统函数

  • $dimensions(array_name)用来返回数组的维度

  • $left(arry_name,dimension)返回指定维度的最左索引值(msb)

    logic [1:2][7:0] word [0:3][4:1];
    $left(word,1) // 返回:0
    $left(word,2) // 返回:4
    $left(word,3) // 返回:1
    $left(word,4) // 返回:7
    
  • $left()类似的还有$right(array_name, dimension)$low(array_name, dimension)$high(array_name, dimension)

  • $size(array_name, dimension)可以返回指定维度的尺寸大小

  • $increment(array_name, dimension),如果指定维度的最左索引大于或等于最右索引,返回1,否则返回-1

  • $bits(expression)可以返回数组存储的比特数

    wire [3:0][7:0] a [0:15];
    $bits(a); // 返回512(4*8*16)
    struct packed {
        byte tag;
        logic [31:0] addr;
    } b;
    $bits(b);  // 返回40,tag占8个bit,addr占32个bit
    

动态数组

  • 与之前定长数组相比,SV还提供了可以重新确定大小的动态数组

  • 动态数组在声明时需要使用[],这表示不会再编译时为其指定尺寸,而是在仿真运行时来确定

  • 动态数组一开始为空,需要使用new[]来为其分配空间

    int dyn[],d2[];   // 声明动态数组的时候为空
    initial begin
        dyn = new[5];	// 	使用new分配空间,5个元素值默认为0
        foreach (dyn[j]) // 使用循环,将索引值赋值给数组内的元素
            dyn[j] = j;
        d2 = dyn;		// 将一个数组拷贝给另一个数组,d2也是五个元素了
        d2[0] = 5;		// d2[0]原来是0,重新赋值为5,syn[0]还是为0
        $dispaly(dyn[0], d2[0]);	// 打印结果:0,5
        dyn = new[20](dyn);	// 重新给动态数组分配20个元素的空间,每个元素都为0,再将原来dyn数组的元素值赋值给dyn的前5个元素
        dyn = new[100]; // 重新给动态数组分配100个元素的空间,每个元素都为0
        dyn.delete(); // 清空一个动态数组
    end
    
  • 内建方法size()可以返回动态数组的大小(元素个数)

  • 内建方法delete()可以清空动态数组,使其尺寸变为0

  • 动态数组在声明时也可以完成其初始化

    bit [7:0] mask[] = '{
        8'b0000_0000, 8'b0000_0001,
        8'b0000_0011, 8'b0000_0111,
        8'b0000_1111, 8'b0001_1111,
        8'b0011_1111, 8'b0111_1111,
        8'b1111_1111
    };
    

队列

  • SV引入了队列类型,结合了数组和链表
  • 可以在队列的任何位置添加或删除数据成员
  • 可以通过索引来访问队列的任何成员
  • 通过[$]来声明队列,队列的索引值从0到$
  • 可以通过内建方法push_back(val)、push_front(val)、pop_back()和pop_front()来顺序添加或者移除并且获得数据成员
  • 可以通过insert(pos,val)来指定位置插入数据成员
  • 可以通过delete()来删除所有数据成员
int j = 1;
int q2[$] = {3,4};  // 声明一个队列q2,队列中有两个元素,元素类型为int
int q[$] = {0,2,3};	// 声明一个队列q,队列中有三个元素,元素类型为int
initial begin
    q.insert(1, j);	// 在队列q索引为1的位置插入元素i,队列变为{0,1,2,3}
    q.delete(1);	// 删除队列中索引为1的数据,队列变为{0,2,3}
    q.push_front(6);// 将6插入到队列的索引为0的位置,队列变为{6,0,2,3}
    j = q.pop_back();	// 从队列q尾部,索引为$的位置取出数据并赋值给j,队列变为{6,0,2}
    q.push_back(8);	// 将8插入到队列最后索引为$的位置,队列变为{6,0,2,8}
    j = q.pop_front();	// 从队列q前面,索引为0的位置取出数据并赋值给j,队列变为{0,2,8}
    foreach (q[i])		// 使用foreach循环获取队列元素
        $dispaly(q[i]);
    q.delete();			// 清空队列
    q = q2;		// 将q2拷贝给q
end
int j = 1,
q2[$] = {3,4},
q[$] = {0,2,5};
initial begin
    q = {q[0],j,q[1:$]}; // 将q索引为0的元素,j,q索引1到最后一个元素拼接在一起,组成一个新的队列赋值给q,q={0,1,2,5}
    q = {q[0:2],q2,q[3:4]}; // q={0,1,2,3,4,5}
    q = {q[0],q[2:$]};	// q={0,2,3,4,5}
    q = {6,q}; // q={6,0,2,3,4,5}
    j = q[$];
    q = q[0:$-1]; // 这两句合在一起等价于j=q.pop_back()
    q = {}; // 等价于q.delete()
end

关联数组

其它语言中,也有叫HASH/Dict,哈希/字典

  • 如果想要在仿真时创建一个大数组,也许动态数组是一个选择,不过有时候我们并不需要这么大的数组

  • 由于处理器在访问存储时的访问时随机散乱的,这意味在整个测试中,处理器也许只会访问几百个存储地址,而剩下大多数地址都将被初始化为0,浪费了仿真时的存储空间

  • SV引入了关联数组,可以用来存放散列的数据成员,这一点于其它脚本语言Perl或python类似,散列的索引类型除了整形以外,还可以为字符串或者其他类型,而散列存储的数据成员也可以为任意类型

在这里插入图片描述

bit [128:0] assoc[longint];	// 声明一个关联数组,数组名:assoc,索引类型:longint,值存储类型:bit[128:0]
longint idx = 1;	// 初始idx=1
repeat (60) begin	// 循环60次,受数据类型长度限制,64次循环,索引会出现内存溢出
    assoc[idx] = idx;	// assoc赋值
    idx = idx << 1;		// idx位运算,等价于idx*2
end
foreach (assoc[i])		// foreach循环,i依次为每个索引
    $display("assoc[%0d] = %0d",i,assoc[i]);
if (assoc.first(idx))	// 取出第一个索引,assoc.first(idx)返回值:1,idx值也是1,不过一个是int,一个是longint
    do 
        $display("assoc[%0d] = %0d", idx, assoc[idx]);
    while (assoc.next(idx));

数组方法

缩减方法
  • 基本的数组缩减方法是把一个数组缩减成一个值

  • 最常用的缩减方法是sum,它对数组中的所有元素求和

    byte b[$] = {2,3,4,5};
    int w;
    w = b.sum();  // w = 2+3+4+5
    w = b.product(); // w = 2*3*4*5
    w = b.and();	// w = 2&3&4&5=0000_0010&
    				//			   0000_0011&
    				//			   0000_0100&
    				//			   0000_0101	= 0000_0000
    
  • 其它数组缩减方法还有product(积)、and(与)、or(或)、xor(异或)

定位方法
  • 对于非合并数组,可以使用数组定位方法,其返回值将是一个队列而非一个数据成员

    int f[6] = '{1,6,2,6,8,6};
    int d[] = '{2,4,6,8,10};
    int q[$] = {1,3,5,7};
    int tq[$];
    tq = q.main();	// tq = {1} q的最小值
    tq = d.max();	// tq = {10} d的最大值
    tq = f.unique();// tq = {1,6,2,8} f去重
    
  • foreach可以实现数组的搜索,不过find…with查找满足条件的数据成员更方便

    int d[] = '{9,1,8,3,4,4};
    int tq[$];
    tq = d.find with (item > 3);	// 将大于3的数据组成一个队列赋值给tq,tq={9,8,44}
    // 等价于下面循环
    foreach (d[i])
        if (d[i] > 3)
            tq.push_back(d[i]);
    tq = d.find_index with (item > 3);	// 将大于3的数据的索引组成队列赋值给tq,tq={0,2,4,5}
    tq = d.find_first with (item > 7); // 将大于7的第一个数据组成队列赋值给tq,tq={9}
    tq = d.find_first_index with (item==8);	// 将第一个等于4的数据的索引组成队列赋值给tq,tq={4}
    tq = d.find_last with (item==4); // tq={4}
    tq = d.find_last_index with (item==4);	// tq={5}
    
排序方法
  • 可以通过排序方法改变数组中元素的顺序,可以对她们进行逆向或者乱序的排列

    int d[] = '{9,1,8,3,4,4};
    d.reverse();	// d = '{4,4,3,8,1,9} 反转
    d.sort();		// d = '{1,3,4,4,8,9} 顺序排列
    d.rsort();		// d = '{9,8,4,4,3,1} 倒序排列
    d.shuffle();	// d = '{9,4,3,8,1,4} 打乱顺序
    
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值