【SV】SystemVerilog 进程控制与事件触发类语句全面解析

内容分享10小时前发布
0 0 0

文章目录

SystemVerilog 进程控制与事件触发类语句全面解析一、核心语句分类总览(思维导图)二、关键语句详解与对比✅ 1. 事件触发 vs 条件等待:`@` 与 `wait` 的根本区别✅ 2. `always_comb` / `always_ff` vs 传统 `always @(…)`✅ 3. `iff` 与 `if` 的本质区别✅ 4. `fork…join` 系列语句对比
三、IC 验证中的典型应用场景(附代码)场景 1:条件时钟采样(避免无效计数)场景 2:事件同步(避免竞争)场景 3:超时控制(`fork…join_any` + `disable fork`)场景 4:断言中使用 `disable iff` 避免复位误报场景 5:事务级通信(事件 + 信箱)场景 6:状态机验证(`wait_state`)
四、总结与建议✅ 核心语句分类速查表📌 最佳实践建议

SystemVerilog 进程控制与事件触发类语句全面解析

在 SystemVerilog(SV)中,
@

wait

always @

iff
等关键字属于进程控制与事件触发类语句,主要用于控制仿真行为、同步多线程、响应信号变化等。这些机制是构建高效、可靠验证环境的核心基础。

本文将对这些语句进行系统性梳理,包括其分类、用法、区别联系以及在实际 IC 验证中的典型应用场景,并提供可直接复用的代码示例。


一、核心语句分类总览(思维导图)


SystemVerilog 进程控制与事件触发
├─ 1. 事件触发与敏感列表类
│   ├─ @(event)             - 事件触发,阻塞直到事件发生
│   ├─ @(*)                 - 自动推断所有输入信号为敏感列表(组合逻辑)
│   ├─ always_comb          - 专用组合逻辑块(自动敏感)
│   ├─ always_ff            - 专用时序逻辑块(推荐用于寄存器建模)
│   └─ always_latch         - 专用锁存器逻辑块
│
├─ 2. 条件等待与阻塞控制类
│   ├─ wait(condition)      - 电平敏感阻塞,条件为真时继续
│   ├─ wait fork            - 等待所有 fork 子进程结束
│   ├─ wait_order           - 断言多个事件按指定顺序触发
│   ├─ disable fork         - 终止所有后台子进程
│   └─ wait_state (IEEE 1800-2017) - 等待状态机进入特定状态
│
├─ 3. 事件限定与过滤类
│   ├─ iff(condition)       - 条件限定事件(仅当 condition 为真才触发)
│   ├─ if (...)             - 普通过程内条件判断
│   └─ unique if / priority if - 带优先级检查的条件语句
│
├─ 4. 进程启动与并发控制类
│   ├─ fork ... join        - 启动并发进程,父线程阻塞直至全部完成
│   ├─ fork ... join_any    - 父线程在任意子线程完成后继续
│   ├─ fork ... join_none   - 异步启动,不等待子线程(常用于后台任务)
│   └─ begin ... end        - 顺序执行块
│
├─ 5. 断言与采样事件控制类
│   ├─ @(event)             - 在断言中定义时钟事件
│   ├─ disable iff(cond)    - 断言禁用条件(如复位期间忽略检查)
│   ├─ throughout(expr)     - 表达式在整个区间保持成立
│   ├─ intersect            - 两个序列同时匹配
│   └─ within               - 一个序列在另一个序列范围内
│
└─ 6. 定时与延迟控制类
    ├─ #delay               - 时间延迟(过程级)
    ├─ ##delay              - 在断言中表示周期延迟
    ├─ -> event             - 触发命名事件(阻塞式)
    └─ ->> event            - 非阻塞式事件触发(NBA区域执行)

提示:以上六大类覆盖了 SV 中几乎所有与“时间”、“事件”、“同步”相关的控制语句。


二、关键语句详解与对比

✅ 1. 事件触发 vs 条件等待:
@

wait
的根本区别

特性
@
操作符

wait
语句
敏感类型 边沿敏感(等待变化) 电平敏感(等待为真)
是否检查当前值 ❌ 不检查,只等下一次变化 ✅ 检查当前值,若已满足立即继续
竞争风险 ⚠️ 高(可能错过同一时间步的事件) ✅ 低(更可靠)
典型用法
@(posedge clk)

wait(ready == 1)

示例对比


// 可能失败:@ 在事件触发后才开始等待
initial begin
  -> ev;
  @(ev); // 错过!因为 ev 已触发
end

// 更安全:wait 检查事件是否已被触发
initial begin
  -> ev;
  wait(ev.triggered); // 成功!即使先触发也能捕获
end

📌 最佳实践优先使用
wait(condition)

wait(event.triggered)
,避免使用
@(condition)


✅ 2.
always_comb
/
always_ff
vs 传统
always @(...)

写法 是否自动推断敏感列表 是否易出错 推荐度

always @(*)
✅ 是 ⚠️ 但部分工具支持不一致

always @(a, b, c)
❌ 否 ✅ 易遗漏信号

always_comb
✅ 是(组合逻辑) ✅ 编译器强制检查 ✅✅✅

always_ff
✅ 是(仅时钟/复位) ✅ 专用于时序逻辑 ✅✅✅

推荐写法


always_comb y = a & b; // 自动敏感 a, b

always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) q <= 0;
  else        q <= d;
end

📌 建议永远不要手写敏感列表!使用
always_comb
/
always_ff
替代传统
always


✅ 3.
iff

if
的本质区别


iff
:仅用于 事件触发断言 中,表示“仅当条件成立时才采样或触发”。


@(posedge clk iff enable); // 仅当 enable==1 时才响应 clk 上升沿

assert property (@(posedge clk) disable iff(!rst_n) (req iff valid));


if
:通用条件语句,用于过程块内部逻辑控制。


if (enable) data <= new_data;

⚠️ 注意
iff
不能出现在普通
always

initial
块中作为控制语句。


✅ 4.
fork...join
系列语句对比

语句 行为 适用场景

fork ... join
父线程阻塞,直到所有子线程结束 需要完全同步(如测试结束前等待所有任务)

fork ... join_any
父线程在任一子线程结束后继续 超时控制、快速响应

fork ... join_none
父线程立即继续,子线程后台运行 启动监控器、周期性任务

配合
wait fork
/
disable fork
可实现精细控制。


三、IC 验证中的典型应用场景(附代码)

场景 1:条件时钟采样(避免无效计数)


// ✅ 推荐写法:使用 always_ff + 条件判断
always_ff @(posedge clk) begin
  if (!rst_n) count <= 0;
  else if (enable) count <= count + 1; // 仅 enable 有效时计数
end

// ❌ 不推荐:混合敏感列表 + 条件
always @(posedge clk iff enable) ... // 可读性差,调试困难

场景 2:事件同步(避免竞争)


event data_ready;

// 发送端
initial begin
  #20;
  -> data_ready; // 触发事件
end

// 接收端(✅ 安全)
initial begin
  wait(data_ready.triggered);
  $display("Data ready at %0t", $time);
end

// ❌ 危险写法(可能死锁)
initial begin
  @(data_ready); // 若事件已触发,则永远等待
end

场景 3:超时控制(
fork...join_any
+
disable fork


event task_done;

initial begin
  fork
    begin // 主任务
      #50;
      -> task_done;
    end
    begin // 超时监控
      #100;
      if (!task_done.triggered) begin
        $error("Task timeout!");
        disable fork; // 终止所有子线程
      end
    end
  join_any
  wait fork; // 等待剩余线程清理
end

场景 4:断言中使用
disable iff
避免复位误报


// ✅ 正确:复位期间禁用断言
assert property (
  @(posedge clk)
  disable iff (!rst_n)
  (req |-> ##[1:3] ack)
) else $error("ACK not received in time");

// ❌ 错误:复位期间可能误报
assert property (@(posedge clk) req |-> ##[1:3] ack);

场景 5:事务级通信(事件 + 信箱)


mailbox #(transaction) mb;
event trans_sent;

class transaction;
  rand bit [7:0] data;
endclass

// 生成器
task generator();
  forever begin
    transaction t = new();
    t.randomize();
    mb.put(t);
    -> trans_sent; // 通知接收方
    #10;
  end
endtask

// 接收器
task receiver();
  forever begin
    wait(trans_sent.triggered);
    transaction t;
    mb.get(t);
    $display("Received: %0d", t.data);
  end
endtask

initial begin
  fork
    generator();
    receiver();
  join_none
end

场景 6:状态机验证(
wait_state


typedef enum {IDLE, BUSY, DONE} state_t;
state_t fsm_state;

// DUT 中的状态机更新...

// 验证平台
initial begin
  wait_state(fsm_state == DONE); // IEEE 1800-2017 新特性
  $display("FSM reached DONE at %0t", $time);
end

注:若工具不支持
wait_state
,可用
wait(fsm_state == DONE)
替代。


四、总结与建议

✅ 核心语句分类速查表

类别 关键语句
事件触发
@
,
@(posedge/negedge)
,
@(*)
进程敏感
always_comb
,
always_ff
,
always_latch
条件等待
wait
,
wait fork
,
wait_order
事件过滤
iff
,
disable iff
,
throughout
并发控制
fork...join
,
begin...end
时序控制
#
,
##
,
->
,
->>
断言专用
assert property
,
assume
,
cover

📌 最佳实践建议


wait(condition)
代替
@(condition)
:避免时间步竞争。
always_comb
/
always_ff
代替
always @(...)
:杜绝敏感列表遗漏。在断言中使用
disable iff(rst)
:防止复位期间误报。事件等待优先用
wait(ev.triggered)
:比
@ev
更可靠。超时控制用
fork...join_any + disable fork
:结构清晰,易于维护。复杂同步考虑
semaphore
/
mailbox
:比纯事件更安全(尤其在 OOP 环境中)。


💡 结语
SystemVerilog 的进程控制机制看似简单,实则蕴含丰富的并发与同步语义。掌握这些语句的本质区别与适用场景,是构建高可靠性、高可维护性验证平台的关键一步。希望本文能成为您日常验证开发中的实用参考手册!

参考资料:IEEE 1800-2017 SystemVerilog 标准、CSDN/Sohu 技术社区

© 版权声明

相关文章

暂无评论

none
暂无评论...