SystemVerilog验证 测试平台编写指南 第二章 数据类型 下

2.6 链表
SystemVerilog提供了链表数据结构,但是应该避免使用它,因为SystemVerilog提供的队列更加高效易用。
2.7 数组的方法
2.7.1 数组的缩减方法
基本的数组缩减方法就是把一个数组缩减成一个值。最常用的方法就是求和sum,除此之外还有product(乘)and(与)or(或)xor(异或)等
在进行数组压缩的时候,应该特别重要的一点需要注意,那就是位宽的问题。
例2.23 数组求和

module test_enum();
bit on[10]; //单比特数组
int total;
initial begin                 
foreach(on[i]) 
on[i]=i; //on[i]的值为0或1
$display("on.sum=%0d",on.sum);  //on.sum是单比特无符号的数 打印出单比特和 on.sum=1
$display("on.sum=%0d",on.sum+32'd0);  //on.sum是32比特数 打印出32比特和 on.sum=5
//由于total是32比特变量,所以数组的和也是32比特变量
total=on.sum;
$display ("total=%0d",total); //total=5
//将数组和一个32比特数进行比较
if (on.sum>=32'd5) //条件成立
$display ("sum has 5 or more 1's'');
//使用带32比特有符号运算的with表达式
$display("int sum=%0d",on.sum with (int'(item))); //利用with来限定on.sum的数据类型,这是一种比较好用的方式
end     
endmodule

SystemVerilog中,对定宽数组、队列、动态数组和关联数组可以使用 $urandom_range( $size(array)-1)来选取随机一个元素,而对于队列和动态数组还可以使用 $urandom_range(array.size()-1)。

2.7.2 数组定位方法
例2.25 数组定位方法min、max、unique、find

module test_enum();
int f[6]={1,6,2,6,8,6},
d[]='{2,4,6,8,10},
q[$ ]={1,3,5,7},
tq[$];
initial
begin                 
tq=q.min();     //求最小值{1}
foreach(tq[i])
$display("min:tq[%0d]=%0d",i,tq[i]);
tq=q.max();    //求最大值{7}
foreach(tq[i])
$display("max:tq[%0d]=%0d",i,tq[i]);
        
tq=f.unique();   //求数组中唯一值的队列
foreach(tq[i])
$display("unique:tq[%0d]=%0d",i,tq[i]);
        
tq=d.find with (item>3);  //利用find函数做操作
foreach(tq[i])
$display("find:tq[%0d]=%0d",i,tq[i]);
tq.delete();     //等价的操作
foreach(d[i])
if(d[i]>3)
tq.push_back(d[i]);
foreach(tq[i])
$display("tq[%0d]=%0d",i,tq[i]);
        
tq=d.find_index with (item>3);  //输出的是index索引也就是第几位的值
foreach(tq[i])
$display("tq[%0d]=%0d",i,tq[i]);  
end     
endmodule

注意:item被称为重复参数,它代表了数组中一个单独的元素,item是缺省的名字,你也可以指定别的名字。下面四种情况是等价的。

 tq=d.find_first with (item= =4);
 tq=d.find_first() with (item= =4);
 tq=d.find_first(item) with (item= =4);
 tq=d.find_first(x) with (x==4);

当数组的缩减方法和条件语句with结合使用时,sum操作符的结果是条件表达式为真的次数。下面我们来看一个例子。

module test_enum();
	int count,
	total,
	d[]='{9,1,8,3,4,4};
	initial
	begin                 
	count=d.sum with (item>7);  //比较表达式返回0或1
	total=d.sum with ((item>7)*item);
	$display("count=%0d total=%0d",count,total);  //2,17 

	count=d.sum with (item<8);
	total=d.sum with (item<8?item:0);
	$display("count=%0d total=%0d",count,total);//4,12
	count=d.sum with (item==4);
	$display("count=%0d",count); //2
	end     
endmodule

2.7.3 数组的排列
SystemVerilog有几个可以改变数组中元素顺序的方法。包括反向、正序、逆序、随机
例2.29 对数组排序

 int d[]='{9,1,8,3,4,4};
 d.reverse(); //反向'{4,4,3,8,1,9}
 d.sort();    //正序{1,3,4,4,8,9}
 d.rsort();   //逆序'{9,8,4,4,3,1}
 d.shuffle(); //随机'{9,4,3,8,1,4}

2.7.4 使用数组定位方法建立记分板

2.8 选择存储类型
其实数据类型的选择是多方面的,我们要考虑灵活性、存储器用量、速度、排序和数据结构等多种方面,在我们以后的应用中,我们将会深入地理解每种不同的数据类型的利弊。

2.9 使用typedef创建新的类型
在原有数据类型之上定义新的数据类型。为了不混淆,本书约定所有用户自定义类型都带后缀“_t”。

 parameter opsize=8;
 typedef reg[opsize-1:0] opreg_t;
 opreg_t op_a,op_b;

 typedef bit[31:0] uint;
 typedef int unsigned uint;  //等价的两种方式

对于新的数组定义并不是很明显。你需要把数组的下标放在新的数组名称中。

typedef int fixed_array5[5];
fixed_array5 f5;
initial
begin
	foreach(f5[i])
		f5[i]=i;
end

2.10 创建用户自定义结构
在SystemVerilog中,我们引入了数据结构的概念。struct只是把数据组织在一起,只是一个数据的集合。
2.10.1 使用struct创建新类型
struct可以把若干个变量组合到一起。我们统一将struct创建的新类型用“_s”来表示。

typedef struct{bit[7:0] r, g,b;} pixel_s;
pixel_s my_pixel;

initial 
begin
	typedef struct {int a,
					byte b,
					shortint c;} my_struct_s;
	my_struct_s st='{32'haaaaaaaa,
					8'hbb,
					16'hcccc};
	$display("st=%x %x %x",st.a,st.b,st.c);
end

2.10.3 创建可容纳不同类型的联合
联合体,通常意义上来讲就是同一位置放置不同类型的数据。如果需要以若干不同的格式对同一寄存器进行频繁读写时,联合体相当有用。我们约定以“_u”为后缀。

typedef union { int i; real f;} num_u;
num_u un;
un.f=0.0; //把数值设为浮点形式

2.10.4 合并结构
通过一个例子我们来描述一下合并结构(packed)可以节省存储空间。

typedef struct packed {bit [7:0] r,g,b} pixel_p_s;
pixel_p_s my_pixel;

2.11 类型转换
2.11.1 静态转换
静态转换不对转换值进行检查。如果越界的话,我们也不能察觉。
基本转换格式:type’(val)
例2.41 在整形和实型之间进行静态转换

int i;
real r;
i=int '(10.0-0.1); //转换是非强制的
r=real '(42);      //转换是非强制的

2.11.2 动态转换
动态转换函数$cast允许对越界的数值进行检查,如果不越界返回1,否则返回0。
2.11.3 流操作符
流操作符>>和<<用于把其后的数据打包成一个比特流。>>是把数据从左到右变成数据流,<<是把数据从右到左变成数据流
例4.42 基本的流操作

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字节倒序
	b={<<{8'b0011_0101}};  //10101100位倒序
	b={<<4 {8'b0011_0101}};//0101_0011半字节倒序
	{>>{q,r,s,t}}=j;       //将j分散到四个字节变量里
	h={>>{t,s,r,q}};       //将四个字节集中到h里
end

2.12 枚举类型
最简单的枚举类型声明包含了一个常量名称列表以及一个或多个变量。
利用内建函数name()可以得到枚举变量值对应的字符串。我们统一用后缀“_e”来表示枚举的数据类型。
2.12.1 定义枚举值
枚举值缺省为从0开始递增的整数,可以自己定义枚举值。通常在我们把0指给枚举常量,可以避免一些不必要的错误。
2.12.2 枚举类型的子程序
(1)first() 返回第一个枚举变量
(2)last() 返回最后一个枚举变量
(3)next() 返回下一个枚举变量
(4)next(N) 返回以后第N个枚举变量
(5)prev() 返回前一个枚举变量
(6)prev(N) 返回以前第N个枚举变量
遍历所有的枚举成员(注意对枚举类型值的定义)
当到达枚举常量列表的头或尾时,函数next和prev会自动以环形方式绕回。
2.12.3 枚举类型的转换
枚举类型的缺省类型为双状态的int
可以通过简单的赋值表达式把枚举变量直接赋值给变量int。
不允许直接把int赋值给枚举变量,这种是出于越界情况的考虑。
例2.51 整型和枚举类型之间的相互赋值

module test_enum();
typedef enum {RED,BLUE,GREEN} COLOR_E;
COLOR_E color,c2;
int c;

initial
begin                 
	color=BLUE; //赋一个已知的合法值
	c=color; //将枚举类型转换成整型
	c++; //整型递增
	if(!$ cast(color,c)) //将整型显示转换回枚举类型
	$ display("cast failed for c=%0d",c);
	$ display("color is %0d/%s",color,color.name);
	c++; //对于枚举类型已经越界
	c2=COLOR_E'(c); //不做类型检查
	$ display("c2 is %0d/%s",c2,c2.name);
	if(!$ cast(color,c))
	$display("cast failed for c=%0d",c);
	end
endmodule

结果:

 #color is 2/GREEN
 #c2 is 3/
 #cast failed for c=3

$cast(color,c)将int型动态转化为枚举类型,如果没有越界返回1,否则返回0;界内(0,1,2),3已经越界了。
2.13 常量
SystemVerilog中支持const修饰符,允许在变量声明时对其进行初始化,但不能在过程代码中改变其值。
2.14 字符串
SystemVerilog中的string类型可以用来保存长度可变的字符串。单个字节是byte类型。字符串使用动态的存储方式,所以不用担心存储空间会全部用完。
例2.53 字符串方法

module test_enum();
string s;

initial
begin                 
	s="IEEE ";
	$display(s.getc(0));//显示:73('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

	task my_log (string message);
	//把信息打印到日志里
	$ display("@%0t:%s",$time, message);
	endtask
endmodule

getc(N) 返回位置N上的字节
tolower()返回一个小写的字符串
putc(M,C)把字节C写到字符串的M位上,M必须介于0和len所给出的长度之间。
substr(start,end),读取从位置start到end之间的所有字符。

发布了38 篇原创文章 · 获赞 29 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_45270982/article/details/96707592