相比于Verilog 将寄存器类型reg和net(线网)类型区分如此清楚,在SV中引入了logic数据类型。任何使用线网的地方均可以使用logic,但要求logic不能有多个结构性的驱动,例如在对双向总线建模时。此时,需要使用线网类型。
四值逻辑类型有:logic,reg,wire,integer;
二值逻辑类型有:bit, byte,int,shortint,longint;
按照有无符号还可以分为:
有符号类型:integer, byte, int,shortint,longint;
无符号类型:logic,reg,wire,bit;
注意:
在遇到变量类型时,应注意它们的符号类型和逻辑类型,在变量的运算中,尽可能避免两种不一致的变量进行操作。
1.对于有符号类型,例如:byte变量的最大值是127,而不是255(它的范围是-128–127)。
2.对于逻辑变量类型,四值逻辑类型默认值为X,二值逻辑类型的默认值为0。
//一维定宽数组
int lo_hi[0:4]; //5个整数[0]...[4];
int c_style [5] ='{0,1,2,3,4}; //对5个元素初始化;
lo_hi = '{0,1,2,3,4}; //对5个元素赋值;
lo_hi[0:2] = '{0,1,2}; //对前3个元素赋值;
lo_hi[0:2] = '{3{3}}; //3个值全为3;
lo_hi = '{3,5,default:2}; //{3,5,2,2,2};
//非合并数组
int array1 [0:2][0:1]; //完整声明一个二维数组;
int array [8][4]; //紧凑的声明;
array [7][3]=2; //设置最后一个元素;
array1 = '{'{0,1},'{1,2},'{2,3}}; //数组赋值;
//合并数组
bit [3:0][7:0] bytes; //数组大小的定义格式为[msb:lsb],而不是[size];
bytes = 32'hCafe_Dada; //赋值;
//合并/非合并数组
bit [0:3][0:7]array [3]; //合并3*32比特
bit [31:0] 1w= 32'h0123_4567; //字
bit [7:0][3:0] nib; //合并数组;
array[0]=1w; //使用一个下标,可以得到一个字的数据;
array[0][3]=8'h01; //使用两个下标,可以得到一个字节的数据;
array[0][1][6]=1'b1; 使用三个下标,可以得到一个比特的数据;
nib = array[0]; //复制合并数组的元素值
注意:当测试平台通过存储器数据的变化来唤醒时,通过@操作符实现,这个操作符只能用于标量或者合并数组。例如上边代码中的非合并数组array,只可以将array[0]或者1w作为敏感信号,但不能用整个array数组,除非把它扩展成:@(array[0] or array[1] or array[2])。
可能用到的系统函数:
$dimensions(array_name) :返回数组的维度
$left(array_name,dimensions) :返回数组指定维度最左边索引值
$size(array_name,dimensions) :返回数组指定维度的大小
$bits(expression) :返回数组存储比特数
logic [1:2][7:0]word[0:3][4:1];
$left(word,1); //返回0
$left(word,2); //返回4
$left(word,3); //返回1
$left(word,4); //返回7
initial begin
bit [31:0] src[5]='{0,1,2,3,4},
dst[5]='{5,4,3,2,1};
if(src==dst)
$display("src==dst");
else
$display("src!=dst");
src=dst; //把dst的所有元素值赋给src;
$display("src[1:4] %s dst[1:4]",(src[1:4]==dst[1:4]?"==":"!=");//片段比较
end
SV中$size函数返回数组的宽度。在foreach循环中,只需要指定数组名,并在其后的()中给出索引变量,系统便会自动遍历数组中的元素,索引变量自动声明,并只在循环中有效。
initial begin
bit [31:0] src[5],dst[5];
for(int i=0;i<=$size(src)-1;i++)
src[i]=i;
foreach(dst[j])
dst[j] = src[i]*2;
end
动态数组在声明时使用空的下标[ ],这意味着数组的宽度不在编译时给出,而在程序运行时指定。数组在最开始是空的,所以必须调用new[ ]操作符来分配空间,同时在方括号中传递数组宽度。
int dyn[],d2[]; //声明两个动态数组
initial begin
dyn = [5]; //分配5个元素
foreach (dyn[i]) dyn[i]= i; //对元素赋值
d2 = dyn; //复制一个数组
d2[0] =5; //d2[0]复制为5
$display(dyn[0],d2[0]);
dyn = new[20](dyn); //分配20个元素并赋值
dyn = new [100]; //分配100个新的整数值
dyn.delete(); //删除所有值
end
动态数组有一些内建的子程序,例如delete和size。
只要基本数据类型相同,比如都是int,定宽数组和动态数组之间可以相互赋值,在元素数目相同的情况下,可以把动态数组的值复制给定宽数组。也可以将定宽数组的值复制给动态数组,此时,SV会调用构造函数new[ ]来分配空间并赋值。
队列的声明是使用带有美元符号的下标[$],但不需要使用构建函数new[ ]来开辟空间,。
队列的操作:
int j=1;
q2[$] = {3,4},
q[$] = {0,2,5},
initial begin
q.insert(1,j); //{0,1,2,5}
q.insert(3,q2); //{0,1,2,3,4,5}
q.delete(1); //{0,2,3,4,5}
q = {q[0],j,q[1:$]}; //{0,1,2,3,4,5};
q = {q[0:2],q2,q[3:$]}; //{0,1,2,3,4,3,4,5}
//下面操作执行速度很快
q.push_front(6); //{6,0,1,2,3,4,3,4,5}
j=q.pop_back; // j=5
q.push_back(8); //{6,0,1,2,3,4,3,4,5,8}
j=q.pop_front; //j=6
j =q[$]; //j=8
q ={q,8}; //{6,0,1,2,3,4,3,4,5,8,8}
q={}; //类似与q.delete
end
队列中的元素是连续存放的,所以队列的初始化赋值用{},而不加'。同样由于其是连续存放数据,所以,在队头和队尾pop/push数据非常方便,执行速度很快,这与队列的长度没有关系。但是在队列中间增加或者删除数据则需要搬移腾出空间,所以该操作耗时与队列的长度有关,应呈现线性相关。另可将动态数组,定宽数组的值复制给队列。
针对与对一个非常大的地址空间寻址时,可利用关联数组,来保存这些稀疏矩阵的元素,只为实际写入的元素分配空间。
关联数组采用在 [ ]中放置数据类型的形式来声明,例如:[int], 可通过X.exists()索引某一元素是否存在。
initial begin
bit [63:0]assoc[bit[63:0]],idx =1;
repeat(64) begin
assoc[idx] = idx;
idx=idx<<1;
end
//使用foreach遍历数组
foreach(assoc[i])
$display("assoc[%h]=%h",i,assoc[i]);
//使用函数遍历数组
if (assoc.first(idx)) //得到第一个索引
begin
do
$display("assoc[%h]=%h",i,assoc[i]);
while(assoc.next(idx)); //得到下一个索引
end
end
所谓缩减方法是将一个数组变成一个值,常见的方法有sum(和),product(积),and(与),or(或)和xor(异或),对于以上运算,要特别注意变量的位宽。SV中没有提供专门从一个数组中随机挑选单个元素的方法,对于定宽数组,队列,动态数组和关联数组可以采用$urandom_range($size(array)-1),而对于队列和动态数组还可以采用$urandom_range(array.size()-1).
1)定位方法可以帮助查找数组中的最大值,特定值之类的信息,但是注意这些方法的返回值通常是一个队列。
int f[6] ='{0,1,2,3,4,5};
int d[]='{2,1,6,3,5};
int q[$]= {1,5,6,5}, tq[$];
tq =f.max(); //{5}
tq =d.min(); //{1}
tq =q.unique(); //{1,6}
2)find定位方法:
int d[] ='{9,1,8,3,4,4},tq[$];
tq =d.find with(item>3); //{9,8,4,4}
tq =d.find_index with(item>3); //{0,2,4,5}
tq =d.find_first with(item>99); //{}
tq =d.find_first_index with(item==8); //{2}
tq =d.find_last with(item==4); //{4}
tq =d.find_last_index with(item==4); //{5}
当把缩减方法与条件语句with结合使用时,sum操作符返回的结果是条件为真时的次数。
int count,total,d[] = '{9,1,8,3,4,4};
count =d.sum with(item>7); //count =2
total =d.sum with((item>7)*item); //total =17
count =d.sum with(item<8); //count =4
total =d.sum with(item<7?item:0); //12
所谓排序方法是对原有数组的数据进行重新定位的方法。
X.reserve() 倒序 不能带with使用
X.sort() 从小到大排序
X.rsort() 从大到小排序
X.shuffle() 随机 不能带with使用
Verilog最大的缺陷就是没有数据结构,而在SV中可以通过struct语句创建数据结构,struct为一个数据的集合,所以它是可综合的。伴随着typedef可以用来创建新的类型,并利用新类型声明更多的变量。
struct {bit [7:0] r, g, b;} pixel; 创建了一个结构体
为共享该类型,通过typedef来创建新类型
typedef struct {bit [7:0] r, g, b;} pixel_s;
pixel_s my_piexl; //声明变量
my_piexl = '{'h10, 'h11, 'h10}; //结构体类型赋值
枚举创建一种强大的变量类型,它仅限于一些特定名称的集合,例如指令中的操作码或者状态机的状态名。例如ADD,MOVE这些名称有利于编写和维护代码。枚举类型enum经常和typedef搭配使用,由此便于用户自定义枚举类型共享使用 。
typedef enum {INIT,DECODE,IDLE} fasmtate_e;
fasmtate_e pstate,nstate; //声明自定义的枚举类型
initial begin
case(pstate)
IDLE: nstate=INIT; //数据赋值
INIT: nstate=DECODE;
default: nstate=IDLE;
endcase
$display("Next state is %s", nstate.name()); //显示状态的符合名
end
枚举值缺省是 为 从0开始递增的整数,如上缺省默认INIT为0,DECODE为1,IDLE为2。也可以自己定义,如下:
typedef enum {INIT, DECODE=2,IDLE} fsmtype_e; //INIT缺省为0,DECODE为2,IDLE为3
如果没有特别指出,枚举类型会被当成int类型,由于枚举类型缺省值为0,所以在给枚举常量赋值赋值时要小心。
typedef enum {INIT=1,DECODE,IDLE} fasmtate_e;
fasmtate_e pstate; //不正确
typedef enum {INIT=0,DECODE,IDLE} fasmtate_e;
fasmtate_e pstate; //正确
枚举类型的子程序:
1. first() 返回第一个枚举常量;
2. last() 返回最后一个枚举常量;
3. next() 返回下一个枚举常量;
4. next(N) 返回后N个枚举常量;
5. prev() 返回前一个枚举常量;
6.prev(N) 返回前N个枚举常量;
当到达枚举常量列表的头和尾时,函数next和prev会自动环形绕回。
与C不同,字符串的结尾并不带标示符null,所有尝试使用字符“\0”的操作会被忽略。字符串采用动态存储方式,所以不用担心存储空间会被用完 。
字符串常见的几种操作:
get(N):返回位置N上的字节
toupper 返回一个所有字符大写的字符串
tolower返回一个小写的字符串
{ }用于串接字符串
putc(M,C)把 字节C写到字符串的M位上
substr(start,end)提取出从位置start到end的所有字符。
string s
initial begin
s="IEEE ";
$display(s.getc(0)); //"I"
$display(s.tolower()); //ieee
s.putc(s.len()-1,"-"); //空格变为-
s={s,"P1800"}; //"IEEE-P1800"
$display(s.substr(2,5)); //EE-P
my_log($psprintf("%s %5d",s,42)); //创建临时字符串
end