定义
一个经常困扰测试台环境的问题是设计和测试台之间的竞争条件。考虑下面的示例。假设一个测试平台需要等待来自DUT的特定响应。一旦它收到响应,在同一仿真时间,它需要发送一组激励到DUT。由于Verilog可以在各种peocedural block(过程块)中无序地执行事件,因此测试台有可能在DUT的响应到达之前就发送激励。
程序块在 program 和 endprogram 关键字对中定义。
程序块是对编写设计和测试台的目标差异的认识。 它旨在方便测试平台的编写。
它有三个目的。
- 它为测试台的执行提供了一个入口点。类似于设计中的module。
- 与模块类似,程序块可以充当封装程序范围数据的作用域。
- 最重要的是在“Reactive region”中安排事件
Program block中的initial块会在reactive区执行,外部的initial会在active区执行
program block 是与package对应的概念,它是给testbench引用的,存在的意义在于避免testbench中对module的引用引起竞争冒险问题,它是如何解决竞争冒险的呢,这就要回到systemverilog最开始的那篇时序上了,program里面对blocking和non blocking assignment的调用是在reactive region,即active region和NBA之后。
它在使用上,大部分时候跟一个testbench中引用普通的module没有什么不同,但一个很关键的点,program block,就像它的名字所揭示的,是一个纯软件的代码,不能综合,所以它里面不能有always语句。
Program:主要是为了在逻辑和仿真时间上,区分开RTL设计与验证平台。在SV搭建的验证环境中,testcase一般就定义一个program来开始执行。program中不能使用always,因为program相比较来说,与C语言更靠近一些。所以多用initial就可以。
program中的仿真时间与RTL中的是有区别的,SV将同一仿真时刻分为四个区域,Active(design), Observed(assertion), Reactive(testbench), Postponed(sample)。相当于在原verilog的基础上又为program增加了一个执行区间,一个采样区间。所以clk的定义不能放在program中。
当program中的initial结束时,SV会调用$finish完成仿真。

| 区域名 | 行为 |
|---|---|
| Active | 仿真模块中的设计代码(RTL、时钟发生器、门级代码) |
| Observed | 执行SystemVerilog 断言 |
| Reactive | 执行程序中的测试平台部分 |
| Postponed | 为测试平台的输入采样信号 |
1,在时间片内首先执行的是Active区域,设计事件在这里运行。这包括RTL和gate代码以及时钟发生器。
2,第二个区域是Observed区域,执行断言。
3,接下来是执行testbench的reactive区域。
注意,时间并不是严格地向前流动的,Observed和Reactive区域中的事件可能会触发当前周期中Active区域中的进一步设计事件。
4,最后是Postponed区域,在设计活动完成后的只读时间段内,对时间片结束时的信号进行采样,如表。
创建程序块
program my_prog ( input clk
, input reset
, input [7:0] in_pkt
, output [7:0] out_pkt
);
initial begin
out_pkt = 8'h0;
...
end
endprogram
Program module可以像module 一样独立定义,然后在module 中实例化。或者,它可以在模块本身中定义。
module my_mod(...);
int i;
program my_prog(...);
...
endprogram
endmodule
在模块中定义被称为隐式定义。在这种情况下,程序名和实例名都是相同的(例如,上面例子中的my_prog)。同样,module变量(比如上面的整数i)也可以从my_prog程序中访问。
Module VS Progam Block
相似处
- 与模块类似,程序块可以有零个或多个input、output和inout端口
- 程序块可以包含零个或多个 initial blocks, cont assignments, generate and specparam statements, concurrent assertions and timeunit declarations
- 类型和数据声明、函数和任务可以在程序块中定义,类似于在模块中定义的功能。
- 层次结构可以包含任意数量的程序块。这些程序块可以通过它们的端口进行交互,也可以相互隔离。这也是模块在设计环境中的工作方式
以下是一些不同之处。
- 程序块不能包含任何always 语句、 UDP、module、interface或其他program。因此,程序块始终是层次结构中的叶级节点
- 如前所述,程序定义可以出现在模块中。显然,不能以这种方式定义模块
- 程序可以调用模块或其他程序中的task或function。但是模块不能调用程序中的task或function
然而,程序和模块之间最大的区别是它们在procedural blocks(过程块)中处理变量赋值的方式。
program内的变量赋值
首先,请注意一个仿真时间片( simulation time slot )可以执行多个事件。这些事件的顺序可能是任意的,这取决于这些事件的生成方式。如果我们确保这两个事件以已知的顺序执行并且互不干扰,那么由测试平台(即程序)生成的事件和由DUT(即模块)生成的事件之间的竞争条件就可以避免。
接下来,请注意,唯一可能出现竞争条件的情况是,当program将一个值赋给module变量(或调用执行该操作的任务或函数)时,同时模块本身正在更新该变量。这是因为设计模块不能给程序的局部变量赋值,因为设计必须不知道有测试台。
这大大简化了问题。现在我们需要做的就是确保所有program变量和所有module变量在模拟时间片的两个不同区域以已知的顺序更新。
幸运的是,通过对program 局部变量使用阻塞赋值(=),和在program中对module 变量使用非阻塞赋值(<=),可以很容易地实现单个时隙内的执行顺序。 这可以使各种代码段以预定的顺序执行。
- 所有module的阻塞赋值( blocking assignment)都在Active区域中执行。
- Program中的非阻塞赋值( non-blocking assignments)在非阻塞赋值(NBA)区域中执行。?
- 在Program block中由阻塞赋值产生的事件都在reactive trgion中执行。
这如下图所示:

program中assignments 的基本规则:
- program的所有局部变量只能使用阻塞赋值来赋值。
- 所有其他变量必须使用非阻塞赋值进行赋值。
Programs and Blocking Tasks
如前所述,模块不能调用程序中定义的任务或函数,但程序可以调用模块或其他程序中定义的任务或函数。当程序调用消耗时间的模块任务时,程序与模块之间分配工作方式的限制会产生有趣的情况。
task mod_task;
a = b;
#5
c <= d;
在这里,模块任务mod_task包含两个赋值:第一个是阻塞的,另一个是非阻塞的。
当一个module调用这个任务时,两个赋值都在Active区域期间执行。
然而,当一个program调用这个任务时,第一个阻塞赋值是在Reactive区域执行的,其他非阻塞赋值可能已经改变了右边的值。第二个非阻塞赋值总是在Active区域中执行。
exit()
系统任务$exit()强制程序终止并退出。调用$exit()将终止程序生成的所有活动进程。注意,$exit()并不像$finish()那样终止模拟,它只是终止调用它的程序。模拟范围内的$finish()隐式终止所有程序。
为什么引入program block
- 它为测试台的执行提供了一个入口点。类似于设计中的module。
- 与模块类似,程序块可以充当封装程序范围数据的作用域。创建一个容器来保存所有其他测试工作台数据,比如任务、类对象和函数。
- 最重要的是在仿真周期的“Reactive region”中安排事件,避免设计中的竞争条件
Reactive region是仿真时间推进之前的最后几个阶段之一,届时所有设计元素语句都已经执行,testbench将看到更新的值。在设计执行和testbench语句之间进行这种划分是很重要的,因为它将在模拟器之间提供更确定的输出。
example
program test1 (input clk, reset);
initial ...
endprogram
program test2 (interface wb_if);
initial ...
endprogram
program test3;
initial ...
endprogram
一个程序块可以嵌套在模块和接口中,因此同一模块中的多个程序可以共享该范围内的局部变量。 在下面的例子中,mode 是 tb 中的一个局部变量,可以被程序 p1 和 p2 访问。
module tb;
bit [3:0] mode;
program p1;
...
endprogram
program p2;
...
endprogram
endmodule
为什么在程序块(program)中不允许使用always块?
在 SystemVerilog 中,你可以在program中使用initial块,但是不能使用always块。这是因为在设计中,always块可能从仿真开始就在每一个时钟的上升沿进行触发执行。但是一个测试平台的执行过程是经过初始化、驱动和相应设计行为等步骤后结束仿真的。在这里,一个连续执行的always块不能正常工作。
如果在program块中加入always块,它将永远不会结束,这样必须调用$exit来发出程序块结束的信号。如果你确实需要一个always块,你可以使用“initial forever”来完成相同的事情。
总结:
Program block中的initial块会在reactive区执行,为tb避免竞争冒险而引入
program 不可综合
不可以使用always。如果在program中想要实现always的功能可以使用initial forever,program中不能使用always。
?多个program的关系
参考
https://zhuanlan.zhihu.com/p/108777199
https://www.project-veripage.com/program_blocks_3.php
本文详细介绍了SystemVerilog中的program块,包括其定义、创建、与module的区别以及使用限制。program块主要用于测试平台的编写,提供了一个执行入口,避免了设计与测试台之间的竞争条件。在program中,变量赋值遵循特定规则,如使用阻塞赋值更新局部变量,非阻塞赋值用于module变量。此外,program块内不允许使用always块,以确保测试台的执行顺序和设计行为的分离。
870

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



