提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
一、程序与模块
模块(module)作为SV从verilog继承来的概念,保持了它原有的特点,除了作为RTL模型的外壳包装和实现硬件行为,在更高的继承层面,模块之间也需要通信和同步。
1.SV的仿真调度机制 - 时间片(Time Slot)
SV的仿真调度在verilog的基础上,扩展出来新的sv结构体,如程序(program)和断言(assertion)。充分理解sv的不同结构体在仿真中的先后执行顺序,有利于理解testbench中对DUT驱动和采样的顺序,进而避免不合理的驱动、采样方式以及两者间的竞争问题。
在这里,非常有必要介绍仿真时间中的一个抽象单位Ts--时间片(time-slot),该单位内所有线程(always,initial,assertion等)和数据对象的赋值(阻塞与非阻塞)被赋予了相应优先级,依次被执行,优先级体现在调度区域(scheduling regions)中,如下图所示。
接下来,对这些调度区域做简单介绍。
Active区域:执行该阶段线程:always,assign,initial等。其中,与非阻塞赋值有关操作执行完毕后,对应线程进入NBA,带有零延时的线程(#0)直接进入inactive区域。
Inactive区域:有零延时操作的线程在inactive区域被激活,被执行前迁往active区域。所以,零延时操作会延缓线程的执行时间。
NBA区域:所有active和inactive区域均没有其他线程之后所到达的调度区,到达该区域后,之前active区域的非阻塞赋值生效,,如果非阻塞赋值触发了其他线程,被触发的线程要挪到active区域。
observe区域:当之前的active/inactive/NBA区域均执行完毕后,表示设计部分的线程已经执行完毕。接下来的区域是sv为验证一侧准备的。进入observe区域后,这一区域是为了属性断言准备的,方便监测设计中的变量确定值。该区域同样适用于接口和程序块中的采样操作,使得采集到的数据是该Ts的最终值。
reactive区域:经历了数据采样工作,断言语句需要进行属性判断,在该区域进行,同时处于testbench区域中的线程也在该区域执行。
postpone区域:分别经历了与设计、testbench相关的区域之后,当前Ts进入postponed区域。该区域内的值保持稳定,且与下一Ts preponed的值保持一致。同时,该区域也作为sv PLI/DPI的回调函数(callback)点,使得在sv外部的调用语言(如C)在使用sv变量时,仍然可以用到最新的数值。
在了解上述6个区域之后,结合阻塞赋值与非阻塞赋值例子进行分析:
阻塞赋值:
a = a + 1; // a在Ts的active区域被赋值,且赋值立即生效
b = a; // b在相同区域被赋值,同时使用被立即赋值的a = a + 1
非阻塞赋值:可以避免一些设计中的竞争情况
a <= a + 1; // a在Ts的active区域被赋值,而在NBA区域生效
b <= a; // b在同一个Ts区域被赋值,且使用被赋值前的a值
接下来,结合TB与DUT在时间片中的执行顺序,来分析一个例子的输出结果:
module counter(input clk);
bit [3:0] cnt;
always @(posedge clk) begin
cnt <= cnt + 1;
$display("@%0t DUT cnt = %0d", $time, cnt);
end
endmodule
program dsample(input clk);
initial begin
forever begin
@(posedge clk)
$display("@%0t TB cnt = %0d", $time, dut.cnt);
end
end
endprogram
module tb;
bit clk1;
bit [3:0] cnt;
initial begin
forever begin
#5ns clk1 <= !clk1
end
end
counter dut(clk1);
dsample spl(clk1);
endmodule
仿真结果:
# @5 DUT cnt = 0
# @5 TB cnt = 1
# @15 DUT cnt = 1
# @15 TB cnt = 2
# @25 DUT cnt = 2
# @25 TB cnt = 3
分析:dut中cnt <= cnt + 1在active区域触发,同时该区域DUT完成采样,采到赋值前的值,到NBA区域,cnt完成+1的赋值,而TB的行动在后面的reactive区域进行,该区域,TB完成采样,采到赋值后的值。

由此看来,sv介绍的程序(program)就是为了将设计和验证调度区域显示的分割开来安排,因此建议将设计部分放到module块,测试采样部分放到program块,下面关于program有些要求与建议:
1. program可视为软件的”领地“,故其中不可出现硬件行为相关的过程语句与实例,如:always,module,interface,也不能出现其他program例化语句。
2. program内部可定义变量,以及发起多个initial块
3. program内部赋值方式应为阻塞赋值(软件方式)
4. program内部在驱动外部的硬件信号时应使用非阻塞赋值(硬件方式)
5. program内部initial块在reactive区域执行,外部initial块在active区域执行。
program的出现,解决了硬件信号采样可能出现的竞争风险,我们可以通过合适的连接和采样方式将验证组件和DUT进行连接。连接后,一旦有了激励,如何结束仿真,以何种方式结束将在下文为各位介绍。
二、测试的始终
设计自身可以作为一个大的线程,内部包含并行线程,模块间通信主要依靠信号的变化。对于一个设计,在仿真开始阶段,如果不加入任何激励(如时钟和复位),则仿真不具备执行的条件,可以认为结束了。因为设计内部不产生任何新的事件,也不存在这些事件触发组合逻辑和时序逻辑。故,在仿真开始提供时钟复位是必要的。
在verilog测试方式中,如果灌入的时钟信号不停止,仿真会一直跑下去,不会主动结束,这时就需要verilog系统函数调用来结束仿真了($finish(), $stop())。
1.program 隐式结束
sv的program讲设计与验证部分隔开,且每个program都作为一个独立的测试,如果TB中只存在一个program,当program最后一个initial执行完毕后,仿真结束;若存在多个program,需要等待所有program的initial块全部执行完,结束仿真。
2.program 显式结束
有时候。initial块中包含无限循环语句,隐式方式就无法实现结束仿真的功能了,这就需要调用函数$exit来强制结束当前program。等待所有program都执行完毕,仿真停止。
本文详细介绍了SystemVerilog中的仿真调度机制,重点讲解了时间片(Time Slot)的概念,包括Active、Inactive、NBA等六个调度区域。此外,还讨论了程序(program)在验证中的作用,提出了程序设计与测试采样区域的划分建议,并解释了program的隐式和显式结束方式,为理解SystemVerilog仿真提供了深入的见解。


&spm=1001.2101.3001.5002&articleId=128068472&d=1&t=3&u=2ac53029f18e46c9ae2c04d39f24125f)
6904

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



