systemverilog之program与module的区别

为避免仿真和设计竞争问题(race condition),systemverilog中引入了program的概念。

在Verilog中,调度如下图所示:

从图中可以看出,阻塞赋值与非阻塞赋值的调度是不一样的,其中#0的阻塞延时赋值则处在中间的调度区域。

对于systemverilog来说,就多添加了几种调度区域。如下图所示

前三个为Verilog准备的,observed处于中间部分,是为SV中的属性断言准备的,该区域的值已经稳定,避免了因采集数据不稳定而导致的属性断言错误。在reactive域正式进行断言判断。

通过几个栗子说明情况:

复制代码
module counter(input clk);
  bit [3:0] cnt;

  always @(posedge clk) begin
    cnt <= cnt + 3;
    $display("%0t DUT cnt = %0d", $time, cnt);
  end
endmodule

module tb1;
bit clk;
bit [3:0] cnt;

  initial begin
    forever #5ns clk <= ~clk;
  end
  counter counter_inst(clk);
  always @(posedge clk) begin
    $display("%0t TB cnt = %0d", $time, counter_inst.cnt);
  end
endmodule

仿真结果如下:

复制代码
run 50ns
# 5 DUT cnt = 0
# 5 TB cnt = 0
# 15 DUT cnt = 3
# 15 TB cnt = 3
# 25 DUT cnt = 6
# 25 TB cnt = 6
# 35 DUT cnt = 9
# 35 TB cnt = 9
# 45 DUT cnt = 12
# 45 TB cnt = 12

因为打印函数处于active调度区域,非阻塞赋值处于NBA调度区域,因此采样到的是变化前的值,即#5时采样得到的是0不是1;其他同理。

如果我们把仿真激励改为如下:

复制代码
module tb2;
bit clk1;
bit clk2;
bit [3:0] cnt;

  initial begin
    forever #5ns clk1 <= !clk1;
  end

  always @(clk1) begin
    clk2 <= clk1;
  end

  counter dut(clk1);

  always @(posedge clk2) begin
    $display("%0t TB cnt = %0d", $time, dut.cnt);
  end
endmodule

那么仿真结果则如下:

复制代码
run 50ns
# 5 DUT cnt = 0
# 5 TB cnt = 3
# 15 DUT cnt = 3
# 15 TB cnt = 6
# 25 DUT cnt = 6
# 25 TB cnt = 9
# 35 DUT cnt = 9
# 35 TB cnt = 12
# 45 DUT cnt = 12
# 45 TB cnt = 15

这是因为clk2和clk1之间存在非阻塞赋值,赋值区域在NBA区,按照先后顺序,一个采样得到的是变化前的值,一个得到的是变化后的值。

因此我们如果Testbench中也一味地使用module,就有可能出现上述第二种问题,在此我不是说这种不行,而是我们需要能控制住采样时刻。那么如果我们有时候需要采样第二种情况,难道每次都需要这样做吗?使用两个采样信号?

在SV中,我们可以使用Program实现上述情况:

假设我们把第一种testbench改为program,如下所示:

复制代码
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 test_tb_top;
  bit clk1;
  bit [3:0] cnt;
  
    initial begin
      forever #5ns clk1 <= !clk1;
    end
  
    counter dut(clk1);
    dsample spl(clk1);
endmodule

此时仿真结果和第二次一致,这是因为program的采样是在reactive中进行的,此时数据已经是变化后的稳定值,不会出现竞争的情况。

因此,我们一般推荐在Testbench中使用program,在设计dut中使用module,在顶层module中例化dut的module和 testbench的program。

program中的注意点:

  • program中不能例化其他program和module
  • 不能出现interface和always,可以使用initial forever替代always
  • program内部可以发起多个initial块
  • program中内部定义的变量最好采用阻塞赋值,当然采用非阻塞仿真器也不会产生error,驱动外部信号则应该采用非阻塞赋值
  • program中的initial块和module中的initial块执行位置不同,前者在reactive,后者在active块中执行。
  • program中存在的多个initial块中,如果有一个initial采用了退出系统函数$exit(),则会结束该program,而不仅仅是该initial块。
相关推荐
ALINX技术博客14 小时前
【黑金云课堂】FPGA技术教程Linux开发:系统定制
linux·运维·fpga开发
塔能物联运维16 小时前
**塔能两相液冷:量化交易场景下的硬核验证,±1.5℃控温如何转化为真金白银**
人工智能·fpga开发·两相液冷
西安景驰电子2 天前
PCIe 授时卡原理及应用
运维·服务器·windows·单片机·嵌入式硬件·fpga开发
落chen2 天前
基于Vivado的FIFO-IP核-强化篇
fpga开发
X_xcccc2 天前
2026智能硬件核心:顶尖SOM方案与开发服务深度解析
fpga开发·智能硬件
公众号:fuwuqiBMC2 天前
(转自“服务器BMC”)服务器BMC芯片——AST1840
运维·服务器·fpga开发
国科安芯2 天前
还是4位?ASC8T245S 与 ASC4T245S 的双向电平转换选型实战
单片机·嵌入式硬件·物联网·fpga开发·机器人·云计算
思尔芯S2C2 天前
白皮书|从RTL适配到硅前验证:如何构建有价值的FPGA原型平台
fpga开发·soc·芯片·验证·asic·prototyping·原型验证
Rambo.xia3 天前
接口协议避坑 · 第5篇:多器件级联时序踩坑实录
fpga开发
紫霄芯语3 天前
观察者模式在FPGA中断系统的实战重构
观察者模式·fpga开发·重构