写在前面:

本文参考的资料有:E课网、路桑V2、芯片验证漫游指南(红宝书)、sv绿皮书、systemverilog与功能验证(钟文枫),努力学习!

 

与verilog相比,systemverilog增加了很多类似C/C++新的数据类型,如下:

 

 

2.1内建数据类型

弄清这几个:

  1. 逻辑数值类型(四值、两值?)

  2. 符号类型(有符号、无符号?)

  3. 矢量位宽(64位的吗)

  • 四值逻辑类型:integer、reg、logic、net-type(例如wire、tri)

  • 二值逻辑类型:byte、shortint、int、longint、bit

  • 点拨:对于int和integer可以根据长度进行记忆

  • 有符号类型:byte、shortint、int、longint、integer

  • 无符号类型:bit、logic、reg、net-type(例如wire、tri)

2.1.1逻辑(logic)类型

Verilog中清楚地区分了寄存器类型reg线网型wire,而SV中新引入了一个数据类型logic

与logic相对应的是bit类型,它们均可以构建矢量类型(vector),而它们的区别在于:

  • logic为四值逻辑,即可以表示0、1、X、Z。

  • bit为二值逻辑,只可以表示0和1。

运行下面代码块感受一下:

E课网例子bit_logic_operator.sv

//bit_logic_operator.sv

module bit_logic_operator ();

bit    [7:0] a = 8'b01xz_01xz;
logic  [7:0] b = 8'b01xz_01xz;
integer      c = 32'b01xz_01xz_01xz_01xz;
int          d = 32'b01xz_01xz_01xz_01xz;
bit    [7:0] bi;
byte         by;
initial begin
  $display ("Value of bit       a = %b", a);
  $display ("Value of logic     b = %b", b);
  $display ("Value of interger  c = %b", c);
  $display ("Value of int       d = %b", d);
  $display (" bit + integer       = %b", a + c);
  $display (" logic + int         = %b", b + d);
  a = 10;
  b = 20;
  c = 30;
  d = 40;
  $display (" bit + logic       = %b", a + b);
  $display (" integer + int     = %b", c + d);
  bi = 8'hff;
  by = 8'hff;
  $display ("bi=%d",bi);
  $display ("by=%d",by);

end

endmodule

小插曲:

我们在学习systemverilog语法的时候,书上的代码如何通过某种方式进行编译并看到结果?

目前只知道,利用VCS可以,但是注意代码中$display.

VCS -R -sverilog bit_logic_operator

2.1.2双状态数据类型

实例:数据类型——芯片验证漫游指南附赠源代码

//数据类型——芯片验证漫游指南附赠源代码

module data_type;

  logic [7:0] logic_vec = 8'b1000_0000;
  bit [7:0] bit_vec = 8'b1000_0000; 
  byte signed_vec = 8'b1000_0000;
  bit [8:0] result_vec;

  // 
  initial begin
    #10;
    result_vec = signed_vec;
    $display("@1 result_vec = 'h%x", result_vec);
    result_vec = unsigned'(signed_vec);
    $display("@2 result_vec = 'h%x", result_vec);
  end

  // 
  initial begin
    #10;
    $display("logic_vec = %d", logic_vec);
    $display("bit_vec = %d", bit_vec);
    $display("signed_vec = %d", signed_vec);
  end

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


  // static conversion
  initial begin
    #10;
    $display("@1 x_vec = 'b%b", x_vec);
    b_vec = int'(x_vec);
    $display("@2 b_vec = 'h%x", b_vec);
  end

  // dynamic conversion
  initial begin
    #10;
    $display("@1 x_vec = 'b%b", x_vec);
    if(!$cast(b_vec, x_vec))
      $display("Failed for type casting");
    $display("@2 b_vec = 'h%x", b_vec);
  end

  // implicit conversion
  initial begin
    $display("@1 x_vec = 'b%b", x_vec);
    b_vec = x_vec;
    $display("@2 b_vec = 'b%b", b_vec);
  end

endmodule: data_type

用这个命令“VCS -R -sverilog bit_logic_operator ”,仿真结果如下:

分开盘一下上面这些代码!

一、

logic [7:0] logic_vec = 8'b1000_0000;
  bit [7:0] bit_vec = 8'b1000_0000; 
  byte signed_vec = 8'b1000_0000;
  bit [8:0] result_vec;

  // 输出初始值
  initial begin
    #10;
    $display("logic_vec = %d", logic_vec);
    $display("bit_vec = %d", bit_vec);
    $display("signed_vec = %d", signed_vec);
  end

 上面这段代码的仿真结果如下:

logic_vec = 128
bit_vec = 128
signed_vec =-128

根据这个十进制的打印结果,可以体现有符号数和无符号数的一点差异 。

  • 这里可以看出:logic和bit来声明的矢量均为无符号(unsigned)变量。signed_vec 被赋值为 8'b1000_0000,表示有符号十进制数值为-128。

  • 'b1000_0000  =  'o200 =  'd128  = 'h80

  • 也就是二进制1000_0000,用八进制表示为o200,用十进制表示为d128,用十六进制表示为h80

二、

 logic [7:0] logic_vec = 8'b1000_0000;
  bit [7:0] bit_vec = 8'b1000_0000; 
  byte signed_vec = 8'b1000_0000;
  bit [8:0] result_vec;

//有符号数赋给无符号数
//有符号变量和无符号变量混用
  initial begin
    #10;
    result_vec = signed_vec;
    $display("@1 result_vec = 'h%x", result_vec);
    result_vec = unsigned'(signed_vec);
    $display("@2 result_vec = 'h%x", result_vec);
  end

 上面这段代码的仿真结果如下:

@1 result_vec = 'h180
@2 result_vec = 'h080
  1. 在第一次赋值操作时 result_vec = signed_vec,右侧的有符号数值-128被赋值到左侧时,需要从8位扩展为9位,且保证有符号数值不变的情况下,首先需要将8'h80扩展为9'h180(均为-128),进而在赋值到左侧。

  2. 在第二次赋值操作时,先进行类型转换操作 unsigned'(signed_vec),则转换结果应为十进制数值128,所以在赋值操作以后result_vec = unsigned'(signed_vec),result_vec同signed_vec就比特位的数值没有发生变化,但是实际表达的十进制数值则从-128被赋值为128。

  • 这里说明一下:计算机只能用0和1表示,要表示-128,就必须使用补码形式,具体怎么操作呢?

  • 首先写出128的二进制数:
    0 1000 0000
    然后按位取反
    1 0111 1111
    然后再加1,变成
    1 1000 0000
    
    注意:本来128是8位,没有前面四个0,但是为了符合规律,要在前面补一个0

    在变量运算中,遇到常见的这些变量类型时,注意它们的逻辑类型和符号类型,避免两种不一致的变量进行操作而出错。

  • 所以,通过上面的例子我们可以发现,在编码时一定要注意操作符左右两侧的符号类型是否一致,如果不一致,应该首先将其转换为同一类型再进行运算。

三、

对于转换方式,可以分为隐式转换显式转换显式转换又可以分为静态转换动态转换静态转换,在转换的表达式前加上单引号即可,而该方式并不会对转换值做检查。如果发生转换失败也无从得知,所以与之对应的动态转换$cast(tgt, src)也被经常运用到转换操作中。静态转换和动态转换均需要操作符号或者系统函数介入,我们统称为显式转换(见后面代码)。

而不需要进行转换的一些操作,我们称之为隐式转换,例如:

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

  // implicit conversion
  initial begin
    $display("@1 x_vec = 'b%b", x_vec);
    b_vec = x_vec;
    $display("@2 b_vec = 'b%b", b_vec);
  end

 这段代码,仿真结果为:

@1 x_vec = 'b111x
@2 b_vec = 'b110

这里有两个问题值得思考:

  1. 被转换的变量为四值逻辑矢量,而被赋值的变量为二值逻辑矢量,且位宽不同。

  2. 在隐式转换中,x_vec[2:0]被保留下来,x_vec[3]则被丢弃,同时x_vec[0]的值'x'在转换过程中被转换为0值,即赋值的最终结果为b_vec = 'b110。

下面这是显示转换的例子,红宝书上面说“上面介绍了静态转换。。。” ,而我没发现,就把还是归类到这里来了!

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

// static conversion
  initial begin
    #10;
    $display("@1 x_vec = 'b%b", x_vec);
    b_vec = int'(x_vec);
    $display("@2 b_vec = 'h%x", b_vec);
  end

// dynamic conversion
  initial begin
    #10;
    $display("@1 x_vec = 'b%b", x_vec);
    if(!$cast(b_vec, x_vec))
    $display("Failed for type casting");
    $display("@2 b_vec = 'h%x", b_vec);
  end

上面这段代码的仿真结果如下: 

@1 x_vec = 'b111x
@2 b_vec = 'h6

@1 x_vec = 'b111x
@2 b_vec = 'h6

到这里,内建数据类型就算结束了吧!但是想写一下,sv数据类型的逻辑仿真特性

  • 四值状态变量的默认初始值为X二值状态变量的默认初始值是0
  • 二值状态的变量不能表示未初始化状态
  • 四值状态的变量赋值给二值状态的变量X和z会转换成0
  • $isunknown( expression)可以检查表达式中是否存在成x或z
  • 二值状态变量的默认初值为0,在initial中可以直接使用~clk变成1,但是如果是logic,必须设置初值为0、或者1.

2.2定宽数组(也是静态数组、固定数组?)

静态数组是指其数组的大小在定义时被显性地指定。sv引入两种类型的数组:压缩数组 (packedarray)和非压缩数组 (unpackedarray)。
 
压缩数组,维数定义在变量标识符之前,如:         bit     [7:0]        c1;    //压缩数组(比特类型)
非压缩数组,维数的定义在变量标识符之后,如: real     u         [7:0];    //非压缩数组(实型)
 

上面两个例子,维数定义都在变量标识符之后,都称为非压缩数组;而后面两个例子不是!

  • 使用一个下标,可以得到一个字的数据 barray[2]。
  • 使用两个下标,可以得到一个字节的数据 barry [0][3]。
  • 使用三个下标,可以访问到单个比特位 barray[0][1][6]。
  • 注意数组声明中在变量名后面指定了数组的大小, barray[3],这个维度是非合并的,所以在使用该数组时至少要有一个下标
  • 例2.16中的最后一行在两个合并数组间实现复制。由于操作是以比特为单位进行的,所以即使数组维度不同也可以进行复制

对于合并数组和非合并数组,存储空间如何呢?还有,这里的bit类型都是32bits吗?

2WORD、3WORD

  • 静态数组的通用定义方法是:

element_data-type [Prange1 ]··· [PragenN]    array_name [Urange1]···[UrangeM]
在array_name前面指定的维数是压缩部分,在array_name后面指定的维数是非压缩部分

  • 静态数组的使用方法:

1)对单个压缩数组的引用可以用来访问一个整体的数组。


2)数组成员访问的方式如下:
   Array_name [Range1] [UrangeM][Range1]···[PrangeN]


3)静态数组的非压缩维数可以通过[ Number-1:0]或者通过「 Number]的方式来定义
  int Array  [0:7][0:31]; //通过范围来定义数组
  int Array  [8][32];       //通过指定上限定义数组


4)如果一个数组被定义为有符号数,那么其存储的所有压缩数组都被默认为有符号数,而每个压缩数组的成员是无符号数。
 

实例

//systemverilog与功能验证(钟文枫)
//源代码2-3 多维数组和压缩数组实例
//chapter2 array_example.sv
module test_array();
  bit [3:0][7:0] reg_32;//4个字节压缩为32bits的向量,合并数组的声明
  bit [3:0][7:0] mix_array [3];
  initial begin
      reg_32 = 32'hdead_beef;
      $display("%b",reg_32);//打印所有的32bits 
      $display(" %h",reg_32[3]);//打印最高位的字节"de" 
      $display("%b",reg_32[3][7]);//打印最高位比特 "1"  
      mix_array[0]=32'h0123_4567;
      mix_array[1]=mix_array[0];
      mix_array[2]=mix_array[1];
      if (mix_array[1][3]==8'h01) $display("Access mix_array[1][3]");
      if (mix_array[2][1][6]==1'b1) $display("Access mix_array[2][1][6]");
      //mix_array[0]=64,mix_array[1]=63,mix_array[2]=62
      mix_array = '{64,63,62};
  end   
endmodule

仿真结果如下: 

 

这个实例的数组的存储方式(对比前面):

 System Verilog还提供了下列系统内置方法来访问数组成员: $left、 $right、$low、$high、 $increment、 $size、 $dimensions和 $unpacked_ dimensions。

基本数组操作

看出foreach和for的区别了没?推荐使用foreach!

//systemverilog绿皮书
//例题2.9 初始化并遍历多维数组
module array2_9();

int md[2][3]='{'{0,1,2},'{3,4,5}};
initial begin
	$display("Initial value:");
	foreach(md[i,j]) //这是正确的语法格式
		$display("md[%0d][%0d]= %0d",i,j,md[i][j]);
		
	$display("New value:");
	//对最后三个元素的重新赋值5
	md = '{'{9,8,7},'{3{32'd5}}};
	foreach(md[i,j])//这是正确的语法格式
		$display("md[%0d][%0d] = %0d",i,j,md[i][j]);
end
endmodule

运行仿真上面的代码,得到这样的结果: 

不知道有没有注意到,%0d,我把全文中的“%0d”全部替换成“%d”,运行的结果就这样如下:

为什么会这样?

%后面加一个“0”的作用是自动调整显示输出数据的宽度。即输出结果总是用最少的位数来显示结果值

推荐使用foreach(不需要声明很多?)

数组基本操作——复制和比较

//systemverilog绿皮书
//例题2.13 数组的复制和比较操作
module array2_13();

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");
		
	//把src所有元素复制给dst
	dst = src;
	
	//只改变一个元素的值
	src[0]=5;
	
	//所有元素的值是否相等(否!)	
	$display("src %s dst",(src == dst)?"==":"!=");
	//使用数组片段对第1-4个元素进行比较
	$display("src[1:4] %s dst[1:4]",(src[1:4]==dst[1:4])?"==":"!=");
end
endmodule

2.3动态数组

Verilog的基本数组和向量就是前面提到的静态数组,数组的大小在编译的时候就确定下来了。但是,若在编译的时候不知道数组的大小,只有在运行的过程中才能确定。SV 提供了动态数组,可以在仿真的过程中动态分配大小。
 

动态数组的声明格式:

  • data_type  array_name [ ];
其中,data_type是数组成员的数据类型,动态数组与静态数组支持相同的数据类型。空的“[ ]”意味着我们不需要在编译的时候指定数组的大小,在运行的过程中动态分配。 动态数组初始定义时是空的,没有分配任何空间,使用前必须通过调用new[ ],并在“[ ]”中输入期望的长度数值来分配空间。

动态数组的例子:
bit[3:0] nib1e [ ];       //4比特向量的动态数组
integer mem [2 ][ ];  //固定大小的非压缩数组中
                                //带2个动态的整型子数组
系统函数$size()可以返回一个静态数组或者动态数组的大小。另外,动态数组还有几个特殊的内置函数,例如 delete()和size();这两个函数是不适用于静态数组的。如果一个动态数组与一个静态数组的维数及深度相同,那么这个动态数组可以被赋值为一个具有兼容类型的静态数组。一个动态数组或静态数组可以被赋值到一个具有兼容类型的动态数组。在这种情况下,赋值会自动分配一个新的动态数组,这个新的动态数组的长度等于静态数组的长度。
 

实例

//systemverilog与功能验证(钟文枫)
//源代码2-4 动态数组实例
//Chapter2 dy_array_example.sv
module text_dy_array();
    int dyn1[],dyn2[];//动态数组

    initial begin
        dyn1 = new[100];//分配100个成员
        foreach (dyn1[i])
            dyn1[1]=i;//初始化成员

            dyn2=new[100](dyn1);//复制一个动态数组
            dyn2[0]=5;//修改一个成员
            //检查dyn1【0】和dyn2【0】的值
            $display("dyn1[0]=%0d,dyn2[2]=%0d",dyn1[0],dyn2[0]);
            //扩展大小并复制
            dyn1 =new [200](dyn1);
            $display("The size of dyn1 is %0d",dyn1.size());
            //改变dyn1的大小
            dyn1 = new [50];
            $display("The size of dyn1 is %0d",dyn1.size());
            dyn1.delete;
            $display("The size of dyn1 is %0d",dyn1.size());
            //复制一个动态数组
            dyn1=dyn2;
            $display("dyn1[0]=%0d,dyn2[0]=%0d",dyn1[0],dyn2[0]);
    end
endmodule

这个实例的仿真结果如下: 

dyn=new[ 0 ];dyn=`{ 0 };也表示删除所有元素

2.4队列

队列是一个大小可変,具有相同数据类型成员的有序集合。队列能够在固定时间内访问它的所有元素,也能够在固定时间内对队列的尾部和头部插人和删除成员。队列中的每个成员都通过一个序号来标识,这个序号代表了成员在队列内的位置,0代表第一个成员,$代表最后一个成员队列类似于动态数组,是一个一维的非压缩数组,它可以自动地增长和缩减。因此,与数组一样,队列可以使用索引、串联、分片、比较操作符进行处理。队列适合于实现FIFO和堆栈之类的数据结构。

队列定义格式:

 

  • data type queue-name[ $ ];
  • data-type queue_namel $: maxsize ];

其中,data_type是数据成员的数据类型,与静态数组和动态数组支持的一致; queue_name是定义的队列变量,若给定 max size,那么队列成员的最大数将受到约束。

实例 

//systemverilog与功能验证(钟文枫)
//源代码2-6 队列实例
//Chapter2 queue_example.sv
module test_queue();
    int queuel[$];
    int n,m,item;
    initial begin
        queuel='{1,2,3,4,5};//$表示数组的上限
        n=8;
        m=9;
        //使用拼接符把n放到queuel的最左边
        queuel={n,queuel};//'{8,1,2,3,4,5}
        //使用拼接符把m放到queuel的最右边
        queuel={queuel,m};//'{8,1,2,3,4,5,9}
        //将queuel左边第一个成员赋值给item
        item = queuel[0];
        //将queuel左边右边最后一个成员赋值给item
        item = queuel[$];
        //通过整数对queuel做遍历访问
        for(int i=0;i<queuel.size();i++)
            begin
                $display("queuel[%0d] = %0d",i,queuel[i]);
            end
        //求出queuel的长度
        n=queuel.size();
        $display("queuel size is %0d",n);
        
		//删除左边第一个成员
        queuel = queuel[1:$];//'{1,2,3,4,5,9}
        //删除右边第一个成员
        queuel = queuel[0:$-1];'{1,2,3,4,5}
        for (int i=0;i<queuel.size();i++)
            begin
                $display("queuel[%0d]=%0d",i,queuel[i]);
            end
        queuel='{};//清空队列
        $display("queuel size is %0d",queuel.size());
    end
endmodule

 这是书上代码仿真出错的提示:

这是修改后的仿真结果:

实例2 

上面这段源码的仿真结果如下: 

对上面的代码调整了输出格式,代码如下:

//systemverilog绿皮书
//例2.19队列的操作(修改了三处输出的格式)
module array2_19();
int  j = 1;
int  q2[$] = {3,4};  //队列的常量不需要使用单引号'
int  q[$]= {0,2,5};//{0,2,5}
 
initial begin
    q.insert(1,j);  //{0,1,2,5}在2之前插入1
    q.insert(3,q2); //{0,1,2,3,4,5}在q中插入一个队列
    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
    $display("Last bit deleted from queue    j= %0d",j);
    q.push_back(8); //{6,0,2,3,4,8}在队列末尾插入
    j = q.pop_front;//{0,2,3,4,8} j = 6
    $display("First bit deleted from queue    j= %0d",j);
    foreach(q[i])begin
        $display("q[%0d] = %0d",i,q[i]);
    end
    q.delete();     //{}删除整个队列
end
endmodule

仿真结果如下: 

 

2.5关联数组

关联数组在使用之前不会分配任何存储空间,并且索引表达式不仅仅是整型表达式,而且可以是任何数据类型。
动态数组可以允许在程序运行的过程中根据需求动态分配特定大小的数组。但是,若我们需要一个超大的数组呢?在具体的应用环境中,如处理器,它可以访问几个GB的地址空间,若我们全部分配,消耗的内存是非常巨大的;而实际测试中我们可能只需要访问某些范围或者离散的地址;关联数组是通过标号来分配空间和访问的数组,其好处就是只分配使用到的特定地址的空间,也就是当你访问某一个较大地址的数组时, System Verilog只针对该地址分配空间。如图2-4所示,关联数组只分配0~5、45、1000、4531和200000地址的数值。存储器分配也会比固定数组和动态数组小。(在这里,两本书有出入,不过应该没多大区别

实例1:关联数组的声明、初始化和使用

//systemverilog绿皮书
//例2.21关联数组的声明、初始化和使用

module array2_21();

initial begin 
bit [63:0] assoc[bit[63:0]],idx=1;

//对稀疏矩阵元素进行初始化
repeat(64) begin
	assoc[idx] = idx;
	idx = idx << 1;//每次左移一位
end

//使用foreach遍历数组
$display("this is 1 foreach:");
foreach(assoc[i])
	$display("assoc[%h]=%h",i,assoc[i]);//这里使用16进制打印,每4位代替16二进制
	
//使用函数遍历数组,first和next函数会返回1或0;
$display("this is 2:");
if(assoc.first(idx))begin
	do 
		$display("assoc[%d]=%d",idx,assoc[idx]);//这里按10进制打印
		while(assoc.next(idx));//得到下一个索引
	end
	
//找到第一个元素
assoc.first(idx);
//删除第一个元素
assoc.delete(idx);
$display("The array now has %0d elements",assoc.num);
end 
endmodule 

 这段代码的仿真结果如下:

实例2:使用带字符串索引的关联数组

//systemverilog绿皮书
//例2.22使用带字符串索引的关联数组(有些不太懂)
module array2_22();

/*
switch.txt(需要把这两句话放在这个文件例运行这段代码)
输入文件的内容如下:
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);
	
	//获取最小地址值,缺省为0
	min_address = switch["min_address"];
	
	//获取最大地址值,缺省为1000
	if(switch.exists("max_address"))
		max_address = switch["max_address"];
	else
		max_address = 1000;
		
	//打印数组的所有元素
	foreach (switch[s])//遍历数组
		$display("switch['%s']=%0d",s,switch[s]);

end 
endmodule 

实例中,exists()函数检查元素是否存在,如果元素尚未被写入,SV会返回数组类型的缺省值,对双状态是0,对于四状态是X,即未知态

上面这个实例的仿真结果如下:

实例3:关联数组实例

//systemverilog与功能验证(钟文枫)
//源代码2-5 关联数组实例
//Chapter2 as_array_example.sv
module test_associate_array();
    bit [7:0] i_array [*];//整型关联数组(未指定索引类型)
						  //未指定索引(*)表明可以是任意整型

    bit [7:0] idx;
    bit [7:0] age[string];//8比特向量的关联数组,索引为字符串
    string tom ="tom";
    int assoc_array[int unsigned]= '{1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,10:10};
    int unsigned idx_int,smallest,largest;
    initial begin
        idx=1;
        repeat(6) begin
            i_array[idx]=idx;
            idx=idx<<1;
        end
        if(i_array.first(idx))
            begin
                do
                    $display("i_array[%0d]=%0d",idx,i_array[idx]);
                while(i_array.next(idx));
            end
			
        age[tom]=21;
        age["joe"]=32;
        $display("%s is %d years of age",tom,age[tom],"[%0d ages avilable]",age.num());
        
		if(!assoc_array.first(smallest))
            $display("ERROR-Assoc array has no indexes");
        
		if(!assoc_array.last(largest))
            $display("ERROR-Assoc array has no indexes");
        
		$display("Smallest index is %0d",smallest);
        $display("Largest index is %0d",largest);
       
	   idx_int = largest;
        do
            $display("Index %0d is set to %0d",idx_int,assoc_array[idx_int]);
        while(assoc_array.prev(idx_int));
    end
endmodule

仿真结果如下: 

 

2.6结构体

  • Verilog的最大缺陷之一是没有数据结构,在SV中可以使用 struct语句创建结构,跟C语言类似。
  • 不过 struct的功能少,它只是一个数据的集合,其通常的使用的方式是将若干相关的变量组合到一个 struct结构定义中。
  • 伴随 typedef可以用来创建新的类型,并利用新类型来声明更多变量。

实例

//systemverilog与功能验证(钟文枫)
//源代码2-7 结构体实例(参考绿皮书添加了输出函数)
//Chapter2 struct_example.sv
module struct_example();
	struct{
		bit [7:0] my_bype;
		int my_data;
		real pi;	
	}my_struct;
	//创建了一个my_struct的结构变量
	//my_byte、my_data、pi是my_struct的成员

	initial begin
	//要想在端口和程序中共享它,则必须创建一个新的类型
	typedef struct{
		bit [7:0] my_bype;
		int my_data;
		real pi;	
	}my_struct_s;
	
	my_struct_s st = '{8'hab,32'haaaa_dddd,3.14};
	$display("str = %0x %0x %0x ",st.my_bype,st.my_data,st.pi);
	my_struct.my_bype = 8'hab;
	$display(" my_struct.my_bype = %0x %0x %0x ",my_struct.my_bype,my_struct.my_data,my_struct.pi);
	my_struct = '{0,99,3.14};//整体赋值
	$display("my_struct = %0x %0x %0x ",my_struct.my_bype,my_struct.my_data,my_struct.pi);
end 
endmodule

仿真结果如下: 

2.7枚举类型

实例:枚举类型

//systemverilog与功能验证(钟文枫)
//源代码 2-1 枚举类型实例(没有编译成功)
//Chapter2  enum_example. sv
module test_enum();
//默认值:red=0;ye11ow=1, green=2;
enum{red, yellow, green} light1,light2;//未命名的枚举类型(int类型)

//正确使用方法:IDLE=0,S0=2,S1=3,S2=x
enum integer {IDLE, S0='b10, S1, S2='x} state,next;

//正确定义方法: bronze和gold都没有指定大小
enum {bronze=3, silver, gold} medal;//silver=4, gold=5

//c被自动地指定为8
enum {a=3, b=7, c} alphabet1;

//d=0,e=7,f=8
enum {d, e=7, f} alphabet2;

initial
	begin
		light1 = red;
		light2 = yellow;
		$display("light1 is %0d or %s",1ight1,light1);
		$display("light2 is %0d or %s",1ight2,light2);
		state=S1;
		next= S2;
		$display("state is %0d or %s", state, state);
		$display("next is %b or %s",next, next);
		medal = silver;
		$display("medal is %0d or %s",medal, medal);
		alphabet1 =c;
		$display("alphabet1 is %0d or %s", alphabet1, alphabet1);
		alphabet2 = d;
		$display("alphabet2 is %0d or %s", alphabet2, alphabet2);
		//下面是错误的使用方法,需要做类型转换
		//1ight1=2;
	end
	
endmodule

2.8字符串

  • 感谢字符串 stringl的出现,让我们可以脱离VHDL和verilog这种"荒蛮时代",加入了 string类型带来的便利(尽管它与其它软件语言的字符串功能使用仍然有差距)。
  • 所有与字符串相关的处理,都请使用 string来保存和处理。
  • 与字符串处理相关的还包括字符串的格式化函数即如何形成一个你想要的字符串句子呢?可以使用SV系统方法$sformatf(),如果你想要将它打印输出,那么就使用 $display()吧。

实例

//systemverilog绿皮书
//例2.53字符串方法
module string2_53();
string s;

initial begin
	s="IEEE";
	$display(s.getc(0));//显示:73('I')
	$display(s.tolower);//显示:ieee

	s.putc(s.len()-1,"-");//将空格变为'-'
	s={s,"P1800"};//"IEEE-P1800"

	$display(s.substr(2,5));//显示:EE-P

	//创建临时字符串,注意格式
	my_log($psprintf("%s %5d",s,42));
end
	
	task my_log(string message);
		//把信息打印到日志里
		$display("@%0t:%s",$time,message);
	endtask

endmodule 

仿真结果如下:

 

 

写在最后:

这一篇花了我一天时间,是不是太浪费时间了?一边仿真,一边码字,还手敲好些代码,数据类型可以完结了。

 

 

 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐