0%

SV之数组

本节主要介绍了SystemVerilog中非组合型与组合型数组的基本概念、动态数组、队列、关联数组等相关内容

非组合型与组合型

1.非组合型(unpacked)

  • SV将Verilog这种声明数组的方式称之为非组合型声明,即数组中成员之间存储数据都是相互独立的

  • 非组合型数组:在内存中非连续存放的数组

    1
    wire [7: 0] table [3:0];

    image-20230819214226718

2.组合型(packed)

  • 组合型数组:在内存中连续存放的数组

  • 很多SV仿真器在存放数组元素时使用32bit的字边界,所有byte、shortint、int都是存在一个字中,而longint则存放到两个字中,logic也是存放到两个字中

  • 维数在数组名左边意味着packed即告诉编译器要打包的意思,而在右边意味着unpacked即告诉编译器要不用打包的意思

  • 有当数组中的全部元素都packed时,才被成为组合数组,若只有低维packed,而高维没有仍旧是非组合数组

    1
    logic [3:0][7:0] data; //2-D packed array

    image-20230821134129052

  • 组合型也可以用来定义结构体的存储方式:

    1
    2
    3
    4
    5
    typedef struct packed{
    logic [7:0] crc;
    logic [63:0] data;
    }data_word;
    data_word [7:0] darray;
  • 组合型数组和其数组片段也可以灵活选择,用来拷贝和赋值

    1
    2
    3
    4
    5
    logic [3:0][7:0] data; //二维组合型数组

    wire [31:0] out = data; //将全部数组赋值
    wire sign = data[3][7]; //选择一个bit赋值
    wire [3:0] nib = data[0][3:0]; //选择部分切片赋值

数组的初始化与拷贝

1.初始化

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

    1
    2
    3
    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}};
  • 非组合型数组初始化时,对每一维以数组的方式进行初始化,需要通过'{}来对数组的每一个维度进行赋值(因为非组合数组中的每一个元素的都是独立的数据,因此必须维数与每一个维数中的元素个数都相等时,才可以赋值)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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;

2.拷贝

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

foreach循环结构

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

  • foreach循环结构中的变量无需声明

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

    1
    2
    3
    4
    int sum [1:8][1:3];
    foreach (sum[i, j]) begin
    sum[i][j] = i + j;
    end

系统函数

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

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

    1
    2
    3
    4
    5
    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
  • $size(array_name, dimension) //返回指定维度的尺寸大小

  • $bits(array_name) //返回数组存储的bit数目

    1
    wire [3:0][7:0] a [0:15]; //$bits(a) 将返回512

动态数组

  • 动态数组的声明:[data_type] [identifier_name] [];

    1
    2
    bit [7:0] stack[]; //动态数组,每个元素代表一个byte
    string names[]; //动态数组,每个元素代表一个string
  • 动态数组的操作与非组合型数组一致

  • 内存分配和初始化

    • 使用new()方法,分配了内存但没有对内存进行初始化

    • 利用列表直接进行初始化,其中隐含了内存分配

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //declaration
      bit [7:0] d_array1[];
      int d_array2[];

      //memory allocation
      d_array1 = new[4]; //dynamic array of 4 elements
      d_array2 = new[6]; //dynamic array of 6 elements

      //array initialization
      d_array1 = '{0,1,2,3};
      foreach(d_array2[j]) d_array2[j] = j;
  • 容量扩张

    1
    2
    3
    //Change the length of the array after declaration/initialization
    d_array1 = new[10]; //dynamic array of 10 elements
    // 以上语句中d_array1被重新分配了10个地址空间,d_array1中原有的数据被扔掉了
    • 用以下形式调用new()的话可以在扩充容量的同时保留d_array1中原有的数据
    1
    2
    //Allocate 6 new elements and retain values of 4 elements.
    d_array1 = new[10](d_array1);
  • 动态数组的复制:动态数组的复制的语法形式与上一节的扩容且保留原数据的调用方法相同,只不过生成的内存块赋给新的一个变量

    1
    d_array2 = new[d_array1.size()](d_array1); //创建了d_array1的一个副本
  • 动态数组的删除

    1
    d_array2.delete(); //删除所有元素

队列

  • 声明: [$] ,队列元素的标号从0到$

  • 队列不需要用new[ ]去创建内存空间,一开始其所占空间为0

  • 结合了链表和数组的优点,可以在任何地方添加或删除元素,并通过索引实现对任一元素的访问

  • 队列中的元素是连续存储的,故不需要使用单引号 ’ { }方式赋值,用{}就行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int q2[$] = {3, 4}, q[$] = {0, 2, 3};
    initial begin
    q.insert(1, j); //在下标1插入j(1),则q={0, 1, 2, 3}
    q.delete(1); //删除元素1,q={0, 2, 3}
    q.push_front(6); //在队首插入一个元素,q={6, 0, 2, 3}
    j = q.pop_back(); //在队尾取出一个元素,q={6, 0, 2},j = 3
    q.push_back(8); //在队尾插入一个元素,q={6, 0, 2, 8}
    j = q.pop_front(); //在队首取出一个元素,q={0, 2, 8},j = 6
    q.delete(); //删除整个队列
    end

关联数组

  • 关联数组实际上是一种查找表,内存空间直到被使用时才会分配,每个数据项都会有一个特定的“键(索引)”,索引的类型不局限于整型

  • 相对于一般的数组,关联数组的内存空间利用会更加充分,因为它是稀疏的,无需一开始就分配很大的内存空间

  • 关联数组也是一种unpacked数组

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    bit [63:0] assoc[int],idx = 1;
    repeat (64) begin //对稀疏分布的元素进行初始化
    assoc[idx] = idx; //assoc[1] = 1
    idx = idx << 1; //assoc[2] = 2,assoc[3] =4,...
    end

    forrach (assoc[i]) //使用foreach遍历数组
    $display("assoc[%h] = %h",i,assoc[i]);

    //找到并删除第一个元素
    assoc.first(idx);
    assoc.delete(idx);
  • 关联数组的一些方法:

    1
    2
    3
    4
    5
    6
    7
    num()  //返回数组长度
    delete() //删除指定元素或者所有元素
    exists() //检查是否元素存在,存在返回1,否则返回0
    first() //将指定的索引变量赋值为数组第一个索引的值
    last() //将指定的索引变量赋值为数组最后一个索引的值
    next() //索引变量被赋值为下一个条目的索引
    prev() //索引变量被赋值为上一个条目的索引

数组中的相关方法

1.缩减方法

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

    1
    2
    3
    4
    5
    byte b[$] = {2, 3, 4, 5};
    int w;
    w = b.sum(); //14 = 2 + 3 + 4 + 5
    w = b.product(); //120 = 2 * 3 * 4 * 5
    w = b.and(); //0000_0000 = 2 & 3 & 4 & 5

2.定位方法

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

    1
    2
    3
    4
    5
    6
    7
    int f[6] = '{1, 6, 2, 6, 8, 6}; //定长数组
    int d[] = '{2, 4, 6, 8, 10}; //动态数组
    int q[$] = {1, 3, 5, 7}, tq[$];

    tq = q.min(); //{1}
    tq = d.max(); //{10}
    tq = f.unique(); //{1, 6, 2, 8}

3.排序方法

  • 可以通过排序方法改变数组中元素的顺序

    1
    2
    3
    4
    5
    int d[] = '{9, 1, 8, 3, 4, 4};
    d.reverse(); //'{4, 4, 3, 8, 1, 9}
    d.sort(); //'{1, 3, 4, 4, 8, 9}
    d.rsort(); //'{9, 8, 4, 4, 3, 1}
    d.shuffle(); //'{9, 4, 3, 8, 1, 4}

Reference

欢迎来到ssy的世界