在 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的特殊性:它不消耗时间,用于在同一时钟沿组合多个信号条件,等价于逻辑与,但在序列中表达更简洁。
✅ 总结一句话
#是仿真时间延迟,用于过程块等待绝对时间;##是断言中的时钟周期延迟,用于序列中等待指定数量的时钟边沿。切记不能混用场景,否则会导致语法错误或仿真行为异常。