diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 51e4b7c7d..72f1e9dc7 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -249,6 +249,7 @@ Natan Kreimer Nathan Graybeal Nathan Kohagen Nathan Myers +Nazar Zibilyuk Nick Brereton Nikolai Kumar Nikolay Puzanov diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 741338aa8..69dc6f46a 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -352,6 +352,10 @@ class EmitCModel final : public EmitCFunc { // Create sensitivity list for when to evaluate the model. putsDecoration(nullptr, "// Sensitivities on all clocks and combinational inputs\n"); puts("SC_METHOD(eval);\n"); + if (v3Global.usesTiming()) { + putsDecoration(nullptr, "// Notified by pending time delays/DPI exports\n"); + puts("sensitive << vlSymsp->__Vm_wakeEvent;\n"); + } for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { if (const AstVar* const varp = VN_CAST(nodep, Var)) { if (varp->isNonOutput() && (varp->isScSensitive() || varp->isPrimaryClock())) { @@ -430,7 +434,7 @@ class EmitCModel final : public EmitCFunc { puts("if (eventsPending()) {\n"); puts("sc_core::sc_time dt = sc_core::sc_time::from_value(nextTimeSlot() - " "contextp()->time());\n"); - puts("next_trigger(dt);\n"); + puts("vlSymsp->__Vm_wakeEvent.notify(dt);\n"); puts("}\n"); puts("}\n"); } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 0ec8a4c67..5b5af5224 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -999,6 +999,7 @@ void EmitCSyms::emitSymHdr() { } if (v3Global.hasClasses()) puts("VlDeleter __Vm_deleter;\n"); puts("bool& __Vm_didInit;\n"); + if (v3Global.opt.systemC()) puts("sc_core::sc_event __Vm_wakeEvent;\n"); if (v3Global.opt.mtasks()) { puts("\n// MULTI-THREADING\n"); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index de96d9ff0..f08c29a53 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -1534,6 +1534,13 @@ class TaskVisitor final : public VNVisitor { } else { cfuncp->addStmtsp(assignp); } + + // With --sc, also wake the SystemC wrapper: it cannot see the flag, and may + // otherwise sleep until its next timed slot. (usesTiming() is not known yet.) + if (v3Global.opt.systemC()) { + assignp->addNextHere(new AstCStmt{ + flp, "vlSymsp->__Vm_wakeEvent.notify(sc_core::SC_ZERO_TIME);\n"}); + } } } diff --git a/test_regress/t/t_timing_sc_clk_while_delay.cpp b/test_regress/t/t_timing_sc_clk_while_delay.cpp new file mode 100644 index 000000000..73ba7eadf --- /dev/null +++ b/test_regress/t/t_timing_sc_clk_while_delay.cpp @@ -0,0 +1,24 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 ViraSemi Inc. +// SPDX-License-Identifier: CC0-1.0 + +#include VM_PREFIX_INCLUDE + +using namespace sc_core; + +// A 10ns clock drives the model while it waits on a long delay +int sc_main(int argc, char* argv[]) { + sc_clock clk{"clk", 10, SC_NS}; + VM_PREFIX* tb = new VM_PREFIX{"tb"}; + tb->clk(clk); + + while (!Verilated::gotFinish() && sc_time_stamp() < sc_time(2, SC_US)) sc_start(1, SC_NS); + if (!Verilated::gotFinish()) + vl_fatal(__FILE__, __LINE__, "tb", "Timeout; never got a $finish\n"); + + tb->final(); + VL_DO_DANGLING(delete tb, tb); + return 0; +} diff --git a/test_regress/t/t_timing_sc_clk_while_delay.py b/test_regress/t/t_timing_sc_clk_while_delay.py new file mode 100755 index 000000000..c717de92b --- /dev/null +++ b/test_regress/t/t_timing_sc_clk_while_delay.py @@ -0,0 +1,24 @@ +#!/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('vlt') + +if not test.have_sc: + test.skip("No SystemC installed") + +if re.search(r'clang', test.cxx_version): + test.skip("Known clang bug on ubuntu-26.04") + +test.compile(make_main=False, verilator_flags2=["-sc --exe --timing", test.pli_filename]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_timing_sc_clk_while_delay.v b/test_regress/t/t_timing_sc_clk_while_delay.v new file mode 100644 index 000000000..c9bc9b7bd --- /dev/null +++ b/test_regress/t/t_timing_sc_clk_while_delay.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 ViraSemi Inc. +// SPDX-License-Identifier: CC0-1.0 + +module t ( + input clk +); + + int edges = 0; + + always @(posedge clk) edges <= edges + 1; + + initial begin + // Every clock edge must be seen while this delay is pending + #995ns; + if (edges != 100) begin + $write("%%Error: %0d clock edges seen while a delay was pending, expected 100\n", edges); + $stop; + end + // No delay is pending from here on; the clock alone must keep the model running + wait (edges == 150); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_timing_sc_dpi_event.cpp b/test_regress/t/t_timing_sc_dpi_event.cpp new file mode 100644 index 000000000..ba87c4ce0 --- /dev/null +++ b/test_regress/t/t_timing_sc_dpi_event.cpp @@ -0,0 +1,37 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 ViraSemi Inc. +// SPDX-License-Identifier: CC0-1.0 + +#include VM_PREFIX_INCLUDE + +#include "svdpi.h" + +using namespace sc_core; + +// Exported by the model +extern "C" void sv_trigger(); + +// Calls into the model at 10ns, from outside its own evaluation +SC_MODULE(poker) { + SC_CTOR(poker) { SC_THREAD(run); } + void run() { + wait(10, SC_NS); + svScope scope = svGetScopeFromName("tb.t"); + if (!scope) vl_fatal(__FILE__, __LINE__, "tb", "svGetScopeFromName failed\n"); + svSetScope(scope); + sv_trigger(); + } +}; + +int sc_main(int argc, char* argv[]) { + VM_PREFIX* tb = new VM_PREFIX{"tb"}; + poker p{"poker"}; + + sc_start(); + + tb->final(); + VL_DO_DANGLING(delete tb, tb); + return 0; +} diff --git a/test_regress/t/t_timing_sc_dpi_event.py b/test_regress/t/t_timing_sc_dpi_event.py new file mode 100755 index 000000000..c717de92b --- /dev/null +++ b/test_regress/t/t_timing_sc_dpi_event.py @@ -0,0 +1,24 @@ +#!/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('vlt') + +if not test.have_sc: + test.skip("No SystemC installed") + +if re.search(r'clang', test.cxx_version): + test.skip("Known clang bug on ubuntu-26.04") + +test.compile(make_main=False, verilator_flags2=["-sc --exe --timing", test.pli_filename]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_timing_sc_dpi_event.v b/test_regress/t/t_timing_sc_dpi_event.v new file mode 100644 index 000000000..00565f3ce --- /dev/null +++ b/test_regress/t/t_timing_sc_dpi_event.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 ViraSemi Inc. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps + +module t; + + event hit; + + time woke_at = 0; + + export "DPI-C" function sv_trigger; + + // Called from SystemC at 10ns. Triggers an event a process is already waiting on. + function automatic void sv_trigger(); + ->hit; + endfunction + + initial begin + @(hit); + woke_at = $time; + end + + initial begin + #50; + if (woke_at != 10) begin + $write("%%Error: the waiting process was released at %0t, expected 10ns\n", woke_at); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule