如何在SystemVerilog中中断未完成的信号延迟?
问题:Verilog实现带中断功能的信号上升/下降延迟
问题背景
我尝试用以下代码为信号设置不同的上升和下降延迟:
timeunit 1ns; timeprecision 1ps; parameter real delay_en_rising_us = 100.0; // 上升延迟100us parameter real delay_en_falling_us = 300.0; // 下降延迟300us logic en; logic en_delayed; always_comb if ($realtime == 0) en_delayed = en; else if (en) en_delayed <= #(delay_en_rising_us*1us) en; else en_delayed <= #(delay_en_falling_us*1us) en;
场景1:使用上述参数时,仿真结果符合预期

场景2:修改延迟参数为以下值时,仿真结果不符合预期
parameter real delay_en_rising_us = 100.0; // 上升延迟100us parameter real delay_en_falling_us = 1500.0; // 下降延迟1500us
实际需求
当信号en变为高电平时,需要立即取消en_delayed上未完成的下降延迟,预期行为如下:
完整仿真代码如下:
module top; timeunit 1ns; timeprecision 1ps; // 正常工作的参数 parameter real delay_en_rising_us = 100.0; // 上升延迟100us parameter real delay_en_falling_us = 300.0; // 下降延迟300us // 存在问题的参数 //parameter real delay_en_rising_us = 100.0; // 上升延迟100us //parameter real delay_en_falling_us = 1500.0; // 下降延迟1500us logic en; logic en_delayed; always_comb if ($realtime == 0) en_delayed = en; else if (en) en_delayed <= #(delay_en_rising_us*1us) en; else en_delayed <= #(delay_en_falling_us*1us) en; initial fork $dumpfile("dump.vcd"); $dumpvars; en = 1; #1000us en = 0; #2000us en = 1; #5000us $finish; join endmodule
解决方案
原代码的核心问题是:always_comb中使用非阻塞延迟赋值时,每次en变化都会向仿真事件队列中添加新的延迟任务,但之前未完成的任务不会被自动取消,导致多个任务同时执行,出现不符合预期的输出。
方法1:任务+禁用任务实现延迟中断
module top; timeunit 1ns; timeprecision 1ps; // 测试存在问题的参数 parameter real delay_en_rising_us = 100.0; // 上升延迟100us parameter real delay_en_falling_us = 1500.0; // 下降延迟1500us logic en; logic en_delayed; // 定义自动任务,每次调用都会生成独立实例 task automatic delay_update; if (en) begin #(delay_en_rising_us*1us); en_delayed = en; end else begin #(delay_en_falling_us*1us); en_delayed = en; end endtask // 当en变化时,取消之前的任务并启动新任务 always @(en) begin disable delay_update; fork delay_update(); join_none end // 仿真控制逻辑 initial fork $dumpfile("dump.vcd"); $dumpvars; en = 1; #1000us en = 0; #2000us en = 1; #5000us $finish; join endmodule
方法2:带状态标记的事件控制实现
通过标记位跟踪未完成的延迟任务,在en变化时主动取消:
module top; timeunit 1ns; timeprecision 1ps; parameter real delay_en_rising_us = 100.0; parameter real delay_en_falling_us = 1500.0; logic en; logic en_delayed; bit update_pending; // 标记是否有未完成的延迟任务 always @(en) begin // 若存在未完成任务,立即取消 if (update_pending) begin disable delayed_update; update_pending = 0; end // 启动新的延迟更新任务 fork : delayed_update update_pending = 1; if (en) begin #(delay_en_rising_us*1us); en_delayed = en; end else begin #(delay_en_falling_us*1us); en_delayed = en; end update_pending = 0; join_none end // 仿真初始化 initial fork $dumpfile("dump.vcd"); $dumpvars; en = 1; #1000us en = 0; #2000us en = 1; #5000us $finish; join endmodule
核心原理
两种方法均通过disable关键字终止未完成的延迟任务,确保每次en变化时,只有最新的延迟逻辑在执行,从而实现“信号变化时中断未完成延迟”的需求。
内容的提问来源于stack exchange,提问作者Du mmyTransistor
相关产品推荐
相关产品推荐

