SV——数据类型

SystemVerilog中扩展了Verilog中的数据类型,增加双状态数据、动态数组、队列等。

1. 内建类型

1.1 logic类型

SV中将reg类型替换成logic关键字,logic既可以在块语句中被赋值,也可以在assign中被赋值,可以用wire的地方都可以用logic来替换,但logic不能有多个驱动,比如双向总线建模的时候需要用wire。

1.2 双状态数据类型

只有0 1两个状态,没有x z,有利于提高仿真器性能,减少内存使用。

1
2
3
4
5
6
7
8
9
10
bit b; //sigle bit
bit [31:0] b32 ;// 32bit unsigned integer
int unsigned ui ;// 32bit unsigned integer
int i ;//// 32bit signed integer
byte b8; // 8bit signed integer
shortint s; // 16bit signed integer
longint l;//4bit signed integer
integer i4; // 32bit signed integer
time t;//64bit unsigned integer
real r; // double precision floating point

如果将双状态的变量连接到DUT的输出,那么如果输出x或z,将被转换成0或1,而不能检测到x z。使用$isunknown操作符可以在表达式位x或z的时候返回1.

2. 定位宽的数组

2.1 定义和初始化

1
2
int lo_hi[0:15]; //声明16个元素的数组
int c_type[16];//声明16个元素的数组

初始化用单引号和花括号组合。

1
2
3
int ascend[4] = '{0,1,2,3}; 
ascend[0:1] = '{1,1}; //修改前两个元素值
ascend='{0,1,default:1}; //最后两个元素默认1

2.2 foreach遍历数组

1
2
3
4
5
initial begin
int ascend[4] = '{0,1,2,3};
foreach (ascend[i]) // 不需要定义i,可以之间拿来用
ascend[i]*=2;
end

2.3 遍历多维数组

1
2
3
4
5
initial begin
int arr[2][3] = '{'{0,1,2},'{3,4,5}};
foreach(arr[i,j])
$display("%d\n",arr[i][j]); //打印6个值
end

也可以分开遍历

1
2
3
4
5
6
7
8
initial begin
int arr[2][3] = '{'{0,1,2},'{3,4,5}};
foreach(arr[i]) begin
$display("%d:\n",i); //打印6个值
foreach(arr[,j])
$display("%d\n",arr[i][j]); //打印6个值
end
end

2.4 赋值和比较

两个数组间可以直接赋值,比较

1
2
arr1 = arr2;
arr1 == arr2;

2.5 合并数组

既可以当作单独的数据,也可以分成几个小份作为数组。

1
2
3
4
5
bit [3:0][7:0] bytes;  //4字节组装成的32bit
bytes = 32'hCafe_Dada;
bytes; // 32bit
bytes[3]; //Ca字节
bytes[3][0];//单bit

用途:

  1. 如果需要标量与数组的转换用合并数组。

    1
    bit [31:0]  b32 = bytes 
  2. 如果需要等待信号变化,用合并数组,比如唤醒@操作符

1
2
3
4
bit [0:3][7:0] bytes;
bit [7:0] arr[4];
@bytes ;//正确,合并数组
@arr;//错误,非合并数组

3. 动态数组

​ 定宽数组的宽读在编译的时候就确定了。如果想在仿真时生成事务,但事务的总量是随机的,那么就需要用一个很宽的数组,但实际上可能只需要很小的数组,造成存储空间浪费。SV中提供动态数组,数组在仿真时分配存储空间。用[ ].用new[]来分配空间,delete()删除元素。

1
2
3
4
5
6
7
initial begin
int dyn[],dyn2[]; //[]符号
dyn = new[4]; //分配4个元素
foreach(dyn[i]) dyn[i] = i;
dyn2=new[4](dyn); // 将dyn的值给dyn2
dyn.delete();
end

定宽数组可以给动态数组赋值,编译器自动调用new函数。

4. 队列 queue

可以在队列任何地方增加或者删除元素,在性能上的损耗比数组小。**$符号。**

还括号初始化,但不需要单引号。

最后一个索引是$.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
initial begin
int j=0;
q1[$] = {0,1};
q2[$] = {2,3,4};
q1.insert(1,10);//在索引1处插入10
q1.insert(1,q2);//插入队列
q1.delete(1);//删除索引1处元素
q1.push_front(1);
j=q1.pop_front;
q1.push_back(1);
j=q1.pop_back();
foreach (q1[m]) q1[m];
q1 = {q1[0],q1[1:4]}; // 最后一个索引是$
q1 = {q1[0],j,q1[1:$]}; // 在任意位置插入元素、队列
end

5. 关联数组

关联数组就是哈希,保存键值对。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//文件内容:
12 min_address
123 max_address
//
initial begin
int switch[string];//定义,int是值类型,string是键类型
int min_add,max_add;//
int i,r,file;
string s,idx;
file = $fopen("switch.txt","r");
while(!$feof(file)) begin
r = $fscanf(file,"%d %s",i,s);
switch[s] = i; // 赋值
end
switch.exist("max_address"); //是否存在某个键
switch.first(idx);// 将第一个键值对的键(索引)保存在idx
switch.next(idx);//得到下一个键值对的键,保存在idx
switch.delete(idx);//删除idx的键对应的键值对
end

6. 数组的方法

6.1 $size

1
2
3
4
initial begin
int arr[4] = '{0,1,2,3};
$size(arr);//返回数组长度
end

6.2 sum\product\or\xor

sum方法,返回元素宽度的值。

1
2
3
4
5
initial begin
bit[0:7] arr[4] = '{1,2,3,4};
arr.sum;//返回8bit的值,也就是返回元素宽度的值
32‘b + arr.sum; //返回32bit
end

6.3 定位数组元素

max最大值、min最小值、unique返回不重复的元素。

这些方法可以用在定宽数组、动态数组、队列中。返回类型是队列。

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

with用来定位数组中的元素,与其他方法一起使用。

1
2
3
4
5
6
7
q = f.find with(item > 2); /找到大于2的元素,也是返回队列
q = f.find_index with(item >2); //大于2 的元素的索引
q = f.find_first with(item >2);
q = f.find_first_index with(item >2);
q = f.find_last with(item >2);
q = f.find_last_index with(item >2);
sum = f.sum with (item >2);//对大于2的元素求和

在条件语句with中item是重复参数,默认是item,也可以制定其他值

6.4 数组排序

reverse,sort(从小到大),rsort(从大到小)

7. 元素类型选择

动态数组:子程序中最好用动态数组,这样子程序可以对不同长度的数组进行处理,只需要程序参数匹配就行了。

关联数组:用在索引不规则的时候。比如由于随机值产生的分布索引

队列:当数组的元素数目变化很大的时候用队列,比如保存预期值的记分板中。

$size(array) 返回数组宽度

8. typedef定义类型

1
typedef unsigned int u32;

9. 创建自定义结构

9.1 struct

1
2
3
4
typedef struct{
int a;
string b;} my_struct;
my_struct st = '{1,'dong'};

用单引号和花括号来初始化。

9.2 合并结构packed

合并结构以连续的bit集来存放数据,

1
typedef struct packed {bit[7:0] a,b,c;} p_s;

合并struct和非合并struct的区别在于仿真的效率上。P42《绿皮书》

10. 枚举类型

10.1 定义

枚举类型可以为列表中的每个名称赋值。

1
2
3
4
5
6
7
8
9
10
typedef enum {INIT,DECODE,IDLE} fstate_e; //定义枚举类型
fstate_e nstate,pstate;//创建枚举变量
initial begin
case(pstete)
IDLE:nstate <= INIT;
INIT:nstate <= DECODE;
DECODE:nstate <= IDLE;
endcase
$display("nstate name is %s",nstate.name()); //
end

name()函数返回当前枚举值的符号名。

声明了枚举变量fstate_e之后,INIT,DECODE,IDLE这些名字可以直接拿过来用。它们的默认值是从0开始,递增,当然也可以自己设置.

注意:枚举类型被当作int来存在,如果定义变量的时候没有初始化,那么默认将枚举变量初始化为0,但如果在定义枚举变量的时候没有0,那么出错。如下面:

1
2
3
4
5
typedef enum {INIT=1,DECODE} state_e;
initial begin
state_e p=1;//正确
state_e pp;//错误,默认给pp赋值0,但state_e中没0
end

10.2 枚举子程序

1
2
3
4
5
6
first();//返回第一个
last()
next()下一个,会绕回
next(N)下N个
prev()前一个
prev(N)前N个

11. 常量 const

12. 字符串 string

1
2
3
4
5
6
getc(index)
tolower()
tohigher()
len()
substr(index1,index2);
$psprintf();//产生格式化的字符串

13. 类型转换

13.1 静态转换

1
2
3
4
initial begin
int i;
i = int'(10.0-0.1);
end

13.2 动态 $cast

13.3 流操作符

“>>”是将bit从左到右排列,转换成一个数据。

“<<”是将bit从右到左排列,转换成一个数据。

1
2
3
4
5
6
7
8
9
10
11
12
initial begin
int h;
bit[0:7] g[4],j[4] = '{8'ha,8'hb,8'hc'8'hd};
bit[0:7] q;
bit[0:7] b1,b2,b3,b4;
h = {>>{j}}; //以bit为单位将数组j从左到右打包成整数 0a0b0c0d
h = {<<{j}}; //以bit为单位将数组j从右到左打包成整数 b030d050
h = {<<byte{j}}; //以byte为单位将数组j从左到右打包成整数 0d0c0b0a
g = {<<byte{j}};
q = {<<4{8'b0001_1000}}; //1000_0001 半字节倒序
{>>byte{b1,b2,b3,b4}} = {>>byte{j}};//先将数组j以byte为单位pack,然后再以byte为单位unpack,并赋值给b1,b2,b3,b4
end