本文主要是介绍芯片验证从零开始系列(二)——SystemVerilog数据结构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
芯片验证从零开始系列(二)——SystemVerilog数据结构
- SystemVerilog的数据类型
- 二值逻辑
- 定宽数组
- 合并数组
- 队列
- 关联数组
- 数组的方法
- 数据类型选择的经验
- 灵活性
- 存储量
- 排序
- 自定义类型
- 联合
- 合并结构
- 类型转换
- 静态转换
- 流操作符
- 枚举
- 字符串
🔈声明:
🔑未经作者允许,禁止转载
🚩推荐一个IC、FPGA新手入门的好网站:👉快 点 击 进 入 学 习 吧👈
IC验证行业中主流的语言就是SystemVerilog,其占比高达70%以上。而验证方法学主流是UVM,占比也是碾压姿态,并且两者还有不断增长的趋势。
所以要想学好IC验证,SystemVerilog和UVM是必学的。
所以笔者从SV开始学习。
SystemVerilog的数据类型
二值逻辑
SV添加了二值逻辑数据类型,帮助更高抽象级建模。
- bit : 1位二值变量
- byte :8位二值变量(等同于C的char)
- shortint:16位二值变量(等同于C的short)
- int:32位二值变量(等同于C的int)
- longint:64位二值变量(等同于C的longlong)
logic或bit构成的vector是无符号的,其余均是有符号的(所以要避免混用)
四值逻辑(reg,integer,logic)仿真开始时为X;二值逻辑(bit,byte)仿真开始时为0。
并且如果四值逻辑和二值逻辑的数据类型之间发送转换,Z和X会转换为0。
定宽数组
SV允许数组的紧凑定义,例如以下两种方式都是定义8行4列的二维数组,但是后者书写更为方便。
int array[7:0][3:0] = int array[8][4];
输入数组越界,四值逻辑会返回X;二值逻辑会返回0。
非合并数组中,低位放数据,高位不使用(如下图所示),所以用四值逻辑放同样数据要比二值逻辑多一倍的存储空间。
- 在SV中for和foreach都可以用来遍历数组,但是foreach用来遍历数组极为方便(不用考虑停止条件),例子如下:
initial begin
bit src[10];
foreach(src[j])src[j]=j;
end
在SV中可以直接复制和比较两个数组,但是只有**==和!=**两种结果,并且其余的数组运算要通过循环来实现。
在SV中还可以同时使用数组下标和位下标,如:
initial beginbit[31:0]src[5]='{5{5}}; //赋值5个 32‘d5$display(src[0][2:1];
end
这里显示的就是10.。
合并数组
合并数组可以将多个小字节组装成一个大的,这种数据类型较为紧凑。如:
bit[3:0][7:0]bytes;
这样的写法就是合并数组的写法,可以将4个8bit的数组合并地放在一起,就不会出现像前文图中的将高位浪费的现象出现。
队列
SV中的队列像C++中的双端队列。
可以使用内置函数插入删除,也可以用拼接的方法进行操作。
int j = 1, q2[$]={3,4},q[$]={0,2,5};
initial beginq.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}
end
关联数组
如果有一个稀疏矩阵需要存储,直接用数组来做肯定十分浪费存储空间。SV为这种情况提供了关联数组。(个人感觉很像哈希表)
- 如果想从一个关联数组中随机选取一个元素,你需要逐个访问它之前的元素,因为没有办法直接访问第N个元素。
数组的方法
在SV中数组的函数返回的都是队列而不是标量。
SV中没有提供专门的随机选取元素的办法,所以对于定宽数组,队列,动态数组和关联数组可以用¥urandom_range($siz(array)-1)。
find with语句可以指示指令如何去搜索。
- 数组的定位是新建一个队列返回,而数组排序是在原数组上排。
- reverse() 反向排序
- sort() 从小到大排序
- rsort()从大到小排序
- shuffle()乱序
数据类型选择的经验
灵活性
- 当数据索引不规则时,例如对于由随机数值或地址产生的稀疏分布索引,应选关联数组。
- 关联数组也可以用来对基于内容寻址的存储器建模。
- 对于仿真过程中元素数目变化很大的数组,用队列会较好。
存储量
因为需要额外的指针,队列的存储效率比定宽或动态数组稍差。(差在哪里呢?)
因为指针带来的额外开销,关联数组里每个元素所占用的空间可能会比定宽或动态数组大好几倍。
排序
如果元素是一次性全部加入的话,用定宽或者动态数组较好;如果元素是一个个加入的话用队列较好。
自定义类型
通过用户自定义类型,将以往Verilog的代码可以通过更少的代码来表示更多功能。
这里特别注意的是自定义数组类型的时候
typedef int fixed_arrays[5]
自定义定义数组长度时要把下标放在自定义名称后。
此外,struct只是一个数据的集合,可以被综合。但如果想生成带约束的随机数据,就应该用类。
联合
待了解后补充
合并结构
合并结构是为了节约存储空间,在结构体中每个数据还是占了一个32位的长字(即使浪费),那么用合并结构就可以充分利用空间,这和合并数组是一个道理。
- 如果经常操作整个结构用合并结构较好,如果经常操作其中成员用非合并结构较好。
类型转换
如果两个数据的比特位分布相同,可以直接赋值;如果比特位不相同,则需要流操作符重新安排。
静态转换
- 和C++的强制类型转换差不多,但是在右值前要加’。
int i;
real r;
i=int'(10.0-0.1);
r=real'(42);
流操作符
流操作符用于把其后的数据打包成比特流(但是此处有一个数组下标赋值乱序的问题还没搞懂,有待补充)
流操作符十分好用比如:
initial beginint h;bit[7:0] j[4]='{8'ha,8'hb,8'hc,8'hd};bit[7:0] q,r,s,t;h={>>{j}}; //0a0b0c0dh={<<{j}}; //b030d050 每一bit从右到左h={<<byte{j}}; //0d0c0b0a 按字节从右到左{>>{q,r,s,t}}=j; //把j分散给qrst;
end
- 操作符>>把数据从左往右变成数据流;操作符<<把数据从右往左变成数据流。
枚举
枚举类型能够自动为列表中的每个名称分配不同的数值。
并可以用内建函数name(),得到对应的字符串。
枚举类型可以直接赋值给int,但是int类型赋值给枚举类型做好用$case()检测一下是否越界。
- 此处还有一个小技巧。
因为枚举类型是一个环,如果大于最后一个数据后会返回头数据,所以遍历所有枚举类型时可以用do-while来实现,如:
typedef enum {red,blue,green} color_e;
color_e color;
color=color.first;
dobegin$display("color=%0d/%s",color,color.name);color=color.next;end
while(color!=color.first);
字符串
SV中string可以保存长度可变的字符串,不需要担心空间的问题。但是和C不同的是,SV的字符串结尾不带NULL。
此外利用$psprintf()可以将想要的信息转换成固定格式的临时字符串,如
$psprintf("%s %5d",s,42);
以上就是SV的数据类型的学习,暂时还有两个问题还没搞清楚,待后续明白后继续补充。
最后给大家安利一个好网站:
Verilog的学习还是要多以练习为主,想要练习Verilog的同学,推荐可以去nowcoder看看,他们现在的题库内容很丰富,属于国内做的很好的了,而且是课程+刷题+面经+求职+讨论区分享,一站式求职学习网站,最最最重要的里面的资源全部免费
这篇关于芯片验证从零开始系列(二)——SystemVerilog数据结构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!