第二章:数据类型(续)

2.6 链表

SystemVerilog提供了链表数据结构,但是应该避免使用它,因为SystemVerilog提供的队列更加高效易用。

2.7数组的方法

SystemVerilog提供了很多种数组的表示方法,那么我们对这些数组的操作方法又有哪些呢?下面就来一一介绍一下。

2.7.1 数组的缩减方法

基本的数组缩减方法就是把一个数组缩减成一个值。最常用的方法就是求和sum,除此之外还有product(乘)and(与)or(或)xor(异或)等。
在进行数组压缩的时候,应该特别重要的一点需要注意,那就是位宽的问题下面就用一个例子来进行说明。

 module test_enum();
    bit on[10];
    int total;
   initial
     begin                 
      foreach(on[i])
      on[i]=i;
      $display("on.sum=%0d",on.sum);         //on.sum是单比特无符号的数
      $display("on.sum=%0d",on.sum+32'd0);  //on.sum是32比特数
      $display("int sum=%0d",on.sum with (int'(item))); //利用with来限定on.sum的数据类型,这是一种比较好用的方式
        
     end     
       endmodule

结果:

 # on.sum=1
 # on.sum=1  //Medesim SE 10.2c仿真出来的结果并不支持相加方式得到的数据转换
 # int sum=5

SystemVerilog中,对定宽数组、队列、动态数组和关联数组可以使用$urandom_range($size(array)-1)来选取随机一个元素,而对于队列和动态数组还可以使用$urandom_range(array.size()-1)。

2.7.2 数组定位方法

如何选取数组中最大的值、最小的值?如何选取出数组中唯一值的队列?如何对数组中满足特定要求的值进行某种操作?下面我们就多这些问题一一作出求解方法。

  module test_enum();
    int f[6]={1,6,2,6,8,6},
         d[]='{2,4,6,8,10},
         q[$]={1,3,5,7},
         tq[$];
   initial
     begin                 
        tq=q.min();     //求最小值
        foreach(tq[i])
        $display("min:tq[%0d]=%0d",i,tq[i]);
        
        tq=q.max();    //求最大值
         foreach(tq[i])
        $display("max:tq[%0d]=%0d",i,tq[i]);
        
        tq=f.unique();   //求数组中唯一值的队列
         foreach(tq[i])
        $display("unique:tq[%0d]=%0d",i,tq[i]);
        
        tq=d.find with (item>3);  //利用find函数做操作
         foreach(tq[i])
        $display("find:tq[%0d]=%0d",i,tq[i]);
        tq.delete();     //等价的操作
        foreach(d[i])
          if(d[i]>3)
            tq.push_back(d[i]);
        foreach(tq[i])
        $display("tq[%0d]=%0d",i,tq[i]);
        
        tq=d.find_index with (item>3);  //输出的是index索引也就是第几位的值
        foreach(tq[i])
        $display("tq[%0d]=%0d",i,tq[i]);  
        
     end     
     
   endmodule

结果:

# min:tq[0]=1
# max:tq[0]=7
# unique:tq[0]=1
# unique:tq[1]=2
# unique:tq[2]=6
# unique:tq[3]=8
# find:tq[0]=4
# find:tq[1]=6
# find:tq[2]=8
# find:tq[3]=10
# tq[0]=4
# tq[1]=6
# tq[2]=8
# tq[3]=10
# tq[0]=1
# tq[1]=2
# tq[2]=3
# tq[3]=4

我想通过上述的代码和注释,大家都能够很好地理解。
注意:item被称为重复参数,它代表了数组中一个单独的元素,item是缺省的名字,你也可以指定别的名字。下面四种情况是等价的。

 tq=d.find_first with (item==4);
 tq=d.find_first() with (item==4);
 tq=d.find_first(item) with (item==4);
 tq=d.find_first(x) with (x==4);

当数组的缩减方法和条件语句with结合使用时,sum操作符的结果是条件表达式为真的次数。下面我们来看一个例子。

module test_enum();
    int count,
        total,
        d[]='{9,1,8,3,4,4};
   initial
     begin                 
       count=d.sum with (item>7);  //比较表达式返回0或1
       total=d.sum with ((item>7)*item);
       $display("count=%0d total=%0d",count,total);  //2,17
       
       count=d.sum with (item<8);
       total=d.sum with (item<8?item:0);
       $display("count=%0d total=%0d",count,total);//4,12
       
       count=d.sum with (item==4);
       $display("count=%0d",count); //2
     end     
     
   endmodule
2.7.3 数组的排序

SystemVerilog有几个可以改变数组中元素顺序的方法。包括反向、正序、逆序、随机。

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

2.8 数据存储类型的选择

其实数据类型的选择是多方面的,我们要考虑灵活性、存储器用量、速度、排序和数据结构等多种方面,在我们以后的应用中,我们将会深入地理解每种不同的数据类型的利弊。

2.9 typedef创建新的类型

这是在原有数据类型之上定义新的数据类型。我们为了不混淆,约定所有用户自定义类型都带后缀“_t”。
下面我们来看几个例子。

 parameter opsize=8;
 typedef reg[opsize-1:0] opreg_t;
 opreg_t op_a,op_b;

 typedef bit[31:0] uint;
 typedef int unsigned uint;  //等价的两种方式

对于新的数组定义并不是很明显。你需要把数组的下标放在新的数组名称中。如下面的例子所示:

 typedef int fixed_array5[5];
 fixed_array5 f5;
 initial
    begin
     foreach(f5[i])
             f5[i]=i;
      end

2.10创建用户自定义结构

Verilog中没有数据结构,这是它的一个比较大的缺陷。在SystemVerilog中,我们引入了数据结构的概念。struct只是把数据组织在一起,只是一个数据的集合。

2.10.1 使用struct创建新类型

struct可以把若干个变量组合到一起。我们统一将struct创建的新类型用“_s”来表示。

 typedef struct{bit[7:0] r, g,b;} pixel_s;
  pixel_s my_pixel;

  initial 
      begin
         typedef struct {int a,
                                 byte b,
                                 shortint c;} my_struct_s;
          my_struct_s st='{32'haaaaaaaa,
                                     8'hbb,
                                      16'hcccc};
           $display("st=%x %x %x",st.a,st.b,st.c);
         end
2.10.2创建可容纳不同类型的联合

联合体,通常意义上来讲就是同一位置放置不同类型的数据。如果需要以若干不同的格式对同一寄存器进行频繁读写时,联合体相当有用。我们约定以“_u”为后缀。

 typedef union { int i; real f;} num_u;
 num_u un;
  un.f=0.0;
2.10.3合并结构

通过一个例子我们来描述一下合并结构(packed)可以节省存储空间。

   typedef struct packed {bit [7:0] r,g,b} pixel_p_s;
   pixel_p_s my_pixel;

2.11类型的转化

SystemVerilog提供了多种数据类型,在我们实际的应用过程中,我们需要对数据类型进行转化。本部分就转化提供了几种方法,下面一一来介绍。

2.11.1静态转换

静态装换不对转换值进行检查。如果越界的话,我们也不能察觉到。
基本转换格式:type'(val)

 int i;
 real r;
  i=int '(10.0-0.1);
  r=real '(42);
2.11.2动态转换

动态转换函数$cast允许对越界的数值进行检查,如果不越界返回1,否则返回0。

2.11.3流操作符

流操作符>>和<<用于把其后的数据打包成一个比特流。>>是把数据从左到右变成数据流,<<是把数据从右到左变成数据流。
基本的流操作

int h;
bit [7:0]   b,
             g[4],
             j[4]='{8'ha,8'hb,8'hc,8'hd};
bit [7:0] q,r,s,t;
 initial
    begin
        h={>>{j}};  //0a0b0c0d
        h={<<{j}};  //b030d050
        h={<>{q,r,s,t}}=j;//将分散到四个字节变量里
        h={>>{t,s,r,q}};//将四个字节集中到h里
     end

2.12 枚举类型

利用内建函数name()可以得到枚举变量值对应的字符串。我们统一用后缀“_e”来表示枚举的数据类型。

  • 定义枚举值
    枚举值缺省为从0开始递增的整数,可以自己定义枚举值。通常在我们把0指给枚举常量,可以避免一些不必要的错误。

  • 枚举类型的子程序
    (1)first() 返回第一个枚举变量
    (2)last() 返回最后一个枚举变量
    (3)next() 返回下一个枚举变量
    (4)next(N) 返回以后第N个枚举变量
    (5)prev() 返回前一个枚举变量
    (6)prev(N) 返回以前第N个枚举变量
    遍历所有的枚举成员(注意对枚举类型值的定义)

     module test_typedef();
         typedef enum{red,green,blue=6,yellow,white,black} colors;
            colors my_colors;
    
            initial
              begin
                 my_colors=my_colors.first;
                do
                begin                                          
                 $display("my_colors=%0d/%s",my_colors,my_colors.name);
                   my_colors=my_colors.next;
               end
              while (my_colors!=my_colors.first);
           end
    
      endmodule
    

结果:

        # my_colors=0/red
        # my_colors=1/green
        # my_colors=6/blue
        # my_colors=7/yellow
        # my_colors=8/white
        # my_colors=9/black
2.12.1枚举类型的转换

枚举类型的缺省类型为双状态的int。

  • 可以通过简单的赋值表达式把枚举变量直接赋值给变量int。

  • 不允许直接把int赋值给枚举变量,这种是出于越界情况的考虑。

      module test_enum();
         typedef enum {RED,BLUE,GREEN} COLOR_E;
          COLOR_E color,c2;
         int c;
        initial
        begin                 
         color=BLUE;
          c=color;
          c++;
            if(!$cast(color,c))
               $display("cast failed for c=%0d",c);
                $display("color is %0d/%s",color,color.name);
               c++;
               c2=COLOR_E'(c);
              $display("c2 is %0d/%s",c2,c2.name);
               if(!$cast(color,c))
               $display("cast failed for c=%0d",c);
          end     
       
     endmodule
    

结果:

  # color is 2/GREEN
  # c2 is 3/
  # cast failed for c=3

$cast(color,c)将int型动态转化为枚举类型,如果没有越界返回1,否则返回0;界内(0,1,2),3已经越界了。

2.13 常量

SystemVerilog中支持const修饰符,允许在变量声明时对其进行初始化,但不能在过程代码中改变其值。

     initial 
         begin 
             const byte colon=":";
            ......
         end

2.14 字符串

SystemVerilog中的string类型可以用来保存长度可变的字符串。单个字节是byte类型。字符串使用动态的存储方式,所以不用担心存储空间会全部用完。

  module test_enum();
      string s;
       initial
         begin                 
          s="IEEE";
         $display(s.getc(0));
         $display(s.tolower());
      
         s.putc(s.len()-1,"-");
         s={s,"P1800"};
      
        $display(s.substr(2,5));    
        my_log($psprintf("%s %5d",s,42));
        end
   
       task my_log (string message);
        $display("@%0t:%s",$time, message);
        endtask
    endmodule
  • getc(N) 返回位置N上的字节

  • tolower()返回一个小写的字符串

  • putc(M,C)把字节C写到字符串的M位上,M必须介于0和len所给出的长度之间。

  • substr(start,end),读取从位置start到end之间的所有字符。

  • task函数是用来返回一个格式化的临时字符串,并且可以直接传递给其他子程序。
    结果:

      #          73   对应字符“I”
      # ieee
      # E-P1
      # @0:IEE-P1800    42
    

你可能感兴趣的:(第二章:数据类型(续))