Fix timed nested fork block with disable (#6720) (#7743)

Fixes #6720.
This commit is contained in:
Marco Bartoli 2026-06-12 16:42:32 +02:00 committed by GitHub
parent 748e48f881
commit 5831cc8d46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 110 additions and 0 deletions

View File

@ -262,6 +262,9 @@ class LinkJumpVisitor final : public VNVisitor {
if (it != m_beginDisableBegins.end()) return it->second;
AstBegin* const beginBodyp = new AstBegin{fl, "", nullptr, false};
// Disable-by-name rewrites kill this detached block-body process, so mark it as process
// backed to ensure fork/join kill-accounting hooks are always emitted.
beginBodyp->setNeedProcess();
if (beginp->stmtsp()) beginBodyp->addStmtsp(beginp->stmtsp()->unlinkFrBackWithNext());
AstFork* const forkp = new AstFork{fl, VJoinType::JOIN};

View File

@ -0,0 +1,3 @@
Fast clock (200ns half-period): o_counter=7
Slow clock (5400ns half-period): o_counter=3
*-* All Finished *-*

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["--binary", "-Wno-ZERODLY"])
test.execute(expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,86 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Clock-period detector exercising a named-block `disable` of a block whose
// body contains a nested fork..join. Disabling such a block from a sibling
// process must release the enclosing fork..join so the surrounding `always`
// block keeps iterating.
module disable_fork (
input logic i_clk,
output logic [2:0] o_counter
);
time delay1 = 500ns; // min period
time delay2 = 3333ns; // max period
logic clk_re = 1'b0; // rising edge of the clock
logic [2:0] counter = 3'b000;
always begin
fork
begin : check1
#delay1;
#1 disable check2;
fork
begin : check3
#(delay2 - delay1);
clk_re <= 1'b0;
#1 disable check4;
if (counter < 3'b111) counter <= counter + 3'b001;
end
begin : check4
@(posedge i_clk);
clk_re <= 1'b1;
counter <= 3'b000;
#1 disable check3;
end
join
end
begin : check2
@(posedge i_clk);
clk_re <= 1'b0;
#1 disable check1;
if (counter < 3'b111) counter <= counter + 3'b001;
end
join
end
assign o_counter = counter;
endmodule
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
// verilog_format: on
module t;
logic clk;
logic [2:0] counter;
task clk_cycle(input time half_period);
clk = 1'b1;
#half_period;
clk = 1'b0;
#half_period;
endtask : clk_cycle
initial begin
// Fast clock (period below delay1): every edge arrives before the
// min-period timeout, so the counter saturates at its max.
repeat (100) clk_cycle(200ns);
$display("Fast clock (200ns half-period): o_counter=%0d", counter);
`checkh(counter, 3'h7);
// Slow clock (period above delay2): the nested fork path runs, which
// only works if disabling check1 releases the inner fork..join.
repeat (100) clk_cycle(5400ns);
$display("Slow clock (5400ns half-period): o_counter=%0d", counter);
`checkh(counter, 3'h3);
$write("*-* All Finished *-*\n");
$finish;
end
disable_fork a_inst(.i_clk(clk), .o_counter(counter));
endmodule