「SV4V」2 Data Structure

System Verilog新的数据类型

  • 双状态数据类型:更好的性能,更低的内存消耗
  • 队列、动态和关联数组:减少内存消耗,支持搜索和分类功能
  • 类和结构:支持抽象数据结构
  • 联合和合并结构:对同一数据有多种视图(view)
  • 字符串:支持内建的字符序列
  • 枚举类型:增加可读性
  1. 内建数据类型

    • 四状态数据类型:0,1,Z,X
      • 变量(reg)、线网(wire)
      • 逻辑(logic)类型
      • 双状态数据类型:0,1
        • bit,byte,shortint,int,longint,real
  2. 定宽数组

    • 声明

      1
      2
      3
      4
      int lo_hi[0:15];
      int c_style[16];
      int array2[0:7][0:3];
      int c_style_array2[8][4];

      Note: System Verilog 仿真器在存放数组元素时使用32比特的字边界,byte、shortint、int存放在一个字中,longint存放到两个字中。

    • 常量数组

      • 初始化:‘{ }

        1
        2
        3
        4
        5
        int ascend[4] = '{0,1,2,3};
        int descend[5];
        descend[0:2] = '{5,6,7};
        ascend = '{4{8}};
        descend = '{9,8,default:1};
    • 基本数组操作:for,foreach

      • 一维数组

        1
        2
        3
        4
        5
        6
        7
        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
      • 多维数组 md[i, j]

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        int md[2][3] = '{'{0,1,2},'{3,4,5}};
        initial begin
        foreach(md[i,j])
        $display("md[%0d][%0d]=%0d",i,j,md[i][j]);

        foreach(md[i]) begin
        $write("%2d:",i);
        foreach(md[,j])
        $write("%3d", md[i][j]);
        $display;
        end
        end
    • 基本数组操作:聚合复制,比较

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      initial begin
      bit [31:0] src[5] = '{0,1,2,3,4},
      dst[5] = '{5,4,3,2,1};
      // 聚合比较
      if (src == dst)
      $display("src == dst");
      else
      $display("src!=dst");
      // 聚合复制
      dst = src;
      end
    • 同时使用位下标和数组下标 (Verilog-2001)

      1
      2
      3
      4
      5
      6
      initial begin
      bit [31:0] src[5] = '{5{5}};
      $display(src[0],, // 'd5
      src[0][0],, // 'b1
      src[0][2:1]); // 'b10
      end
    • 合并数组:存放方式是连续的bit集合

      • Principle 0 合并的位和数组的大小是数据类型的一部分

      • Principle 1 数组大小定义的格式必须是[msb:lsb],不是[size]

      • Principle 2 合并数组和非合并数组可以混合使用

      • Principle 3 敏感信号只能是标量或者合并数组

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        bit [3:0] [7:0] bytes;  // 4个字节组成32比特
        bytes = 32'hCafe_Data;
        $display(bytes,, // 'hCafe_Data
        bytes[3],, // 'hCA
        bytes[3][7]); // 'b1

        bit [3:0] [7:0] barray [3]; // 具有3个合并元素的非合并数组
        barray[0] = 32'h0123_4567; // 使用一个下标可以得到一个字的数据
        barray[0][3] = 8'h01; // 使用两个下标可以得到一个字节的数据
        barray[0][1][6] = 1'b1; // 使用三个下标可以得到一个比特位的数据

        @(barray) // 错误
        @(barray[0] or barray[1] or barray[2]) // 正确
  3. 动态数组

    • 声明时使用空的下标[],仿真运行时调用new[]分配空间

    • 内建子程序(routines):delete,size

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      int dyn[], d2[];
      initial begin
      dyn = new[5]; // 分配空间:5个元素
      foreach (dyn[j]) dyn[j] = j; // 初始化
      d2 = dyn; // 复制动态数组
      d2[0] = 5; // 修改元素值
      dyn = new[20](dyn); // 分配20个整数值,复制dyn 5个值,删除原dyn
      dyn = new[100]; // 分配100个整数值,删除原dyn 20个值的存储
      dyn.delete(); // 删除所有元素
      end
  4. 队列

    • 声明时使用带美元符号的下标[$]

    • 可以使用方法(method)在队列中增加和删除元素

    • 可以使用字下标串联替代方法

      • [$:2] 中\$代表最小值, [1:\$]中\$代表最大值
    • 队列常量(literal)只有大括号没有数组常量中开头的单引号

    • 不要对队列使用构造函数new[]

    • 可以把定宽或动态数组的值赋给队列

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      int j = 1,
      q2[$] = {3,4},
      q[$] = {0,2,5};

      initial begin
      q.insert(1,j); // {0,1,2,5}
      q.insert(3,q2); // {0,1,2,3,4,5}
      q.delete(1); // {0,2,3,4,5}
      q.push_front(6); // {6,0,2,3,4,5}
      j=q.pop_back; // {6,0,2,3,4}, j = 5
      q.push_back(8); // {6,0,2,3,4,8}
      j=q.pop_front; // {0,2,3,4,8}, j = 6
      foreach(q[i])
      $display(q[i]);
      q.delete();

      q = {q[0],j,q[1:$]);
      q = {q[0:2],q2,q[3:$]};
      q = {q[0],q[2:$]};
      q = {6,q};
      j = q[$];
      q = q[0:$-1];
      q = {}; // 删除队列
      end
  5. 关联数组

    • 保存稀疏矩阵的元素:超大地址空间,但只为实际写入的元素分配空间。

    • 仿真器可以采用树或哈希表的形式存放关联数组。

    • 声明时在方括号中放置数据类型,如[int],[packet]。

    • 内建方法:num, exists

    • 可以使用字符串索引寻址

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      initial begin
      bit [63:0] assoc[bit[63:0]], idx = 1;

      // 对稀疏分布的元素进行初始化:1,2,4,8,16,...
      repeat (64) begin
      assoc[idx] = idx;
      idx = idx << 1;
      end

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

      // 使用函数遍历数组
      if (assoc.first(idx)) begin // 得到第一个索引
      do
      $display("assoc[%h]=%h",idx,assoc[idx]);
      while (assoc.next(idx)); // 得到下一个索引
      end

      // 找到并删除第一个元素
      assoc.first(idx);
      assoc.delete(idx);
      $display("The array now has %0d elements",assoc.num);
      end

      // 带字符串索引的关联数组
      /* 输入文件内容如下:
      42 min_address
      1492 max_address
      */

      int switch[string], min_address, max_address;
      initial begin
      int i,r,file;
      string s;
      file=$fopen("switch.txt","r");
      while(!$feof(file)) begin
      r = $fscanf(file,"%d %s", i,s);
      switch[s] = i;
      end
      $fclose(file);

      min_address = switch["min_address"]; // 获取最小地址值,缺省为0
      if (switch.exists("max_address")) // 获取最大地址值,缺省为1000
      max_address = switch["max_address"];
      else
      max_address = 1000;
      foreach (switch[s]) // 打印数组所有元素
      $display("switch['%s']=%d",s,switch[s]);
      end
  6. 链表

    • 应避免使用链表,优先使用队列。
  7. 数组的方法

    • 对象:非合并数组类型

    • 数组缩减方法:sum、product、and、or、xor

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      bit on[10];
      int total;

      initial begin
      foreach(on[i])
      on[i] = i;

      $display("on.sum = %0d", on.sum); // 1
      $display("on.sum = %0d", on.sum + 32'd0); // 5
      total = on.sum;
      $display("total = %d", total); // 5
      $display("int sum = %d", on.sum with (int'(item))); // 5
      end

      int count,total,d[]='{9,1,8,3,4,4};
      count = d.sum with (item > 7); // 2: {9,8}
      total = d.sum with ((item>7) * item); // 17: 9+8
      count = d.sum with (item < 8); // 4: {1,3,4,4}
      total = d.sum with (item < 8? item : 0); // 12: 1+3+4+4
      count = d.sum with (item == 4); // 2: {4,4}
    • 随机选取一个元素

      • $urandom_range(\$szie(array)-1) : 定宽数组、队列、动态数组、关联数组

      • $urandom_range(array.size()-1):队列、动态数组

        1
        2
        3
        4
        5
        6
        7
        8
        9
        // 关联数组中取一个元素,需要逐个访问它之前的元素
        int aa[int], rand_idx, element, count;
        element = $urandom_range(aa.size()-1);
        foreach(aa[i])
        if (count++ == element) begin
        rand_idx = i;
        break;
        end
        $display("%0d element aa[%0d]=%0d", element, rand_idx, aa[rand_idx]);
    • 数组定位方法:min、max、unique、find

      • 返回值:队列

      • 条件语句with中,item是重复参数,代表数组中一个单独的元素

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        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}
        tq = f.find with (item > 3); // {6,6,8,6}
        tq = f.find_index with (item > 3); // {1,3,4,5}
        tq = f.find_first with (item > 99); // {}
        tq = f.find_first_index with (item == 6); // {1}
        tq = f.find_last with (item == 6); // {6}
        tq = f.find_last_index with (item == 6); // {5}
    • 数组的排序:reverse、sort、rsort、shuffle

      • reverse和shuffle方法不能带with条件语句,作用范围是整个数组

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        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}

        // 对结构数组排序
        struct packed {byte red,green,blue;} c[];
        initial begin
        c=new[100];
        foreach(c[i])
        c[i] = $urandom;
        c.sort with (item.red); // 只对红色像素进行排序
        c.sort(x) with ({x.green,x.blue}); // 先对绿色像素后对蓝色像素进行排序
        end
  8. 选择存储类型

    • 灵活性
    • 存储器用量
    • 速度
    • 排序
    • 选择最优的数据结构
  9. 使用typedef创建新类型

    1
    2
    3
    // uint定义:32比特双状态无符号数
    typedef bit [31:0] uint;
    typedef int unsigned uint;
  10. 自定义结构

    1
    2
    3
    // 声明
    typedef struct {bit [7:0] r,g,b;} pixel_s;
    pixel_s my_pixel;

---------Thanks for your attention---------
0%