SystemVerilog基础(1):数据类型

目录

一、 Verilog  HDL中的数据类型

二、SystemVerilog的内建数据类型

  1. 逻辑类型:logic

  2. 双状态数据类型:bit, byte, shortint, int, longint

  3. 分类

  4. 注意事项

  5. 类型转换

    (1)隐式转换

    (2)显式转换

  (a)静态转换( ' )

  (b)动态转换( $cast )

三、定宽数组

  1. 定宽数组的声明

  2. 多维数组的维度和$size

  3. 合并数组、非合并数组

  4 定宽数组的初始化

  5. 数组的基本操作:foreach

四、动态数组

   1. 动态数组的声明

   2. 动态数组的初始化 

   3. 动态数组的其他操作

五、队列

六、关联数组

七、数组的方法


一、 Verilog  HDL中的数据类型

    Verilog HDL中有19种数据类型:

    1. 物理数据类型(与实际硬件电路映射)

        (1)线网(net)类型

                  ① wire, tri:标准连线型

                  ② wor, trior:多重驱动时,具有线或特性的连线型

                  ③ wand, trand:多重驱动时,具有线或特性的连线型

                  ④ trireg:具有电荷保持特性的连线型(用于电容节点的建模,缺省初始值为x)

                  ⑤ tri1, tri0:上拉/下拉电阻(若无驱动源,值为1/0)

                  ⑥ supply1, supply0:对电源(VCC)/地(GND)建模,即高电平1/低电平0

        (2)寄存器型:reg

        (3)存储器型:mem

    2. 抽象数据类型(辅助设计和验证)

        (1)整型:integer(32位有符号整数)

        (2)时间型:time(64位无符号数)

        (3)实型:real

        (4)参数型:parameter

二、SystemVerilog的内建数据类型

    SystemVerilog增加了很多新的数据类型,方便设计和验证:

    (1)双状态数据类型:提高仿真器性能,减少内存消耗

    (2)队列、动态数组、关联数组:减少内存消耗,自带搜索和分类功能

    (3)类和结构:抽象数据结构

    (4)字符串

    (5)枚举类型:增强代码可读性

  1. 逻辑类型:logic

        (1)可以由过程赋值语句或连续赋值语句赋值、被门单元和模块驱动

        (2)只能有一个驱动(否则编译时会报错)

  2. 双状态数据类型:bit, byte, shortint, int, longint

  3. 分类

        (1)逻辑类型

                四值逻辑:net-type(wire等),reg,integer,logic

                二值逻辑:bit,byte,shortint,int,longint

              * 四值逻辑 → 硬件(硬件设计)

              * 二值逻辑 → 软件(验证环境)

        (2)符号类型

                  有符号类型:integer,byte,shortint,int,longint

                  无符号类型:net-type,reg,bit,logic

  4. 注意事项

        (1)避免有符号变量、无符号变量的混用

                 例:

byte        signed_vec ;  // 8位有符号变量
bit [8:0]   result_vec ;  // 9位无符号变量

initial begin
    signed_vec   = 8'h80 ;
    result_vec   = signed_vec ;             // 'h180
    result_vec   = unsigned'(signed_vec) ;  // 'h080 
end

        (2)把双状态变量连接到被测设计的输出时,应检查未知值的传播

                 例:

logic [3:0]   x_vec = 'b111x ;
bit   [2:0]   b_vec          ;

initial begin
    b_vec = x_vec ;  // 'b110 (x → 0) 
end

  Tips: $isunknown( )可以在表达式的任意位出现X或Z时返回1。

5. 类型转换

systemverilog中的类型转换$cast_systemverilog string转int怎么实现-CSDN博客

    (1)隐式转换

               Verilog对整数和实数类型,或不同位宽的向量之间进行隐式转换。

    (2)显式转换

               (a)静态转换( ' )

                        静态转换在编译时完成,不对转换值进行检查。

                        ① 即使等号两侧的数据类型不匹配,也会进行强制转换。

                             例:将string类型转换为int类型

  string s = "abcd"  ;
  int    c           ;

  initial begin
      c = int'(c) ;
      $display($sformatf("c = %0d", c));  // c = 0
  
  end

                         ② 如果转换失败,我们也无从得知。

                              例:枚举类型的数值越界

  typedef enum {RED, BLUE, GREEN} COLOR_E;

  COLOR_E color ;
  int     c     ;

  initial begin
      c = 1              ;
      color = COLOR_E'(c); // BLUE
      $display($sformatf("color = %s (%0d)", color.name, color)); // color = BLUE (1)
      c     = 3          ;
      color = COLOR_E'(c); // 越界
      $display($sformatf("color = %s (%0d)", color.name, color)); // color =  (3)
  
  end
            (b)动态转换( $cast )

                     ① 在仿真时对转换结果进行动态检查(run-time check)。

                     ① $cast是function还是task?

                          答:既可以用作task,也可以用作function(返回int类型的0或1)。

                     ② 如果转换失败,destination变量的值不会发生变化。

                          用作function时,会返回0

  typedef enum {RED, BLUE, GREEN} COLOR_E;

  COLOR_E color ;
  int     c     ;

  initial begin
      c = 3 ;
      if(!$cast(color, c))
          $display("Cast fail for c = %0d", c); // Cast fail for c = 3
  end

                          用作task时,会报告run-time error

  typedef enum {RED, BLUE, GREEN} COLOR_E;

  COLOR_E color ;
  int     c     ;

  initial begin
      color = GREEN;
      $display("Before cast: color = %s (%0d)", color.name, color) ;
      c = 3 ;
      $cast(color, c) ;
      $display("After cast: color = %s (%0d)", color.name, color) ;
  end

// 仿真结果:
// # Before cast: color = GREEN (2)
// # ** Error: (vsim-3971) $cast to type 'enum int test.COLOR_E' from 'int' failed in file // D:/****.sv at line 12.
// #    Time: 0 ns  Iteration: 0  Instance: /test
// # After cast: color = GREEN (2)

三、定宽数组

  1. 定宽数组的声明

        在verilog中,数组的声明中必须给出索引的上、下界[m:n],例如[5:0], [0:5], [6:1]等都可以。

        SystemVerilog则允许更加便捷的声明方式,只给出数组宽度:

bit[31:0] array1 [0:3]; // 完整的声明,一维数组
int array2 [0:7][0:3];  // 完整的声明,二维数组

int array3 [8][4];      // 紧凑的声明

bit[31:0] array4 [4];   // 这样也可以

  2. 多维数组的维度和$size

        对于多维数组,如何确定高维和低维?可以使用“逆时针索引法”:图中的红色箭头,由高维度指向低维度。

        SystemVerilog的内建函数$size(array, n)返回指定维度的元素个数:

bit [3:0][4:0][5:0] a [0:1][0:2] ;

$display("$size(a)    = %0d", $size(a));      // 2: 默认为最高维
$display("$size(a, 1) = %0d", $size(a, 1));   // 2
$display("$size(a, 2) = %0d", $size(a, 2));   // 3
$display("$size(a, 3) = %0d", $size(a, 3));   // 4
$display("$size(a, 4) = %0d", $size(a, 4));   // 5
$display("$size(a, 5) = %0d", $size(a, 5));   // 6

  3. 合并数组、非合并数组

        合并数组(packed array)        :数组的维数全部在数组名的左侧。

                                                            (注意:合并数组的数据类型只能是bit或logic)

bit[3:0][7:0] packed_a1 ; // 这种声明方式更常见

bit[0:3][0:7] packed_a2 ;

        非合并数组(unpacked array):数组名的维数全部或部分在数组名的右侧。

                                                            (左侧为合并部分,右侧为非合并部分)

bit[7:0] unpacked_a1 [0:3] ; 
bit[7:0] unpacked_a2 [4] ; 
bit unpacked_a3 [4][8] ;

    那么,合并/非合并是什么意思?

    合并,即打包,仿真器会将这部分数据打包成一个向量,在内存中连续存储。

    非合并,即不打包,在内存中独立存储。

   (很多SV仿真器在存放数组元素时使用32bit的字边界,字的低位用来存放数据,高位不使用)

    注意:操作符@只能用于标量或合并数组。

  4 定宽数组的初始化

        (1)合并数组的初始化:可以看作矢量,整体赋值

bit [3:0][7:0] a ; // 高维为4,低维为8
bit [7:0][3:0] b ; // 高维为8,低维为4


a = 32'habcd_1234;       // 作为整体赋值
$display("a = %0h", a);  // abcd1234

b = a ;                  // 每个维度的元素数目不同,但可以直接赋值
$display("b = %0h", b);  // abcd1234

        (2)非合并数组的初始化:'{ }

byte unsigned a[4] ;
byte unsigned b[0:3] ; // 两种声明方式等价

bit c[4][2];           // 等价于[0:3][0:1]


a = '{'h00, 'h11, 'h22, 'h33} ;
b = '{'h00, 'h11, 'h22, 'h33} ;
c = '{'{'b1, 'b0}, '{'b0, 'b0}, '{'b1, 'b1}, '{'b0, 'b1}} ;

$display("a[0] = %h, b[0] = %h", a[0], b[0]);  // a[0] = 00, b[0] = 00
$display("a[1] = %h, b[1] = %h", a[1], b[1]);  // a[1] = 11, b[1] = 11
$display("a[2] = %h, b[2] = %h", a[2], b[2]);  // a[2] = 22, b[2] = 22
$display("a[3] = %h, b[3] = %h", a[3], b[3]);  // a[3] = 33, b[3] = 33

$display("c[0][0] = %b, c[0][1] = %b", c[0][0], c[0][1]);  // c[0][0] = 1, c[0][1] = 0
$display("c[1][0] = %b, c[1][1] = %b", c[1][0], c[1][1]);  // c[1][0] = 0, c[1][1] = 0
$display("c[2][0] = %b, c[2][1] = %b", c[2][0], c[2][1]);  // c[2][0] = 1, c[2][1] = 1
$display("c[3][0] = %b, c[3][1] = %b", c[3][0], c[3][1]);  // c[3][0] = 0, c[3][1] = 1

        (3)混合数组:合并部分可以整体赋值

bit[7:0] a [0:3]; 
bit[0:7] b [3:0]; // 两种声明方式有区别


initial begin

a = '{8'haa, 8'hbb, 8'hcc, 8'hdd}; // 合并部分可以整体赋值
b = '{8'haa, 8'hbb, 8'hcc, 8'hdd}; // 等价于:b = a

$display("a[0] = %0h, b[0] = %0h", a[0], b[0]);      // a[0] = aa, b[0] = dd
$display("a[1] = %0h, b[1] = %0h", a[1], b[1]);      // a[1] = bb, b[0] = cc
$display("a[2] = %0h, b[2] = %0h", a[2], b[2]);      // a[2] = cc, b[0] = bb
$display("a[3] = %0h, b[3] = %0h", a[3], b[3]);      // a[3] = dd, b[0] = aa

$display("a[3][7] = %b, b[0][7] = %b", a[3][7], b[0][7]); // a[3][7] = 1, b[0][7] = 1
$display("a[3][6] = %b, b[0][6] = %b", a[3][6], b[0][6]); // a[3][6] = 1, b[0][6] = 0
$display("a[3][5] = %b, b[0][5] = %b", a[3][5], b[0][5]); // a[3][5] = 0, b[0][5] = 1
$display("a[3][4] = %b, b[0][4] = %b", a[3][4], b[0][4]); // a[3][4] = 1, b[0][4] = 1

  5. 数组的基本操作:foreach

        在foreach循环中,System Verilog会自动遍历数组中的元素。

        索引变量自动声明,仅在循环内有效。

        (注意:数组的不同声明方式会影响索引变量的遍历顺序)

一维数组:

int array1 [2]  = '{45, 36} ; // [2]等价于[0:1]
foreach(array1[i]) begin
    $display("array1[%0d] = %0d", i, array1[i]);
end

// array1[0] = 45
// array1[1] = 36

int array2 [0:1]  = '{45, 36} ;
foreach(array2[i]) begin
    $display("array2[%0d] = %0d", i, array2[i]);
end

// array2[0] = 45
// array2[1] = 36

int array3 [1:0]  = '{45, 36} ;
foreach(array3[i]) begin
    $display("array3[%0d] = %0d", i, array3[i]);
end

// array3[1] = 45
// array3[0] = 36

多维数组:

int array4 [2][3] = '{'{1, 2, 3}, '{4, 5, 6}} ; 

foreach(array4[i,j]) begin
    $display("array4[%0d][%0d] = %0d", i, j, array4[i][j]);
end


// array4[0][0] = 1
// array4[0][1] = 2
// array4[0][2] = 3
// array4[1][0] = 4
// array4[1][1] = 5
// array4[1][2] = 6

i为高维度索引变量,j为低维度索引变量。

遍历的顺序为:先固定i,遍历j,再遍历i。

也可以只遍历其中一个维度:

      只遍历高维度:foreach(array[ i ])

      只遍历低维度:foreach(array[, j ])

四、动态数组

        定宽数组的宽度在编译时已经确定,而动态数组可以在仿真时分配空间或调整宽度,避免存储空间的浪费。

   1. 动态数组的声明

int d1[]       ; // 声明一个一维动态数组       

int d2[], d3[] ; // 声明两个相同数据类型的动态数组

bit[31:0] d4[] ; // 声明二维动态数组

   2. 动态数组的初始化 

d1 = new[3] ;               // 分配3个元素的空间
foreach(d1[i]) begin
    d1[i] = i ;             // 逐一初始化每个元素
end

d2 = '{0, 1, 2, 3} ;        // 分配空间并初始化

int d5[]= '{5, 5, 5, 5,5} ;  //也可以在声明时完成初始化

   3. 动态数组的其他操作

    (1)复制

int d1[], d2[], d3[], d4[], d5[]; // 声明5个int型动态数组


d1 = '{0, 1, 2} ;  // 为d1分配空间并赋初值

d2 = d1 ;          // d2 = '{0, 1, 2} ,不需要先调用new[]来分配空间

d3 = new[5] ;      // 先给d3分配5个元素的空间
d3 = d1 ;          // d3 = '{0, 1, 2} ,不需要重新new

d4 = new[1];      // 先给d3分配1个元素的空间
d4 = d1 ;         // d4 = '{0, 1, 2} ,不需要重新new

d5 = new[5](d1) ; // d5 = '{0, 1, 2, 0, 0},分配空间并复制

d1 = new[5](d1) ; // d1 = '{0, 1, 2, 0, 0},改变宽度并复制

    (2)删除所有元素

d1.delete() ;

d2 = new[0] ;

d3 = '{ } ; 

五、队列

    1. 队列的声明

int i,  q1[$] ;   // 一维队列


bit[7:0]  q2[$] ; // 二维队列

    2. 队列的初始化(不需要new[ ],不加')

      (队列中的元素是连续存放的,所以不需要加 ' )

      (加 ' 也不会报错,是存储方式有区别么?)

int q1[$] = {5, 4, 3, 2, 1} ; // 声明时初始化

int q2[$] ;
q2 = {1, 2, 3, 4, 5} ;        // 先声明,再初始化

    3. 队列的其他操作

        (1)插入元素

        (2)删除元素

        (3)在索引中使用$

六、关联数组

七、数组的方法

-----------------未完待续-------------

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值