第二章 数据类型
1.内建数据类型
1.1 logic类型
原本的verilog代码中,需要分辨reg以及wire两种类型。在sv中,新加入了logic类型(逻辑类型)。
logic类型既可以代替reg,也可以代替wire。但是logic不能有多个结构性的驱动,所以在对双向总线建模时,不能使用logic,而是应该使用wire类型
在代码中一律使用logic,这样当一个信号被多次驱动就会编译报错。如果你知道该信号确实需要被多次驱动,可以直接使用wire。
1.2 双状态数据类型
双状态(0,1) | bit(单比特),byte(8位有符号整数),int(32位有符号整数),shortint(16位有符号整数),longint(64位有符号整数),real(双精度浮点数) |
---|---|
四状态(0,1,x,z) | integer(32位有符号整数),time(64位无符号整数),logic,wire,reg |
使用双状态数据,如果出现x或者z,它会被默认转换为0或者装换为1,可以使用指令$isunknown(iport)在任意位出现x或者z时候直接返回为1
2.定宽数组
2.1定宽数组的声明以及初始化
一维数组
int array[0:15]
和int array[16]
等价,都是索引从0到15的一维数组。
多维数组
int array[0:7][0:3]和int array[8][4]
等价,都是8行4列的数组。
越界地址访问的默认缺省值
四状态(logic) | X(未知态) |
---|---|
双状态(int ,bit) | 0 |
wire没有被驱动 | Z(高阻态) |
2.2合并数组和非合并数组
合并数组 | 合并数组同时存在同一块内存中,有利于数组和标量进行转换,也有利于直接将合并数组做为@操作符中的敏感信号。 |
---|---|
非合并数组 | 非合并数组不是放在同一块内存空间中,有利于分散存放,会造成空间的浪费 |
//合并数组的定义方法,数组大小必须为[msb:lsb]而不是[size],行列都必须在数组名之前定义
int [3:0][7:0] a;
a=32'h1111_1111; //可以直接对合并数组赋值
//非合并数组的定义方法,一般将行定义在等号右边,列定义在数组名之前
int [7:0] a[3:0];
//直接将合并数组转换
int [31:0] b=a;
//注意分辨什么情况下是合并数组
int [3:0][7:0] a[3]; //此时a是一个包括3个合并数组的非合并数组
2.3常量数组
//初始化数组的几种方法
//(1)定义时直接赋值
int a[5]='{1,2,3,4,5};
//(2)为部分元素赋值
a[0:2]='{5,6,7};
//(3)拼接赋值,类似verilog中的字符拼接
int a[5]='{5{8}};
//(4)缺省赋值,使用default默认赋值剩余元素,类似python的语法
int a[5]='{1,2,default:-1};
2.4数组循环
一维遍历
//for循环
initial begin
int a[5]='{5{8}};
for(int i=0;i<$size(a);i++) //其中的$size()输出为数组的长度,类似于C++中的.size().
a[i]=a[i]+1;
//foreach循环,类似于在C++中直接使用 for(auto i:a)遍历
foreach(a[j])
a[j]=a[j]+1;
end
二维遍历
initial begin
int a[2][2]='{'{0,1},'{2,3}};
//foreach一次循环,这里必须写成a[i,j]而不是a[i][j]
foreach(a[i,j])
a[i][j]=a[i][j]+1;
//foreach两次循环,分别对行列进行遍历的过程
foreach(a[i]) begin //行
foreach(a[,j]) begin //列
a[i][j]=a[i][j]+1;
end
end
end
foreach相当于与for对应:
int a[5] 类比于int a[0:4] foreach(a[i]) 等同于 for(int i=0;i<=4;i++)
int a[6:2] foreach(a[i]) 等同于 for(int i=6;i>=2;i–)
3.动态数组
类似java生成动态数组的方式,sv也可以通过new操作来定义数组的大小。
//初始化数组的几种方法
//定义动态数组
int a[],b[];
//通过new来定义数组的大小
a=new[5];
//赋值动态数组
b=a;
a=new[100](a); //分配100个新的数值,原来的前面5个值也被复制过来,但是原有的5个值所占的空间被释放
a=new[100]; //分配100个新的数值,原来的前面5个值也被覆盖掉,但是原有的5个值所占的空间被释放
//删除a的所有元素
a.delete();
当元素数目相同时,动态数组的值可以被赋给定宽数组;
当把定宽数组赋给动态数组,动态数组会默认通过new操作来获得所需要的操作空间。
4.队列
队列的特点是先入先出。
队列使用 $ 符号,并且队列的编号从0开始到 $ 结束。
//队列的创建
a[$]={1,2,3}; //队列不用',这可能是因为队列定义的空间是在一个块内的
b[$]={1,2};
//队列的插入insert和删除delete
a.insert(1,4); //{1,4,2,3}在第1个位置上插入2
a.insert(0,b); //{1,2,1,4,2,3}在第0个位置上插入b
a.delete(0); //{2,1,4,2,3}删除第0个位置的元素
//队列在对头和结尾增加和删除
a.push_front(6); //{6,2,1,4,2,3}
a.push_back(7); //{6,2,1,4,2,3,7}
i = a.pop_front(); //{2,1,4,2,3,7} i=6
i = a.pop_back(); //{2,1,4,2,3} i=7
a[$]={0,1,2};
[$:2] 在左边表示最小值0
[0:$]在右边表示最大值2
[$]默认取为[2]
5.关联数组
关联数组其实就是哈希表的一种实现方式。
//关联数组的定义
bit [63:0] assoc[bit[63:0]],idx=1;
//初始化
repeat(32) begin
assoc[idx]=idx;
idx=idx<<1;
end
//foreach遍历
foreach(assoc[idx])
$display("assoc[%h]=%h",i,assoc[i]);
//函数first和next遍历
if(assoc.fisrt(idx))
begin
do
$display("assoc[%h]=%h",i,assoc[i]);
while(assoc.next(idx));
end
//删除第一个元素
assoc.first(idx);
assoc.delete(idx);
string也能进行关联数组,可以用exist()来判断元素是否存在,对于双状态不存在会返回0,四状态不存在会返回X。(例2.2)
6.数组的方法
sum
//在用bit时候,求和也会变成1位的结果
bit a[10];
int total;
initial begin
foreach(a[i])
a[i]=i;
//打印单比特和
$display("a.sum=%0d",on.sum); //on.sum=1
//打印32比特和
$display("a.sum=%0d",on.sum+32’d0); //on.sum=5
//打印32比特和
total = on.sum
$display("a.sum=%0d",total); //total=5
$urandom_range( $ size(array)-1)
$urandom_range(array.size()-1)
随机选取1个元素对应的索引
$urandom 随机数
min最小的元素
max最大的元素
unique返回具有唯一值的队列
find
int a = '{1,2,3,4,5},b[$];
b=a.find with(item>3); //返回大于3的元素的队列 {4,5}
b=a.find_index with(item>3); //返回大于3的元素的索引队列 {3,4}
b=a.find_first with(item>3); //返回大于3的第一个元素的队列 {4}
b=a.find_first_index with(item==3); //返回等于3的元素的索引队列 {2}
b=a.find_last with(item>3); //返回大于3的最后元素的队列 {5}
b=a.find_last_index with(item>3); //返回大于3的最后元素的索引队列 {4}
//以下四条语句都是等价的
b=a.find_fisrt with(item==3);
b=a.find_fisrt() with(item==3);
b=a.find_fisrt(item) with(item==3);
b=a.find_fisrt(x) with(x==3);
sort排序
int a[] = '{1,3,2,4};
a.reverse(); //{4,2,3,1}
a.sort(); //{1,2,3,4}
a.rsort(); //{4,3,2,1}
a.shuffle(); //混排序列中的元素
7.typedef创建新的类型
typedef bit [31:0] uint;
typedef int unsigned uint;
定义32位双状态无符号数
8.struct
//创建新类型结构体
struct {
bit [7:0] r,g,b;
}pixel;
//创建一个结构体类型
typedef struct{
bit[7:0] r,g,b;
}pixel_s;
//用自己定义的结构体类型来创建一个对象,并对结构进行初始化
pixel_s my_pixel= ‘{8’b0,8'b1,8'b0};
//为了节约空间,可以将定义的结构合并,这样只需要用3个字节就能解决问题
typedef struct packed{
bit[7:0] r,g,b;
}pixel_s;
union联合体,可以把不同的元素放置在同一个位置上
typedef union{
int i;
real f;
} num_u;
num_u un;
un.f=0.0; // 将数值设置为浮点类型
9.类型转换
//静态转换
int a;
real b;
a=int'(10.0);
b=real'(42);
//动态转换
int a;
real b;
$cast(a,b); //把b转化为a,将右边强制转化为左边
流操作能够将字数组转换为字节数组,还能将结构体转换为字节数组的作用。
//流操作符号 >> <<
bit[7:0] j[4]='{8'ha,8'hb,8'hc,8'hd};
int h;
h={>>{j}}; //0a0b0c0d 将数组打包为整形
h={<<{j}}; //b030d050 位倒序
h={<<byte{j}}; //0d0c0b0a 字节倒序
bit[7:0] g[4];
g={<<byte{j}}; //0d,0c,0b,0a 拆分为数组
bit[7:0] b;
b={<<{8'b0011_0101}}; //1010_1100 位倒序
b={<<4{8'b0011_0101}}; //0101_0011 4位倒序
10.枚举类型
//定义枚举结构体
typedef enum{INT,DECODE,IDLE} fs;
fs a,b;
initial begin
case(a) //case语句来赋值b的状态
INT: b=INT;
IDLE: b=IDLE;
default: b=DECODE;
endcase
$display("Next state is %s",b.name()); //打印b的state
end
//默认缺省从0开始,就是确保第一个的默认值为0
typedef enum{INT=0,DECODE,IDLE} fs; //True
typedef enum{INT,DECODE=2,IDLE} fs; //True
typedef enum{INT=1,DECODE,IDLE} fs; //False
//遍历枚举,先设为first,再遍历一圈到first时候来结束,用do…while
typedef enum{INT,DECODE,IDLE} fs;
fs a;
a=a.first;
do
begin
$display("state = %0d / %s",a,a.name); //a默认值为数字,a.name才是对应的state
a=a.next;
end
while(a!=a.first)
//强制类型转换
typedef enum{INT,DECODE,IDLE} fs;
fs a;
int b;
b=a;
b++;
if(!$cast(a,b)) //如果$cast成功会返回1,失败则返回0
$display("Cast change is failed when b=%0d",b);
11.字符串
string s;
initial begin
s="IEEE";
$display(s.getc(0)); //get char 得到I,打印出来为73
$display(s.tolower()); //“ieee”
s.putc(s.len()-1,"-"); //"IEE-"
s={s,"11"}; //"IEE-11"
$display(s.substr(2,5)); //"E-11"
end
用字符串来打印日志有关的信息
task my_log(string message)
$display("@%0t : %s ",$time,message);
initial begin
mylog($psprintf("%s %5d",s,42));
//$psprintf是临时字符串,返回一个格式化字符串,可以直接传递给其他子程序
end