其他分享
首页 > 其他分享> > 一、数据类型

一、数据类型

作者:互联网

一、内建数据类型

1. 逻辑(logic)类型

  logic类型是对reg类型的改进,使得它除了作为一个变量以外,还可以被连续赋值、门单元和模块驱动。任何使用线网的地方都可以使用logic,但要求logic不能有多个结构性驱动,如在双向总线建模时,不能使用logic。

2. 双状态数据类型

  (1)最简单的双状态数据类型是bit,他是无符号的。另四中带符号的双状态数据类型是byte,shortint,int和longint。

  (2)使用($isunknown)操作符,可以在表达式的任意位出现X或Z时,返回1。

二、定宽数组

1. 声明

  int  lo_hi[0 : 15] ;    // 16个整数[0] ....[15],等价于 int lo_hi[16] ;

  可以通过在变量名后面指定维度的方式来创建多维定宽数组

  int  array[0 : 7][0 : 3] ;   // 完整的声明

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

  如果代码试图从一个越界的地址中读取数据,那个SV将会返回数组元素的缺省值。对于一个元素为四状态类型的数组,例如logic,返回的是X,而对于双状态类型,例如int或bit,则返回0。这适用于所有的数组类型,包括定宽数组、动态数组、关联数组和队列,也同时适用于地址中含有X或Z的情况。线网在没有驱动的时候输出Z。

  SV仿真器在存放数组元素时,使用32比特的字边界,所以byte,shortint和int都是存放在一个字中,而longint则存放到两个字中。在非合并数组中,字的低位用来存放数据,高位则不用。

2. 常量数组

  int  ascend[4] = `{0, 1, 2, 3};    // 对4个元素进行初始化

  int descend[5];

  descend[0:2] = `{5,. 6, 7};     // 为前3个元素赋值

  ascend = `{4{8}};         // 四个值全是8

  descend = `{9, 8, default:1};    // {9, 8, 1, 1, 1}

3. 基本数组操作——for和foreach

  对多维数组使用foreach时,并不是像[i][j]这样把每个下标分别放在不同的括号里,而是用逗号隔开放在同一个方括号里,像[i, j],如foreach(md[i, j]) begin ... end

4.合并数组

  声明合并数组时,合并的位和数组大小作为数据类型的一部分必须放在变量名前面指定。数组的大小定义的格式必须是[msb : lsb],而不是[size] 。

  eg:bit[3 : 0] [7 : 0] bytes ;   // 4个字节组装成32比特

 

 

三、动态数组

 

  动态数组在声明时使用空的下标[ ],这意味着数组的宽度不在编译时给出,而在程序运行时再指定。数组在最开始时是空的。所以你必须用new[ ]操作符来分配空间,同时在方括号中传递数组宽度。可以把数组名传给new[ ]构造符,并把已有数组的值复制到新数组里。

eg:int dyn[], d2[];

  initial begin

    dyn = new[5];    // 分配5个元素

    foreach(dyn[j])  // 对元素进行初始化

      dyn[j] = j;    

    d2 = dyn;      // 复制一个动态数组

    dyn = new[20](dyn);  // 分配20个整数并进行复制

    dyn = new[100];    // 分配100个新的整数值,旧值不复存在

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

  end

  系统函数$size的返回值是数组的宽度。动态数组可以在声明直接进行初始化。当把一个定宽数组复制给一个动态数组时,SV会自动调用构造函数new[ ]来分配空间并复制数值。

四、队列

  队列的声明是使用带美元符号的下标:[$]。队列元素的编号从0到$。队列的常量中只有大括号而没有数组常量中开头的单引号。注意不要对队列使用构造函数new[ ]。

  队列在声明以后,只能通过索引或方法两种方式进行初始化。

  
  队列的方法有insert 、pop_front、push_back、delete等。

  如果把$放在一个范围表达式的右边,则代表最大值,如果$放在一个范围表达式的左边,则代表最小值。

五、关联数组

  关联数组采用在方括号中放置数据类型的形式来声明。例如[int]或[packet]。

  关联数组的声明、初始化和遍历过程:

  initial begin

    bit [63:0] assoc[bit [63:0]] , idx = 1;   // 声明

    repeat (64) begin          // 初始化

      assoc[idx] = idx;

      idx << 1;

    foreach (assoc[i])           // 使用foreach进行遍历

      $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);

  end

六、数组的方法

1. 数组的缩减方法

  最常用的缩减方法是sum,它对数组中的元素进行求和。这里必须对SV处理操作位宽的规则十分小心。缺省情况下,如果你把一个单比特数组中的元素相加,其和也是单比特的。但如果你使用32比特的表达式,把结果保存在32比特的变量里,与一个32比特的变量进行比较,或者使用适当的with表达式,SV都会在数组求和过程中使用32比特位宽。

 

 

  SV没有提供专门从数组中随机选取一个元素的方法,所以对于定宽数组、队列、动态数组和关联数组来说,可以使用$urandom_range($size(array) - 1),而对于队列和动态数组还可以使用$urandom_range(array.size() - 1)。

 

2. 数组的定位方法

 

  常用的数组定位方法有min、max、unique和find,unique方法返回的是在数组中具有唯一值的队列,即排除掉重复的数值。以上min、max、unique和find方法的返回值是一个队列

 

 

 

 

  当把数组缩减方法与条件语句with结合使用时,你会发现惊人的结果。例如sum方法,其结果是条件表达式为真的次数。

 

3.数组的排序

  常用的排序方法有reverse(逆向排序),sort(从小到大排序),rsort(从大到小排序),shuffle(随机排序)。其中,reverse和shuffle方法不能带with条件语句。

七、使用typedef创建新的类型

  在Verilog中,可以使用宏(macro)为一个操作数定义位宽和类型,然后通过(` 变量名)的方式引用。这种情况下并没有创建新的类型,只是在进行文本替换。SV中可以使用typedef创建新的类型。

eg:parameter  OPSIZE = 8;

  typedef reg[OPSIZE - 1 : 0] opreg_t;   // 新的数据类型

  opreg_t  op_a, op_b;

八、创建用户自定义结构

1. 使用struct创建新类型

  把若干个变量组合到一个结构,struct  {bit  [7 : 0]  red, green, blue;}  pixel;  // 创建一个新的结构

  上面只是创建了一个新的结构,要想使用它,必须创建一个新的类型, typedef  struct  {bit  [7 : 0]  red, green, blue;}  pixel_s;  //创建一个类型

2. 对结构进行初始化

  typedef  struct {int a; byte b; shortint c; int d;}  my_struct_s;

  my_struct_s  st = `{32'haaaa_aaaa, 8'hbb, 16'hcccc, 32'hdddd_dddd};

3. 创建可容纳不同类型的联合

  typedef union {int i; real f;}  num_u;

4. 合并结构

  typedef struct packed {bit  [7 : 0]  red, green, blue;}  pixel_p_s;

  pixel结构使用了三个数值,所以占用了三个长字的存储空间,即使它只需要三个字节,使用packed可以将三个变量合并在尽可能小的空间里。

九、类型转换

1. 静态转换

  静态转换操作不对转换值进行检查。转换时指定目标类型,并在需要转换的表达式前加上单引号即可。

  eg:int  i;

    real  r;

    i = int `(10.0-0.2);

    r = real `(42);

2. 动态转换

  动态转换函数$cast允许你对越界的数值进行检查。当$cast被调用时,目的在于把其右边的值赋给左边的量。如果赋值成功,$cast()返回1.

3. 流操作符

   流操作符<<和>>用在赋值表达式的右边,后面带表达式、结构或数组,用于把其后的数据打包成一个比特流。流操作符>>把数据从左至右变成流,而<<则把数据从右至左变成流。不能将比特流直接赋给非合并数组,而应该在赋值表达式的左边使用流操作符把比特流拆分到非合并数组中。

  数组下标失配是在数组间进行流操作时常见的错误。数组声明中的下标[256]等同于[0:255],而非[255:0],使用流操作把使用[high:low]形式数组的值赋给带[size]下标形式的数组,会造成元素倒序。同样,如果把声明形式为bit [7:0] src[255:0]的非合并数组使用流操作赋值给声明形式为bit [7:0] [255:0] dst的合并数组,则数值的顺序会被打乱。对于合并的字节数组,正确的声明形式应该是bit [255:0] [7:0] dst.

十、枚举类型

1. 定义枚举值

  在学会使用枚举类型之前,你只能使用文本宏。宏的作用范围太大,而且大多数情况下对于调试者是可见的。枚举类型创建了一种强大的变量类型,它仅限于一些特定名称的集合,例如指令中操作码或者状态机中的状态名。

eg:  typedef enum {INIT, DECODE, IDLE} fsmstate_e;

    fsmstate_e pstate, nstate;  //声明自定义类型变量。

  如果不指定枚举值,默认从0开始,每次递增1。如上述例子,INIT代表0,DECODE代表1,IDLE代表2。

 

 

 

 

   使用do...while循环来遍历所有的值。

 

2. 枚举类型的转换

   枚举类型的缺省值为双状态int,可以使用简单的赋值表达式把枚举类型变量的值直接赋给非枚举变量如int,但SV不允许在没有进行显式类型转换的情况下把整型变量赋给枚举变量。目的在于让你意识到可能存在的数值越界的情况。

 

十一、字符串

   SV中的string类型可以用来保存长度可变的字符串。单个字符串是byte类型。

  字符串相关的几种操作:get(N)可以返回位置N上的字节,toupper返回一个所有字符大写的字符串,tolower返回一个小写的字符串,大括号{ }用于串接字符串。任务putc(M, C)把字节C写到字符串的M位上,M必须介于0和len所给出的长度之间。函数substr(start,end)提取出从位置start到end之间的所有字符。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

    

 

 

标签:int,数据类型,assoc,数组,使用,类型,bit
来源: https://www.cnblogs.com/muye-sana/p/14232414.html