From 8ed0acf1ece4fc76b317540efaadbbcd2a9e8070 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Mon, 6 Jul 2026 19:12:01 +0200 Subject: [PATCH] Support a sequence used as an event control (#7797) (#7846) --- src/V3Assert.cpp | 75 ++++++++++++-- src/V3Assert.h | 1 + src/V3AssertNfa.cpp | 45 ++++++--- src/V3AssertPre.cpp | 4 +- src/V3AstNodeStmt.h | 4 + src/V3AstNodes.cpp | 2 + src/Verilator.cpp | 1 + test_regress/t/t_assert_seq_event.py | 18 ++++ test_regress/t/t_assert_seq_event.v | 97 +++++++++++++++++++ test_regress/t/t_assert_seq_event_bad.out | 6 ++ test_regress/t/t_assert_seq_event_bad.py | 16 +++ test_regress/t/t_assert_seq_event_bad.v | 23 +++++ test_regress/t/t_assert_seq_event_ctl.py | 18 ++++ test_regress/t/t_assert_seq_event_ctl.v | 73 ++++++++++++++ test_regress/t/t_assert_seq_event_unsup.out | 13 +++ test_regress/t/t_assert_seq_event_unsup.py | 16 +++ test_regress/t/t_assert_seq_event_unsup.v | 42 ++++++++ test_regress/t/t_debug_emitv.out | 15 +++ test_regress/t/t_debug_emitv.v | 11 +++ test_regress/t/t_sequence_ref_bad.out | 7 ++ ...nce_ref_unsup.py => t_sequence_ref_bad.py} | 0 ...uence_ref_unsup.v => t_sequence_ref_bad.v} | 0 test_regress/t/t_sequence_ref_unsup.out | 6 -- 23 files changed, 465 insertions(+), 28 deletions(-) create mode 100755 test_regress/t/t_assert_seq_event.py create mode 100644 test_regress/t/t_assert_seq_event.v create mode 100644 test_regress/t/t_assert_seq_event_bad.out create mode 100755 test_regress/t/t_assert_seq_event_bad.py create mode 100644 test_regress/t/t_assert_seq_event_bad.v create mode 100755 test_regress/t/t_assert_seq_event_ctl.py create mode 100644 test_regress/t/t_assert_seq_event_ctl.v create mode 100644 test_regress/t/t_assert_seq_event_unsup.out create mode 100755 test_regress/t/t_assert_seq_event_unsup.py create mode 100644 test_regress/t/t_assert_seq_event_unsup.v create mode 100644 test_regress/t/t_sequence_ref_bad.out rename test_regress/t/{t_sequence_ref_unsup.py => t_sequence_ref_bad.py} (100%) rename test_regress/t/{t_sequence_ref_unsup.v => t_sequence_ref_bad.v} (100%) delete mode 100644 test_regress/t/t_sequence_ref_unsup.out diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index e7724cfda..cbf462260 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -90,6 +90,58 @@ public: explicit DefaultDisablePropagateVisitor(AstNetlist* nodep) { iterate(nodep); } }; +// Lower a sequence used as an event control ('@seq', IEEE 1800-2023 9.4.2.4) into a +// synthesized event fired by an internal 'cover sequence' on each end-of-match +class SeqEventLowerVisitor final : public VNVisitor { + // STATE + AstNodeModule* m_modp = nullptr; // Current module + V3UniqueNames m_names{"__VseqEvent"}; // Synthesized event names + + // VISITORS + void visit(AstNodeModule* nodep) override { + VL_RESTORER(m_modp); + m_modp = nodep; + iterateChildren(nodep); + } + void visit(AstSenItem* nodep) override { + AstFuncRef* const funcrefp = VN_CAST(nodep->sensp(), FuncRef); + if (funcrefp && VN_IS(funcrefp->taskp(), Sequence)) { + FileLine* const flp = nodep->fileline(); + AstVar* const eventp = new AstVar{flp, VVarType::MODULETEMP, m_names.get(nodep), + m_modp->findBasicDType(VBasicDTypeKwd::EVENT)}; + eventp->lifetime(VLifetime::STATIC_EXPLICIT); + m_modp->addStmtsp(eventp); + v3Global.setHasEvents(); + funcrefp->unlinkFrBack(); + nodep->sensp(new AstVarRef{flp, eventp, VAccess::READ}); + const bool automaticActual = funcrefp->exists([](const AstNodeVarRef* refp) { + return refp->varp() && refp->varp()->lifetime().isAutomatic(); + }); + if (automaticActual) { + nodep->v3error("Arguments to a sequence used as an event control must be" + " static (IEEE 1800-2023 9.4.2.4)"); + VN_AS(funcrefp->taskp(), Sequence)->isReferenced(false); + VL_DO_DANGLING(pushDeletep(funcrefp), funcrefp); + return; + } + AstFireEvent* const firep + = new AstFireEvent{flp, new AstVarRef{flp, eventp, VAccess::WRITE}, false}; + AstCover* const coverp + = new AstCover{flp, new AstPropSpec{flp, nullptr, nullptr, funcrefp}, firep, + VAssertType::CONCURRENT}; + coverp->isCoverSeq(true); + coverp->isSeqEvent(true); + m_modp->addStmtsp(coverp); + return; + } + iterateChildren(nodep); + } + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + explicit SeqEventLowerVisitor(AstNetlist* nodep) { iterate(nodep); } +}; + } // namespace void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) { @@ -97,6 +149,11 @@ void V3AssertCommon::collectDefaultDisable(AstNetlist* nodep) { { DefaultDisablePropagateVisitor{nodep}; } } +void V3AssertCommon::lowerSequenceEvents(AstNetlist* nodep) { + { SeqEventLowerVisitor{nodep}; } + V3Global::dumpCheckGlobalTree("assertseqevent", 0, dumpTreeEitherLevel() >= 3); +} + //###################################################################### // AssertDeFutureVisitor // If any AstFuture, then move all non-future varrefs to be one cycle behind, @@ -546,14 +603,19 @@ class AssertVisitor final : public VNVisitor { bool selfDestruct = false; bool passspGated = false; - if (const AstCover* const snodep = VN_CAST(nodep, Cover)) { + const AstCover* const coverp = VN_CAST(nodep, Cover); + // A sequence event control is not an assertion directive; no assertion control + const bool seqEvent = coverp && coverp->isSeqEvent(); + if (coverp) { ++m_statCover; - if (!v3Global.opt.coverageUser()) { + if (seqEvent) { + // Keep the event-fire action, with no coverage bucket + } else if (!v3Global.opt.coverageUser()) { selfDestruct = true; } else { // V3Coverage assigned us a bucket to increment. - AstCoverInc* const covincp = VN_AS(snodep->coverincsp(), CoverInc); - UASSERT_OBJ(covincp, snodep, "Missing AstCoverInc under assertion"); + AstCoverInc* const covincp = VN_AS(coverp->coverincsp(), CoverInc); + UASSERT_OBJ(covincp, coverp, "Missing AstCoverInc under assertion"); covincp->unlinkFrBackWithNext(); // next() might have AstAssign for trace if (message != "") covincp->declp()->comment(message); if (passsp) { @@ -597,7 +659,8 @@ class AssertVisitor final : public VNVisitor { FileLine* const flp = nodep->fileline(); bool passspAlreadyGated = false; if (passsp && VN_IS(passsp, If)) passspAlreadyGated = VN_AS(passsp, If)->user1(); - if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr)) { + if (passsp && !passspGated && !passspAlreadyGated && !VN_IS(propExprp, PExpr) + && !seqEvent) { passsp = newIfAssertPassOn(passsp, nodep->directive(), nodep->userType(), /*vacuous=*/false); } @@ -607,7 +670,7 @@ class AssertVisitor final : public VNVisitor { AstNode* bodysp = assertBody(nodep, propExprp, passsp, failsp); if (disablep) bodysp = new AstIf{flp, new AstLogNot{flp, disablep}, bodysp}; // Add assertOn check last, for better combining - bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType()); + if (!seqEvent) bodysp = newIfAssertOn(bodysp, nodep->directive(), nodep->userType()); if (sentreep) bodysp = new AstAlways{flp, VAlwaysKwd::ALWAYS, sentreep, bodysp}; if (passsp && !passsp->backp()) VL_DO_DANGLING(pushDeletep(passsp), passsp); diff --git a/src/V3Assert.h b/src/V3Assert.h index f144688b6..3bda2528e 100644 --- a/src/V3Assert.h +++ b/src/V3Assert.h @@ -27,6 +27,7 @@ class V3AssertCommon final { public: static void collectDefaultDisable(AstNetlist* nodep) VL_MT_DISABLED; + static void lowerSequenceEvents(AstNetlist* nodep) VL_MT_DISABLED; }; class V3Assert final { diff --git a/src/V3AssertNfa.cpp b/src/V3AssertNfa.cpp index 6a35273a5..f01e0da62 100644 --- a/src/V3AssertNfa.cpp +++ b/src/V3AssertNfa.cpp @@ -287,6 +287,8 @@ class SvaNfaBuilder final { // not just the first. Builder builds parallel-branch (no first-match-wins) // topology when true. Default false preserves cover_property semantics. bool m_isCoverSeq = false; + // Unsupported endpoint topology must reject, not ignore, or the wait hangs + bool m_isSeqEvent = false; struct RangeDelayRejectInfo final { SvaStateVertex* startp = nullptr; @@ -294,6 +296,15 @@ class SvaNfaBuilder final { int rhsLen = 0; }; + void warnEndpointUnsupported(FileLine* flp, const string& what) const { + if (m_isSeqEvent) { + flp->v3warn(E_UNSUPPORTED, + "Unsupported: sequence used as an event control with " << what); + } else { + flp->v3warn(COVERIGN, "Ignoring unsupported: cover sequence with " << what); + } + } + AstNodeExpr* throughoutCond(AstNodeExpr* baseCondp, FileLine* flp) { if (m_throughoutStack.empty()) return baseCondp; // AND all throughout conditions (supports nesting) @@ -574,8 +585,7 @@ class SvaNfaBuilder final { // overlapping ends and the nested-sequence merge collapses them, so // reject those for a cover sequence rather than under-count. if (m_isCoverSeq && (range > kChainLimit || VN_IS(rhsExprp, SExpr))) { - flp->v3warn(COVERIGN, - "Ignoring unsupported: cover sequence with this ranged cycle delay"); + warnEndpointUnsupported(flp, "this ranged cycle delay"); outErrorEmitted = true; return false; } @@ -881,8 +891,7 @@ class SvaNfaBuilder final { // terminal, so a cover sequence would under-count. Reject the ranged form // (the single-count b[->N] has one end and is enumerated correctly). if (m_isCoverSeq && hasMax && maxN > minN) { - flp->v3warn(COVERIGN, - "Ignoring unsupported: cover sequence with a ranged goto repetition"); + warnEndpointUnsupported(flp, "a ranged goto repetition"); return BuildResult::failWithError(); } @@ -946,8 +955,7 @@ class SvaNfaBuilder final { // than under-count. Plain boolean disjunction has one end per cycle and // is handled by the OR-fold. if (m_isCoverSeq && (lhs.termVertexp != entryVtxp || rhs.termVertexp != entryVtxp)) { - flp->v3warn(COVERIGN, - "Ignoring unsupported: cover sequence with a sequence operand of 'or'"); + warnEndpointUnsupported(flp, "a sequence operand of 'or'"); return BuildResult::failWithError(); } SvaStateVertex* const mergeVtxp = scopedCreateVertex(); @@ -1442,11 +1450,12 @@ class SvaNfaBuilder final { public: SvaNfaBuilder(SvaGraph& graph, AstNodeModule* modp, V3UniqueNames& propTempNames, - bool isCoverSeq = false) + bool isCoverSeq = false, bool isSeqEvent = false) : m_graph{graph} , m_modp{modp} , m_propTempNames{propTempNames} - , m_isCoverSeq{isCoverSeq} {} + , m_isCoverSeq{isCoverSeq} + , m_isSeqEvent{isSeqEvent} {} // Reset scope between antecedent and consequent: liveness must not leak. void resetScope() { @@ -2876,11 +2885,17 @@ class AssertNfaVisitor final : public VNVisitor { bool senTreeOwned = false; // True if we created senTreep locally AstPropSpec* const propSpecp = VN_CAST(assertp->propp(), PropSpec); UASSERT_OBJ(propSpecp, assertp, "Concurrent assertion must have PropSpec"); + AstCover* const coverp = VN_CAST(assertp, Cover); + const bool isCover = coverp != nullptr; + const bool isCoverSeq = coverp && coverp->isCoverSeq(); + // A sequence event control is not an assertion directive; no default + // disable iff, no assertion control + const bool isSeqEvent = coverp && coverp->isSeqEvent(); // Inherit module defaults (IEEE 14.12, 16.15) when assertion has none. if (!propSpecp->sensesp() && m_defaultClockingp) { propSpecp->sensesp(m_defaultClockingp->sensesp()->cloneTree(true)); } - if (!propSpecp->disablep() && m_defaultDisablep) { + if (!propSpecp->disablep() && m_defaultDisablep && !isSeqEvent) { propSpecp->disablep(m_defaultDisablep->condp()->cloneTreePure(true)); } if (!senTreep && propSpecp->sensesp()) { @@ -2892,12 +2907,9 @@ class AssertNfaVisitor final : public VNVisitor { if (!senTreep) return; FileLine* const flp = assertp->fileline(); - const bool isCover = VN_IS(assertp, Cover); - AstCover* const coverp = VN_CAST(assertp, Cover); - const bool isCoverSeq = coverp && coverp->isCoverSeq(); SvaGraph graph; - SvaNfaBuilder builder{graph, m_modp, m_propTempNames, isCoverSeq}; + SvaNfaBuilder builder{graph, m_modp, m_propTempNames, isCoverSeq, isSeqEvent}; const BuildResult result = buildAssertionGraph(builder, graph, seqBodyp, parts, flp); if (result.valid()) wireMatchAndMidSources(graph, result, flp); @@ -2939,13 +2951,16 @@ class AssertNfaVisitor final : public VNVisitor { std::vector perMidSrcs; AstNodeExpr* const alwaysTriggerp - = assertOnCond(flp, assertp->userType(), assertp->directive()); + = isSeqEvent ? new AstConst{flp, AstConst::BitTrue{}} + : assertOnCond(flp, assertp->userType(), assertp->directive()); AstNodeExpr* const outputExprp = m_loweringp->lower( flp, graph, alwaysTriggerp, senTreep, result.finalCondp, isCover, disableExprp ? disableExprp->cloneTreePure(false) : nullptr, negated, needMatch ? &matchExprp : nullptr, disableCntVarp, snapshotVarp, needPerSrcFail ? &requiredStepSrcs : nullptr, isCoverSeq ? &perMidSrcs : nullptr, - assertp->userType(), assertp->directive()); + isSeqEvent ? VAssertType{VAssertType::INTERNAL} : assertp->userType(), + isSeqEvent ? VAssertDirectiveType{VAssertDirectiveType::INTERNAL} + : assertp->directive()); AstSenTree* const perSrcSenTreep = (requiredStepSrcs.size() >= 2) ? senTreep->cloneTree(false) : nullptr; diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index f45df83f3..d9332ff59 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -1466,7 +1466,9 @@ private: iterateAndNextNull(nodep->sensesp()); if (m_senip && m_senip != nodep->sensesp()) nodep->v3warn(E_UNSUPPORTED, "Unsupported: Only one PSL clock allowed per assertion"); - if (!nodep->disablep() && m_defaultDisablep) { + const AstCover* const coverp = VN_CAST(nodep->backp(), Cover); + const bool seqEvent = coverp && coverp->isSeqEvent(); + if (!nodep->disablep() && m_defaultDisablep && !seqEvent) { nodep->disablep(m_defaultDisablep->condp()->cloneTreePure(true)); } m_disablep = nodep->disablep(); diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 25de3ad08..fbbceaec7 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -1612,6 +1612,8 @@ class AstCover final : public AstNodeCoverOrAssert { // @astgen op3 := coverincsp: List[AstNode] // Coverage node bool m_isCoverSeq = false; // 'cover sequence' (IEEE 1800-2023 16.14.3): fires per // end-of-match, not per property success + bool m_isSeqEvent = false; // Synthesized for a sequence used as an event control + // (IEEE 1800-2023 9.4.2.4) public: ASTGEN_MEMBERS_AstCover; AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, VAssertType type, @@ -1622,6 +1624,8 @@ public: void dumpJson(std::ostream& str) const override; bool isCoverSeq() const { return m_isCoverSeq; } void isCoverSeq(bool flag) { m_isCoverSeq = flag; } + bool isSeqEvent() const { return m_isSeqEvent; } + void isSeqEvent(bool flag) { m_isSeqEvent = flag; } }; class AstRestrict final : public AstNodeCoverOrAssert { public: diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index aba8a9d0f..26126e5fd 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2241,9 +2241,11 @@ void AstNodeCoverOrAssert::dumpJson(std::ostream& str) const { void AstCover::dump(std::ostream& str) const { this->AstNodeCoverOrAssert::dump(str); if (isCoverSeq()) str << " [COVERSEQ]"; + if (isSeqEvent()) str << " [SEQEVENT]"; } void AstCover::dumpJson(std::ostream& str) const { dumpJsonBoolFuncIf(str, isCoverSeq); + dumpJsonBoolFuncIf(str, isSeqEvent); this->AstNodeCoverOrAssert::dumpJson(str); } void AstClocking::dump(std::ostream& str) const { diff --git a/src/Verilator.cpp b/src/Verilator.cpp index b1f0a9af0..b694691dd 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -261,6 +261,7 @@ static void process() { // Assertion insertion // After we've added block coverage, but before other nasty transforms V3AssertCommon::collectDefaultDisable(v3Global.rootp()); + V3AssertCommon::lowerSequenceEvents(v3Global.rootp()); V3AssertNfa::assertNfaAll(v3Global.rootp()); // V3AssertProp removed: NFA subsumes multi-cycle property lowering. // Unsupported constructs fall through to V3AssertPre. diff --git a/test_regress/t/t_assert_seq_event.py b/test_regress/t/t_assert_seq_event.py new file mode 100755 index 000000000..e8cdbc78d --- /dev/null +++ b/test_regress/t/t_assert_seq_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('vlt') + +test.compile(verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_assert_seq_event.v b/test_regress/t/t_assert_seq_event.v new file mode 100644 index 000000000..d82078cff --- /dev/null +++ b/test_regress/t/t_assert_seq_event.v @@ -0,0 +1,97 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module t ( + input clk +); + int unsigned crc = 32'h1; + bit a, b, c; + bit a1, a2, a3, b1; + int cyc; + int seq_hits, seq_hits2, ref_hits, one_hits, dc_hits; + int rng_hits, rng_ref; + + // verilog_format: off // verible does not support clocking events inside sequence declarations + sequence seq; + @(posedge clk) a ##1 b ##1 c; + endsequence + + sequence seq_one; + @(posedge clk) a; + endsequence + + sequence seq_rng; + @(posedge clk) a ##[1:3] b; + endsequence + // verilog_format: on + + // seq_dc inherits the default clocking; counts must match seq + default clocking @(posedge clk); + endclocking + + sequence seq_dc; + a ##1 b ##1 c; + endsequence + + // ref_hits and rng_ref are independent shift-register oracles; + // simultaneous end points resume a blocked waiter once + initial forever begin + @seq; + seq_hits = seq_hits + 1; + end + initial forever begin + @seq; + seq_hits2 = seq_hits2 + 1; + end + initial forever begin + @seq_one; + one_hits = one_hits + 1; + end + initial forever begin + @seq_dc; + dc_hits = dc_hits + 1; + end + initial forever begin + @seq_rng; + rng_hits = rng_hits + 1; + end + + always @(posedge clk) begin + if (a2 && b1 && c) ref_hits = ref_hits + 1; + if (b && (a1 || a2 || a3)) rng_ref = rng_ref + 1; + a3 <= a2; + a2 <= a1; + a1 <= a; + b1 <= b; + end + + // a/b/c bit spacing exceeds the ##2 window to decorrelate the LFSR taps + always @(posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[30:0], crc[31] ^ crc[21] ^ crc[1] ^ crc[0]}; + a <= crc[0]; + b <= crc[4]; + c <= crc[8]; + if (cyc == 60) $finish; + end + + // Counts read in final (Postponed) to avoid same-timestep races. + final begin + `checkd(seq_hits, 14); + `checkd(seq_hits2, 14); + `checkd(ref_hits, 14); + `checkd(one_hits, 30); + `checkd(dc_hits, 14); + `checkd(rng_hits, 26); + `checkd(rng_ref, 26); + $write("*-* All Finished *-*\n"); + end +endmodule diff --git a/test_regress/t/t_assert_seq_event_bad.out b/test_regress/t/t_assert_seq_event_bad.out new file mode 100644 index 000000000..7e24b4b9f --- /dev/null +++ b/test_regress/t/t_assert_seq_event_bad.out @@ -0,0 +1,6 @@ +%Error: t/t_assert_seq_event_bad.v:19:7: Arguments to a sequence used as an event control must be static (IEEE 1800-2023 9.4.2.4) + : ... note: In instance 't' + 19 | @(s_arg(x)); + | ^~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Exiting due to diff --git a/test_regress/t/t_assert_seq_event_bad.py b/test_regress/t/t_assert_seq_event_bad.py new file mode 100755 index 000000000..f051d14b9 --- /dev/null +++ b/test_regress/t/t_assert_seq_event_bad.py @@ -0,0 +1,16 @@ +#!/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') + +test.lint(expect_filename=test.golden_filename, verilator_flags2=['--timing'], fails=True) + +test.passes() diff --git a/test_regress/t/t_assert_seq_event_bad.v b/test_regress/t/t_assert_seq_event_bad.v new file mode 100644 index 000000000..d6621aa9d --- /dev/null +++ b/test_regress/t/t_assert_seq_event_bad.v @@ -0,0 +1,23 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +module t ( + input clk +); + + // verilog_format: off + sequence s_arg(x); + @(posedge clk) x; + endsequence + // verilog_format: on + + task automatic f; + bit x = 1; + @(s_arg(x)); + endtask + + initial f(); +endmodule diff --git a/test_regress/t/t_assert_seq_event_ctl.py b/test_regress/t/t_assert_seq_event_ctl.py new file mode 100755 index 000000000..e8cdbc78d --- /dev/null +++ b/test_regress/t/t_assert_seq_event_ctl.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('vlt') + +test.compile(verilator_flags2=['--timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_assert_seq_event_ctl.v b/test_regress/t/t_assert_seq_event_ctl.v new file mode 100644 index 000000000..ce9931e4b --- /dev/null +++ b/test_regress/t/t_assert_seq_event_ctl.v @@ -0,0 +1,73 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module t ( + input clk +); + int unsigned crc = 32'h1; + bit a, b, rst; + bit a1; + int cyc; + int hits, ref_hits, one_hits, one_ref; + + // Neither the default disable iff nor $assertoff may suppress a sequence event control + default disable iff (rst); + + // verilog_format: off // verible does not support clocking events inside sequence declarations + sequence seq; + @(posedge clk) a ##1 b; + endsequence + + sequence seq_one; + @(posedge clk) 1; + endsequence + // verilog_format: on + + initial begin + $assertoff; + #300 $assertkill; + end + + initial forever begin + @seq; + hits = hits + 1; + end + initial forever begin + @seq_one; + one_hits = one_hits + 1; + end + + always @(posedge clk) begin + if (a1 && b) ref_hits = ref_hits + 1; + one_ref = one_ref + 1; + a1 <= a; + end + + always @(posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[30:0], crc[31] ^ crc[21] ^ crc[1] ^ crc[0]}; + a <= crc[0]; + b <= crc[4]; + rst <= crc[2]; + end + + always @(negedge clk) begin + if (cyc == 60) $finish; + end + + final begin + `checkd(hits, ref_hits); + `checkd(one_hits, one_ref); + `checkd(hits, 19); + `checkd(one_hits, 60); + $write("*-* All Finished *-*\n"); + end +endmodule diff --git a/test_regress/t/t_assert_seq_event_unsup.out b/test_regress/t/t_assert_seq_event_unsup.out new file mode 100644 index 000000000..61d38b9f7 --- /dev/null +++ b/test_regress/t/t_assert_seq_event_unsup.out @@ -0,0 +1,13 @@ +%Error-UNSUPPORTED: t/t_assert_seq_event_unsup.v:18:5: Unsupported: non-edge clocking event on a sequence; use an edge such as @(posedge clk) + : ... note: In instance 't' + 18 | @(g) a ##1 b; + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_assert_seq_event_unsup.v:28:30: Unsupported: sequence used as an event control with a sequence operand of 'or' + 28 | @(posedge clk) (a ##1 b) or (a ##2 b); + | ^~ +%Error-UNSUPPORTED: t/t_assert_seq_event_unsup.v:21:12: Unsupported: sequence referenced outside assertion property + : ... note: In instance 't' + 21 | sequence s_ref; + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_assert_seq_event_unsup.py b/test_regress/t/t_assert_seq_event_unsup.py new file mode 100755 index 000000000..f051d14b9 --- /dev/null +++ b/test_regress/t/t_assert_seq_event_unsup.py @@ -0,0 +1,16 @@ +#!/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') + +test.lint(expect_filename=test.golden_filename, verilator_flags2=['--timing'], fails=True) + +test.passes() diff --git a/test_regress/t/t_assert_seq_event_unsup.v b/test_regress/t/t_assert_seq_event_unsup.v new file mode 100644 index 000000000..eda3b0772 --- /dev/null +++ b/test_regress/t/t_assert_seq_event_unsup.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +module t ( + input clk +); + bit a, b; + logic g = 0; + + default clocking @(posedge clk); + endclocking + + // verilog_format: off + sequence s_nonedge; + @(g) a ##1 b; + endsequence + + sequence s_ref; + @(posedge clk) a; + endsequence + + // Legal but its endpoint topology is not buildable, so the wait could never + // resume; rejected rather than silently ignored. + sequence s_or; + @(posedge clk) (a ##1 b) or (a ##2 b); + endsequence + // verilog_format: on + + // Legal: p is never asserted, so s_ref stays referenced outside any + // assertion property, which is not yet supported. + property p; + s_ref; + endproperty + + initial begin + @s_nonedge; + @s_or; + end +endmodule diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 8c0ad2245..5fcf5c7d7 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -1318,6 +1318,21 @@ module Vt_debug_emitv_sub; endfunction real r; endmodule +module Vt_debug_emitv_seq_event; + input logic clk; + bit a; + bit b; + bit c; + sequence sq; + @(posedge clk) a##1 b##1 c + endsequence + initial begin + begin + + ???? // EVENTCONTROL + @( sq())end + end +endmodule package Vt_debug_emitv_p; logic pkgvar; endpackage diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index 813d02ea9..7e474c305 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -138,6 +138,7 @@ module t (/*AUTOARG*/ endfunction sub sub(.*); + seq_event seq_event(.*); initial begin int other; @@ -443,6 +444,16 @@ module sub(input logic clk); real r; endmodule +module seq_event(input logic clk); + bit a, b, c; + sequence sq; + @(posedge clk) a ##1 b ##1 c; + endsequence + initial begin + @sq; + end +endmodule + package p; logic pkgvar; endpackage diff --git a/test_regress/t/t_sequence_ref_bad.out b/test_regress/t/t_sequence_ref_bad.out new file mode 100644 index 000000000..afe2fe2f7 --- /dev/null +++ b/test_regress/t/t_sequence_ref_bad.out @@ -0,0 +1,7 @@ +%Error: t/t_sequence_ref_bad.v:14:6: Concurrent assertion has no clock (IEEE 1800-2023 16.16) + : ... note: In instance 't' + : ... Suggest provide a clocking event, a default clocking, or a clocked procedural context + 14 | @s_one; + | ^~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Exiting due to diff --git a/test_regress/t/t_sequence_ref_unsup.py b/test_regress/t/t_sequence_ref_bad.py similarity index 100% rename from test_regress/t/t_sequence_ref_unsup.py rename to test_regress/t/t_sequence_ref_bad.py diff --git a/test_regress/t/t_sequence_ref_unsup.v b/test_regress/t/t_sequence_ref_bad.v similarity index 100% rename from test_regress/t/t_sequence_ref_unsup.v rename to test_regress/t/t_sequence_ref_bad.v diff --git a/test_regress/t/t_sequence_ref_unsup.out b/test_regress/t/t_sequence_ref_unsup.out deleted file mode 100644 index 41fb50aad..000000000 --- a/test_regress/t/t_sequence_ref_unsup.out +++ /dev/null @@ -1,6 +0,0 @@ -%Error-UNSUPPORTED: t/t_sequence_ref_unsup.v:9:12: Unsupported: sequence referenced outside assertion property - : ... note: In instance 't' - 9 | sequence s_one; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to