SystemVerilog 第2章 数据类型

  • 2.1 内建数据类型
  • 2.1.1 logic
  • 2.1.2 双状态数据
  • 2.2 定宽数组
  • 2.2.1 定宽数组的声明和初始化
  • 2.2.2 常量数组
  • 2.2.3 for and foreach
  • 2.2.4 复制和比较
  • 2.2.5 同时使用位下标和数组下标
  • 2.2.6 合并数组
  • 2.2.7 合并数组举例
  • 2.2.8 合并&非合并数组的选择
  • 2.3 动态数组
  • 2.4 队列
  • 2.5 关联数组
  • 2.6 链表
  • 2.7 数组方法
  • 2.7.1 缩减方法
  • 2.7.2 定位方法
  • 2.7.3 排序方法
  • 2.8 选择存储类型
  • 2.9 创建类型
  • 2.10 结构类型
  • 2.10.1 创建struct类型
  • 2.10.2 struct初始化
  • 2.10.3 联合(union)
  • 2.10.4 合并结构
  • 2.10.5 合并VS非合并
  • 2.11 类型转换
  • 2.11.1 静态转换
  • 2.11.2 动态转换
  • 2.11.3 流操作
  • 2.12 枚举类型
  • 2.12.1 定义枚举值
  • 2.12.2 枚举方法
  • 2.12.3 枚举类型的转换
  • 2.13 常量
  • 2.14 字符串
  • 2.15 表达式位宽



systemverilog 队列去重_verilog


systemverilog 队列去重_systemverilog_02


2.1 内建数据类型

2.1.1 logic

reg and wire 都可以用logic表示。
logic不能有多接口性的驱动,譬如inout接口只能用wire。

2.1.2 双状态数据

为什么要引入双状态数据?
systemverilog引入了两态的数据类型来减少仿真器对内存的使用和提高仿真的运行效率。

双、四状态数据类型比较

数据类型

数值

符号

默认位数

默认值

Using

byte(整型)

2

yes

8 or one ASCII char

0

SV

shortint(整型)

2

yes

16

0

SV

int(整型)

2

yes

32

0

SV

longint(整型)

2

yes

64

0

SV

bit(整型)

2

no

1

0

SV

integer(整型)

4

yes

32

x

V

reg(整型)

4

no

1

x

V

wire

4

no

1

z

V

logic(整型)

4

no

1

x

SV

time(整型)

4

no

64

x

V

real

2

yes

64 (C double)

0

V

shortreal

2

yes

32 (C float)

0

SV

realtime

2

yes

64 (same to real)

0

V

$isunknown()操作符可以在表达式中出现X/Z时候返回1。

2.2 定宽数组

2.2.1 定宽数组的声明和初始化

声明: int array[n-1:0];
int hi_lo[15:0];
int lo_hi[16];

所有数组类型 未初始化的四值逻辑数组默认值为x,二值逻辑数组默认值为0,线网没有驱动时候为z。

SystemVerilog 存放数组元素时候以32比特字为边界。
非合并数组中低位用来存放数据,高位不使用。
四值逻辑类数据 采用两个及以上的字存放。

2.2.2 常量数组

初始化
单引号加括号: '{}
赋值:int ascend[3:0] = '{0,1,2,3};
切片:ascend[2:0] = '{4,5,6};
重复:ascend = '{4{8}};
设置默认值:ascend = '{0,1,default:3};

注意
向量定义的位宽和赋值时候的位置是一一对应的:
int ascend[3:0] = '{0,1,2,3};
int descend[5] = '{0,1,2,3,4}

ascend[3:0]

[3]

[2]

[1]

[0]

对应data

0

1

2

3

descend[5]

[0]

[1]

[2]

[3]

[4]

对应data

0

1

2

3

4

2.2.3 for and foreach

格式
for(int i=0;i<$size(src);i++) command;
foreach(src[i]) command;
多维:
foreach(src[i,j]) command; // can not use src[i][j]
嵌套遍历:

foreach(src[i]) begin
	command1;
	foreach(src[,j])
	command2;
end

补充说明
foreach(src[i])中i是从高位到低位依次取的,并不一定是从0开始:
src[3:0]: i from 3 to 0
src[4]: i from 0 to 3

2.2.4 复制和比较

在不使用循环的情况下,只适用于数组的组合复制和比较(等于 == 和不等于 !==)。
dst = src; // SV中此复制操作后的数组相互独立,互不影响。

2.2.5 同时使用位下标和数组下标

src[0][1]
src[0][2:1]

2.2.6 合并数组

即将一个字的32位存储空间分为多个子数组用,位宽和子数组数量在变量名之前定义。
变量名之后有位宽定义就是非合并数组。

2.2.7 合并数组举例

数组大小的定义不能用[size]形式,只能用[msb:lsb].
复制操作是按比特bit为单位

2.2.8 合并&非合并数组的选择

如果需要让测试平台在存储器数据变化时被唤醒(如@),则只能用合并数组(或者标量)。

2.3 动态数组

声明&初始化: int array[]; & array = new[n];
 int dy2[];
 dy2 = new[5]; //必须调用new[]分配存储空间
 dy2 = '{0,1,2,3,4}; //初始化数据
 dy3 = new[10](dy2); //分配空间并复制一个数组进去
 dy2.delete(); //删除所有元素
 dy2 = '{}; //删除所有元素2.4 队列
声明&初始化: int array[$]
 int q[$] = {0,2,5}; //不用new[], 初始化不需要单引号
 q.insert(1,1); //{0,1,2,5}
 q.delete(1); //{0,2,5}
 q.push_front(6); //{6,0,2,5}
 q.pop_back; //{6,0,2}
 q.push_back(8); //{6,0,2,8}
 q.pop_front; //{0,2,8}
 q = {q[0],1,q[1:2]}; //用连接符 {0,1,2,8}
 q.delete(); //删除列队
 q = {}; //删除整个队列
 q = {q[0:2],q2,q[3:$]}; //可以拼接入另一个队列,但是不能用insert插入

注意
①队列切片时,$位于队列下标框[]左边表示最小值,位于右边表示最大值。譬如q为[0,2,8], 则q[ $:2 ]即为q[0:2], q[ $ ]即为q[2].
②可以将定宽数组或动态数组复制给队列

2.5 关联数组

在数组下标框[]中放置 数据类型进行声明。
数据类型指的是索引的数据类型,数组存储的值的类型由数组名前面的数据类型声明。
[*]的声明方式类似字典,可以后续放入不同类型的索引。

声明&初始化: int array[string];/int array[*];
 bit[63:0] assoc[bit[63:0]];
 assoc[1] = 1;
 foreach(assoc[i]) command;
 assoc.first(idx); // 将第一个索引值给idx
 assoc.next(idx); //将下一个索引值给idx
 assoc.prev(idx); //上一个索引值给idx2.6 链表
基本不用,列队比这个链表好用多了。
2.7 数组方法
2.7.1 缩减方法
array.sum/.product/.and/.or/.xor
 从数组中随机选取一个元素:
 ①$urandom_range($size(array)-1) //适用与定宽、动态、队列、关联数组
 ②$urandom_range(array.size()-1) //适用于队列和动态数组

2.7.2 定位方法

此类方法返回的是列表,返回的索引变量默认类型为int

q.min()/.max()
 q.unique() //返回唯一值的集合
 tq = q.find with (item>3); //大于3的元素列表
 tq = q.find_index with (item>3); //大于3的元素的索引列表
 tq = q.find_first with (item>3); //大于3的第一个元素的列表
 tq = q.find_first_index with (item>3); //大于3的第一个元素的索引的列表
 tq = q.find_last with (item>3);
 tq = q.find_last_index with(item>3);

2.7.3 排序方法

d.sort(); //正序
 d.rsort(); //倒序
 d.reverse(); //原数组的反序,作用于整个数组不可用with语句
 d.shuffle(); //洗牌,作用于整个数组不可用with语句

2.8 选择存储类型

依据灵活性,存储器用量,速度和排序要求选择最佳存储器类型。

2.9 创建类型

所有用户定义的类型后面都要带 “_t”
定义和声明变量

typedef bit[31:0] unit_t;
unit_t q;

2.10 结构类型

结构和类的区别在哪里?
类里面可以像module一样包含数据和对数据进行操作的程序,可以用于生成带约束的随机数据。
结构只是一个数据的集合,不包含程序,可以用于模块端口传递复杂数据。

2.10.1 创建struct类型

创建&声明

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

typedef struct {bit [7:0] r, g, b;} pixel_s; // 定义结构类型
pixel_s my_pixel; // 声明一个结构类型的变量

2.10.2 struct初始化

可以在声明同时或者过程赋值语句中给结构进行初始化赋值。
创建&声明&初始化

typedef struct {int a;
				byte b;
				shortint c;
				int d;} my_struct_s; //{}里面放的是一个一个的语句

my_struct_s st = '{32'haaaa_aaad,
					8'hbb,
					16'hcccc,
					32'hdddd_dddd}; //初始化赋值

2.10.3 联合(union)

union可以在同一个存储空间容纳不同类型的数据。
创建&声明&初始化

typedef union {int i; real f;} num_u
num_u un;
un.f = 0.0;

2.10.4 合并结构

类似于合并数组,将结构中的数据以连续的方式存放,关键字packed。
创建&声明: struct packed {int r, g, b} arrary_p_s;
typedef struct packed {bit [7:0] r, g, b;} pixel_p_s;
pixel_p_s my_pixel;

2.10.5 合并VS非合并

经常对整个结构进行频繁操作用合并结构,否则用非合并。

2.11 类型转换

2.11.1 静态转换

静态转换不对转换值进行检查
type’(val)
i = int’(10.0);
r = real’(42);

2.11.2 动态转换

$cast(类型1,类型2)
常用于父类句柄赋值给子类句柄,也可以用于子类赋值给父类(这类直接用 father=child 即可)
动态转换函数$cast()可以对转换进行检查:将类型2的变量值赋给类型1的变量,成功返回1,不成功返回0。

2.11.3 流操作

操作符 << 和 >> 可将其后跟的表达式、结构或数组转换为比特流并打包赋值给等号左边的变量。也可以在流操作符与{}之间加上分段宽度说明,从而按宽度转换成流。

h={>>{j}}; //lift to right
h={<<{j}}; //right to lift
h={<<byte{j}}; //按字节(8bit)倒序

注意
当在合并和非合并数组间进行流操作时候要注意下标的顺序:
[256]等效于[0:255],而不是[255:0]
bit [7:0] src [255:0]对应的非合并数组顺序为bit [255:0][7:0] dst,而不是bit [7:0][255:0] dst

2.12 枚举类型

实现文本替换工具有三:
①文本宏('define 宏名 宏内容):作用范围过大
②参数定义(parameter 参数名=常量;):单个数值单独定义
③枚举创建(enum {RED,BLUE,GREEN} color;):限于特定名称的集合

枚举类型声明和赋值

enum {RED,BLUE,GREEN} color; // only one enum variable

typedef enum {INIT, DECODE, IDLE} fsmstate_e; //定义枚举类型,默认值从0开始依次递增
fsmstate_e nstate; //声明枚举变量
nstate = DECODE; //初始化enum变量
nstate.name() //显示状态的符号名,返回值为字符串

2.12.1 定义枚举值

默认从0开始,依次加1。若人为指定值,则其后未指定值的变量从指定的值开始依次加1。
注意
枚举类型默认值为int,声明后若未初始化则默认值为0,因此定义枚举变量时,应确保有为0的枚举值存在(第一个枚举变量不赋值即可)。

2.12.2 枚举方法

color.first() //返回第一个枚举常量
color.last() //返回最后一个枚举常量
color.next() //下一个
color.next(N) //之后第N个
color.prev() //前一个
color.prev(N) //之前第N个

color.next()/.prev() 对枚举变量访问时会形成回圈,遍历时应采用color.first()/color.last()作为条件边界。

2.12.3 枚举类型的转换

默认为int,system verilog允许将枚举变量直接赋值给非枚举变量(如int),但是不允许没有显示转换的情况下把整型变量赋值给枚举变量。

$cast(color,int_c) //显式-动态
 int_c = COLOR_C’(color); //显示-静态

2.13 常量

  • 数字
  • parameter: parameter name = value;
  • const: 关键字放在数据类型前,声明的变量被强制转换为常量

2.14 字符串

声明和初始化: string s;
 string s;
 s = “IEEE”;
 s.getc(0) //返回第0位
 s.toupper() //大写
 s.tolower() //小写
 {s,“abc”} //连接字符串
 s.len() //长度
 s.putc(n,c) //第n位替换为c
 s.substr(2,5) //返回切片s[2:5]

2.15 表达式位宽

sum = a+b+c;
赋值表达式的位宽由=右边数值(a,b,c)最大的位宽而定;
赋值表达式最后赋值结果的位宽由被赋值变量(sum)的位宽决定;