双状态数据类型:更好的性能,更低的内存
队列、动态和关联数组:减少内存消耗,自带搜索和分类功能
类和结构:支持抽象数据结构
枚举类型:方便代码编写,增加可读性
队列:适用于创建计分板,可频繁增加或删减数据
动态数组:允许在程序允许时指定数组宽度,为测试平台提供了极大灵活性
关联数组:用于稀疏存储和一些只有单一索引的计分板
枚举类型:创建具名常量列表使代码更便于读写。
bit b; //双状态,单比特
bit [31:0] bit; //双状态,32比特无符号整数
int i; //双状态,32比特有符号整数
byte b8; //双状态,8比特有符号整数
shortint s; //双状态,16比特有符号整数
longint l; //双状态,64比特有符号整数
interger i4; //四状态,32比特有符号整数
time t; //四状态,64比特无符号数
real r; //双状态,双精度浮点数
sv引入logic是因为Verilog作为硬件描述语言,倾向于考虑哪些变量作为寄存器、哪些变量作为线网类型,利于后端综合,便于设计者理解;sv侧重于验证语言,不关心logic综合成什么,只是作为单纯的变量进行赋值操作,这些变量只属于软件环境构建;另外也是为了方便验证人员驱动和连接硬件模块,不用考虑使用reg还是wire,节约了时间,避免出错。除了多个驱动,logic任意用。若存在多个驱动,会编译报错。至于有logic还要bit是因为在硬件里logic更多,在软件里bit更多。
logic [7:0] logic_vec=8'b1000_0000;
二值逻辑:byte、shortint、int、longint、bit
四值逻辑:interger、logic、reg、net-type(例如wire、tri)
有符号类型:byte、shortint、int、longint、interger
无符号类型:bit、logic、reg、net-type(如wire、tri)、time、real
带符号变量随机化可能会造成意想不到的结果
双状态变量连接到被测设计时要注意,可能存在X或Z转换为1,可以使用$isunkonwn操作符进行判断,可以在表达式任意位出现X或Z时返回1。(if($isunknown(iport) == 1))
静态转换:不对转换值检查。编译的时候做检查
动态转换:动态转换函数$cast允许对越界数值进行检查,仿真时才会检查是否成功
以上都是显示转换,带操作符或函数;反之问隐式转换
int i;
i=int '(10.0-0.1) //转换非强制,转换时指定目标类型,在需要转换的表达式前加上单引号即可
编译时数组的宽度就确定了
bit [31:0] b32; //32位无符号整数
bit b33 [0:32]; //33个整数
bit b34 [33]; //33个整数
int array [0:7][0:3]; //多维数组
int array1 [8][4];
int [3:0][7:0] array;
int [7:0] array [0:3]; //注意数组下标顺序
int ascend[4]='{1,2,defualt:3}; //数组定义'{}
注意:若从越界地址读取数据,那么sv将返回数组元素的缺省值。对于四状态的数,返回X,双状态类型返回0。线网在没有驱动时输出是Z。(适用于所有数组类型,包括定宽数组、动态数组、关联数组和队列,也适用于地址中含X或Z的情况)
注意多维数组的合并和非合并的写法,哪个为高维度。高维度数值表示有几个数,低维度可以认为是位宽?如果需要等待数组中的变化,必须是合并数组。合并数组既可以作为数组,也可以当作独立的数据。定义格式必须是[msb:lsb],而不是[size]
合并是连续存储,非合并是分开存储,sv仿真器使用32bit的字边界存放数组。表征4值逻辑要连续两位存储;如下面的若为logic数组,合并型:3*8*2=48>32,需要两个word;非合并型: 8*2=16<32,仍然只需3个word即可。
在16位的系统中(比如8086微机) 1字 (word)= 2字节(byte)= 16(bit)
在32位的系统中(比如win32) 1字(word)= 4字节(byte)=32(bit)
在64位的系统中(比如win64)1字(word)= 8字节(byte)=64(bit)
利用for和foreach对数组进行循环操作,比如算术运算
复制(=)、比较(==、!=)可聚合操作(对整个数组而不是单个元素操作)
//注意两种循环区别,第二种内部循环时有个逗号[,j]
byte twoD [4][6];
foreach(twoD[i,j]) //全部遍历,循环i和j是从0开始的;格式是[i,j]而不是[i][j]
foreach(twoD[i])
foreach(twoD[,j]) //外层遍历第一个循环,内层循环中遍历第二个维度。是不是可以直接看某一层
bit[31:0] src [5]='{0,1,2,3,4} 非合并数组
int array[0:7][0:3] 多维数组8*4
$size(src) 默认返回最高的维度完整形式为$sizw(src,1) 若为$size(src,2)则返回第二个维度个数
常量数组:用单引号加大括号初始化数组
最大特点:在仿真运行时分配空间或者调整宽度(灵活调节数组大小即存储量)
利用[ ]进行声明,最开始是空的。利用new[ ]分配空间,[ ]内传递数组宽度,同时可以传递数组名。
int dyn[],d2[]; //sv里int类型,默认为0,interger默认x?
dyn=new[5];
foreach(dyn[i])
dyn[i]=i;
d2=dyn; //直接赋值
dyn=new[10](dyn); 也是赋值,但只占另一数组的一部分,最开始dyn的5个元素所占空间会释放,其余为0
dyn.delete(); //删除所有
bit [7:0] mask [] //用来保存元素数量不定的列表
只要数据类型相同,定宽数组和动态数组可以互相转换。动态数组和定宽数组元素个数相同时,可以将动态数组直接复制定宽数组;当把定宽数组复制给动态数组时,sv会调用构造函数new[]分配空间并复制数组。
结合了链表和数组的优点。可以在队列任何地方增加或删除元素,在性能损失上比动态数组小,因为动态数组需要分配新的数组并复制所有元素的值。但是队列需要额外的指针,存取效率比定宽或动态数组稍差。
队列常量(literal)只有大括号没有数组常量中开头的单引号。当往队列添加元素时,若超过原有空间的容量,sv会自动分配更多空间。
q2[$]={3,4}; //注意没有单引号
q[$]={0,2,5};
q.insert(1,q2); // {0,3,4,2,5}
q.delete(1); //{0,4,2,5}
q={}; //删除所有元素
用$声明,不用new进行声明,且不用 ’ 。可用自带的push_back()和pop_front()实现FIFO。
队列是连续存放的,而关联数组只为实际写入的元素分配空间。
可以把定宽或动态数组的值复制给队列。
动态数组只为实际写入的元素分配空间 。关联数组可用来保存稀疏矩阵的元素。采用在方括号中放置数据类型的形式进行声明。 bit [63:0] assoc[int],idx=1; 后面idx=1为变量初始化,标号类型(索引类型)为int类型。
如果想从一个关联数组中随机选取一个元素,需要逐个访问它之前的元素。因为无法直接访问到第N个元素。
因为指针带来额外开销,关联数组里每个元素所占用的空间可能会比定宽或动态数组所占用的空间大好几倍。
可用于任何一种非合并的数组类型,包括定宽数组、动态数组、队列和关联数组。
缩减方法
//如果把一个单比特数组的所有元素相加,其和也是单比特。如果使用32比特的表达式,把结果保存在32比特的变量里,与一个32比特的变量进行比较。或者使用适当的with表达式,sv都会在数组求和过程中使用32比特位宽。
bit on[10];
foreach(on[i])
on[i]=i;
$display("on.sum=%0d",on.sum); //on.sum=1 如果方法不带参数,圆括号可以省略
$display("on.sum=%0d",on.sum+32'd0); //on.sum=5
定位方法
int d[]='{2,4,4,6,8,10};
int tq[$];
tq=d.min(); // {2},注意:min,max,unique方法返回的是一个队列而非标量
d.find with (item > 3); // 返回{4,4,6,8,10}
//item为重复参数,代表数组中一个单独的元素,item为缺省值,可以指定别的
名字
d.find_index with (item > 3); //返回{1,2,3,4,5} 返回的是索引
//此处的find_index也是一种方法,与以下几种方法一样
//返回值为索引的数组定位方法返回的队列类型为int
d.find_index() with (item > 3);
d.find_index(item) with (item > 3);
d.find_index(x) with (x>3);
cout=d.sum() with (item>5); //3 缩减方法与条件语句with结合使用时,注意结果。如sum与with结合,
//返回的是条件表达式为真的次数
排序
排序方法改变了原始数组,而数组定位方法新建了一个队列来保存结果
int d='{1,3,2,5,9};
d.reverse(); //直接逆序
d.sort(); //正排序
d.rsort(); //逆排序
d.shuffle(); //乱序 reverse和shuffle方法不能带with,它们作用范围是整个数组
结构数组???
structure packed { byte red,green,blue;} c[];
灵活性、存储器用量、速度、排序、最优的数据结构
数组宽度编译时确定已确,选择定宽数组;程序运行时才知道数组宽度选择动态数组。
定宽和动态数组都是存放在连续的存储器空间,访问其中任何元素耗时都是相同的,与数组大小无关。队列读写速度与定宽或动态数组基本相当。在队列中间插入或删除时速度受队列长度影响,因为需要搬移其他元素以便腾出空间。关联数组读写时,仿真器必须在存储器里进行搜索,要求更多运算量,所以存取速度最慢。
可以把parameter和typedef放在一个程序包(package)里以使其能被整个设计和测试平台共用。用户自定义的最有用的类型是双状态的32比特无符号整数。
'define OPSIZE 8
typedef bit[31:0] unit;
typedef unsigned unit; //与上一个定义一样
typedef int fixed_array[5]; //创建新的类型
fixed_array f5; //声明了一个这种类型的数组
verilog最大缺陷之一是没有数据结构。sv可用struct语句创建结构,struct只是一个数据的集合。所以是可综合的。
将若干个变量组合到一个struct中,利用typedef创建新的类型,从而可在端口和程序使用共享它.
typedef struct {bit[7:0] r,g,b} pixel_s; 创建结构体,声明新类型
pixel_s my_pixel; //声明变量
my_pixel='{'h10,'h10,'h10}; // 结构类型赋值,还可将多个不同类型赋给结构体,实现初始化,初始化加
了单引号,而创建时没有加
合并结构
上面的pixel_s占用了三个长字的存储空间,即使实际只需三个字节。可将其合并到尽可能小的空间里。当希望减少存储器的使用量或储存器的部分位代表了数值时,可用使用合并结构。
对结构操作很频繁时,使用合并结构效率比较高。若操作经常是针对结构内的个体成员而非整体,那就应该使用非合并结构。
typedef struct packed {bit[7:0] r,g,b} pixel_p_s;
pixel_P_s my_pixel;
my_pixel = '{'h10,'h10,'h10}; //非合并型要加'
bit [15:0] wq[$]={16'h1234,16'h5678};
bit [7:0] bq[$];
bq={>>{wq}}; //12 34 56 78,字数组转换成字节数组
bq={8'h98,8'h76,8'h54,8'h32};
wq={>>{bq}}; //9876 5462
对数值单独定义时,枚举类型能够自动位列表中的每个名称分配不同的数值。使用内建函数name可以得到枚举变量值对应的字符串。
enum常和typedef搭配使用,以便于共享使用仅限于一些特定名称的集合:如操作码的状态名
typedef enum{INIT,DECODE,IDLE} fsmsate_e; 创建枚举类型
fsm_state pstate,nstate; //申明变量
case{pstate}
IDLE:nstate=INIT; //数值赋值
IDLE:nstate=DECODE;
IDLE:nstate=IDLE;
endcase
$display("next state is %s",nstate.name()); //显示状态名
typedef enum {INIT,DECODE=2,IDLE} fsmtype_s; //无特别指出,枚举类型会被当做int类型存储,枚举值
//缺省为从0开始递增的整数。此处INIT为0,DECODE为
//2,IDLE为3
typedef enum {FIRST=1,SECOND,THIRD} ordinal_e;
ordinal_e position; //指定枚举类型不正确,position会被初始化为0
typedef enum {BAD_O=0,FIRST=1,SECOND} ordinal_e; //枚举类型正确,将0指定给一个枚举常量
用for循环遍历枚举类型比较困难,可以使用do...while,或者用first访问第一个成员,用next访问后面的成员。
可以直接用简单的赋值表达式将枚举变量的值赋给非枚举变量,如int;但sv不允许在没有显示类型转换的情况下将整型变量赋给枚举变量。显示转换是为了检查其是否存在数值越界的情况。
创建常量最典型的方法是使用文本宏。
verilog中需要使用“·”符号才能使其被编译器识别和拓展。SV中可以用typedef替换宏,还可以用parameter(作用范围仅限于单个模块)。另外,sv还支持const修饰符,允许在变量声明时对其进行初始化,但不能在过程代码中改变其值。
利用string保存长度可变的字符串。使用动态的存储方式,不用担心存储空间会全部用完。
函数$psprintf()可返回一个格式化的临时字符串,并且可以直接传递给其他子程序;可用$formatf()形成字符串句子;若只需打印输出,使用$display()即可。
bit [7:0] b8;
bit one=1'b1; //单比特
$display(one+one); //0 还是单比特
b8=one+one; //2 赋值表达式左侧有8比特变量
$display(one+one+2'b0); //2 使用了常量
$display(2'(one)+one); //2 采用强制类型转换,第一个值被指定为2比特的值
//为啥此处是强制类型转换,精度升高?