、
引言
本文简单介绍 System Verilog 语言的 控制流。
前文链接:
我的 System Verilog 学习记录(1)
我的 System Verilog 学习记录(2)
我的 System Verilog 学习记录(3)
我的 System Verilog 学习记录(4)
循环
简介
啥是循环 ?
循环是一段一遍又一遍地执行的代码。条件语句通常包含在循环中,以便在条件为真时终止。如果循环永远运行,则仿真将无限期挂起。
SV中有如下几种循环结构:
foreach
这是一个无限循环,就像 while(1) 一样。请注意,除非在 forever 块中包含时间延迟以提前仿真时间,否则仿真将挂起。
repeat
用于将一个块中的语句重复一定次数。下面显示的示例将显示消息5次,并继续执行代码的其余部分。
while
如果您知道verilog/C,您已经知道这一点。只要条件为真,它就会重复这个块。Counter最初是零,然后递增,直到它达到10。
for
do while
foreach
这最适合遍历数组变量,因为您不必找到数组大小,将变量设置为从0开始到数组大小-1,然后在每次迭代时递增。
while / do - while
while 循环首先检查条件是否为真,然后执行true语句。如果条件是假的,这个循环就在那里结束。
一个 do while 循环首先执行一次语句,然后检查该条件是否为真。如果条件为true,则执行语句集,直到条件变为false。如果条件为false,则循环就在那里结束。
因此,这两者之间的区别是,一个 do while 循环至少执行一次语句集。
语法
示例
while
int 变量如果定义但是没有初始化,默认给0;
用 Questa Sim 验证:
do while
foreach
SystemVerilog 数组是允许在单个变量中存储许多值的数据结构。foreach 循环仅用于迭代这样的数组,这是最简单和最方便的方法。
语法
示例:一维数组
foreach 等价于 for 的如下代码:
示例:多维数组
for
语法
对于循环,使用三步方法来控制其语句的执行:
1.初始化影响循环运行次数的变量
2.在执行该循环之前,请检查该条件是否为真
3.修改器在每次迭代结束时执行,并跳转到步骤2。
示例:数组迭代
示例:多个初始化
示例:增加多个修改项
forever
语法
forever 语句块内必须要有对应的延时信息。
SV中,always 块不能放在类中以及其他SV程序块内。我们可以用 forever 循环达到相同目的。
下面显示的伪代码模拟了Testbench中监视器的功能1,一旦启动,只要它的监视器上有活动,就允许其运行。
repeat
语法
示例
break,continue
break
continue
if-else-if
SV引入如下几种 if - else 结构:
- unique-if
- unique0-if
- priority-if
unique-if,unique0-if
unique-if 以任意顺序评估判断条件:
- 除非有明确的else分支语句,否则匹配不到if条件的内容是会报告一个error;
- 在if-else条件中匹配到都于一个的分支语句也会报告一个error;
注意,unique0-if 在匹配不到任何条件时不会报告error。
示例:无else分支的unique-if
示例:unique-if中存在多条件匹配
priority-if
priority-if完全按照顺序评估判断条件。当出现以下情况时,会报告一个违例:
- 没有一个判断条件为真
- 缺少else分支,且前面几个判断条件均不满足
示例:priority-if 缺少 else分支
示例:priority-if匹配第一个条件后退出
case
unique-case,unique0-case
所有case语句都可以通过 unique 或 unique0 关键字进行限定来执行违例检查,就像我们在 if-else-if 构造中看到的那样。
unique 和 unique0 确保没有重叠的大小写项,因此可以并行计算。如果存在重叠的案例项,则会报告违规行为。
- 如果多于1个case语句可以和已知表达式匹配,会报告一个违例并且执行第一个匹配的case分支;
- 如果所有的case语句都不匹配,仅在 unique 关键词下报告一个违例,unique0 则不会报告违例;
示例:unique 无一匹配
示例:unique 多个匹配
priority-case
阻塞 & 非阻塞语句
此处和 Verilog 的相同,给几个示例:
阻塞语句的赋值的立刻的。仿真结果:
加入延迟:
非阻塞赋值示例:
仿真结果:
执行过程:
加入延迟:
执行过程:
非阻塞右侧表达式在一开始便执行计算,但是赋值过程发生在下一时间刻度。
事件
事件是在两个或多个并发活动的进程之间进行同步的静态对象句柄。一个进程将触发该事件,另一个进程等待该事件。
- 可以和其他事件变量比较或者指定为其他事件变量
- 可以传递给队列、函数和任务
- - 可以指定为 null
- - 将两个变量赋给另一个事件时,这两个变量指向相同的同步对象
如何触发/等待事件 ?
- 可以使用 ->;或 ->> 运算符触发已命名事件;
- 可以用 @ 或者 .triggered 让进程等待事件;
示例
@ 和 .triggered 有啥区别 ?
事件的触发状态在整个时间步长中持续存在,一直到仿真进一步执行。因此,如果等待事件和事件的触发同时发生,则会出现竞争条件,而 triggered 的属性有助于避免这种情况。
等待已触发状态的进程始终解除阻塞,而不管等待和触发的顺序如何。
示例
仿真结果:
请注意,由于 @ 和 -> 操作符之间的竞争条件,Thread2从未收到触发。
等待顺序
等待按给定顺序触发事件,如果有任何事件无序执行,则会发出错误。
示例:
合并事件
将一个事件变量分配给另一个事件变量时,等待触发第一个事件的所有进程都将等待第二个变量被触发。
函数
SV中的函数和Verilog 中的具有相同特性。
函数的主要目的就是可以在表达式计算上调用,且不耗费仿真时间。
- 函数不能有时间控制语句,比如 @ ,#,fork join ,wait
- 函数不能调用任务,因为任务可以有时间控制语句
示例:ANSI-C 风格的声明
示例:使用声明和IO口
如何通过值传递参数 ?
按值传递是将参数传递给子例程的默认机制。每个参数都被复制到子例程区域,对子例程区域中的该局部变量所做的任何更改在子例程外部不可见。
仿真log:
如何通过参考传递参数
通过引用传递的参数不会复制到子例程区域,而是将对原始参数的引用传递给子例程。参数声明前面带有ref关键字。对子例程内的变量所做的任何更改都将反映在子例程外部的原始变量中。
对具有静态(static)生存期的子例程使用按引用传递参数是非法的。
任务
函数用于对输入进行一些处理并返回单个值,而任务更为通用,它可以计算多个结果值并使用output 和 inout 类型的参数返回计算结果。任务可以包含时间控制语句,如@posedge等。
语法
静态任务
如果任务是静态的,则其所有成员变量将在已启动以并发运行的同一任务的不同调用之间共享。
启用任务的参数(x,y,z)与任务定义的参数(a,b,c)相对应。因为a和b是输入,所以x和y的值将分别放在a和b中。因为c被声明为输出,并且在调用期间与z连接,所以和将自动从c传递给变量z。
自动任务
关键字 aotumatic 将使任务重入,否则默认情况下它将是静态的。自动任务中的所有项都是为每个调用动态分配的,而不是在并发运行的同一任务的调用之间共享。请注意,层次结构引用不能访问自动任务项。
为了便于说明,考虑从并发运行的不同初始块中调用的静态任务显示。在这种情况下,任务中声明的整数变量在任务的所有调用中共享,因此每个调用显示的值应该增加。
示例:
自动任务:
全局任务
在所有模块外部声明的任务称为全局任务,因为它们具有全局作用域,并且可以在任何模块内调用。
如果任务是在模块 des 中声明的,则必须引用模块实例名称来调用它。
函数和任务的区别
当函数尝试调用任务或包含耗时的语句时,编译器会报告错误。
任务禁止
可以用 disable 关键字禁止任务执行。