From 9d9d36730223cb7a36fca425f6fc5bcbb43e7b02 Mon Sep 17 00:00:00 2001 From: Saqib Khan <11544614+saqibkh@users.noreply.github.com> Date: Tue, 29 Sep 2026 06:50:57 -0600 Subject: [PATCH] Support waiting on clocking block event through modport (#8403) (#8530) --- docs/CONTRIBUTORS | 1 + src/V3LinkDot.cpp | 11 ++- .../t/t_mod_interface_clocking_event.py | 18 +++++ .../t/t_mod_interface_clocking_event.v | 79 +++++++++++++++++++ 4 files changed, 107 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_mod_interface_clocking_event.py create mode 100644 test_regress/t/t_mod_interface_clocking_event.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 715feab24..f1efe8a20 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -294,6 +294,7 @@ Ryan Ziegler Ryszard Rozak Saksham Gupta Samuel Riedel +Saqib Khan Sean Cross Sebastien Van Cauwenberghe Secturion diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 8ef4bda56..b8244eae3 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3482,6 +3482,13 @@ class LinkDotResolveVisitor final : public VNVisitor { } defParamPins.emplace(nodep->paramPath(), nodep); } + static AstClocking* sensClockingp(AstNode* nodep) { + // Return the clocking block referenced by nodep, either directly or through a modport + if (AstClocking* const clockingp = VN_CAST(nodep, Clocking)) return clockingp; + if (const AstModportClockingRef* const clockingRefp = VN_CAST(nodep, ModportClockingRef)) + return clockingRefp->clockingp(); + return nullptr; + } VSymEnt* getCreateClockingEventSymEnt(AstClocking* clockingp) { AstVar* const eventp = clockingp->ensureEventp(true); if (!eventp->user1p()) eventp->user1p(new VSymEnt{m_statep->symsp(), eventp}); @@ -4465,7 +4472,7 @@ class LinkDotResolveVisitor final : public VNVisitor { bool ok = false; // Special case: waiting on clocking event if (m_inSens && foundp && m_ds.m_dotPos != DP_SCOPE && m_ds.m_dotPos != DP_FIRST) { - if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) { + if (AstClocking* const clockingp = sensClockingp(foundp->nodep())) { foundp = getCreateClockingEventSymEnt(clockingp); } } @@ -5081,7 +5088,7 @@ class LinkDotResolveVisitor final : public VNVisitor { foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true); } if (m_inSens && foundp) { - if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) { + if (AstClocking* const clockingp = sensClockingp(foundp->nodep())) { foundp = getCreateClockingEventSymEnt(clockingp); } } diff --git a/test_regress/t/t_mod_interface_clocking_event.py b/test_regress/t/t_mod_interface_clocking_event.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_mod_interface_clocking_event.py @@ -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"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_mod_interface_clocking_event.v b/test_regress/t/t_mod_interface_clocking_event.v new file mode 100644 index 000000000..97c8770c7 --- /dev/null +++ b/test_regress/t/t_mod_interface_clocking_event.v @@ -0,0 +1,79 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Saqib Khan +// SPDX-License-Identifier: CC0-1.0 + +// Wait on a clocking block event through a modport (#8403) + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +`timescale 1ns / 1ns + +interface simple_if ( + input logic clk +); + logic req; + clocking mem_cb @(posedge clk); + output req; + endclocking + clocking mon_cb @(negedge clk); + input req; + endclocking + modport mem_mp(clocking mem_cb); + modport mon_mp(clocking mon_cb); +endinterface + +module via_mp ( + simple_if.mem_mp p, + simple_if.mon_mp m +); + int mem_count = 0; + int mon_count = 0; + + task automatic wait_mon(); + @(m.mon_cb); + endtask + + initial begin + repeat (3) begin + @(p.mem_cb); + mem_count++; + $display("[%0t] mem_cb %0d", $time, mem_count); + end + p.mem_cb.req <= 1'b1; + end + + initial begin + repeat (3) begin + wait_mon(); + mon_count++; + $display("[%0t] mon_cb %0d", $time, mon_count); + end + end +endmodule + +module t; + logic clk = 0; + always #5 clk = ~clk; + + simple_if intf (.clk(clk)); + initial intf.req = 1'b0; + + via_mp u ( + .p(intf.mem_mp), + .m(intf.mon_mp) + ); + + initial begin + #100; + `checkd(u.mem_count, 3); + `checkd(u.mon_count, 3); + `checkd(intf.req, 1'b1); + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule