本节将对SV的数据类型以及相应的特点和使用方法进行整理。
一.内建数据类型
1.verilog基本数据类型
对于verilog,有两种数据类型:线网(wire)和变量(reg)
其共同之处:verilog作用在于硬件电路的描述,因而reg和wire均具有四种状态取值:1,0,Z, X
而区别如下:
在绿皮书上的相关总结为:
2.Systemverilog内建数据类型
systemverilog扩展自verilog,向下兼容。在数据类型上的体现是:
- 弱化了reg和wire之间的区别,扩展了reg的使用场景,并将改进数据结构称为logic。(SystemVerilog对reg的数据类型做了改进,reg可以被连续赋值语句、门逻辑和模块直接驱动。SystemVerilog引入一个新的四态数据类型logic,可以替代reg;但不能用在双向总线和多驱动的情况下,此时只能使用网线类型(wire)。)
- 为提高仿真器的性能,减少内存使用量,引入了双状态数据类型,如下所示:
//bit无符号
bit a;//两态,单比特
bit[31:0] b32; //两态,32比特无符号数
//如下有符号
Int c32; //两,32比特有符号数
byte d8; //两态,8比特有符号数
shortint e16;//两态,16比特有符号数
longint 1;//两态 ,64比特有符号数
这样的话,按照状态划分:
按照有无符号划分:
二.systemverilog的其他数据类型
SV在数组的使用上也相较于Verilog(定宽数组)进行了扩展。
1.定宽数组
1.1. 数组的声明与初始化
在定宽数组声明方式改进在于:
- verilog声明数组时要求给出数组的上下界
- SystemVerilog声明定宽数组的时候,可以给出数组的上下界,也可以只给出数组宽度。
//一维数组
int a[0:15]; //16个整数 【0】...【15】
int b[16]; //16个整数 【0】...【15】 只给出数组宽度就是大端模式
int c[15:0]; //16个整数, a和b是一样的,c与其相反
//多维数组
int a[0:7][0:3];
int a[8][4];
a[7][3] = 1;
对于常亮数组数据初始化:
方式:采用单引号+大括号的方式
- 可以用一个单引号加大括号来初始化数组,在大括号前标上重复次数可以对多个元素重复赋值;
int ascend[4]; ascend = '{0,1,2,3}; int array[2][3]; array = '{'{0,1,2},'{3,4,5}};
- 可以在大括号前标上重复次数来对多个元素重复赋值;
int descend; descend = '{5{8}};//descend五个元素都是8
- 可以为没有显示赋值的元素指定一个缺省值
descend = '{9,8,default:1}; // {9,8,1,1,1}
1.2.数组的基本操作(遍历,定位,排序和缩减)
Systemverilog提供了很多数组的方法,例如searching、ordering和reduction(缩减),这些方法可用于任何一种非合并的数组类型,包括定宽数组、动态数组、队列和关联数组。其中这些方法会遍历数组的所有元素,然后用这些值去计算with指定的表达式。
1.2.1.利用for和foreach的遍历操作
在SystemVerilog中,foreach
循环是一种方便且灵活的循环结构,用于遍历数组、队列、链表等类型的集合元素。以下是foreach
循环的一些优势:
-
简洁性:
foreach
循环提供了一种简洁的语法形式来遍历集合元素,相比传统的for
循环,可以减少冗余的代码。您无需手动迭代索引,而是直接在循环中使用集合元素。 -
方便性:
foreach
循环可用于遍历各种集合类型,如数组、队列、链表等。它能够自动适应集合中的元素类型,并自动进行迭代。 -
代码可读性:使用
foreach
循环可以更直观地表达您的意图,使代码更易于阅读和理解。通过在循环中使用集合元素的名称,可以使代码更加清晰和可读。 -
遍历范围控制:
foreach
循环可以遍历集合的全部元素,也可以通过使用foreach
循环的迭代变量来指定遍历的范围。这使得您可以选择性地遍历特定的元素范围,而无需手动控制索引。
其基本使用可以参照以下帖子。
遍历基本语法https://blog.csdn.net/llxxyy507/article/details/122677065对于遍历,其中比较重要的就是遍历的顺序,其对于赋值的位置理解非常重要:
遍历顺序https://blog.csdn.net/moon9999/article/details/104190800eg:
已知一个多维混合数组的定义为:
bit [3:0][7:0][15:0] Array [3:0][7][6];
那么当我们写下
Array[2][3][2][2] = xxxx;
的时候,到底是对哪个位置赋值了??
答案其实很简单,如下简单的图示展示了方法,就是逆时针索引法:
因而对应结果为:
1.2.2.数组定位方法
数组方法https://blog.csdn.net/qq_33332955/article/details/107641152#t1
想要在非合并数组中查找数据,可以使用数组定位方法,数组定位方法会搜索满足expression的数组元素或者数组索引,这些方法的返回值通常是一个队列。
- 关联数组的索引定位返回一个与关联索引类型相同的队列(关联数组的索引为统配字符时不能用索引定位的方法);
- 除关联数组以外的数组索引定位返回一个int类型的队列;
- 如果没有元素满足with表达式或者数组是一个空数组,则返回一个空队列。
数组支持以下定位方法,这些定位方法必须带有with表达式:
—— find() 返回所有满足with表达式的元素
—— find_index() 返回所有满足with表达式的元素索引
—— find_first() 返回满足with表达式的第一个元素
—— find_first_index() 返回满足with表达式的第一个元素索引
—— find_last() 返回满足with表达式的最后一个元素
—— find_last_index() 返回满足with表达式的最后一个元素索引
int arr[];
int q[$];
...
// find all items equal to their position (index)
q = arr.find with ( item == item.index );//数组元素的值与索引值相等
对于以下定位方法,with表达式可以省略:
—— max() 返回最大元素组成的队列或者元素的表达式计算结果是最小值
—— min() 返回最小元素组成的队列或者元素的表达式计算结果是最小值
—— unique() 返回数组中具有唯一值的队列
—— unique_index() 返回数组中具有唯一值的索引队列
eg:
string SA[10], qs[$];
int IA[int], qi[$];
// Find all items greater than 5
qi = IA.find( x ) with ( x > 5 );
qi = IA.find( x ); // shall be an error
// Find indices of all items equal to 3
qi = IA.find_index with ( item == 3 );
// Find first item equal to Bob
qs = SA.find_first with ( item == "Bob" );
// Find last item equal to Henry
qs = SA.find_last( y ) with ( y == "Henry" );
// Find index of last item greater than Z
qi = SA.find_last_index( s ) with ( s > "Z" );
// Find smallest item
qi = IA.min;
// Find string with largest numerical value
qs = SA.max with ( item.atoi );
// Find all unique string elements
qs = SA.unique;
// Find all unique strings in lowercase
qs = SA.unique( s ) with ( s.tolower );
1.2.3.数组的排序方法
SystemVerilog提供了可以改变数组中元素顺序的方法,可以对数组中的元素进行正排序、逆排序或者打乱数组中元素的顺序。(关联数组除外)
数组支持以下排序方法:
—— reverse() 对数组中的元素进行逆排序,此方法不能与with条件语句一起使用。
—— sort() 按升序对数组进行排序
—— rsort() 按降序对数组进行排序
—— shuffle() 按随机顺序对数组进行排序,此方法不能与with条件语句一起使用。
如下具体示例:
string s[] = { "hello", "sad", "world" };
s.reverse; // s becomes { "world", "sad", "hello" };
int q[$] = { 4, 5, 3, 1 };
q.sort; // q becomes { 1, 3, 4, 5 }
struct { byte red, green, blue; } c [512];
c.sort with ( item.red ); // sort c only using the red field
c.sort( x ) with ( {x.blue, x.green} ); // sort by blue then green
1.2.4.数组缩减(待补充示例)
数组缩减方法是把一个数组缩减成一个值,这个值的类型与数组元素的类型相同,或者与with表达式值的类型相同。
数组支持的缩减方法如下:
—— sum() 返回所有数组元素的和,如果指定with表达式,则返回所有数组元素with表达式计算值的和。
—— product()积 返回所有数组元素的积,如果指定with表达式,则返回所有数组元素with表达式计算值的积。
—— and() 返回所有数组元素的与,如果指定with表达式,则返回所有数组元素with表达式计算值的与。
—— or() 返回所有数组元素的或,如果指定with表达式,则返回所有数组元素with表达式计算值的或。
—— xor() 返回所有数组元素的或非,如果指定with表达式,则返回所有数组元素with表达式计算值的或非。
2. 合并数组(Packed array)和非合并数组(Unpacked array)
2.1. 合并数组
- 一维的packed array也被称为Vector;
- 一个packed array被表示为一个连续的位集合。
- 数组大小定义的格式必须是[msb:lsb],而不是[size]。
例如:
bit[2:0] [7:0] array5;
在存储时是连续的:
2.2.非合并数组
- 可以是任意数据类型;
- 定义数组大小在名字之后;
- 在存储上bit组是不连续的的。
(很多SystemVerilog仿真器在存放数组元素时使用32bit的字边界,所以byte(8bit),shortint和int都是存放在一个字中,而longint则存放在两个字中。)
eg:
bit[7:0] array4[2:0] 或 bit[7:0] array4[3]
在存储时:
注:合并数组和非合并数组可以混用,例如:
bit[3:0] [7:0] barray[3]; //合并:3x32比特
3.动态数组
SystemVerilog提供了动态数组类型,可以在仿真时分配空间或调整数组宽度,这样在仿真中就可以使用最小的存储量。
动态数组在声明时使用空的下标[ ],如下:
定义:data_type array_name[ ];
基本操作:
new[ ] ——> allocates the storage.
size() ——> returns the current size of a dynamic array.
delete() ——> empties the array, resulting in a zero-sized array.
eg:
int dyn[], d2[]; //声明动态数组
initial begin
dyn = new[5]; //分配5个元素的空间内存
foreach(dyn[j]) dyn[j] = j;//对元素进行初始化
d2 = dyn; //复制一个动态数组,这是d2会自动调用new函数
d2[0] = 5; //修改复制值
$display(dyn[0],d2[0]); //显示数字(0和5)
dyn = new[20](dyn); //分配20个整数值并进行复制
dyn = new[100]; //分配100个新的整数值,旧值不复存在
dyn = dyn.delete(); //删除所有元素,此时数组大小为0
end
注:只要基本数据类型相同,定宽数组和动态数组之间就可以相互赋值。
- 元素个数相同的情况下,可以把动态数组的值复制到定宽数组;
- 当你把一个定宽数组复制给一个动态数组时,SystemVerilog会自动调用构造函数new[ ]来分配空间并复制数值。
4.队列
结合了链表和队列的优点:
- 如链表,可以在任何地方增加和删除元素(这类操作在性能上的损失比动态数组小很多,因为动态数组需要分配新的数组并复制所有元素的值。)
- 通过索引实现对任一元素的访问(无需像链表那样遍历目标元素之前的所有元素)
3.1. 声明方式:
- 第一种是bounded queue,定义了队列的上界;
- 第二种是unbounded queue,没有定义上界。
bit queue_1[$]; // queue of bits (unbound queue),队列的元素编号从0到$ int queue_2[$]; // queue of int byte queue_3[$:255]; // queue of byte (bounded queue with 256 entries) string queue_4[$]; // queue of strings
3.2. 队列的相关操作:
- 队列与数组相似,可以通过索引实现对任意元素的访问。
- 队列的元素是连续存放的,所以在队列的前面或后面存取数据非常方便。
- 在队列中间增加或删除元素需要对已经存在的数据进行搬移以便腾出空间。
- 可以把定宽数组或者动态数组的值复制给队列。
int j = 1;
int q[$] = {3,4}; //队列的常量不需要使用单引号'
int q2[$]= {0,2,5};
initial begin
q2.insert(1,j); //{0,1,2,5}在2之前插入1
q2.insert(3,q); //{0,1,2,3,4,5}在q2中插入一个队列
q2.delete(1); //{0,2,3,4,5}删除第一个元素
//下面的操作执行速度很快
q.push_front(6);//{6,0,2,3,4,5}在队列前面插入
j = q.pop_back; //{6,0,2,3,4} j = 5
q.push_back(8); //{6,0,2,3,4,8}在队列末尾插入
j = q.pop_front;//{0,2,3,4,8} j = 6
foreach(q[i])begin
$display(q[i]);
end
q.delete(); //{}删除整个队列
end
5.关联数组
SystemVerilog提供了关联数组类型,用来保存稀疏矩阵的元素。这意味着当你对一个非常大地址空间进行寻址时,SystemVerilog只为实际写入的元素分配空间。
语法: data_type array_name[index_type];
- 将内容存储在稀疏矩阵;
- 关联数组只有在使用时才分配存储空间;
- 关联数组为数组元素分配一个查找表,其中索引作为键。
int a_array1[*] ; // associative array of integer (unspecified index)
bit [31:0] a_array2[string]; // associative array of 32-bit, indexed by string
ev_array [myClass]; //associative array of event,indexed by class
-
借鉴引用:
数据类型https://zhuanlan.zhihu.com/p/146178041数组归纳https://blog.csdn.net/qq_33332955/article/details/107641152#t1