Title:System Verilog 学习
背景与发展
什么是SV
-
啥是SystemVerilog?
就是用来专门写验证和测试的 Verilog 升级版——在verilog的基础上加了些C++的思想、语法、模块。
-
为啥要搞出一个SystemVerilog?
设计IC (integrated circuit)时用的是 verilog,语法内有 intial 和 task 用来写 testbench. (跑过综合就明白,综合的IC code里不能有intial等的,intial等只能用于testbench或功能级IC code) 但即使如此,Verilog写testbench时也很不方便:模块化、随机化验证都不够。
1999年OSCI推出的StstemC本质是C++库,写一些给IC code用的reference model时方便很多(尤其是算法类设计)。但C++的指针,玩不好容易让人心碎——发生内存泄露、写一些构建异常的测试用例都让人泪目。
2002年推出的SystemVerilog取他们精华、去他们糟粕。是verilog的扩展版,加了C++的OOP(面向对象)元素(封装、继承、多态)来方便写验证代码,加了新的验证特性(随机化、约束、功能覆盖率),简化了内存管理(多了内存回收机制),调用封装好的C程序也很方便,会verilog的人贼容易上手它,是IC验证人员的好伙伴~
(个人写习惯了C/C++,刚开始写verilog的testbench时有感触verilog磨唧…)
-
SystemVerilog和UVM的关系
我们用SystemVerilog写验证,而UVM既是一个思考模式,也是被实现好的规范、通用的验证库。关系像是C++和其内的 std容器。 UVM文件都是SystemVerilog文件,即后缀
.sv;
验证方法学
- 三种验证方法
- 断言验证 (Assertion Based Verification, ABV)
- 覆盖率驱动验证 (Coverage Driven Verification, CDV)
- 约束随机激励测试 (CR TB)
2 数据类型和编程结构
2.1 数据类型介绍
数值类型分类
-
SV内新增的数据类型——图示
P.S. 阿这,SV里没有
char类型,只有byte类型…
-
Verilog 1995的数据类型
-
基本数据类型:reg 和 wire
四值类型:(0/1/X/Z)
-
其他得注意的类型:integer (四态、有符号)
变量统统默认是static的。
-
-
SV内新增的数据类型
两态类型、枚举/联合类型、自定义类型、动态/关联数组、队列、字符串、类;
-
两值类型(only 0/1):bit、byte、shortint/int/longint;
1位、8位、16位/32位/64位;
3种int和byte都是默认有符号的.
-
四值类型:logic
-
-
-
二值/四值的区别
-
Z和X态是什么?
-
Z是高阻态,悬空未驱动,电阻相当于无穷大;
-
X是不定态,需要自行check逻辑确保一下;
-
-
2值的意义是比4值占用空间少一半,故module的端口用4值、module内变量用2值.
-
-
二值/四值分类总结:
-
二值逻辑:(bit, byte, shortint/int/longint)
默认初值为0.
-
四值逻辑:(logic, reg, wire, integer, time)
默认初值为x.
-
有符号类型:byte, shortint, int, longint, integer
-
无符号类型:bit, reg, logic, wire
-
其他类型:(实数 real,初值为0)
-
SV的logic类型
-
作用
四态数据类型,可以代替reg.
-
引入logic类型的意义
SV中引入了logic类型,因为dv人员无所谓变量是wire还是reg,用logic表示单纯赋值操作;logic类型是基于reg增强的,但又具有了连续赋值assign、当端口的各种特性,故dv人员用起来很方便——缺陷:不能被多个驱动。
-
限制
双向总线和多驱动的情况下不能使用,此场景只能用wire.
-
logic和 reg、wire 的区别
-
wire是net类型,映射到硬件 → 导线;
作用:传递、驱动、必须用assign连续赋值;
兄弟类型:tri、wand、supply0、inout等
不可赋初值;
-
reg是variable类型,映射到硬件 → 寄存器(没被综合优化的话);
作用:存储结构、必须用过程赋值;
兄弟类型:integer、time、real、realtime等;
可赋初值,但初值不可综合;
-
logic类型
编译器可自行推断logic是 reg还是wire. 可综合.
但logic只能有1个输入,不可多重驱动(不可既是in口也是out口).
注:Verilog有专门的inout端口写法,是用wire来实现的.
-
枚举类型 enum
-
语法
//使用方法1: //定义 enum 类型{ // `类型`可略,默认是 int name0 = value0, // 不写 `value` 则默认 从0递增 name1, name2 = value2 }var_name; // 是变量,不是类型! // 赋值 var_name = name0; // 不用加域 // 打印 $display("变量var_name的name: %s, 值: %0d", var_name, var_name); //使用方法2: //定义 typedef enum 类型{ name0 = value0, name1, name2 = value2 }var_type; // 是类型,不是变量! // 赋值 var_type var_name = var_type::name0; // 需要加域 // 打印 $display("变量var_name的name: %s, 值: %0d", var_name, var_name);-
枚举的“子变量类型”与“子枚举内部值”可略
“子变量类型” 默认是 int;
“子枚举内部值” 默认 是 从0递增 或 接着前者增加;
-
枚举变量的赋值:可自动转换为int值;
赋值为内部定义的枚举值时,不用加域!
-
枚举变量的打印,用
%s和%d,会自适应打印name或value.
-
-
内建函数
.first()返回此类型第一个枚举值 (enum);.last()返回此类型最后一个枚举值 (enum);.next(int N)返回下N个枚举值,默认是下1个 (enum);.prevt(int N)返回上N个枚举值,默认是上1个 (enum);.num()返回此类型枚举值的个数 (int);.name()返回此枚举值的名称 (string);
自定义类型 typedef
-
作用
-
起别名:
可以给任何已有的数据类型、联合体等,起新名字(别名)。
-
自定义新的变量类型
-
提前声明 (forward typedef):
用户自定义类型,必须在使用前被定义(同C/C++)。
故,可以在使用前,用
typedef提前声明,后续再定义。
-
-
语法
//起别名 typedef int hyp_defined_super_dilicious_type; // 给原有类型起别名 parameter SIZE = 666; typedef reg [SIZE-1:0] usr_type; // 自定义类型:封装为向量 typedef int Array[100]; // 自定义int的数组类型,大小100;使用时: Array a; typedef int Queue[$]; // 自定义int的队列类型;使用时:Queue q; // 提前声明 typedef class usr_class; // 提前声明自定义的 类或其他类型 typedef enum usr_enum; typedef
字符串类型 string
-
声明/初始化语法
string name1; string name2 = "abc";串若未初始化,默认为空串;
-
运算符
==/!=字符串判等 or 判不等</<=/>/>=字符串按字典序比大小{}字符串拼接{n {Str}}字符串复制n次串[i]打印串的第i个字符(第i个字节)
-
内建函数
-
.len()返回串的长度,不含终结字符; -
.putc(index, c)把index的字符替换成c字符;越界访问不报错,不改原串注:SV中单个字符也得用
"",别用'';或者用ASCII码的整数。 -
.getc(index)返回串[index]字符的ASCII;越界访问不报错,不改原串 -
.toupper()返回串的大写形式;不改原串 -
.tolower()返回串的小写形式;不改原串 -
.compare(string)和其他串按字典序比较;类似C语言的strcmp() -
.icompare(string)和其他串按字典序比较,不区分大小写 -
.substr(i, j)返回index是 [ i , j ] [i,j] [i,j] 的子串;越界则返回""空串. -
.atoi()将串转为数字——从串的头部(左边)寻找数字前缀,遇到非数字就停止;没找到就返回0. -
.atohex()同上,找前缀的十六进制串,遇到非数字就停止;没找到就返回0. -
.atooct()同上,找前缀的八进制串,遇到非数字就停止;没找到就返回0. -
.atobin()同上,找前缀的二进制串,遇到非数字就停止;没找到就返回0. -
.atoreal()同上,找前缀的浮点串,遇到非浮点数就停止;没找到就返回0. -
.itoa(value)把整数value,转为串存下来; -
.hextoa(value)把十六进制数value,转为串存下来; -
.octtoa(value)把八进制数value,转为串存下来; -
.realtoa(value)把实数value,转为串存下来;
-
结构体
-
作用
和C里的
struct一样。 -
定义与赋值语法
-
直接声明变量;
-
用
typedef声明自定义类型,再用类型定义变量;(和别的数组啥,flow一样其实)
// 方法1: struct{ // 自定义成员 int a; int b; }usr_var; // 方法2: typedef struct{ // 自定义成员 }usr_Type; usr_Type tmp; // 赋值方式 c = '{a:0, b:0.0}; // 用成员名进行赋值 c = '{default:0}; // 设置默认值 c = '{int:1, real:0.0}; // 为所有“此类型”的成员,设置默认值. -
-
结构体数组
typedef struct { int a; real b; }Node; // 定义结构体 Node arr[1:0] = '{ '{1, 1.0}, '{2, 2.0}}; // 结构体数组的初始化 // X: 非法的赋值过程(不能“并”在一起赋值) Node arr[1:0] = '{ 1, 1.0, 2, 2.0}; // 结构体数组的初始化 -
压缩(packed)、非压缩(unpacked)与存储结构
结构体可被定义为“压缩” 或 “非压缩” 的状态。这将影响其内成员的存储方式与计算方式。
-
压缩结构体:内部成员将无缝拼接在一起,相当于1个自定义vector,参与运算时也可整个以vector进行计算——“压缩”的概念.
-
压缩结构体可被视为1个vector,故可分为signed / unsigned,默认是unsigned;
-
压缩结构体内只要含有四态成员,整体struct即为四态类型;
-
压缩结构体内不允许用real等非整数类型变量、非压缩数组。
(“压缩/非压缩”数组的内容,见后面)
-
-
非压缩结构体:内部成员以字节为最小粒度进行存储,不足1字节就补齐。
默认是非压缩结构体.

// 压缩类型、带符号 struct packed signed{ //... }packed_var; // 默认是非压缩结构体 (unpacked) // 就算是压缩结构体,默认也是 unsigned 的 struct{ bit [3:0] data[4][5]; }mem; initial begin $display("%x",mem); // 非法! 这可是非压缩结构体,不能当vector用. $display("%x",mem.data[2][1]); // ok!这下用的data的压缩部分. end -
常量
-
定义
不会被改变的数据。
-
分类
simulator中,一般分为compile time(编译环节)、elaboration time(映射计算hierarchy 与 parameter的override传递 环节)、run-time(具体执行代码、仿真的环节)的三个运行环节。
SV中提供了 3种elaboration-time类型 和 1种run-time类型的常量。
-
elaboration-time类型
必须定义时初始化,将在elaboration-time中被赋值并一直固定。
- parameter
- localparam
- specparam
可用于 module、interface、program、class、package.
-
run-time类型
- const
-
-
语法格式
-
parameter
-
普通定义
parameter [数据类型/默认int/也可以为type类型] 参数名 = 参数值; -
模板内类型定义
parameter type T=shortint;定义模板里的类型. -
使用
$,表示无上限的边界parameter rv = $; property inq(lv,rv); endproperty assert inq(3);
-
-
localparam
同上,但localparam不能在例化时通过传入进行更改,只能内部使用.
-
specparam
只能在
specify block内使用,用于定义延时参数.先略。
-
const
-
特点
虽然类似localpram,但因为是run-time阶段赋值,故可赋更多元的变量类型(甚至可以为OOP对象)、被层次化引用.
-
定义方式
const 类型 变量名 = 值;- 右边可以用new()、new()内实参必须是常量表达式;
- 但此变量不可被重写。
-
-
文本
-
数值
-
指定位宽
e.g.
4'b1001 -
不指定位宽
e.g.
4356,默认十进制;e.g.
'b10000110自行确定位宽. -
批量所有位赋值
使用
'符号、并不指定基数:bit [7:0] data; data = '1; // data为 8'b11111111;我靠,我一直以为是等于1!!!
- 注意:
'1、'b1、1三个的区别!
- 注意:
-
-
字符串
-
语法:用
""包裹. -
多行字符串:行尾 用
\来换行. (新行空白格也将被算作字符) -
末尾间隔符:似乎会插入
\0作为间隔符(和C一样,ASCII值为0).我很怀疑有没有…因为打印不出此间隔符…
-
-
时间表示
- 时间文本被解释为 realtime类型,将根据当前的时间精度四舍五入。
- 可选的时间单位有:s、ms、us、ns、ps、fs.
注释
- 同C,有行注释
//和块注释\*, *\.
2.2 赋值语句
变量间赋值
-
四态值赋给两态值
这是合法的;所有
X和Z都会变成0.-
但关键是要检查是否有“未知态
X传播”:Solution: 用
$isunknown(),若表达式内有X或Z,则返回1.
-
参数传递与设置
SV提供了四种参数设置/传递的方式:
-
顺序赋值
类型 # (值1, 值2, ..., 值i); -
显示引用形参名赋值
类型 # (.形参名1(值1), .形参名2(值2), ..., .形参名i(值i)); -
defparam语句(不推荐)
类型强制转换
-
语法
// 取一个极端的例子,强转成自定义类型 typedef enum{red, green, blue}color; initial begin color tmp; tmp = color'(2); // 把2转成color类型. end
模块例化的信号传递 ⭐️
Verilog/SV提供了三种模块例化的信号传递方式:
-
自动的默认连接:同名信号自动连接
模块名 实例名(.*); -
位置关联

最低0.47元/天 解锁文章
1783

被折叠的 条评论
为什么被折叠?



