目录
一、数据类型
1. 二值逻辑
bit、byte、int、shortint、longint(除bit外均有无符号类型)。
特点:性能更好,节省内存,用以模拟计算机验证的环境,X,Z默认为0。
2. 四值逻辑
logic、integer、reg、wire(除integer外均为无符号类型),模拟外部世界。
其中logic为拓展传统reg类型,也可像wire类型一样进行连线,使得在验证过程中不需过多考虑对应逻辑被综合成寄存器还是线网,使用时简单理解成赋值即可。同时需注意logic不能有多个结构性的驱动,多驱动(mutil-drive)时必须使用wire类型。
*使用有符号数据类型时需注意其取值范围,如byte最大值是127,而非255。
二、 数组
1. 基本数组特性参考C。
2. 动态数组:SV提供动态数组,以便仿真时再分配空间,达到节省存储的目的。使用时在动态数组声明时使用空下标[],运行时使用new[]分配空间。
int array1[] //动态数组
initial begin
array = new[5]; //new[]分配空间
foreach(new[i])
new[i] = i;
end
3. 队列:引入队列可在队列具体位置增加(.insert)、删除元素(.delete),比动态数组性能损失小(动态数组需要分配新的数组并复制元素的值)。队列声明使用[$],表示元素编号从0到$。
int q[$] = {1,2,3};
initial begin
q.insert(1,5); //第一个元素后插入5
q.delete(1); //删除第一个元素之后的元素
q.push_front(0); //在队列首位插入0
q.push_back(4); //在队列尾插入4
end
4. 关联数组
三、数组操作与方法
1. 数组循环(遍历)for & foreach
initial begin
bit [31:0] array[1:0];
for(i=0;i<=$size(array);i++)
array[i] = i;
foreach (array[i])
array[i] = array[i] * 2;
end
补充:foreach循环只会遍历原始声明中的数组范围,例如对于数组array[5:3],foreach(array[5:1])等同于for(int i=5;i>=3,i--)。
2. 数组复制和比较
复制:直接使用“=”进行赋值
比较:determine statements? if true : if false
$display("array1 %s array2", (array1 = array2) ? "==" : "!=");
3. 数组缩减:将一个数组缩减成一个值,常用 .sum, .product, .and, .or, .xor
bit on[10];
int all;
initial begin
foreach(on[i])
on[i] = i;
all = on[i].sum;
end
4. 数组定位:在非合并数组中查找数据,使用数组定位方法返回一个队列。常用:.min, .max, .unique和.find。
int a[] = '{9,6,4,1,5}, tq[$];
tq = a.find with(item>5); //{9,6},找出所有大于5的元素
tq = a.find_index with (item>5); //{0.1}
tq = a.find_first_index with (item==1); //{3}, a[3] = 1
示例需注意:find_index(返回值为索引的数组定位),返回的队列类型是int,非integer!
5. 数组排序:.reverse .sort .rsort .shuffle
其中reverse与shuffle不能带with条件语句。
四、typedef、struct创建新类型
1. typedef创建新类型,适应不同位宽操作数的算术逻辑单元编译时可配置的要求。
//自定义数组
typedef int fixed_array5[5];
fixed_array5 f5;
initial begin
foreach(f5[i])
f5[i] = i;
end
2. struct结构体是将不同类型数据组合成数据结构的方法(C),在SV中其是一个数据集合,即可综合。
initial begin
typedef struct{ //创建新类型
int a;
byte b;} my_struct_s;
my_struct_s st = '{ //结构体初始化
32'haaad,
8'hbb};
$display("str=%x %x %x %x",st.a, st.b, st.c, st.d);
end
3. 联合:将不同类型变量存放在同一位置,与C相同
typedef union{int i;real f;} num_u;
num_u un;
un.f = 0.0;
五、类型转换
1. 静态转换:转换时指定目标类型,并需在要转换的表达式前加上单引号,操作时不对转换值进行检查。
2. 动态转换:函数$cast,允许对转换值进行检查。
3. 流操作符:<<与>>,放在表达式、结构或数组的右边,将其后的数据打包成一个比特流。<<将数据从右至左变成流,>>将数据从左至右打包成流。不能将比特流结果直接赋值给非合并数据,而应该在复制表达式的左边使用流操作符把比特流拆分到合并数组中。
initial begin
int h;
bit [7:0] b,g[4],j[4] = '{8'ha, 8'hb, 8'hc, 8'hd};
bit [7:0] q,r,s,t;
h = {>> {j} }; //0a0b0c0d 把数组打包成整形
h = {<< {j} }; //b030d050 位倒序
h = {<<byte {j} }; //0d0c0b0a 字节倒序
h = {>>byte {j} }; //0d,0c,0b,0a 拆分成数组
h = {<< {8'b0011_0101} }; //1010_1100 位倒序
h = {<<4 {8'b0011_0101} }; //0101_0011 半字节倒序
{>> {q,r,s,t} } = j; //把j分散到四个字节变量里
h = {>> {t,s,r,q} }; //把字节集中到h里
end
六、枚举类型与转换
1.枚举类型:创建强大的变量类型,仅限于特定名称的集合,例如指令中的操作码或状态机中的状态名。使用枚举定义常量能自动为列表中的每个名称分配不同的数值。枚举类型子程序:
.first()/.last() | 返回第一个枚举常量 |
.next()/.next(N) | 返回下一个/N个枚举常量 |
.prev()/.prev(N) | 返回前一个/N个枚举常量 |
2. 枚举类型转换:SV要求整型变量赋值给枚举变量时需显示类型转换 ,目的在于使开发者使用时能意识到可能存在的数值越界情况。
typedef enum {RED,BLUE,GREEN} COLOR_E;
COLOR_E color, c2;
int c;
initial begin
color = BLUE; //赋一个合法的值
c = color; //枚举类型转换成整数型 1
c++; //整型递增 2
if(!$cast(color,c)) //整型转换为枚举类型
$display("cast failed for c= %0d", c);
$display("color is %0d /%s",color,color.name);
c++; //c = 3 对于枚举类型以越界
c2 = COLOR_E'(c); //不做类型检查
$display("c2 is %0d/%s",c2,c2.name);
end
七、 常量、字符串、表达式位宽
1. 常量:SV支持const修饰符,允许变量在声明时对其进行初始化,但不能在过程代码中改变其值。
2. 字符串:SV中string类型可用来保存长度可变的字符串。单个字符串为byte类型,长度为N的字符串元素编号从0到N-1。与C不同的是结尾不带null符,所有“\0”的操作都会被忽略。其使用动态存储方式,存储空间可变。常用操作见下表:
.getc(N) | 返回第N位的字符 |
.tolower()/.upper() | 返回小写/大写字符串 |
.putc(N,"s") | 第N位用"s"代替 |
{s,"-"} | {}将字符串s与“-”拼接 |
.substr(start,end) | 返回位置start到end的字符串 |
$psprintf() | 新建字符串 |
3. 表达式的位宽:表达式位宽常常造成不可预知情况,避免由位宽溢出造成的情况常采取方法
使用临时变量 |
加入其他值强制获取最小精度 |
变量强制转换达到期望的精度 |
bit[7:0] b8;
bit one = 1'b1;
$displayb(one + one); //1+1=0,两单比特相加,宽度溢出
b8 = one + one; //1+1=2 ,b8宽度为8 bit
$displayb(b8);
$displayb(one + one + 2'b0); //1+1=2,增加亚元参数,强制SV转换2 bit精度
$displayb(2'(one) + one); //1+1=2,操作数强制转换为2 bit值