system verilog(二)数据类型

首先 verilog-1995 中有两种基本数据类型:变量和线网(有四种取值,0,1,z,x)
变量可以是(所有存储都是静态的)

  • 单比特或多比特的无符号数(reg [7:0] m)
  • 32bit的有符号数(integer)
  • 64bit的无符号数(time)
  • 浮点数(real)

线网可以用来连接设计中不同的部分,例如门和模块实例(wire)

逻辑(logic)类型:任何使用线网的地方均可使用logic,但要求logic只能有一个驱动,不能有多个结构性的驱动(例如双向总线时,信号就需要被定义成线网类型,wire)
在这里插入图片描述
相比四状态数据类型,system verilog引入的双状态数据类型有利于提高仿真器的性能并减少内存的使用量
在这里插入图片描述
双状态变量连接到被测设计时,产生的X和Z的值会转换成双状态值(即随时检查未知值的传播,$i sunknown操作符,可以在表达式的任意位出现X或Z时返回1)
在这里插入图片描述
定宽数组
1、定宽数组的声明和初始化
verilog要求在声明中必须给出数组的上下界,而systemverilog允许只给出数组宽度的声明方式
在这里插入图片描述
可以通过在变量名后面指定维度的方式来创建多维定宽数组
在这里插入图片描述
如果代码试图从一个越界的地址中读取数据,那么systemverilog将返回数组元素的缺省值(对于四状态类型的数组,返回X;对于双状态类型,则返回0;适用于所有的数组类型,线网在没有驱动的时候输出是Z)

很多systemverilog仿真器在存放数组元素时使用32bit的字边界,所以byte,shortint和int都是存放在一个字中,而longint则存放到两个字中。(通常使用两个或两个以上连续的字来存放logic和integer等四状态类型)
例如:字节数组b_unpack存放到三个字的空间中
在这里插入图片描述
常量数组:使用一个单引号加大括号来初始化数组,可以部分或全部赋值,在大括号前标上重复次数可以对多个元素重复赋值,还可以为没有显式赋值的元素指定一个缺省值default
在这里插入图片描述
for 与 foreach
systemverilog的$size函数返回数组的宽度,在foreach循环中,只需要指定数组名并在后面的方括号中给出索引变量,systemverilog会自动遍历数组中的元素。
在这里插入图片描述
在这里插入图片描述
在多维数组中使用foreach并不是像[ i ][ j ]这样把每个下标分别列在不同的方括号中,而是用逗号隔开放在同一个方括号中,像[ i,j ]
在这里插入图片描述
在这里插入图片描述
打印多维数组的输出结果:
在这里插入图片描述
foreach循环会遍历原始声明中的数组范围,数组f[5]等同于f[0:4],而foreach(f[i])等同于for(int i=0;i<=4;i++);对于数组f[6:2]来说,foreach(f[i])语句等同于for(int i=6;i>=2;i–)

复制和比较:
比较只限于等于比较或不等于比较

同时使用位下标和数组下标
verilog 2001中改进,而在verilog 1995中不可以:
在这里插入图片描述
合并数组
对某些数据类型,希望既可以把它作为一个整体来访问,也可以把它分解成更小的单元
声明合并数组时,合并的位和数组大小作为数据类型的一部分必须在变量名前面指定,数组大小定义的格式必须是[msb:lsb],而不是[size]
在这里插入图片描述
合并和非合并数组可以混合使用,可以使用数组来表示存储单元,这些单元可以按比特、字节或字的方式进行存储
在这里插入图片描述
在这里插入图片描述
使用一个下标,可以得到一个字的数据barray[2] ; 使用两个下标,可以得到一个字节的数据barray[0][3] ; 使用三个下标,可以访问单个比特位barray[0][1][6];注意,barray[3],这个维度是非合并的,所以使用该数组时至少有一个下标。对于@操作符,只能用于标量或者合并数组;可以把barray[0]用作敏感信号,但不能用整个barray数组,除非扩展成:@(barray[0] or barray[1] or barray[2])

动态数组
由于定宽数组可能需要定义的很大,造成存储空间的浪费;动态数组在声明时使用空的下标[],即数组宽度不在编译时给出,而在程序运行时再指定。数组再最开始时是空,所以必须调用new[]操作符来分配空间,同时在方括号中传递数组宽度。可以把数组名传给new[]构造符,并把已有数组的值复制到新数组中
在这里插入图片描述
在这里插入图片描述
A:调用new[5]分配5个元素,动态数组dyn于是有了5个整型元素
B:数组的索引值赋给相应的元素
C:将dyn数组的元素值复制进d2
D和E:显示dyn和d2是相互独立的
F:分配20个新元素并把原来的dyn数组复制给开始的5个元素,然后释放dyn数组原有5个元素所占空间
G:调用new分配100个元素,但并不复制原有的值
(动态数组有一些内建的子程序,例如delete 和 size)

如果声明一个常数数组但不需要统计元素的个数,可以使用定态数组并使用常量数组进行赋值
在这里插入图片描述
只要基本数据类型相同,例如都是int,定宽数组和动态数组之间可以相互赋值,在元素数目相同的情况下,可以把动态数组的值复制到定宽数组,当把一个定宽数组复制给一个动态数组时,会调用构造函数new[]来分配空间并复制数值

队列:可以在队列中的任何地方增加和删除元素,在性能上的损失比动态数组小得多,因为动态数组需要分配新的数组并复制所有的元素的值,队列与数组相似,可以通过索引实现对任一元素的访问,而不需要像链表那样去遍历目标元素之前的所有元素
队列的声明是使用[$]:队列元素的编号从0到 $
在这里插入图片描述

可以用字下标串联来替换方法,如果把 $ 放在一个范围表达式的左边,那么 $ 代表最小值,例如[ $:2]代表[0:2] 。如果 $放在表达式的右边,代表最大值
在这里插入图片描述
在这里插入图片描述
关联数组
假设对一个几个G的字节寻址范围的处理器建模,但处理器可能只访问了用来存放代码和数据的几百或几千个字节,这种情况对几个G字节的存储空间进行分配和初始化显然是浪费的
systemverilog采用关联数组类型,用来保存稀疏矩阵的元素,即只为实际写入的元素分配空间(仿真器可以采用树或者哈希表的形式存放关联数组,但有一定的额外开销),在图中,只保留0…3、42、1000等位置上的值。
在这里插入图片描述
关联数组采用在方括号中放置数据类型的形式来进行声明,例如[int]或[packet]

initial
begin
bit [63:0] assoc[bit[63:0]],idx=1;//声明64个64位的关联数组
//对稀疏分布的元素进行初始化
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",idx,assoc[idx]);
	while(assoc.next(idx));//得到下一个索引
end

关联数组assoc具有稀疏分布的元素:1,2,4,8,16等等,first,next函数可以修改索引参数的值,然后根据数组是否为空返回0或1.

扫描二维码关注公众号,回复: 8635834 查看本文章

关联数组也可以使用字符串进行寻址,可以使用函数exists()来检查元素是否存在,如果试图读取尚未写入的元素,systemverilog会返回数组类型的缺省值default
输入文件的内容如下:
使用字符串索引读取文件,并建立关联数组switch,以实现从字符串到数字的映射

42 min_address
1492 max_address

在这里插入图片描述
数组的方法
1、数组缩减方法(i从0->9)
缺省情况下,如果把一个单比特数组的所有元素相加,其和也是单比特的,如果使用32bit的表达式,把结果保存在32bit的变量里,与一个32bit的变量进行比较,或者使用适当的with表达式,systemverilog都会在数组求和的过程中使用32bit位宽
在这里插入图片描述
其他的数组缩减方式还有product(积),and(与),or(或),和xor(异或)

对于定宽数组、队列、动态数组和关联数组可以使用 $urandom_range( $size(array)-1),而对于队列和动态数组还可以使用 $urandom_range( $size()-1),
(如果从一个关联数组中随机选取一个元素,需要逐个访问之前的元素,无法直接访问第N个元素)
在这里插入图片描述
数组定位方法
使用一个定宽数组f[6],一个动态数组d[]和一个队列q[ $];注意返回的是一个队列而非标量;min和max函数能够找出数组中的最小值和最大值,方法unique 返回的是在数组中具有唯一值的队列
在这里插入图片描述
表达式with 可以指示systemverilog如何进行搜索,代替foreach在数组的完全搜索,在条件语句with中,item被称为重复参数,代表了数组中一个单独的元素,是缺省的名字。

int d[]=`{9,1,8,3,4,4},tq[$];
tq=d.find with(item>3);
//等效代码
tq.delete();
foreach(d[i])
	if(d[i]>3)
	tq.push_back(d[i]);
tq = d.find_index with (item>3);  //{0,2,4,5} 返回值为索引的数组定位方法,其返回的队列类型是int而非integer
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}

在这里插入图片描述
在这里插入图片描述
数组的排序
在这里插入图片描述
reverse和shuffle方法不能带with条件语句,所以作用范围是整个数组
而sort和 rsort 可以带with条件语句

使用数组定位方法建立记分板
在这里插入图片描述
在这里插入图片描述
find_index()方法返回一个int队列,如果该队列为空(size == 0 ),没有匹配值;如果该队列有一个成员(size==1),说明有一个匹配,该匹配元素随后被check_addr()函数删除掉,如果该队列有多个成员
(size>1),说明计分板有多个包地址和目标值匹配

选择存储类型
如果数组的索引是连续的非负整数0、1、2、3等,则应使用定宽或动态数组,当数组的宽度在编译时已经确定时选择定宽数组,如果要等到程序运行时才知道数组宽度的话,选择动态数组;编写数组的子程序时,最好使用动态数组,只要元素类型匹配,同一个子程序可以处理宽度不同的数组。
当数组索引不规则时,应选择关联数组(可以用来对基于内容寻址的存储器建模)
对于在仿真过程中元素数目变化很大的数组,应选择队列。

1、存储器用量:使用双状态类型可以减少仿真时的存储器用量,应尽量选择32bit的整数倍作为数据位宽(使用合并数组有助于节省存储空间)

2、速度:
定宽和动态数组存放在连续的存储器空间中,访问任何元素时间相同
队列的读写速度与定宽或动态数组相当,但在很长的队列中插入新元素时,测试程序会变得很慢
对关联数组进行读写时,仿真器必须在存储器进行搜索,所以关联数组的存取速度是最慢的

3、排序
如果数据是一次性全部加入,选择定宽或动态数组,只需对数组进行一次分配
如果数据是逐个加入,选择队列,因为在队列首尾加入元素的效率很高
如果数组的值不连续且彼此互异,可以使用关联数组并把元素值本身作为索引(使用子程序first、next和prev可以从数组中查找某个特定值并进而找到它的相邻值)

使用typedef创建新的类型(define只是在进行文本替换)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

创建用户自定义结构
1、使用struct创建新类型
创建一个pixel类型变量

struct {bit[7:0] r,g,b;}pixel;

pixel结构

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

初始化

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

2、联合:与C语言相同,把变量存放在同一位置上
在这里插入图片描述
类型转换
1、静态转换:不对转换值进行检查,转换时指定目标类型,并在需要转换的表达式前加上单引号
在这里插入图片描述
2、动态转换:$cast允许对越界的数值进行检查
3、流操作符:<<和>>用在赋值表达式的右边,后面带表达式、结构或数组,用于把其后的数据打包成一个bit流,操作符>>将数据从左至右变成流,而<<将数据从右至左变成流
在这里插入图片描述
如果需要打包或拆分数组,可以使用流操作符来完成具有不同尺寸元素的数组间的转换

枚举类型
使用参数需要对每个数值进行单独的定义,而枚举类型却能够自动为列表中的每个名称分配不同的数值

enum {red,blue,green} color;

在这里插入图片描述
定义枚举值
枚举值缺省为从0开始递增的整数,如下例中,INIT代表缺省值0,DECODE代表2,IDLE代表3

typedef enum {INIT,DECODE=2,IDLE} fsmtype_e;

如果没有特别指出,枚举类型会当成int类型的存储,由于int类型的缺省值是0,所以给枚举常量赋值需小心。
在这里插入图片描述
枚举类型的子程序
sv提供了可以遍历枚举类型的函数
(1)first()返回第一个枚举常量
(2)last()返回最后一个枚举常量
(3)next()返回下一个枚举常量
(4)next(N)返回以后第N个枚举常量
(5)prev()返回前一个枚举常量
(6)prev(N)返回以前第N个枚举常量
当到达枚举常量的头或尾,函数next和prev会自动以环形方式绕回
而同样可以用do…while循环来遍历所有值
在这里插入图片描述
枚举类型的转换
枚举类型的缺省类型为双状态int,可以使用简单的赋值表达式把枚举变量的值直接赋给非枚举变量如int,但不允许没有进行显示类型转换的情况下把整型变量赋给枚举变量

常量
创建常量最典型的方法是使用文本宏(具有全局作用范围并且可以用于位段和类型定义,但缺点也是宏具有全局性,只需要局部变量时会发生冲突)此外还需要使用“."符号
typedef、以及parameter、const同样可以定义常量

sv中string类型可以用来保存长度可变的字符串,单个字符是byte类型。(注意,字符串的结尾并不带标识符null,字符串使用动态存储方式。
字符串相关的操作:

  • getc(N)返回位置N上的字节
  • toupper返回一个所有字符大写的字符串
  • tolower返回一个小写的字符串
  • 大括号{}用于串接字符串
  • putc(M,C)把字节C写到字符串的M位上,M必须介于0和len之间
  • substr(start,end)提取出从位置start到end之间的所有字符
发布了57 篇原创文章 · 获赞 4 · 访问量 1778

猜你喜欢

转载自blog.csdn.net/buzhiquxiang/article/details/103981202