SV 数据类型小结

1.内建数据类型


相比于Verilog 将寄存器类型reg和net(线网)类型区分如此清楚,在SV中引入了logic数据类型。任何使用线网的地方均可以使用logic,但要求logic不能有多个结构性的驱动,例如在对双向总线建模时。此时,需要使用线网类型。
四值逻辑类型有:logic,reg,wire,integer;
二值逻辑类型有:bit, byte,int,shortint,longint;
按照有无符号还可以分为:
有符号类型:integer, byte, int,shortint,longint;
无符号类型:logic,reg,wire,bit;
注意:
在遇到变量类型时,应注意它们的符号类型和逻辑类型,在变量的运算中,尽可能避免两种不一致的变量进行操作。
1.对于有符号类型,例如:byte变量的最大值是127,而不是255(它的范围是-128–127)。
2.对于逻辑变量类型,四值逻辑类型默认值为X,二值逻辑类型的默认值为0。

2.定宽数组

2.1.定宽数组的声明与赋值

//一维定宽数组
int lo_hi[0:4];    //5个整数[0]...[4];
int c_style [5] ='{0,1,2,3,4};     //对5个元素初始化;
lo_hi = '{0,1,2,3,4};  //对5个元素赋值;
lo_hi[0:2] = '{0,1,2};  //对前3个元素赋值;
lo_hi[0:2] = '{3{3}};     //3个值全为3;
lo_hi = '{3,5,default:2};  //{3,5,2,2,2};
//非合并数组
int array1 [0:2][0:1];   //完整声明一个二维数组;
int array [8][4];      //紧凑的声明;
array [7][3]=2;        //设置最后一个元素;
array1 = '{'{0,1},'{1,2},'{2,3}}; //数组赋值;
//合并数组
bit [3:0][7:0] bytes;   //数组大小的定义格式为[msb:lsb],而不是[size];
bytes = 32'hCafe_Dada;  //赋值;
//合并/非合并数组
bit [0:3][0:7]array [3];  //合并3*32比特
bit [31:0] 1w= 32'h0123_4567;  //字
bit [7:0][3:0] nib;         //合并数组;
array[0]=1w;               //使用一个下标,可以得到一个字的数据;
array[0][3]=8'h01;         //使用两个下标,可以得到一个字节的数据;
array[0][1][6]=1'b1;        使用三个下标,可以得到一个比特的数据;
nib = array[0];             //复制合并数组的元素值

 注意:当测试平台通过存储器数据的变化来唤醒时,通过@操作符实现,这个操作符只能用于标量或者合并数组。例如上边代码中的非合并数组array,只可以将array[0]或者1w作为敏感信号,但不能用整个array数组,除非把它扩展成:@(array[0] or array[1] or array[2])。

可能用到的系统函数:

$dimensions(array_name) :返回数组的维度

$left(array_name,dimensions) :返回数组指定维度最左边索引值

$size(array_name,dimensions) :返回数组指定维度的大小

$bits(expression) :返回数组存储比特数

 logic [1:2][7:0]word[0:3][4:1];
  $left(word,1);      //返回0
  $left(word,2);       //返回4
  $left(word,3);        //返回1
  $left(word,4);         //返回7

 2.2 基本的数组操作----复制和比较

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;
   $display("src[1:4] %s dst[1:4]",(src[1:4]==dst[1:4]?"==":"!=");//片段比较
   end

2.2 基本的数组操作----for & foreach

      SV中$size函数返回数组的宽度。在foreach循环中,只需要指定数组名,并在其后的()中给出索引变量,系统便会自动遍历数组中的元素,索引变量自动声明,并只在循环中有效。

initial begin
 bit [31:0] src[5],dst[5];
  for(int i=0;i<=$size(src)-1;i++)
  src[i]=i;
  foreach(dst[j])
  dst[j] = src[i]*2;
  end

3.动态数组

动态数组在声明时使用空的下标[ ],这意味着数组的宽度不在编译时给出,而在程序运行时指定。数组在最开始是空的,所以必须调用new[ ]操作符来分配空间,同时在方括号中传递数组宽度。

int dyn[],d2[];       //声明两个动态数组
  initial begin
  dyn = [5];            //分配5个元素
  foreach (dyn[i]) dyn[i]= i;  //对元素赋值
  d2 = dyn;      //复制一个数组
  d2[0] =5;      //d2[0]复制为5
  $display(dyn[0],d2[0]);
  dyn = new[20](dyn);  //分配20个元素并赋值
  dyn = new [100];      //分配100个新的整数值
  dyn.delete();         //删除所有值
  end

动态数组有一些内建的子程序,例如delete和size。

只要基本数据类型相同,比如都是int,定宽数组和动态数组之间可以相互赋值,在元素数目相同的情况下,可以把动态数组的值复制给定宽数组。也可以将定宽数组的值复制给动态数组,此时,SV会调用构造函数new[ ]来分配空间并赋值。

4.队列

队列的声明是使用带有美元符号的下标[$],但不需要使用构建函数new[ ]来开辟空间,。

队列的操作:

int j=1;
    q2[$] = {3,4},   
	q[$] = {0,2,5},
  initial begin
  q.insert(1,j);   //{0,1,2,5}
  q.insert(3,q2);   //{0,1,2,3,4,5}
  q.delete(1);      //{0,2,3,4,5}
  q = {q[0],j,q[1:$]};   //{0,1,2,3,4,5};
  q = {q[0:2],q2,q[3:$]};   //{0,1,2,3,4,3,4,5}
  //下面操作执行速度很快
  q.push_front(6);       //{6,0,1,2,3,4,3,4,5}
  j=q.pop_back;           // j=5
  q.push_back(8);         //{6,0,1,2,3,4,3,4,5,8}
  j=q.pop_front;          //j=6
  j =q[$];                 //j=8
  q ={q,8};                 //{6,0,1,2,3,4,3,4,5,8,8}
  q={};                     //类似与q.delete 
  end

队列中的元素是连续存放的,所以队列的初始化赋值用{},而不加'。同样由于其是连续存放数据,所以,在队头和队尾pop/push数据非常方便,执行速度很快,这与队列的长度没有关系。但是在队列中间增加或者删除数据则需要搬移腾出空间,所以该操作耗时与队列的长度有关,应呈现线性相关。另可将动态数组,定宽数组的值复制给队列。

5.关联数组

针对与对一个非常大的地址空间寻址时,可利用关联数组,来保存这些稀疏矩阵的元素,只为实际写入的元素分配空间。

关联数组采用在 [ ]中放置数据类型的形式来声明,例如:[int], 可通过X.exists()索引某一元素是否存在。

initial begin
  bit [63:0]assoc[bit[63:0]],idx =1;
  repeat(64) begin
  assoc[idx] = idx;
  idx=idx<<1;
  end
  //使用foreach遍历数组
  foreach(assoc[i])
  $display("assoc[%h]=%h",i,assoc[i]);
  //使用函数遍历数组
  if (assoc.first(idx))                    //得到第一个索引
  begin 
  do
  $display("assoc[%h]=%h",i,assoc[i]);
  while(assoc.next(idx));                //得到下一个索引
  end
  end

6.数组的方法

6.1 缩减方法

          所谓缩减方法是将一个数组变成一个值,常见的方法有sum(和),product(积),and(与),or(或)和xor(异或),对于以上运算,要特别注意变量的位宽。SV中没有提供专门从一个数组中随机挑选单个元素的方法,对于定宽数组,队列,动态数组和关联数组可以采用$urandom_range($size(array)-1),而对于队列和动态数组还可以采用$urandom_range(array.size()-1).

6.2 定位方法

1)定位方法可以帮助查找数组中的最大值,特定值之类的信息,但是注意这些方法的返回值通常是一个队列。

int f[6] ='{0,1,2,3,4,5};
int d[]='{2,1,6,3,5};
int q[$]= {1,5,6,5}, tq[$];
tq =f.max();     //{5}
tq =d.min();     //{1}
tq =q.unique();  //{1,6}

2)find定位方法:

int d[] ='{9,1,8,3,4,4},tq[$];
  tq =d.find with(item>3);         //{9,8,4,4}
  tq =d.find_index with(item>3);   //{0,2,4,5}
  tq =d.find_first with(item>99);  //{}
  tq =d.find_first_index with(item==8);  //{2}
  tq =d.find_last with(item==4);         //{4}
  tq =d.find_last_index with(item==4);   //{5}

当把缩减方法与条件语句with结合使用时,sum操作符返回的结果是条件为真时的次数。

int count,total,d[] = '{9,1,8,3,4,4};
count =d.sum with(item>7);         //count =2
total =d.sum with((item>7)*item);  //total =17
count =d.sum with(item<8);         //count =4
total =d.sum with(item<7?item:0);  //12

6.3排序方法

所谓排序方法是对原有数组的数据进行重新定位的方法。

X.reserve() 倒序 不能带with使用

X.sort() 从小到大排序

X.rsort() 从大到小排序

X.shuffle() 随机 不能带with使用

7. typedef 自定义方法

7.1使用struct创建新类型

Verilog最大的缺陷就是没有数据结构,而在SV中可以通过struct语句创建数据结构,struct为一个数据的集合,所以它是可综合的。伴随着typedef可以用来创建新的类型,并利用新类型声明更多的变量。

struct {bit [7:0] r, g, b;} pixel;        创建了一个结构体

为共享该类型,通过typedef来创建新类型

typedef struct {bit [7:0] r, g, b;} pixel_s; 
pixel_s my_piexl;        //声明变量
my_piexl = '{'h10, 'h11, 'h10};   //结构体类型赋值

7.2枚举类型

枚举创建一种强大的变量类型,它仅限于一些特定名称的集合,例如指令中的操作码或者状态机的状态名。例如ADD,MOVE这些名称有利于编写和维护代码。枚举类型enum经常和typedef搭配使用,由此便于用户自定义枚举类型共享使用 。

typedef enum {INIT,DECODE,IDLE} fasmtate_e;
 fasmtate_e pstate,nstate;    //声明自定义的枚举类型 
 initial begin 
    case(pstate)
	     IDLE: nstate=INIT;   //数据赋值
		 INIT: nstate=DECODE;
		 default: nstate=IDLE;
    endcase
	$display("Next state is %s", nstate.name()); //显示状态的符合名
	end 

枚举值缺省是 为 从0开始递增的整数,如上缺省默认INIT为0,DECODE为1,IDLE为2。也可以自己定义,如下:

typedef enum {INIT, DECODE=2,IDLE} fsmtype_e;   //INIT缺省为0,DECODE为2,IDLE为3

如果没有特别指出,枚举类型会被当成int类型,由于枚举类型缺省值为0,所以在给枚举常量赋值赋值时要小心。

typedef enum {INIT=1,DECODE,IDLE} fasmtate_e;
 fasmtate_e pstate;              //不正确
typedef enum {INIT=0,DECODE,IDLE} fasmtate_e;
 fasmtate_e pstate;              //正确

枚举类型的子程序:

1. first() 返回第一个枚举常量;

2. last() 返回最后一个枚举常量;

3. next() 返回下一个枚举常量;

4. next(N) 返回后N个枚举常量;

5. prev() 返回前一个枚举常量;

6.prev(N) 返回前N个枚举常量;

当到达枚举常量列表的头和尾时,函数next和prev会自动环形绕回。

8. 字符串

与C不同,字符串的结尾并不带标示符null,所有尝试使用字符“\0”的操作会被忽略。字符串采用动态存储方式,所以不用担心存储空间会被用完 。

字符串常见的几种操作:

get(N):返回位置N上的字节

toupper 返回一个所有字符大写的字符串

tolower返回一个小写的字符串

{ }用于串接字符串

putc(M,C)把 字节C写到字符串的M位上

substr(start,end)提取出从位置start到end的所有字符。

string s
	initial begin
	s="IEEE ";
	$display(s.getc(0));     //"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 

你可能感兴趣的:(SV,前端)