SV笔记(一) 数据类型

        SV为了区分硬件设计和软件验证环境,在四值逻辑的基础上引入了二值逻辑,在sv的硬件视线中,更多的是四值逻辑,在软件实现中更多的是二值逻辑。

1.内建数据类型

        在内建数据类型中,四值逻辑包含0,1,x,z,其中x表示信号的不确定,x表示信号未被驱动。verilog中使用寄存器类型reg和线网类型wire来表示四值逻辑,在sv中新增了logic等类型,logic类型是对reg类型的增强,不仅可以作为变量,也可以被赋值语句,门单元和模块驱动,但不能多驱。

        常见的四值逻辑和二值逻辑如下:

  • 四值逻辑:integer,logic,reg,net_type(wire,tri),time
  • 二值逻辑:bit,int,byte,shortint,longint

        在二值逻辑中,区分数据的有符号和无符号,最高位表示符号位,0为正,1为负。在进行数据的计算时需要注意有符号和无符号类型的转换。

2.定宽数组

        在数组初始化的时候,相比于verilog要求给出数组的上下边界,sv在此处做了优化,允许只给出数组宽度的便捷声明方式,跟C语言类似。

2.1一维数组和多维数组

        一维数组中,所有的数组都是以0作为数组索引的起始点。多维数组中,维度在变量的右侧,从左往右,维度依次降低。一维数组在初始化时使用大括号加 ` 的方式进行初始化。初始化示例如下:

int a[4] = `{0,1,2,3};

        多维数组分合并型数组和非合并型数组。非合并型数组在声明时,所有维度均在变量的右边,此方式可能会造成空间上的浪费。合并型数组声明,位和数组大小作为数据类型的一部分放在变量名的前面指定。数组大小定义的格式必须是[msb:lsb],而不是[size]。对于非同一侧,高维度永远在变量的右边。对于同一侧,从左往右,维度依次降低。合并型数组和非合并型数组的声明及其在内存的放置方式如下:

/*非合并型数组 4 * 8bit */
byte array[4]; 
/*合并型数组 1 * 32bit*/
bit[3:0][7:0] barray[1] ;

        非合并型数组在内存的保存方式

合并型数组在内存的保存方式

        在进行合并数组和非合并数组的选择时,需要和标量进行相互转换时,使用合并数组会非常方便。例如需要以字节或字位单位对存储单元进行操作时,如上例中的barray数组。

2.2数组的操作

        对数组常见的操作方式为for和foreach,也可使用$size返回数组的宽度。常用for循环和foreach循环对数组进行赋值或初始化。操作示例如下:

bit [31:0] src[5], dst[5];
for(int i = 0; i < $size(src); i++)
  src[i] = i;
foreach(dst[j)
  dst[j] = src[j] * 2; //dst value值为src的两倍

        可在不使用循环的情况下对数组进行聚合比较和复制,其中比较仅限于等于和不等于。数组的复制和比较操作如下:

// 把src的值复制到 dst
 dst = src ;
if(src[1:3] == dst[1:3]) begin // compare src and dst
    ...

3.动态数组

        sv中的动态数组类型,可以在仿真时分配空间或者调整宽度,这样在仿真中可以使用最小的存储量。使用[]进行动态数组的声明,使用new[]进行空间的分配,在方括号中传递数组元素的个数。并且可以把数组名传给new[],并把已有的数组的值复制到新数组中。动态数组也可通过$size返回数组的大小,并可通过$delete删除数组。动态数组示例如下:

int dyn[],d2[];
initial begin
  dyn = new[5];  //分配5个元素
  foreach(dyn[j) dyn[j] = j ; //数组元素初始化
  d2 = dyn ;    //复制数组
  d2[0] = 5 ;  //修改数组值
  dyn = new[20](dyn) ;  //分配20个整数值并进行复制 原来的5个元素分配到新开辟的空间
  dyn.delete();  //删除所有元素
end  

4.队列

        sv引入了新数据类型,队列,该数据类型结合了链表和数组的优点,可以在队列中的任何地方增加或删除元素,且赋值不需要使用 `,该方式在性能的损失上比动态数组要小。队列的声明方式为[$],队列的元素编号从0到$。队列集成了一系列的的操作方法,常用队列操作示例如下:

int j = 1;
int q1[$] = {3,4};     //队列声明和赋值
int q2[$] = {0,2,5};
/********/
q2.insert(1,j);  //在2之前插入1
q2.insert(3,q1);  //在q2队列中插队队列q1
q2.delete(1);    //删除第1个元素
//以下操作执行速度较快
q2.push_front(6);  //在队列前插入6
j = q2.pop_back;  //弹出队列最后一个元素 j= 5
q2.push_back(8);  //在队列末尾插入8
j = q2.pop_front;  //弹出队列首个元素 j = 6
q2.delete();      //删除队列

5.关联数组

        sv提供关联数组,用于来保存稀疏矩阵元素。该数组意味着,当对一个非常大的地址空间进行寻址时,实际只为写入的元素分配空间。该方式相比于动态数组,避免了空间上的浪费。在声明时,使用[*]进行声明,*为通配符,指代数据变量类型,可以为任意数据变量类型,如string,int等。关联数组的示例操作如下:

/*变量声明*/
int idx = 1; 
bit[31:0] assoc[int] ; /* 关联数组,类型为bit[31:0],索引为int*/
/* 对稀疏分布的元素进行初始化*/
repeat(32) begin
  assoc[idx] = idx ;
  idx = idx << 1;
end
/*找到并删除第一个元素,idx返回值为元素的索引号*/
assoc.first(idx); 
assoc.delete(idx);

6.结构体类型

        sv中的结构体类型,是一堆数据的集合,可将多个变量组合到一个结构体当中,结构体类型为非合并型数据。结构体类型通过关键字struct进行整体操作,一般使用结构体的名字对其中的变量进行操作。可以使用typedef来定义结构体,实现对同一结构体的多次例化,增强结构体的重用性。结构体示例如下:

struct {
  bit[7:0] r,b,g;
  int idx;
  shortint num;
} pixel;  /*结构体名字 pixel*/
typedef struct {
  int height;
  int weight;
  logic[7:0] legs;
  logic[7:0] eyes;
}animal;
animal duck; // 结构体例化
animal dog;  //结构体例化

        sv提供的合并结构允许数据在存储器中的排布有更多的控制。合并结构体是以连续比特集的方式存放,中间没有闲置的空间。采用关键字packed进行声明,示例如下:

typedef struct packed {int r,g,b;} pixel_p_s; //合并结构声明
pixel_p_s my_pixel;

7.枚举类型

        最简单的枚举类型声明包含一个常量名称列表及一个或多个变量。关键字为enum,也可通过typedef重定义枚举类型变量,增加复用性。枚举类型缺省值为0,可自行定义。后续变量开始加1递增,可在任意变量位置重新给变量赋值,后续变量从新的赋值开始加1递增。枚举类型声明示例如下:

enum {
  RED,  //缺省值为0,即RED = 0
  GREEN,    //变量自动递增,值为1
  BLUE,    //变量自动递增,值为2
  YELLOW = 6,  //重新定义变量值
  WHITE    //变量自动递增,值为7
}color;

        sv提供了一些可以遍历枚举类型的函数,示例如下:

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

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

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

  • next(N) 返回以后第N个枚举常量;

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

  • prev(N) 返回以前第N个枚举常量;

        当达到枚举常量列表的头或者尾时,函数next和prev会自动以环形方式绕回。可使用first访问第一个枚举变量,使用next访问后续的枚举变量。对于循环的终止,可使用color != color.first,则循环会在访问到最后一个枚举变量后终止。

        对于枚举变量的转换,枚举变量的缺省类型为双状态的int。可使用简单的赋值表达式把枚举变量的值直接赋值给非枚举变量int。但不允许整型变量直接赋值给枚举类型。需要经过$cast进行显示转换后,再赋值给枚举类型。

8.字符串

        sv中的string可用来保存长度可变的字符串,单个字符串时byte类型。长度为N的字符串,元素编号为0到N-1。与C语言不通的是,字符串的结尾不带标识符null。string类型声明时初始缺省为"",可使用系统函数$display()进行打印,使用$sformatf()进行格式化。

9.数据类型的转换

9.1静态转换

        静态转换不对转换值进行检查,不能区分是否转换失败。转换时指定目标类型,并在需要转换的表达式前加上单引号即可。 verilog对整数和实数,或者不同位宽的向量之间进行隐式转换,隐式转换不需要操作符或者系统函数介入,由编译器自行转换。

9.2动态转换

        动态转换使用系统函数$cast(tgt,src),动态转换允许对越界数值的检查,如果转换成功返回问哦0,转换失败返回为1.  

9.3流操作符

        流操作符<<和>>用在赋值表达式的右边,后面带表达式、结构或者数组。流操作符>>把数据从左至右变成流,而<<则把数据从右至左变成流。也可以指定一个片段宽度,把源数据按照这个宽度分段以后再转变成流。但是不能把比特流结果直接赋值给非合并数组,应该在赋值表达式的左边使用流操作符把比特流拆分到非合并数组中。流操作符示例如下:

initial begin
  int h;
  bit [7:0] b ,g[4];
  bit [7:0] j[4] = `{8'ha,8'hb,8'hc,8'hd};
  bit [7:0] q,r,s,t;

  h = {>>{j}};  //数组打包 h为0a0b0c0d
  h = {<<{j}};   //位倒序 h为b030d050
  h = {<<byte{j}};  //字节倒序,h为0d0c0b0a
  g = {<<byte{j}};  //拆分数组 g 为 0d,0c,0b,0a
  {>>{q,r,s,t}} = j;   //把j 分散到4个字节变量中
  h = {>>{t,s,r,q}};  //把字节集中到h中
end

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值