SV中#和##的区别与用法

在 SystemVerilog 中,# 和 ## 都是用于延迟 的操作符,但它们的使用场景 和延迟基准完全不同:

  • # :仿真时间延迟 ,用于过程块(initial / always)中,让执行等待一段指定的绝对时间 (由时间单位 timescale 或 timeunit 决定)。

  • ## :时钟周期延迟 ,专门用于断言(SVA,SystemVerilog Assertions) 的序列中,表示等待指定数量的时钟边沿 (通常为时钟的 posedge 或 negedge)。

下面详细解释并给出示例。


1️⃣ # 延时操作符

语法

复制代码
#<delay>;            // 延迟 <delay> 个时间单位
#(<expression>);     // 延迟由表达式计算出的时间

特点

  • 只能在 过程块 (initial, always, task, function) 中使用。

  • 延迟量可以是常数 或表达式(表达式结果须为整数或实数)。

  • 时间单位由 timescale 或模块内的 timeunit / timeprecision 指定。

  • 仿真器执行到 #delay 时会挂起当前进程,直到时间前进指定的量后恢复。

示例

复制代码
`timescale 1ns/1ps

module delay_demo;
    logic sig;

    initial begin
        $display("[%0t] start", $time);
        sig = 0;
        #10;                          // 延迟 10 ns
        sig = 1;
        $display("[%0t] after 10ns", $time);
        
        #(5.3);                       // 延迟 5.3 ns(需 timeprecision 支持)
        sig = 0;
        $display("[%0t] after 5.3ns", $time);
        
        // 也可以用表达式
        int d = 20;
        #(d) $display("[%0t] after 20ns", $time);
    end
endmodule

输出示例:

复制代码
[0] start
[10] after 10ns
[15.3] after 5.3ns
[35.3] after 20ns

2️⃣ ## 周期延迟操作符(SVA)

语法

复制代码
##n                // 延迟 n 个时钟周期(n 为正整数或 0)
##[m:n]            // 延迟范围 m 到 n 个时钟周期
##[m:$]            // 从 m 到无穷

特点

  • 只能在断言序列 (sequence) 中使用,不能出现在普通的 initial / always 过程块中(除非作为 assert property 的一部分)。

  • 延迟的基准是时钟 ,由序列中定义的 @(posedge clk) 或 @(negedge clk) 决定。

  • ##1 表示下一个时钟沿,##0 表示当前时钟沿(用于组合)。

  • 可以用于设计并发断言 (assert property)。

示例

复制代码
module sva_demo;
    logic clk, a, b, c;
    
    // 生成时钟
    initial clk = 0;
    always #5 clk = ~clk;
    
    // 简单序列:在 a 为高的时钟沿之后 2 个时钟周期,b 必须为高
    property p1;
        @(posedge clk) a |=> ##2 b;
    endproperty
    assert property (p1) else $error("Property failed");
    
    // 序列中使用 ##0 表示同一时钟沿的组合(a 和 b 在同一时钟沿同时为高)
    property p2;
        @(posedge clk) a ##0 b;
    endproperty
    assert property (p2) else $error("a and b not both high");
    
    // 使用范围:在 a 为高后的 1~3 个时钟周期内,c 必须为高
    property p3;
        @(posedge clk) a |-> ##[1:3] c;
    endproperty
    assert property (p3) else $error("c not high within 1-3 cycles");
endmodule

注意

  • ## 不能用于过程延时,比如 ##10; 写在 initial 里会报语法错误。

  • 在普通代码中如果需要等待指定数量的时钟周期 ,通常使用 repeat 和 @(posedge clk) 组合,而不是 ##。例如:

    复制代码
    // 等待 5 个时钟上升沿
    repeat(5) @(posedge clk);

3️⃣ 区别总结表

特性 # (仿真延时) ## (周期延迟)
适用领域 过程块(initial/always/task/function) 断言序列(SVA sequence / property)
延迟基准 仿真绝对时间(ns/ps等) 时钟周期数(基于指定的时钟边沿)
是否受时钟影响 否 是,依赖于时钟的变化
时间单位 timescale / timeunit 定义的时间单位 无单位,仅是周期计数
是否可综合 通常不可综合(仅仿真) 断言本身用于验证,不综合为硬件,但可配合形式工具
常见写法 #10;,#(5ns),#(delay_expr) ##2,##[1:3],##[0:$]

4️⃣ 常见误区澄清

  • 不能在断言外使用 ##:以下代码是错误的

    复制代码
    initial begin
        ##5;   // 编译错误!## 只能用于 sequence/property
    end
  • # 不能用于周期计数 :若想等待 5 个时钟周期,用 #(5*周期时间) 不健壮,因为时钟频率可能改变。正确做法是用 repeat + @(posedge clk)。

  • ##0 的特殊性:它不消耗时间,用于在同一时钟沿组合多个信号条件,等价于逻辑与,但在序列中表达更简洁。


✅ 总结一句话

# 是仿真时间延迟,用于过程块等待绝对时间;## 是断言中的时钟周期延迟,用于序列中等待指定数量的时钟边沿。

切记不能混用场景,否则会导致语法错误或仿真行为异常。

相关推荐
星夜离尘564 小时前
FPGA 复位回归实验:双时钟停钟、释放中重入与消费端首拍自检
fpga开发
szxinmai主板定制专家5 小时前
ZYNQ 数据采集方案踩坑总结:电力强电磁干扰、工业现场多协议适配优化
运维·人工智能·fpga开发·机器人·zynq
幻智星科技6 小时前
一个开源的纯FPGA网口采集卡
网络协议·fpga开发·udp
bobuddy7 小时前
硬件加速三件套:emulator-FPGA 原型-云仿真
fpga开发
szxinmai主板定制专家12 小时前
软硬协同!ZYNQ 高速多通道同步采集,变电站 / 工厂设备状态监测方案详解
人工智能·深度学习·机器学习·fpga开发·zynq·rk3576
FPGA小徐12 小时前
FPGA 8阶LFSR加扰/解扰器设计
fpga开发
逐光者93314 小时前
超前进位加法器:原理、推导与 Verilog 实现
fpga开发
szxinmai主板定制专家14 小时前
ZYNQ PL+PS 架构拆解|工业自动化与电力物联网高速数据采集、预处理与协议上云
arm开发·人工智能·物联网·fpga开发·数据采集卡·rk3576
szxinmai主板定制专家15 小时前
告别 ARM 实时性短板:基于 ZYNQ 的电力故障录波 + 工业振动数据采集系统设计
arm开发·人工智能·fpga开发·zynq·数据采集卡·arm+fpga·rk3588+fpga
FPGA小徐17 小时前
FPGA 实现 Canny 边缘检测|大白话原理 + 专业工程解析
fpga开发