• 第二章:数据类型(续)


    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={<<byte{j}}; //0d0c0b0a
            b={<<{8'b0011_0101}};//10101100
            b={<<4 {8'b0011_0101}};//0101_0011
            {>>{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
  • 相关阅读:
    【microstation CE二次开发】不打开microstation,以COM方式启动Microstation
    【microstation CE二次开发】环境搭建
    Node安装与卸载命令汇总
    Maven进行clean时报错,解决方法
    Django 报ckeditor/skins/moono/skin.js 404
    Handler dispatch failed; nested exception is java.lang.AbstractMethodError: Method com/mchange/v2/c3p0/impl/NewProxyResultSet.isClosed()Z is abstract
    精准测试系列分享之一:JaCoCo 企业级应用的优缺点分析
    Java 中常见的细粒度锁实现
    JVM 的运行时数据区域分布
    Java 细粒度锁续篇
  • 原文地址:https://www.cnblogs.com/xuqing125/p/9022925.html
Copyright © 2020-2023  润新知