From bd6b9161dce2fa1a0d60a099a2f48a654350c134 Mon Sep 17 00:00:00 2001 From: Thomas Santerre Date: Wed, 24 Jun 2026 05:43:05 -0400 Subject: [PATCH 01/41] Optimize bit-scan loops into $mostsetbitp1 / $countones (#7822) Recognize the common single-bit scan loop idioms in V3Unroll (before it unrolls) and lower them to bit-reduction primitives, replacing a literal W-iteration loop with one intrinsic-backed expression: target=0; for (i=0;i $mostsetbitp1(vec) target=0; for (i=0;i $countones(vec) The leading-one form lowers to a new AstMostSetBitP1 node, emitted as VL_MOSTSETBITP1_{I,Q,W}; those runtime helpers now use __builtin_clz where available (same pattern as VL_REDXOR's __builtin_parity), with the existing bit scan as fallback. The count-ones form reuses AstCountOnes ($countones, popcount); as the DFG requires a 32-bit countones result it is built at 32 bits and narrowed to the accumulator width with a select. Matching is structural to stay sound: the index must start at 0, increment by exactly 1, and scan all W==width(vec) bits via a single 1-bit select of a distinct vector, with the target pre-zeroed and no else branch. The loop bound is accepted as a strict ascending 'idx < W' written either way and signed or unsigned (Gt/GtS/Lt/LtS). Gated by -fbit-scan-loops (on at -O). Adds t_bit_scan_loops (I/Q/W, count-ones and unsigned-index positives; step-2, start-1, idx*2+1, vec[idx+1], target=idx and W!=width negatives, all self-checked and asserted via --stats not to lower) plus t_bit_scan_loops_off for the disable flag. Motivated by a transformer inference design whose 80-bit leading-one detector ran every cycle (~37% of runtime); the lowering is worth ~39% there. --- docs/CONTRIBUTORS | 1 + docs/guide/exe_verilator.rst | 4 + include/verilated_funcs.h | 30 +++- src/V3AstNodeExpr.h | 16 ++ src/V3Number.cpp | 14 ++ src/V3Number.h | 1 + src/V3Options.cpp | 2 + src/V3Options.h | 2 + src/V3Unroll.cpp | 156 ++++++++++++++++++ test_regress/t/t_bit_scan_loops.py | 28 ++++ test_regress/t/t_bit_scan_loops.v | 169 ++++++++++++++++++++ test_regress/t/t_bit_scan_loops_off.py | 24 +++ test_regress/t/t_bit_scan_loops_xassign0.py | 32 ++++ 13 files changed, 472 insertions(+), 7 deletions(-) create mode 100755 test_regress/t/t_bit_scan_loops.py create mode 100644 test_regress/t/t_bit_scan_loops.v create mode 100755 test_regress/t/t_bit_scan_loops_off.py create mode 100755 test_regress/t/t_bit_scan_loops_xassign0.py diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 44f7ecb2c..a593324c8 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -273,6 +273,7 @@ Teng Huang Thomas Aldrian Thomas Brown Thomas Dybdahl Ahle +Thomas Santerre Tim Hutt Tim Snyder Tobias Jensen diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 952500400..807216565 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -662,6 +662,10 @@ Summary: .. option:: -fno-assemble +.. option:: -fno-bit-scan-loops + + Rarely needed. Disable converting bit counting loops into built-in operations. + .. option:: -fno-case Rarely needed. Disable all case statement optimizations. diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index d69bca256..88da40387 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -903,15 +903,31 @@ static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_PURE { return 0; } +static inline IData VL_MOSTSETBITP1_I(IData lhs) VL_PURE { + if (VL_UNLIKELY(!lhs)) return 0; // __builtin_clz is undefined for 0 +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return VL_EDATASIZE - __builtin_clz(lhs); +#else + for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { + if (VL_BITISSET_E(lhs, bit)) return bit + 1; + } + return 0; // LCOV_EXCL_LINE // Can't get here - one bit must be set +#endif +} +static inline IData VL_MOSTSETBITP1_Q(QData lhs) VL_PURE { + if (VL_UNLIKELY(!lhs)) return 0; +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return 64 - __builtin_clzll(static_cast(lhs)); +#else + const IData hi = static_cast(lhs >> 32ULL); + return hi ? (VL_EDATASIZE + VL_MOSTSETBITP1_I(hi)) + : VL_MOSTSETBITP1_I(static_cast(lhs)); +#endif +} static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_PURE { - // MSB set bit plus one; similar to FLS. 0=value is zero for (int i = words - 1; i >= 0; --i) { - if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken - for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { - if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1; - } - // Can't get here - one bit must be set - } + // Shorter worst case if predict not taken + if (VL_UNLIKELY(lwp[i])) return i * VL_EDATASIZE + VL_MOSTSETBITP1_I(lwp[i]); } return 0; } diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 85c5b0892..e8f065861 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -5737,6 +5737,22 @@ public: void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; }; +class AstMostSetBitP1 final : public AstNodeUniop { + // Most-significant set bit plus one (bit-width); 0 if value is zero +public: + AstMostSetBitP1(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_MostSetBitP1(fl, lhsp) { + dtypeSetInteger2State(); + } + ASTGEN_MEMBERS_AstMostSetBitP1; + void numberOperate(V3Number& out, const V3Number& lhs) override { out.opMostSetBitP1(lhs); } + string emitVerilog() override { return "%f$mostsetbitp1(%l)"; } + string emitC() override { return "VL_MOSTSETBITP1_%lq(%lW, %P, %li)"; } + bool cleanOut() const override { return true; } + bool cleanLhs() const override { return true; } + bool sizeMattersLhs() const override { return false; } + int instrCount() const override { return widthInstrs() * 16; } +}; class AstNToI final : public AstNodeUniop { // String to any-size integral public: diff --git a/src/V3Number.cpp b/src/V3Number.cpp index a9c505359..d97e4ed50 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1464,6 +1464,20 @@ V3Number& V3Number::opCLog2(const V3Number& lhs) { setZero(); return *this; } +V3Number& V3Number::opMostSetBitP1(const V3Number& lhs) { + // Most-significant set bit plus one (bit-width / find-last-set); 0 if value is zero + NUM_ASSERT_OP_ARGS1(lhs); + NUM_ASSERT_LOGIC_ARGS1(lhs); + if (lhs.isFourState()) return setAllBitsX(); + for (int bit = lhs.width() - 1; bit >= 0; bit--) { + if (lhs.bitIs1(bit)) { + setLong(bit + 1); + return *this; + } + } + setZero(); + return *this; +} V3Number& V3Number::opLogNot(const V3Number& lhs) { NUM_ASSERT_OP_ARGS1(lhs); diff --git a/src/V3Number.h b/src/V3Number.h index 3791f2f19..d81e32d72 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -761,6 +761,7 @@ public: V3Number& opOneHot(const V3Number& lhs); V3Number& opOneHot0(const V3Number& lhs); V3Number& opCLog2(const V3Number& lhs); + V3Number& opMostSetBitP1(const V3Number& lhs); V3Number& opClean(const V3Number& lhs, uint32_t bits); V3Number& opConcat(const V3Number& lhs, const V3Number& rhs); V3Number& opLenN(const V3Number& lhs); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index a23a408b2..7a85c848d 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1448,6 +1448,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-facyc-simp", FOnOff, &m_fAcycSimp); DECL_OPTION("-fassemble", FOnOff, &m_fAssemble); + DECL_OPTION("-fbit-scan-loops", FOnOff, &m_fBitScanLoops); DECL_OPTION("-fcase", CbFOnOff, [this](bool flag) { m_fCaseDecoder = flag; m_fCaseTable = flag; @@ -2359,6 +2360,7 @@ void V3Options::optimize(int level) { const bool flag = level > 0; m_fAcycSimp = flag; m_fAssemble = flag; + m_fBitScanLoops = flag; m_fCaseDecoder = flag; m_fCaseTable = flag; m_fCaseTree = flag; diff --git a/src/V3Options.h b/src/V3Options.h index f3dd59863..182379fe9 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -392,6 +392,7 @@ private: // MEMBERS (optimizations) bool m_fAcycSimp; // main switch: -fno-acyc-simp: acyclic pre-optimizations bool m_fAssemble; // main switch: -fno-assemble: assign assemble + bool m_fBitScanLoops; // main switch: -fno-bit-scan-loops: convert bit scan loops to builtins bool m_fCaseDecoder; // main switch: -fno-case-decoder: case decoder conversion bool m_fCaseTable; // main switch: -fno-case-table: case table conversion bool m_fCaseTree; // main switch: -fno-case-tree: case tree conversion @@ -731,6 +732,7 @@ public: // ACCESSORS (optimization options) bool fAcycSimp() const { return m_fAcycSimp; } bool fAssemble() const { return m_fAssemble; } + bool fBitScanLoops() const { return m_fBitScanLoops; } bool fCaseDecoder() const { return m_fCaseDecoder; } bool fCaseTable() const { return m_fCaseTable; } bool fCaseTree() const { return m_fCaseTree; } diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index b5413e8b6..62cf42fa9 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -62,6 +62,8 @@ struct UnrollStats final { Stat m_nPragmaDisabled{"Pragma unroll_disable"}; Stat m_nUnrolledLoops{"Unrolled loops"}; Stat m_nUnrolledIters{"Unrolled iterations"}; + Stat m_bitScanLowered{"Lowered priority-encoder to mostsetbitp1"}; + Stat m_countOnesLowered{"Lowered count-set-bits to countones"}; }; //###################################################################### @@ -422,6 +424,157 @@ class UnrollAllVisitor final : VNVisitor { UnrollStats m_stats; // Statistic tracking UnrolllBindings m_bindings; // Variable bindings + // METHODS + // Peel value-preserving width casts (Extend/ExtendS, or a low-bits Sel with lsb 0) to the + // underlying VarRef. A Sel kept narrower than 'minWidth' is a lossy narrowing (idx[1:0]) + // and is rejected. + static AstVarRef* unwrapToVarRef(AstNodeExpr* nodep, int minWidth) { + while (true) { + if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) return refp; + if (AstExtend* const ep = VN_CAST(nodep, Extend)) { + nodep = ep->lhsp(); + } else if (AstExtendS* const ep = VN_CAST(nodep, ExtendS)) { + nodep = ep->lhsp(); + } else if (AstSel* const sp = VN_CAST(nodep, Sel)) { + const AstConst* const lsbp = VN_CAST(sp->lsbp(), Const); + if (!lsbp || lsbp->toUInt() != 0 || sp->width() < minWidth) return nullptr; + nodep = sp->fromp(); + } else { + return nullptr; + } + } + } + // True if 'nodep' is exactly '1 + var' for 'vscp' (V3Const puts the constant on the LHS). + // Passing the add's width as minWidth rejects a lossy increment like 32'(i[1:0]) + 1. + bool isVarPlus1(AstNode* nodep, const AstVarScope* vscp) { + AstAdd* const addp = VN_CAST(nodep, Add); + if (!addp || !addp->lhsp()->isOne()) return false; + const AstVarRef* const r = unwrapToVarRef(addp->rhsp(), addp->width()); + return r && r->varScopep() == vscp; + } + // Resize the 32-bit reduction to the accumulator width; truncating the low bits matches + // the original counted loop's wrap-around. + static AstNodeExpr* resizeToWidth(AstNodeExpr* exprp, const AstVarRef* targetRefp) { + const int width = targetRefp->width(); + if (width == 32) return exprp; + FileLine* const flp = exprp->fileline(); + if (width < 32) return new AstSel{flp, exprp, 0, width}; + AstExtend* const extp = new AstExtend{flp, exprp}; + extp->dtypeFrom(targetRefp); + return extp; + } + // Match a strict ascending loop bound 'idx < W'. V3Const canonicalizes this to the + // 'W > idx' form (Gt unsigned, GtS signed), so only that form is matched. + static bool ascendingBound(AstNodeExpr* condp, AstConst*& wp, AstVarRef*& idxRefp) { + if (!VN_IS(condp, Gt) && !VN_IS(condp, GtS)) return false; + AstNodeBiop* const bp = VN_AS(condp, NodeBiop); + wp = VN_CAST(bp->lhsp(), Const); + idxRefp = VN_CAST(bp->rhsp(), VarRef); + return wp && idxRefp && !wp->num().isFourState(); + } + // Recognize the redundant in-range guard Verilator auto-inserts for a select into a + // non-power-of-two vector. V3Const canonicalizes 'idx <= C' to '(C >= idx)' (Gte/GteS, + // const on the LHS), so only that form occurs; with C >= W-1 it is always true for idx + // in 0..W-1. + static bool isInRangeGuard(AstNodeExpr* condp, const AstVarScope* idxVscp, uint32_t width, + int addrBits) { + if (!VN_IS(condp, Gte) && !VN_IS(condp, GteS)) return false; + AstNodeBiop* const bp = VN_AS(condp, NodeBiop); + const AstConst* const cp = VN_CAST(bp->lhsp(), Const); + if (!cp || cp->num().isFourState() || cp->toUInt() < width - 1) return false; + const AstVarRef* const r = unwrapToVarRef(bp->rhsp(), addrBits); + return r && r->varScopep() == idxVscp; + } + // Recognize a single-bit scan loop over all W bits of 'vec' (idx 0..W-1, target + // pre-zeroed) and lower it to a bit-reduction primitive. Two idioms are matched: + // target = 0; idx = 0; + // loop { looptest(W > idx); if (...vec[idx]...) target = ; idx = idx + 1; } + // where, when W == width(vec): + // = idx + 1 => target = $mostsetbitp1(vec) (leading-one / bit-width) + // = target + 1 => target = $countones(vec) (population count) + bool tryLowerBitScanLoop(AstLoop* loopp) { + AstLoopTest* const testp = VN_CAST(loopp->stmtsp(), LoopTest); + if (!testp) return false; + AstIf* const ifp = VN_CAST(testp->nextp(), If); + if (!ifp) return false; + AstAssign* const incp = VN_CAST(ifp->nextp(), Assign); + if (!incp || incp->nextp()) return false; + AstConst* wp = nullptr; + AstVarRef* idxRefp = nullptr; + if (!ascendingBound(testp->condp(), wp, idxRefp)) return false; + AstVarScope* const idxVscp = idxRefp->varScopep(); + const uint32_t width = wp->toUInt(); + // Bits needed to address all W bits of 'vec' (clog2(W)); a narrower index is lossy. + const int addrBits = width <= 1 ? 1 : V3Number::log2b(width - 1) + 1; + const AstConst* const idxInitp = m_bindings.get(idxVscp); + if (!idxInitp || !idxInitp->isZero()) return false; + AstVarRef* const incLhsp = VN_CAST(incp->lhsp(), VarRef); + if (!incLhsp || incLhsp->varScopep() != idxVscp) return false; + if (!isVarPlus1(incp->rhsp(), idxVscp)) return false; + if (ifp->elsesp()) return false; + AstAssign* const thenp = VN_CAST(ifp->thensp(), Assign); + if (!thenp || thenp->nextp()) return false; + AstVarRef* const targetRefp = VN_CAST(thenp->lhsp(), VarRef); + if (!targetRefp) return false; + AstVarScope* const targetVscp = targetRefp->varScopep(); + if (targetVscp == idxVscp) return false; + const bool isLeadingOne = isVarPlus1(thenp->rhsp(), idxVscp); + const bool isCountOnes = !isLeadingOne && isVarPlus1(thenp->rhsp(), targetVscp); + if (!isLeadingOne && !isCountOnes) return false; + // If-cond is the 1-bit select 'vec[idx]', possibly wrapped in the redundant in-range + // guard Verilator auto-inserts (as 'guard && sel') for a non-power-of-two vector: + // '(idx <= W-1) && vec[idx]' (default / --x-assign 0; a LogAnd), or + // '(idx <= W-1) ? vec[idx] : ' (--x-assign unique; a Cond). + // The guard is always true for idx in 0..W-1, so peel it to reach the select. Any + // other compound condition (e.g. 'vec[idx] && en') leaves a non-select, rejected below. + AstNodeExpr* condp = ifp->condp(); + if (AstLogAnd* const andp = VN_CAST(condp, LogAnd)) { + if (isInRangeGuard(andp->lhsp(), idxVscp, width, addrBits)) condp = andp->rhsp(); + } else if (AstCond* const ternp = VN_CAST(condp, Cond)) { + if (isInRangeGuard(ternp->condp(), idxVscp, width, addrBits)) condp = ternp->thenp(); + } + AstSel* const selp = VN_CAST(condp, Sel); + if (!selp || selp->width() != 1) return false; + const AstVarRef* const fromp = VN_CAST(selp->fromp(), VarRef); + if (!fromp) return false; + const AstVarScope* const fromVscp = fromp->varScopep(); + if (fromVscp == idxVscp || fromVscp == targetVscp) return false; + AstNodeExpr* const vecExprp = selp->fromp(); + // Must scan all W bits of 'vec', indexed by exactly 'idx' (address kept >= clog2(W), + // so a lossy narrowing like vec[idx[2:0]] is rejected). + if (static_cast(width) != vecExprp->width()) return false; + const AstVarRef* const idxInSel = unwrapToVarRef(selp->lsbp(), addrBits); + if (!idxInSel || idxInSel->varScopep() != idxVscp) return false; + // 'target' must be const-0 immediately before the loop (collected in m_bindings), + // so that an all-zero 'vec' yields 0, matching $mostsetbitp1's definition. + const AstConst* const targetInitp = m_bindings.get(targetVscp); + if (!targetInitp || !targetInitp->isZero()) return false; + // Rewrite to 'target = (vec); idx = W'. The 'idx = W' store preserves the + // loop's exit value, so this is sound even if idx is read afterwards (else DCE drops it). + FileLine* const flp = loopp->fileline(); + AstNodeExpr* reducep; + if (isLeadingOne) { + reducep = new AstMostSetBitP1{flp, vecExprp->cloneTree(false)}; + } else { + AstCountOnes* const conep = new AstCountOnes{flp, vecExprp->cloneTree(false)}; + conep->dtypeSetInteger2State(); + reducep = conep; + } + reducep = resizeToWidth(reducep, targetRefp); + AstAssign* const newp = new AstAssign{flp, targetRefp->cloneTree(false), reducep}; + newp->addNext(new AstAssign{flp, incLhsp->cloneTree(false), wp->cloneTree(false)}); + loopp->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(loopp), loopp); + if (isLeadingOne) { + UINFO(4, "Lowered priority-encoder loop to $mostsetbitp1: " << newp); + ++m_stats.m_bitScanLowered; + } else { + UINFO(4, "Lowered count-set-bits loop to $countones: " << newp); + ++m_stats.m_countOnesLowered; + } + return true; + } + // VISIT void visit(AstLoop* nodep) override { // Gather variable bindings from the preceding statements @@ -450,6 +603,9 @@ class UnrollAllVisitor final : VNVisitor { m_bindings.set(lhsp->varScopep(), valp); } + // Recognize a bit counting loop and lower it to a builtin + if (v3Global.opt.fBitScanLoops() && tryLowerBitScanLoop(nodep)) return; + // Attempt to unroll this loop const std::pair pair = UnrollOneVisitor::apply(m_stats, m_bindings, nodep); diff --git a/test_regress/t/t_bit_scan_loops.py b/test_regress/t/t_bit_scan_loops.py new file mode 100755 index 000000000..8d0b83c51 --- /dev/null +++ b/test_regress/t/t_bit_scan_loops.py @@ -0,0 +1,28 @@ +#!/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') + +# --unroll-count 0 so the loops are recognized without relying on unrolling. +test.compile(verilator_flags2=['--stats', '--unroll-count', '0']) + +# The leading-one positives lower to $mostsetbitp1, the count-ones positive to +# $countones; the negatives are left as loops (a wrong lowering would raise a count). +test.file_grep(test.stats, + r'Optimizations, Loop unrolling, Lowered priority-encoder to mostsetbitp1\s+(\d+)', + 8) +test.file_grep(test.stats, + r'Optimizations, Loop unrolling, Lowered count-set-bits to countones\s+(\d+)', + 1) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_bit_scan_loops.v b/test_regress/t/t_bit_scan_loops.v new file mode 100644 index 000000000..fa2c1df00 --- /dev/null +++ b/test_regress/t/t_bit_scan_loops.v @@ -0,0 +1,169 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Exercises the bit-counting loop idioms that V3Unroll lowers to builtins: +// leading-one for (b=0;b $mostsetbitp1(vec) +// count-ones for (b=0;b $countones(vec) +// Positives must lower (counted via --stats by the .py); negatives compute a +// different value than the builtin and so must be left as loops. +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// 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 ( + input clk +); + + // ---- positives: must lower ---- + logic [31:0] p32; + logic [5:0] n32; // I path, narrow target (select resize) + logic [47:0] p48; + logic [6:0] n48; // Q path + logic [79:0] p80; + logic [6:0] n80; // W path + logic [31:0] pu; + logic [5:0] nu; // unsigned loop index + logic [31:0] p32e; + logic [31:0] n32e; // 32-bit target (no resize) + logic [31:0] p32w; + logic [39:0] n40; // >32-bit target (extend resize) + logic [31:0] pc; + logic [5:0] nc; // count-ones -> $countones + logic [31:0] kvec; // const (set in initial) -> exercises $mostsetbitp1 fold + logic [5:0] kn; + initial kvec = 32'h0000_0100; + logic [31:0] kvec0; // const 0 -> $mostsetbitp1(0)=0 (covers the zero path) + logic [5:0] kn0; + initial kvec0 = 32'h0; + always_comb begin + n32 = 0; + for (int b = 0; b < 32; b++) if (p32[b]) n32 = 6'(b + 1); + end + always_comb begin + n48 = 0; + for (int b = 0; b < 48; b++) if (p48[b]) n48 = 7'(b + 1); + end + always_comb begin + n80 = 0; + for (int b = 0; b < 80; b++) if (p80[b]) n80 = 7'(b + 1); + end + always_comb begin + nu = 0; + for (int unsigned b = 0; b < 32; b++) if (pu[b]) nu = 6'(b + 1); + end + always_comb begin + n32e = 0; + for (int b = 0; b < 32; b++) if (p32e[b]) n32e = 32'(b + 1); + end + always_comb begin + n40 = 0; + for (int b = 0; b < 32; b++) if (p32w[b]) n40 = 40'(b + 1); + end + always_comb begin + nc = 0; + for (int b = 0; b < 32; b++) if (pc[b]) nc = nc + 1; + end + always_comb begin + kn = 0; + for (int b = 0; b < 32; b++) if (kvec[b]) kn = 6'(b + 1); + end + always_comb begin + kn0 = 0; + for (int b = 0; b < 32; b++) if (kvec0[b]) kn0 = 6'(b + 1); + end + + // ---- negatives: must NOT lower (each yields a different value than the builtin) ---- + logic [31:0] vn; // shared input, bits {2,4,5,7} + logic [31:0] vw; // has a set bit above the scan bound + logic [31:0] vt; // for the truncated-index case + logic en1; // runtime gate for the compound-condition case + logic [5:0] e_step2; + logic [6:0] e_start1; + logic [6:0] e_mul; + logic [5:0] e_off; + logic [5:0] e_noP1; + logic [5:0] e_narrow; + logic [5:0] e_comp; + logic [5:0] e_trunc; + always_comb begin + e_step2 = 0; + for (int b = 0; b < 32; b += 2) if (vn[b]) e_step2 = 6'(b + 1); + end + always_comb begin + e_start1 = 0; + for (int b = 1; b < 32; b++) if (vn[b]) e_start1 = 7'(b + 1); + end + always_comb begin + e_mul = 0; + for (int b = 0; b < 32; b++) if (vn[b]) e_mul = 7'(2 * b + 1); + end + always_comb begin + e_off = 0; + for (int b = 0; b < 31; b++) if (vn[b+1]) e_off = 6'(b + 1); + end + always_comb begin + e_noP1 = 0; + for (int b = 0; b < 32; b++) if (vn[b]) e_noP1 = 6'(b); + end + always_comb begin + e_narrow = 0; + for (int b = 0; b < 16; b++) if (vw[b]) e_narrow = 6'(b + 1); + end + always_comb begin + e_comp = 0; + for (int b = 0; b < 32; b++) if (vn[b] && en1) e_comp = 6'(b + 1); + end + // verilator lint_off WIDTHEXPAND + always_comb begin + e_trunc = 0; + for (int b = 0; b < 32; b++) if (vt[b[2:0]]) e_trunc = 6'(b + 1); + end + // verilator lint_on WIDTHEXPAND + + int cyc = 0; + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 0) begin + p32 <= 32'h8000_0000; + p48 <= 48'h0; + p48[47] <= 1'b1; + p80 <= 80'h0; + p80[79] <= 1'b1; + pu <= 32'h0001_0000; // bit 16 + p32e <= 32'h8000_0000; + p32w <= 32'h8000_0000; + pc <= 32'hf0f0_f0f0; // 16 ones + vn <= 32'h0000_00b4; // bits {2,4,5,7} + vw <= 32'h0010_0008; // bits {3,20} + vt <= 32'h0000_0080; // bit 7 + en1 <= 1'b0; // gate off -> compound loop yields 0 + end + else if (cyc == 1) begin + `checkh(n32, 6'd32); + `checkh(n48, 7'd48); + `checkh(n80, 7'd80); + `checkh(nu, 6'd17); // unsigned-index leading-one, bit 16 -> 17 + `checkh(n32e, 32'd32); + `checkh(n40, 40'd32); + `checkh(nc, 6'd16); // popcount(0xF0F0F0F0) + `checkh(kn, 6'd9); // mostsetbitp1(0x100), constant-folded + `checkh(kn0, 6'd0); // mostsetbitp1(0)=0, constant-folded (zero path) + // negatives, hand-computed for vn = 0xB4 (bits 2,4,5,7): + `checkh(e_step2, 6'd5); // highest even set bit (4) + 1 + `checkh(e_start1, 7'd8); // highest set bit in [1,32) (7) + 1 + `checkh(e_mul, 7'd15); // 2*7 + 1 + `checkh(e_off, 6'd7); // idx where vec[idx+1]; highest 6 -> 7 + `checkh(e_noP1, 6'd7); // highest set bit (7), no +1 + `checkh(e_narrow, 6'd4); // W=16 != width(vec): only low bits scanned (bit 3) + `checkh(e_comp, 6'd0); // && en1 (=0); a wrong lowering would give 8 + `checkh(e_trunc, 6'd32); // vt[b[2:0]] last hits b=31; a wrong lowering would give 8 + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_bit_scan_loops_off.py b/test_regress/t/t_bit_scan_loops_off.py new file mode 100755 index 000000000..cdf34ea55 --- /dev/null +++ b/test_regress/t/t_bit_scan_loops_off.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') + +# Reuse the same design; only the optimization switch differs. +test.top_filename = "t/t_bit_scan_loops.v" + +test.compile(verilator_flags2=['--stats', '--unroll-count', '0', '-fno-bit-scan-loops']) + +# With the optimization disabled, nothing lowers. +test.file_grep(test.stats, r'Lowered priority-encoder to mostsetbitp1\s+([0-9])', 0) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_bit_scan_loops_xassign0.py b/test_regress/t/t_bit_scan_loops_xassign0.py new file mode 100755 index 000000000..083a30cba --- /dev/null +++ b/test_regress/t/t_bit_scan_loops_xassign0.py @@ -0,0 +1,32 @@ +#!/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') + +# Reuse the same design. '--x-assign 0' makes the auto-inserted out-of-range guard on a +# non-power-of-two bit-select a plain '(idx <= W-1) && vec[idx]' (AstLogAnd), rather than +# the ternary '(idx <= W-1) ? vec[idx] : ' (AstCond) produced under the driver's default +# '--x-assign unique'. This exercises the matcher's other guard-peel branch. +test.top_filename = "t/t_bit_scan_loops.v" + +test.compile(verilator_flags2=['--stats', '--unroll-count', '0', '--x-assign', '0']) + +# Same lowering counts as the default run -- only the guard shape differs, not the result. +test.file_grep(test.stats, + r'Optimizations, Loop unrolling, Lowered priority-encoder to mostsetbitp1\s+(\d+)', + 8) +test.file_grep(test.stats, + r'Optimizations, Loop unrolling, Lowered count-set-bits to countones\s+(\d+)', + 1) + +test.execute() + +test.passes() From 84cc08b756b9f5f5056175e1c571512aec8f68dd Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 24 Jun 2026 09:44:11 +0000 Subject: [PATCH 02/41] Apply 'make format' [ci skip] --- test_regress/t/t_bit_scan_loops.py | 3 +-- test_regress/t/t_bit_scan_loops_xassign0.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test_regress/t/t_bit_scan_loops.py b/test_regress/t/t_bit_scan_loops.py index 8d0b83c51..07b7818b1 100755 --- a/test_regress/t/t_bit_scan_loops.py +++ b/test_regress/t/t_bit_scan_loops.py @@ -20,8 +20,7 @@ test.file_grep(test.stats, r'Optimizations, Loop unrolling, Lowered priority-encoder to mostsetbitp1\s+(\d+)', 8) test.file_grep(test.stats, - r'Optimizations, Loop unrolling, Lowered count-set-bits to countones\s+(\d+)', - 1) + r'Optimizations, Loop unrolling, Lowered count-set-bits to countones\s+(\d+)', 1) test.execute() diff --git a/test_regress/t/t_bit_scan_loops_xassign0.py b/test_regress/t/t_bit_scan_loops_xassign0.py index 083a30cba..b44c2cdf7 100755 --- a/test_regress/t/t_bit_scan_loops_xassign0.py +++ b/test_regress/t/t_bit_scan_loops_xassign0.py @@ -24,8 +24,7 @@ test.file_grep(test.stats, r'Optimizations, Loop unrolling, Lowered priority-encoder to mostsetbitp1\s+(\d+)', 8) test.file_grep(test.stats, - r'Optimizations, Loop unrolling, Lowered count-set-bits to countones\s+(\d+)', - 1) + r'Optimizations, Loop unrolling, Lowered count-set-bits to countones\s+(\d+)', 1) test.execute() From 350158c857def29a35f4cb52a5182bb1591338ab Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Wed, 24 Jun 2026 11:51:42 +0200 Subject: [PATCH 03/41] Fix scheduling of virtual interface method writes (#7641) Fix scheduling of writes in virtual interfaces, there were missing triggers (see added test). Make V3SchedVirtIface handle writes done inside methods called through a virtual interface. The pass first records direct vif.member writes, VIF method calls, and candidate interface member VarScopes. It then walks the methods reachable from those VIF calls, writes to persistent interface variables in those method bodies are treated as VIF writes, and nested calls are followed with the same interface context. Function locals, temps, and events are ignored because they are not persistent interface storage observable through a later VIF read. Triggers are still created only from the intersection of (interface type, member name) writes and matching VarScopes, so unrelated interface variables and interfaces with no virtual access do not get extra triggers. --- src/V3SchedVirtIface.cpp | 67 ++++++++++++++-- .../t/t_virtual_interface_method_sched.py | 18 +++++ .../t/t_virtual_interface_method_sched.v | 79 +++++++++++++++++++ 3 files changed, 158 insertions(+), 6 deletions(-) create mode 100755 test_regress/t/t_virtual_interface_method_sched.py create mode 100644 test_regress/t/t_virtual_interface_method_sched.v diff --git a/src/V3SchedVirtIface.cpp b/src/V3SchedVirtIface.cpp index 4dd95662d..41684cd2b 100644 --- a/src/V3SchedVirtIface.cpp +++ b/src/V3SchedVirtIface.cpp @@ -39,11 +39,18 @@ namespace { class VirtIfaceVisitor final : public VNVisitor { private: // STATE + using IfaceMember = std::pair; + using IfaceCallable = std::pair; + // Set of (iface, member) pairs written through VIF -- defines which members need triggers - std::set> m_vifWrittenMembers; + std::set m_vifWrittenMembers; // All candidate VarScopes of interface members (keyed by interface type + member name) - std::map, std::vector> - m_candidateVscps; + std::map> m_candidateVscps; + std::set> m_seenCandidateVscps; + // VarScope index and callable worklist for VIF method-body writes. + std::map> m_vscpsByVar; + std::vector m_reachableIfaceCallables; + std::set m_seenReachableIfaceCallables; VirtIfaceTriggers m_triggers; // METHODS @@ -70,12 +77,24 @@ private: } iterateChildren(nodep); } + void visit(AstCMethodCall* nodep) override { + if (const AstIfaceRefDType* const dtypep + = VN_CAST(nodep->fromp()->dtypep()->skipRefp(), IfaceRefDType)) { + if (VL_UNCOVERABLE(!dtypep->isVirtual())) { + // Concrete interface method calls are lowered before this pass. + } else { + addReachableIfaceCallable(dtypep->ifaceViaCellp(), nodep->funcp()); + } + } + iterateChildren(nodep); + } void visit(AstVarScope* nodep) override { // Collect candidate VarScopes. sensIfacep() is set on interface members // accessed via any MemberSel (virtual or non-virtual). - if (const AstIface* const ifacep = nodep->varp()->sensIfacep()) { - m_candidateVscps[{ifacep, nodep->varp()->name()}].push_back(nodep); - } + AstVar* const varp = nodep->varp(); + if (varp->isTemp()) return; + m_vscpsByVar[varp].push_back(nodep); + if (const AstIface* const ifacep = varp->sensIfacep()) addCandidateVscp(ifacep, nodep); } void visit(AstNodeProcedure* nodep) override { // Disable lifetime optimization for variables in AlwaysPost blocks @@ -87,6 +106,19 @@ private: } void visit(AstNode* nodep) override { iterateChildren(nodep); } + void addCandidateVscp(const AstIface* const ifacep, AstVarScope* const vscp) { + const IfaceMember member{ifacep, vscp->varp()->name()}; + if (m_seenCandidateVscps.emplace(member, vscp).second) + m_candidateVscps[member].push_back(vscp); + } + + void addReachableIfaceCallable(AstIface* const ifacep, AstCFunc* const funcp) { + const IfaceCallable callable{ifacep, funcp}; + if (m_seenReachableIfaceCallables.emplace(callable).second) { + m_reachableIfaceCallables.push_back(callable); + } + } + // Build final trigger list by intersecting VIF writes with candidate VarScopes void buildTriggers() { for (const auto& written : m_vifWrittenMembers) { @@ -103,6 +135,29 @@ public: // CONSTRUCTORS explicit VirtIfaceVisitor(AstNetlist* nodep) { iterate(nodep); + for (size_t i = 0; i < m_reachableIfaceCallables.size(); ++i) { + const IfaceCallable callable = m_reachableIfaceCallables[i]; + callable.second->foreach([this, callable](AstNodeExpr* const nodep) { + if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) { + // Only persistent interface storage is observable through a VIF read. + UASSERT_OBJ(refp->varScopep(), refp, "No var scope"); + AstVar* const varp = refp->varp(); + if (!refp->access().isWriteOrRW() || varp->isFuncLocal() || varp->isTemp() + || varp->isEvent() || !VN_IS(refp->varScopep()->scopep()->modp(), Iface)) { + return; + } + varp->sensIfacep(callable.first); + m_vifWrittenMembers.emplace(callable.first, varp->name()); + const auto it = m_vscpsByVar.find(varp); + UASSERT_OBJ(it != m_vscpsByVar.end(), varp, + "No VarScope for interface member"); + for (AstVarScope* const vscp : it->second) + addCandidateVscp(callable.first, vscp); + } else if (AstNodeCCall* const callp = VN_CAST(nodep, NodeCCall)) { + addReachableIfaceCallable(callable.first, callp->funcp()); + } + }); + } buildTriggers(); } ~VirtIfaceVisitor() override = default; diff --git a/test_regress/t/t_virtual_interface_method_sched.py b/test_regress/t/t_virtual_interface_method_sched.py new file mode 100755 index 000000000..6ac2815da --- /dev/null +++ b/test_regress/t/t_virtual_interface_method_sched.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=["--timing"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_virtual_interface_method_sched.v b/test_regress/t/t_virtual_interface_method_sched.v new file mode 100644 index 000000000..48dc01565 --- /dev/null +++ b/test_regress/t/t_virtual_interface_method_sched.v @@ -0,0 +1,79 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +class msg; + string context_name; + + function void set_context(string name); + context_name = name; + endfunction +endclass + +package helper_pkg; + int package_counter; + + function void bump(); + package_counter++; + endfunction +endpackage + +interface intf(output logic a, output logic b); + msg m; + event e; + + task go(); + helper_pkg::package_counter++; + go_helper(); + go_helper(); + endtask + + task go_helper(); + // verilator no_inline_task + m.set_context("go_helper"); + helper_pkg::bump(); + -> e; + a <= 1; + endtask +endinterface + +class driver; + virtual intf vif; + + function new(virtual intf vif); + this.vif = vif; + endfunction + + task go(); + vif.go(); + endtask +endclass + +module t; + wire a; + wire b; + virtual intf vif; + + intf i(a, b); + + initial begin + driver d; + vif = t.i; + t.i.m = new; + d = new(t.i); + d.go(); + end + + always @(posedge a) begin + vif.b <= 1; + end + + always @(*) begin + if (a && b) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule From 995534d3ed31cf1bf0f48cf22789458b58f1d286 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 24 Jun 2026 13:44:49 +0200 Subject: [PATCH 04/41] Fix insertion of expression coverage statement (#7832) Signed-off-by: Ryszard Rozak --- src/V3Coverage.cpp | 4 ++++ test_regress/t/t_cover_expr_fork.py | 18 ++++++++++++++++++ test_regress/t/t_cover_expr_fork.v | 28 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100755 test_regress/t/t_cover_expr_fork.py create mode 100644 test_regress/t/t_cover_expr_fork.v diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 12bb973dc..afd932909 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -375,6 +375,8 @@ class CoverageVisitor final : public VNVisitor { } else { itemp->addElsesp(stmtp); } + } else if (AstBegin* const itemp = VN_CAST(nodep, Begin)) { + itemp->addStmtsp(stmtp); } else { nodep->v3fatalSrc("Bad node type"); } @@ -776,6 +778,8 @@ class CoverageVisitor final : public VNVisitor { // covers the code in that line.) VL_RESTORER(m_beginHier); VL_RESTORER(m_inToggleOff); + VL_RESTORER(m_exprStmtsp); + m_exprStmtsp = nodep; m_inToggleOff = true; if (nodep->name() != "") { m_beginHier = m_beginHier + (m_beginHier != "" ? "__DOT__" : "") + nodep->name(); diff --git a/test_regress/t/t_cover_expr_fork.py b/test_regress/t/t_cover_expr_fork.py new file mode 100755 index 000000000..8b6c049d2 --- /dev/null +++ b/test_regress/t/t_cover_expr_fork.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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--coverage-expr --binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_cover_expr_fork.v b/test_regress/t/t_cover_expr_fork.v new file mode 100644 index 000000000..fd2474bad --- /dev/null +++ b/test_regress/t/t_cover_expr_fork.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +module t; + int cnt = 0; + task automatic myTask; + fork + begin + bit x; + if (!x) begin + cnt++; + end + end + join_none + endtask + + initial begin + myTask(); + #1; + if (cnt != 1) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From d456384d39a6f8a44fe995015389219998f9e456 Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Wed, 24 Jun 2026 04:47:36 -0700 Subject: [PATCH 05/41] Support hierarchical reference cross members (#7749) (#7820) --- src/V3AstNodeOther.h | 11 +++++++- src/V3Covergroup.cpp | 14 ++++++++++- src/verilog.y | 25 +++++++++++++++++-- test_regress/t/t_covergroup_cross.out | 8 +++--- test_regress/t/t_covergroup_cross.v | 9 +++++++ .../t/t_covergroup_cross_opt_unsup.out | 9 +++++++ test_regress/t/t_covergroup_cross_opt_unsup.v | 6 +++++ .../t/t_vlcov_covergroup.annotate.out | 22 +++++++++++++--- 8 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 19f4d07ee..666a3ec22 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1185,12 +1185,21 @@ public: }; class AstCoverpointRef final : public AstNode { // Reference to a coverpoint used in a cross - const string m_name; // coverpoint name + // @astgen op1 := exprp : Optional[AstNodeExpr] // Non-standard: hierarchical/dotted + // // reference (implicit coverpoint), e.g. + // // 'cross a.b'; nullptr for a plain coverpoint + // // name. An AstDot at parse time, resolved + // // by the time V3Covergroup runs. + const string m_name; // coverpoint name; empty when exprp() carries a hierarchical reference public: AstCoverpointRef(FileLine* fl, const string& name) : ASTGEN_SUPER_CoverpointRef(fl) , m_name{name} {} + AstCoverpointRef(FileLine* fl, AstNodeExpr* exprp) + : ASTGEN_SUPER_CoverpointRef(fl) { + this->exprp(exprp); + } ASTGEN_MEMBERS_AstCoverpointRef; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; diff --git a/src/V3Covergroup.cpp b/src/V3Covergroup.cpp index 054f27c34..b1890e001 100644 --- a/src/V3Covergroup.cpp +++ b/src/V3Covergroup.cpp @@ -98,7 +98,7 @@ class FunctionalCoverageVisitor final : public VNVisitor { for (AstCoverCross* crossp : m_coverCrosses) { for (AstNode* itemp = crossp->itemsp(); itemp; itemp = itemp->nextp()) { if (const AstCoverpointRef* const refp = VN_CAST(itemp, CoverpointRef)) - m_crossedCpNames.insert(refp->name()); + if (!refp->exprp()) m_crossedCpNames.insert(refp->name()); } } @@ -1295,6 +1295,18 @@ class FunctionalCoverageVisitor final : public VNVisitor { while (itemp) { AstNode* const nextp = itemp->nextp(); AstCoverpointRef* const refp = VN_AS(itemp, CoverpointRef); + if (refp->exprp()) { + // Non-standard hierarchical/dotted cross item (e.g. 'cross a.b'): an implicit + // coverpoint over the referenced expression (carried in refp->exprp()). The + // grammar already warned NONSTD; implicit coverpoints are not yet implemented, so + // generate no sampling code for this cross. When support is added the implicit + // coverpoint should be synthesized upstream (V3LinkParse) as a real AstCoverpoint + // so it flows through the normal coverpoint path - by here coverpoint lowering has + // already run. + refp->v3warn(COVERIGN, + "Unsupported: cross of hierarchical reference (implicit coverpoint)"); + return; + } // Find the referenced coverpoint via name map (O(log n) vs O(n) linear scan) const auto it = m_coverpointMap.find(refp->name()); AstCoverpoint* const foundCpp = (it != m_coverpointMap.end()) ? it->second : nullptr; diff --git a/src/verilog.y b/src/verilog.y index 6d6efea6b..db145ea8e 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7339,8 +7339,29 @@ cross_itemList: // IEEE: part of list_of_cross_items ; cross_item: // ==IEEE: cross_item - id/*cover_point_identifier*/ - { $$ = new AstCoverpointRef{$1, *$1}; } + // // IEEE: cover_point_identifier | variable_identifier - both are a + // // simple identifier. We parse idDotted (a plain hierarchical + // // reference a.b.c, with no bit/array selects) to also accept the + // // non-standard dotted form (e.g. 'cross a.b') that several + // // simulators support; the common simple-identifier case is detected + // // and handled exactly as before. + idDotted + { + if (AstParseRef* const refp = VN_CAST($1, ParseRef)) { + // Standard: simple cover_point_identifier / variable_identifier + $$ = new AstCoverpointRef{refp->fileline(), refp->name()}; + VL_DO_DANGLING(refp->deleteTree(), refp); + } else { + // Verilator extension beyond strict IEEE (cross_item is a simple + // identifier): some tools accept a hierarchical/dotted reference. + // Carry the reference expression (still an AstDot here) out of the + // parser unchanged; later stages resolve and, eventually, implement + // it as an implicit coverpoint. + $1->v3warn(NONSTD, "Non-standard hierarchical reference as a coverage " + "cross item (an implicit coverpoint)"); + $$ = new AstCoverpointRef{$1->fileline(), $1}; + } + } ; cross_body: // ==IEEE: cross_body diff --git a/test_regress/t/t_covergroup_cross.out b/test_regress/t/t_covergroup_cross.out index a7311da80..abb035e5e 100644 --- a/test_regress/t/t_covergroup_cross.out +++ b/test_regress/t/t_covergroup_cross.out @@ -109,10 +109,10 @@ cg_range.cp_addr.hi_range: 2 cg_range.cp_addr.lo_range: 2 cg_range.cp_cmd.read: 2 cg_range.cp_cmd.write: 2 -cg_unnamed_cross.__cross7.a0_x_read [cross]: 1 -cg_unnamed_cross.__cross7.a0_x_write [cross]: 0 -cg_unnamed_cross.__cross7.a1_x_read [cross]: 0 -cg_unnamed_cross.__cross7.a1_x_write [cross]: 1 +cg_unnamed_cross.__cross8.a0_x_read [cross]: 1 +cg_unnamed_cross.__cross8.a0_x_write [cross]: 0 +cg_unnamed_cross.__cross8.a1_x_read [cross]: 0 +cg_unnamed_cross.__cross8.a1_x_write [cross]: 1 cg_unnamed_cross.cp_a.a0: 1 cg_unnamed_cross.cp_a.a1: 1 cg_unnamed_cross.cp_c.read: 1 diff --git a/test_regress/t/t_covergroup_cross.v b/test_regress/t/t_covergroup_cross.v index e86f179a4..f0857bc01 100644 --- a/test_regress/t/t_covergroup_cross.v +++ b/test_regress/t/t_covergroup_cross.v @@ -17,6 +17,9 @@ module t; logic mode; logic parity; + typedef struct packed {logic m_p; logic h_mode;} cfg_t; + cfg_t s_cfg = '0; + // 2-way cross covergroup cg2; cp_addr: coverpoint addr {bins addr0 = {0}; bins addr1 = {1};} @@ -90,6 +93,12 @@ module t; addr_cmd_unsup: cross cp_addr, cp_cmd{ option.per_instance = 1; // unsupported for cross - expect COVERIGN warning } + // Non-standard hierarchical reference as a cross item (an implicit coverpoint): + // accepted with NONSTD, but implicit coverpoints are unsupported so the whole + // cross is dropped (COVERIGN, suppressed here) - it contributes no bins. + /* verilator lint_off NONSTD */ + cross_hier: cross cp_addr, s_cfg.m_p; + /* verilator lint_on NONSTD */ endgroup // Covergroup with an unnamed cross - the cross is reported under the default name "cross" diff --git a/test_regress/t/t_covergroup_cross_opt_unsup.out b/test_regress/t/t_covergroup_cross_opt_unsup.out index d07db1513..e1e2c5621 100644 --- a/test_regress/t/t_covergroup_cross_opt_unsup.out +++ b/test_regress/t/t_covergroup_cross_opt_unsup.out @@ -1,3 +1,8 @@ +%Warning-NONSTD: t/t_covergroup_cross_opt_unsup.v:19:34: Non-standard hierarchical reference as a coverage cross item (an implicit coverpoint) + 19 | cross_hier: cross cp_a, s_cfg.m_p; + | ^ + ... For warning description see https://verilator.org/warn/NONSTD?v=latest + ... Use "/* verilator lint_off NONSTD */" and lint_on around source to disable this message. %Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:13:7: Ignoring unsupported coverage cross option: 'per_instance' 13 | option.per_instance = 1; | ^~~~~~ @@ -7,4 +12,8 @@ : ... note: In instance 't' 15 | cross_implicit: cross cp_a, var_x; | ^~~~~ +%Warning-COVERIGN: t/t_covergroup_cross_opt_unsup.v:19:34: Unsupported: cross of hierarchical reference (implicit coverpoint) + : ... note: In instance 't' + 19 | cross_hier: cross cp_a, s_cfg.m_p; + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_cross_opt_unsup.v b/test_regress/t/t_covergroup_cross_opt_unsup.v index bb63a86be..720fbad6c 100644 --- a/test_regress/t/t_covergroup_cross_opt_unsup.v +++ b/test_regress/t/t_covergroup_cross_opt_unsup.v @@ -13,7 +13,13 @@ module t; option.per_instance = 1; // unsupported for cross; triggers COVERIGN } cross_implicit: cross cp_a, var_x; + // Non-standard hierarchical/dotted cross item: can only be a data reference + // (implicit coverpoint), never a coverpoint. Accepted with a NONSTD warning; + // implicit coverpoints are unsupported so the cross is dropped (COVERIGN). + cross_hier: cross cp_a, s_cfg.m_p; endgroup + typedef struct packed {logic m_p; logic h_mode;} cfg_t; + cfg_t s_cfg = '0; logic var_x = 1'b0; cg cg_i = new; initial begin diff --git a/test_regress/t/t_vlcov_covergroup.annotate.out b/test_regress/t/t_vlcov_covergroup.annotate.out index b8616ad2d..16b86a66e 100644 --- a/test_regress/t/t_vlcov_covergroup.annotate.out +++ b/test_regress/t/t_vlcov_covergroup.annotate.out @@ -28,6 +28,14 @@ -000001 point: type=toggle comment=parity:0->1 hier=top.t -000000 point: type=toggle comment=parity:1->0 hier=top.t + typedef struct packed {logic m_p; logic h_mode;} cfg_t; +%000001 cfg_t s_cfg = '0; +-000001 point: type=line comment=block hier=top.t +-000000 point: type=toggle comment=s_cfg.h_mode:0->1 hier=top.t +-000000 point: type=toggle comment=s_cfg.h_mode:1->0 hier=top.t +-000000 point: type=toggle comment=s_cfg.m_p:0->1 hier=top.t +-000000 point: type=toggle comment=s_cfg.m_p:1->0 hier=top.t + // 2-way cross covergroup cg2; %000002 cp_addr: coverpoint addr {bins addr0 = {0}; bins addr1 = {1};} @@ -257,6 +265,12 @@ // cross: [addr1, write] option.per_instance = 1; // unsupported for cross - expect COVERIGN warning } + // Non-standard hierarchical reference as a cross item (an implicit coverpoint): + // accepted with NONSTD, but implicit coverpoints are unsupported so the whole + // cross is dropped (COVERIGN, suppressed here) - it contributes no bins. + /* verilator lint_off NONSTD */ + cross_hier: cross cp_addr, s_cfg.m_p; + /* verilator lint_on NONSTD */ endgroup // Covergroup with an unnamed cross - the cross is reported under the default name "cross" @@ -268,13 +282,13 @@ -000001 point: type=covergroup comment= hier=cg_unnamed_cross.cp_c.read -000001 point: type=covergroup comment= hier=cg_unnamed_cross.cp_c.write %000001 cross cp_a, cp_c; // no label: reported under the default cross name --000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a0_x_read +-000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a0_x_read // cross: [a0, read] --000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a0_x_write +-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a0_x_write // cross: [a0, write] --000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a1_x_read +-000000 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a1_x_read // cross: [a1, read] --000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross7.a1_x_write +-000001 point: type=covergroup comment= hier=cg_unnamed_cross.__cross8.a1_x_write // cross: [a1, write] endgroup From 9462c2a9109a5e363af3888688bf1a8ce0fd91a4 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Wed, 24 Jun 2026 15:44:47 +0200 Subject: [PATCH 06/41] Fix unclocked concurrent assertion misreported as unsupported (#7831) --- src/V3AssertPre.cpp | 5 +- .../t/t_assert_seq_clocking_unsup.out | 16 +++-- test_regress/t/t_assert_unclocked_bad.out | 22 +++++++ ...xpr_unsup.py => t_assert_unclocked_bad.py} | 6 +- test_regress/t/t_assert_unclocked_bad.v | 12 ++++ test_regress/t/t_property_pexpr_unsup.out | 26 -------- .../t/t_property_s_eventually_unsup.out | 10 +-- .../t/t_property_s_eventually_unsup.v | 2 +- .../t/t_sequence_first_match_unsup.out | 47 -------------- .../t/t_sequence_first_match_unsup.py | 18 ------ test_regress/t/t_sequence_first_match_unsup.v | 62 ------------------- 11 files changed, 54 insertions(+), 172 deletions(-) create mode 100644 test_regress/t/t_assert_unclocked_bad.out rename test_regress/t/{t_property_pexpr_unsup.py => t_assert_unclocked_bad.py} (67%) create mode 100644 test_regress/t/t_assert_unclocked_bad.v delete mode 100644 test_regress/t/t_property_pexpr_unsup.out delete mode 100644 test_regress/t/t_sequence_first_match_unsup.out delete mode 100755 test_regress/t/t_sequence_first_match_unsup.py delete mode 100644 test_regress/t/t_sequence_first_match_unsup.v diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 22f4112b7..f45df83f3 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -93,7 +93,10 @@ private: fromAlways = true; } if (!senip) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion"); + nodep->v3error("Concurrent assertion has no clock (IEEE 1800-2023 16.16)\n" + << nodep->warnMore() + << "... Suggest provide a clocking event, a default" + " clocking, or a clocked procedural context"); newp = new AstSenTree{nodep->fileline(), nullptr}; } else { if (cassertp && fromAlways) cassertp->senFromAlways(true); diff --git a/test_regress/t/t_assert_seq_clocking_unsup.out b/test_regress/t/t_assert_seq_clocking_unsup.out index 5ab9bb741..ba12d4ad2 100644 --- a/test_regress/t/t_assert_seq_clocking_unsup.out +++ b/test_regress/t/t_assert_seq_clocking_unsup.out @@ -15,16 +15,20 @@ : ... note: In instance 't' 27 | @clk a | ^ -%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.v:32:3: Unsupported: Unclocked assertion - : ... note: In instance 't' +%Error: t/t_assert_seq_clocking_unsup.v:32:3: 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 32 | assert property (s_nest ##1 a); | ^~~~~~ -%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.v:33:3: Unsupported: Unclocked assertion - : ... note: In instance 't' + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_assert_seq_clocking_unsup.v:33:3: 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 33 | assert property (s_level); | ^~~~~~ -%Error-UNSUPPORTED: t/t_assert_seq_clocking_unsup.v:34:3: Unsupported: Unclocked assertion - : ... note: In instance 't' +%Error: t/t_assert_seq_clocking_unsup.v:34:3: 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 34 | assert property (s_level2); | ^~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_assert_unclocked_bad.out b/test_regress/t/t_assert_unclocked_bad.out new file mode 100644 index 000000000..60590a943 --- /dev/null +++ b/test_regress/t/t_assert_unclocked_bad.out @@ -0,0 +1,22 @@ +%Error: t/t_assert_unclocked_bad.v:9:3: 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 + 9 | assert property (a); + | ^~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_assert_unclocked_bad.v:10:22: 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 + 10 | assert property (a |=> b); + | ^~~ +%Error: t/t_assert_unclocked_bad.v:10:3: 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 + 10 | assert property (a |=> b); + | ^~~~~~ +%Error: t/t_assert_unclocked_bad.v:11:3: 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 + 11 | cover property (a); + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_property_pexpr_unsup.py b/test_regress/t/t_assert_unclocked_bad.py similarity index 67% rename from test_regress/t/t_property_pexpr_unsup.py rename to test_regress/t/t_assert_unclocked_bad.py index 9768aebfc..77a0ac64b 100755 --- a/test_regress/t/t_property_pexpr_unsup.py +++ b/test_regress/t/t_assert_unclocked_bad.py @@ -4,15 +4,13 @@ # 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: 2024 Wilson Snyder +# SPDX-FileCopyrightText: 2026 Wilson Snyder # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap test.scenarios('vlt') -test.compile(expect_filename=test.golden_filename, - verilator_flags2=['--timing', '--error-limit 1000'], - fails=test.vlt_all) +test.lint(expect_filename=test.golden_filename, fails=True) test.passes() diff --git a/test_regress/t/t_assert_unclocked_bad.v b/test_regress/t/t_assert_unclocked_bad.v new file mode 100644 index 000000000..ae659cf7b --- /dev/null +++ b/test_regress/t/t_assert_unclocked_bad.v @@ -0,0 +1,12 @@ +// 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; + logic a, b; + assert property (a); + assert property (a |=> b); + cover property (a); +endmodule diff --git a/test_regress/t/t_property_pexpr_unsup.out b/test_regress/t/t_property_pexpr_unsup.out deleted file mode 100644 index bae43d802..000000000 --- a/test_regress/t/t_property_pexpr_unsup.out +++ /dev/null @@ -1,26 +0,0 @@ -%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:115:21: Unsupported: Unclocked assertion - : ... note: In instance 't' - 115 | assert property ((s_eventually a) implies (s_eventually a)); - | ^~~~~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:115:46: Unsupported: Unclocked assertion - : ... note: In instance 't' - 115 | assert property ((s_eventually a) implies (s_eventually a)); - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:115:3: Unsupported: Unclocked assertion - : ... note: In instance 't' - 115 | assert property ((s_eventually a) implies (s_eventually a)); - | ^~~~~~ -%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:21: Unsupported: Unclocked assertion - : ... note: In instance 't' - 117 | assert property ((s_eventually a) iff (s_eventually a)); - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:42: Unsupported: Unclocked assertion - : ... note: In instance 't' - 117 | assert property ((s_eventually a) iff (s_eventually a)); - | ^~~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_property_pexpr_unsup.v:117:3: Unsupported: Unclocked assertion - : ... note: In instance 't' - 117 | assert property ((s_eventually a) iff (s_eventually a)); - | ^~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_property_s_eventually_unsup.out b/test_regress/t/t_property_s_eventually_unsup.out index 6a9be1c3a..e4355f13f 100644 --- a/test_regress/t/t_property_s_eventually_unsup.out +++ b/test_regress/t/t_property_s_eventually_unsup.out @@ -1,12 +1,8 @@ -%Error-UNSUPPORTED: t/t_property_s_eventually_unsup.v:14:20: Unsupported: Unclocked assertion +%Error-UNSUPPORTED: t/t_property_s_eventually_unsup.v:14:35: Unsupported: cycle delay in s_eventually : ... note: In instance 't' - 14 | assert property (s_eventually ##1 1); - | ^~~~~~~~~~~~ + 14 | assert property (@(posedge clk) s_eventually ##1 1); + | ^~~~~~~~~~~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_property_s_eventually_unsup.v:14:3: Unsupported: Unclocked assertion - : ... note: In instance 't' - 14 | assert property (s_eventually ##1 1); - | ^~~~~~ %Error-UNSUPPORTED: t/t_property_s_eventually_unsup.v:15:35: Unsupported: cycle delay in s_eventually : ... note: In instance 't' 15 | assert property (@(negedge clk) s_eventually ##1 1); diff --git a/test_regress/t/t_property_s_eventually_unsup.v b/test_regress/t/t_property_s_eventually_unsup.v index 9338a0f4d..02a22a127 100644 --- a/test_regress/t/t_property_s_eventually_unsup.v +++ b/test_regress/t/t_property_s_eventually_unsup.v @@ -11,7 +11,7 @@ module t; localparam MAX = 3; integer cyc = 1; - assert property (s_eventually ##1 1); + assert property (@(posedge clk) s_eventually ##1 1); assert property (@(negedge clk) s_eventually ##1 1); always @(posedge clk) begin diff --git a/test_regress/t/t_sequence_first_match_unsup.out b/test_regress/t/t_sequence_first_match_unsup.out deleted file mode 100644 index e4e3a5d37..000000000 --- a/test_regress/t/t_sequence_first_match_unsup.out +++ /dev/null @@ -1,47 +0,0 @@ -%Error: t/t_sequence_first_match_unsup.v:51:34: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 'main' - 51 | initial p0 : assert property ((##1 1) or(##2 1) |-> x == 1); - | ^~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_sequence_first_match_unsup.v:51:44: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 'main' - 51 | initial p0 : assert property ((##1 1) or(##2 1) |-> x == 1); - | ^~ -%Error-UNSUPPORTED: t/t_sequence_first_match_unsup.v:51:16: Unsupported: Unclocked assertion - : ... note: In instance 'main' - 51 | initial p0 : assert property ((##1 1) or(##2 1) |-> x == 1); - | ^~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_sequence_first_match_unsup.v:54:47: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 'main' - 54 | initial p1 : assert property (first_match ((##1 1) or(##2 1)) |-> x == 1); - | ^~ -%Error: t/t_sequence_first_match_unsup.v:54:57: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 'main' - 54 | initial p1 : assert property (first_match ((##1 1) or(##2 1)) |-> x == 1); - | ^~ -%Error-UNSUPPORTED: t/t_sequence_first_match_unsup.v:54:16: Unsupported: Unclocked assertion - : ... note: In instance 'main' - 54 | initial p1 : assert property (first_match ((##1 1) or(##2 1)) |-> x == 1); - | ^~~~~~ -%Error: t/t_sequence_first_match_unsup.v:57:38: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 'main' - 57 | initial p2 : assert property (1 or ##1 1 |-> x == 0); - | ^~ -%Error-UNSUPPORTED: t/t_sequence_first_match_unsup.v:57:16: Unsupported: Unclocked assertion - : ... note: In instance 'main' - 57 | initial p2 : assert property (1 or ##1 1 |-> x == 0); - | ^~~~~~ -%Error: t/t_sequence_first_match_unsup.v:60:51: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 'main' - 60 | initial p3 : assert property (first_match (1 or ##1 1) |-> x == 0); - | ^~ -%Error-UNSUPPORTED: t/t_sequence_first_match_unsup.v:60:16: Unsupported: Unclocked assertion - : ... note: In instance 'main' - 60 | initial p3 : assert property (first_match (1 or ##1 1) |-> x == 0); - | ^~~~~~ -%Error: Internal Error: t/t_sequence_first_match_unsup.v:51:34: ../V3Ast.cpp:#: AstSExpr must have non-nullptr delayp() - : ... note: In instance 'main' - 51 | initial p0 : assert property ((##1 1) or(##2 1) |-> x == 1); - | ^~ - ... This fatal error may be caused by the earlier error(s); resolve those first. diff --git a/test_regress/t/t_sequence_first_match_unsup.py b/test_regress/t/t_sequence_first_match_unsup.py deleted file mode 100755 index 235ad76f1..000000000 --- a/test_regress/t/t_sequence_first_match_unsup.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/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: 2025 Wilson Snyder -# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 - -import vltest_bootstrap - -test.scenarios('simulator') - -test.lint(expect_filename=test.golden_filename, - verilator_flags2=['--assert --error-limit 1000'], - fails=True) - -test.passes() diff --git a/test_regress/t/t_sequence_first_match_unsup.v b/test_regress/t/t_sequence_first_match_unsup.v deleted file mode 100644 index 5f4d08838..000000000 --- a/test_regress/t/t_sequence_first_match_unsup.v +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-FileCopyrightText: 2001-2020 Daniel Kroening, Edmund Clarke -// SPDX-License-Identifier: BSD-3-Clause -// -// (C) 2001-2020, Daniel Kroening, Edmund Clarke, -// Computer Science Department, University of Oxford -// Computer Science Department, Carnegie Mellon University -// -// All rights reserved. Redistribution and use in source and binary forms, with -// or without modification, are permitted provided that the following -// conditions are met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the University nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. -// -// You can contact the author at: -// - homepage : https://www.cprover.org/ebmc/ -// - source repository : https://github.com/diffblue/hw-cbmc - -module main ( - input clk -); - - reg [31:0] x = 0; - - always @(posedge clk) x <= x + 1; - - // Starting from a particular state, - // first_match yields the sequence that _ends_ first. - - // fails - initial p0 : assert property ((##1 1) or(##2 1) |-> x == 1); - - // passes - initial p1 : assert property (first_match ((##1 1) or(##2 1)) |-> x == 1); - - // fails - initial p2 : assert property (1 or ##1 1 |-> x == 0); - - // passes - initial p3 : assert property (first_match (1 or ##1 1) |-> x == 0); - -endmodule From def1e2ccbcfc352e37a4245b21086da5cce0e969 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Wed, 24 Jun 2026 15:59:32 +0200 Subject: [PATCH 07/41] Fix lifetime of expression coverage variable (#7834) Signed-off-by: Ryszard Rozak --- src/V3Coverage.cpp | 1 + test_regress/t/t_cover_expr_fork.v | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index afd932909..9c19319d8 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -824,6 +824,7 @@ class CoverageVisitor final : public VNVisitor { if (pair.second) { varp = new AstVar{fl, VVarType::MODULETEMP, m_exprTempNames.get(frefp), dtypep}; + varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); pair.first->second = varp; if (m_ftaskp) { varp->funcLocal(true); diff --git a/test_regress/t/t_cover_expr_fork.v b/test_regress/t/t_cover_expr_fork.v index fd2474bad..c111e5947 100644 --- a/test_regress/t/t_cover_expr_fork.v +++ b/test_regress/t/t_cover_expr_fork.v @@ -13,6 +13,9 @@ module t; if (!x) begin cnt++; end + if (!$onehot(x)) begin + cnt++; + end end join_none endtask @@ -20,7 +23,7 @@ module t; initial begin myTask(); #1; - if (cnt != 1) $stop; + if (cnt != 2) $stop; $write("*-* All Finished *-*\n"); $finish; From 249608a42f8c8134509cb08edf77743e8066ec5e Mon Sep 17 00:00:00 2001 From: Wolfgang Mayerwieser Date: Thu, 25 Jun 2026 04:43:24 +0200 Subject: [PATCH 08/41] Fix performance on large package-scoped structs (#7830) --- docs/CONTRIBUTORS | 1 + src/V3CUse.cpp | 1 + test_regress/t/t_cuse_struct_pkg.py | 18 +++++++ test_regress/t/t_cuse_struct_pkg.v | 76 +++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 test_regress/t/t_cuse_struct_pkg.py create mode 100644 test_regress/t/t_cuse_struct_pkg.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index a593324c8..10db04fcc 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -302,6 +302,7 @@ Vito Gamberini Wei-Lun Chiu William D. Jones Wilson Snyder +Wolfgang Mayerwieser Xi Zhang Yan Xu Yangyu Chen diff --git a/src/V3CUse.cpp b/src/V3CUse.cpp index 2fddf7f82..51d6fd688 100644 --- a/src/V3CUse.cpp +++ b/src/V3CUse.cpp @@ -65,6 +65,7 @@ class CUseVisitor final : public VNVisitorConst { iterateConst(nodep->lhsp()->dtypep()); } void visit(AstNodeDType* nodep) override { + if (nodep->user1SetOnce()) return; // Process once if (nodep->virtRefDTypep()) iterateConst(nodep->virtRefDTypep()); if (nodep->virtRefDType2p()) iterateConst(nodep->virtRefDType2p()); diff --git a/test_regress/t/t_cuse_struct_pkg.py b/test_regress/t/t_cuse_struct_pkg.py new file mode 100644 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_cuse_struct_pkg.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_cuse_struct_pkg.v b/test_regress/t/t_cuse_struct_pkg.v new file mode 100644 index 000000000..197bbe62a --- /dev/null +++ b/test_regress/t/t_cuse_struct_pkg.v @@ -0,0 +1,76 @@ +// 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 + +package pkg; + typedef struct { + logic [7:0] value; + } field_t; + typedef struct { + field_t f0; + field_t f1; + } reg_t; + typedef struct { + reg_t R0; + reg_t R1; + reg_t R2; + } hwif_t; +endpackage + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + import pkg::*; + + hwif_t hwif_in; + hwif_t hwif_out; + hwif_t storage; + + integer cyc = 0; + + always_ff @(posedge clk) begin + storage.R0.f0.value <= hwif_in.R0.f0.value; + storage.R0.f1.value <= hwif_in.R0.f1.value; + storage.R1.f0.value <= hwif_in.R1.f0.value; + storage.R1.f1.value <= hwif_in.R1.f1.value; + storage.R2.f0.value <= hwif_in.R2.f0.value; + storage.R2.f1.value <= hwif_in.R2.f1.value; + end + + always_comb begin + hwif_out.R0.f0.value = storage.R0.f0.value; + hwif_out.R0.f1.value = storage.R0.f1.value; + hwif_out.R1.f0.value = storage.R1.f0.value; + hwif_out.R1.f1.value = storage.R1.f1.value; + hwif_out.R2.f0.value = storage.R2.f0.value; + hwif_out.R2.f1.value = storage.R2.f1.value; + end + + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 0) begin + hwif_in.R0.f0.value <= 8'h11; + hwif_in.R0.f1.value <= 8'h22; + hwif_in.R1.f0.value <= 8'h33; + hwif_in.R1.f1.value <= 8'h44; + hwif_in.R2.f0.value <= 8'h55; + hwif_in.R2.f1.value <= 8'h66; + end + else if (cyc == 3) begin + if (hwif_out.R0.f0.value !== 8'h11) $stop; + if (hwif_out.R0.f1.value !== 8'h22) $stop; + if (hwif_out.R1.f0.value !== 8'h33) $stop; + if (hwif_out.R1.f1.value !== 8'h44) $stop; + if (hwif_out.R2.f0.value !== 8'h55) $stop; + if (hwif_out.R2.f1.value !== 8'h66) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule From 0ebae43713406004dc6b4fd00cbda054eb4937b7 Mon Sep 17 00:00:00 2001 From: github action Date: Thu, 25 Jun 2026 02:44:28 +0000 Subject: [PATCH 09/41] Apply 'make format' [ci skip] --- test_regress/t/t_cuse_struct_pkg.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test_regress/t/t_cuse_struct_pkg.py diff --git a/test_regress/t/t_cuse_struct_pkg.py b/test_regress/t/t_cuse_struct_pkg.py old mode 100644 new mode 100755 From 000afcf52dcad0c9245487c73e45f181e8a396e0 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Thu, 25 Jun 2026 13:41:53 +0200 Subject: [PATCH 10/41] Support variable-length intersect in SVA sequences (#7835) --- src/V3AssertNfa.cpp | 276 +++++++++++++++++- test_regress/t/t_sequence_intersect.v | 4 + .../t/t_sequence_intersect_len_warn.out | 10 - .../t/t_sequence_intersect_len_warn.v | 22 -- ....py => t_sequence_intersect_nevermatch.py} | 8 +- .../t/t_sequence_intersect_nevermatch.v | 57 ++++ .../t/t_sequence_intersect_range_unsup.out | 32 +- .../t/t_sequence_intersect_range_unsup.v | 23 +- test_regress/t/t_sequence_intersect_varlen.py | 18 ++ test_regress/t/t_sequence_intersect_varlen.v | 71 +++++ 10 files changed, 457 insertions(+), 64 deletions(-) delete mode 100644 test_regress/t/t_sequence_intersect_len_warn.out delete mode 100644 test_regress/t/t_sequence_intersect_len_warn.v rename test_regress/t/{t_sequence_intersect_len_warn.py => t_sequence_intersect_nevermatch.py} (71%) create mode 100644 test_regress/t/t_sequence_intersect_nevermatch.v create mode 100755 test_regress/t/t_sequence_intersect_varlen.py create mode 100644 test_regress/t/t_sequence_intersect_varlen.v diff --git a/src/V3AssertNfa.cpp b/src/V3AssertNfa.cpp index b6ec6964f..7949f8563 100644 --- a/src/V3AssertNfa.cpp +++ b/src/V3AssertNfa.cpp @@ -384,6 +384,74 @@ class SvaNfaBuilder final { return 0; } + // Contiguous match-length range [lo,hi] for an operand whose length varies + // from AT MOST ONE ranged cycle delay; {-1,-1} otherwise. Drives the + // variable-length `intersect` lowering (IEEE 1800-2023 16.9.6): more than + // one ranged delay would make the per-length realization ambiguous, so it + // is reported unsupported rather than mis-paired. + static std::pair lengthRange(AstNodeExpr* nodep) { + if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) { + AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay); + if (!delayp || !delayp->isCycleDelay()) return {-1, -1}; + std::pair delayRange; + if (delayp->isRangeDelay()) { + if (delayp->isUnbounded()) return {-1, -1}; + const int minD = getConstInt(delayp->lhsp()); + const int maxD = getConstInt(delayp->rhsp()); + if (minD < 0 || maxD < 0 || maxD < minD) return {-1, -1}; + delayRange = {minD, maxD}; + } else { + const int d = getConstInt(delayp->lhsp()); + if (d < 0) return {-1, -1}; + delayRange = {d, d}; + } + std::pair preRange{0, 0}; + if (AstNodeExpr* const prep = sexprp->preExprp()) { + preRange = lengthRange(prep); + if (preRange.first < 0) return {-1, -1}; + } + const std::pair bodyRange = lengthRange(sexprp->exprp()); + if (bodyRange.first < 0) return {-1, -1}; + const int variableParts = (preRange.first != preRange.second) + + (delayRange.first != delayRange.second) + + (bodyRange.first != bodyRange.second); + if (variableParts > 1) return {-1, -1}; + return {preRange.first + delayRange.first + bodyRange.first, + preRange.second + delayRange.second + bodyRange.second}; + } + if (AstSThroughout* const throughp = VN_CAST(nodep, SThroughout)) { + return lengthRange(throughp->rhsp()); + } + if (nodep->isMultiCycleSva()) return {-1, -1}; + return {0, 0}; // plain boolean -- 0 cycles + } + + // Clone `operand` with its sole variable ranged cycle delay pinned so the + // total match length is exactly `len`. `lo` is the operand's minimum length + // (lengthRange().first). A fixed operand has no such delay and is returned + // as a plain clone (callers only request its single achievable length). + static AstNodeExpr* realizeAtLength(AstNodeExpr* operand, int len, int lo) { + AstNodeExpr* const clonep = operand->cloneTreePure(false); + AstDelay* rangeDelayp = nullptr; + clonep->foreach([&](AstDelay* dp) { + if (!rangeDelayp && dp->isRangeDelay() && !dp->isUnbounded() + && getConstInt(dp->lhsp()) != getConstInt(dp->rhsp())) { + rangeDelayp = dp; + } + }); + if (rangeDelayp) { + FileLine* const flp = rangeDelayp->fileline(); + const int pinned = getConstInt(rangeDelayp->lhsp()) + (len - lo); + AstNodeExpr* const oldMinp = rangeDelayp->lhsp(); + oldMinp->replaceWith(new AstConst{flp, static_cast(pinned)}); + VL_DO_DANGLING(oldMinp->deleteTree(), oldMinp); + // Drop the max bound so it lowers as a fixed `##d`, not `##[d:d]`. + AstNode* const oldMaxp = rangeDelayp->rhsp()->unlinkFrBack(); + VL_DO_DANGLING(oldMaxp->deleteTree(), oldMaxp); + } + return clonep; + } + // Cuts AST size from O(N * sizeof(exprp)) to O(N) + O(sizeof(exprp)) by // sharing a single `VarRef` across N check edges. Hoist also matches the // IEEE 1800-2023 16.9.9 "single preponed-region snapshot" semantic for @@ -1028,6 +1096,188 @@ class SvaNfaBuilder final { return result; } + // Collect the boolean leaf checks of a fixed-length sequence keyed by their + // clock offset from the start. Returns false for anything other than nested + // AstSExpr with fixed cycle delays over boolean leaves (e.g. throughout). + static bool flattenFixedSeq(AstNodeExpr* nodep, int baseOffset, + std::map>& out) { + if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) { + AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay); + if (!delayp || !delayp->isCycleDelay() || delayp->isUnbounded()) return false; + const int delayCycles = getConstInt(delayp->lhsp()); + if (delayCycles < 0) return false; + if (delayp->isRangeDelay() && getConstInt(delayp->rhsp()) != delayCycles) return false; + int preLen = 0; + if (AstNodeExpr* const prep = sexprp->preExprp()) { + if (!flattenFixedSeq(prep, baseOffset, out)) return false; + preLen = fixedLength(prep); + if (preLen < 0) return false; + } + return flattenFixedSeq(sexprp->exprp(), baseOffset + preLen + delayCycles, out); + } + if (nodep->isMultiCycleSva()) return false; + out[baseOffset].push_back(nodep); + return true; + } + + // Conjoin two equal-length fixed sequences into one: at each clock offset + // AND the boolean checks of both operands (IEEE 1800-2023 16.9.6 -- both + // operands match the same window). Returns null if either operand is not a + // plain fixed sequence of boolean leaves. + static AstNodeExpr* conjoinFixedSeqs(AstNodeExpr* lhsp, AstNodeExpr* rhsp, FileLine* flp) { + std::map> checks; + if (!flattenFixedSeq(lhsp, 0, checks) || !flattenFixedSeq(rhsp, 0, checks)) return nullptr; + if (checks.empty()) return nullptr; + AstNodeExpr* resultp = nullptr; + int prevOffset = 0; + for (const auto& offsetChecks : checks) { + const int offset = offsetChecks.first; + AstNodeExpr* condp = nullptr; + for (AstNodeExpr* const leafp : offsetChecks.second) { + AstNodeExpr* const clonep = leafp->cloneTreePure(false); + if (!condp) { + condp = clonep; + } else { + condp = new AstLogAnd{flp, condp, clonep}; + condp->dtypeSetBit(); + } + } + if (!resultp) { + if (offset > 0) { + AstDelay* const delayp = new AstDelay{ + flp, new AstConst{flp, static_cast(offset)}, /*isCycle=*/true}; + resultp + = new AstSExpr{flp, new AstConst{flp, AstConst::BitTrue{}}, delayp, condp}; + resultp->dtypeSetBit(); + } else { + resultp = condp; + } + } else { + AstDelay* const delayp = new AstDelay{ + flp, new AstConst{flp, static_cast(offset - prevOffset)}, + /*isCycle=*/true}; + resultp = new AstSExpr{flp, resultp, delayp, condp}; + resultp->dtypeSetBit(); + } + prevOffset = offset; + } + return resultp; + } + + // `seq` is a simple ranged sequence `start ##[m:n] end` (start/end boolean, + // start may be absent). Used to collapse a both-variable intersect to one + // ranged delay. + struct SimpleRanged final { + bool ok = false; + AstNodeExpr* startp = nullptr; // may be null (absent start) + AstNodeExpr* endp = nullptr; + }; + static SimpleRanged asSimpleRanged(AstNodeExpr* nodep) { + AstSExpr* const sexprp = VN_CAST(nodep, SExpr); + if (!sexprp) return {}; + AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay); + if (!delayp || !delayp->isCycleDelay() || !delayp->isRangeDelay() || delayp->isUnbounded()) + return {}; + if (getConstInt(delayp->lhsp()) == getConstInt(delayp->rhsp())) return {}; + AstNodeExpr* const prep = sexprp->preExprp(); + if (prep && fixedLength(prep) != 0) return {}; + if (fixedLength(sexprp->exprp()) != 0) return {}; + return {true, prep, sexprp->exprp()}; + } + + // Build the NFA for a synthesized intersect lowering tree, then free it. + // buildExpr returns the terminal condition (finalCondp) by reference into the + // tree; detach a clone so the tree can be freed here. The graph already holds + // clones/hoists of every edge condition, so nothing else dangles. + BuildResult buildFromLoweringTree(AstNodeExpr* treep, SvaStateVertex* entryVtxp, + bool isTopLevelStep) { + BuildResult result = buildExpr(treep, entryVtxp, isTopLevelStep); + if (result.valid() && result.finalCondp) { + result.finalCondp = result.finalCondp->cloneTreePure(false); + } + VL_DO_DANGLING(treep->deleteTree(), treep); + return result; + } + + // Empty common-length intersection -- unequal fixed lengths, or disjoint + // ranged lengths. IEEE 1800-2023 16.9.6 requires both operands to match + // over a window of the same length, so with no common length the intersect + // simply never matches. This is legal (matching nothing), not an error, so + // lower to a constant false rather than rejecting legal code. + BuildResult buildNeverMatchIntersect(AstNodeExpr* nodep, SvaStateVertex* entryVtxp, + bool isTopLevelStep) { + AstNodeExpr* const falsep = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; + return buildFromLoweringTree(falsep, entryVtxp, isTopLevelStep); + } + + // Lower `seq1 intersect seq2` when an operand's match length varies + // (IEEE 1800-2023 16.9.6: both match over one window, equal start and end). + // The common length range is [lo,hi] = intersection of the two operands' + // achievable lengths. The equal-length combiner is avoided -- it mis-handles + // operands with an internal boolean check -- by lowering to plain sequences: + // - lo == hi (one shared length, e.g. one fixed + one ranged operand): + // pin each operand to that length and conjoin them cycle-by-cycle into a + // single fixed sequence. + // - lo < hi with simple `bool ##[m:n] bool` operands: collapse to one + // ranged delay `(start1 & start2) ##[lo:hi] (end1 & end2)`. (An OR of + // per-length branches cannot reject correctly -- a single missed length + // would fail the whole intersect, cf. Lesson 48.) + // - otherwise unsupported (clean error, not the legacy fall-through crash). + BuildResult buildVarLenIntersect(AstSIntersect* nodep, SvaStateVertex* entryVtxp, + bool isTopLevelStep) { + const std::pair lhsRange = lengthRange(nodep->lhsp()); + const std::pair rhsRange = lengthRange(nodep->rhsp()); + if (lhsRange.first < 0 || rhsRange.first < 0) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: intersect with this variable-length operand"); + return BuildResult::failWithError(); + } + const int lo = std::max(lhsRange.first, rhsRange.first); + const int hi = std::min(lhsRange.second, rhsRange.second); + if (lo > hi) { + // Disjoint length ranges share no common length -> never matches. + return buildNeverMatchIntersect(nodep, entryVtxp, isTopLevelStep); + } + FileLine* const flp = nodep->fileline(); + if (lo == hi) { + AstNodeExpr* const lp = realizeAtLength(nodep->lhsp(), lo, lhsRange.first); + AstNodeExpr* const rp = realizeAtLength(nodep->rhsp(), lo, rhsRange.first); + AstNodeExpr* const conjp = conjoinFixedSeqs(lp, rp, flp); + VL_DO_DANGLING(lp->deleteTree(), lp); + VL_DO_DANGLING(rp->deleteTree(), rp); + if (!conjp) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: intersect operand is not a plain boolean sequence"); + return BuildResult::failWithError(); + } + return buildFromLoweringTree(conjp, entryVtxp, isTopLevelStep); + } + const SimpleRanged sl = asSimpleRanged(nodep->lhsp()); + const SimpleRanged sr = asSimpleRanged(nodep->rhsp()); + if (!sl.ok || !sr.ok) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: intersect of two sequences that each vary in length over a" + " range with internal structure"); + return BuildResult::failWithError(); + } + const auto andBool = [&](AstNodeExpr* ap, AstNodeExpr* bp) -> AstNodeExpr* { + AstNodeExpr* const aClonep + = ap ? ap->cloneTreePure(false) : new AstConst{flp, AstConst::BitTrue{}}; + AstNodeExpr* const bClonep + = bp ? bp->cloneTreePure(false) : new AstConst{flp, AstConst::BitTrue{}}; + AstLogAnd* const andp = new AstLogAnd{flp, aClonep, bClonep}; + andp->dtypeSetBit(); + return andp; + }; + AstDelay* const delayp = new AstDelay{flp, new AstConst{flp, static_cast(lo)}, + /*isCycle=*/true}; + delayp->rhsp(new AstConst{flp, static_cast(hi)}); + AstSExpr* const reducedp + = new AstSExpr{flp, andBool(sl.startp, sr.startp), delayp, andBool(sl.endp, sr.endp)}; + reducedp->dtypeSetBit(); + return buildFromLoweringTree(reducedp, entryVtxp, isTopLevelStep); + } + BuildResult buildThroughout(AstSThroughout* nodep, SvaStateVertex* entryVtxp, bool isTopLevelStep = false) { // Mark entryVtxp so "cond false at tick 0" is detected as throughout-drop. @@ -1232,18 +1482,26 @@ public: return buildAndCombiner(andp->lhsp(), andp->rhsp(), entryVtxp, andp->fileline()); } if (AstSIntersect* const intp = VN_CAST(nodep, SIntersect)) { - // IEEE 1800-2023 16.9.6: SAnd with equal-length constraint. - // Variable-length intersect deferred to follow-up. + // IEEE 1800-2023 16.9.6: both operands match over one window with + // equal start and end (equal length). Lower to a single sequence + // that conjoins both operands' per-cycle checks -- correct under + // concurrent attempts, where the done-latch combiner conflates the + // two operands' start times and over-accepts. The combiner remains + // only as a fallback for operands that do not flatten. const int lhsLen = fixedLength(intp->lhsp()); const int rhsLen = fixedLength(intp->rhsp()); - if (lhsLen < 0 || rhsLen < 0) return BuildResult::fail(); - if (lhsLen != rhsLen) { - intp->v3error("Intersect sequence length mismatch: left " + std::to_string(lhsLen) - + " cycles, right " + std::to_string(rhsLen) - + " cycles (IEEE 1800-2023 16.9.6)"); - return BuildResult::failWithError(); + if (lhsLen >= 0 && rhsLen >= 0) { + if (lhsLen != rhsLen) { + // Unequal fixed lengths share no common length -> never matches. + return buildNeverMatchIntersect(intp, entryVtxp, isTopLevelStep); + } + if (AstNodeExpr* const conjp + = conjoinFixedSeqs(intp->lhsp(), intp->rhsp(), intp->fileline())) { + return buildFromLoweringTree(conjp, entryVtxp, isTopLevelStep); + } + return buildAndCombiner(intp->lhsp(), intp->rhsp(), entryVtxp, intp->fileline()); } - return buildAndCombiner(intp->lhsp(), intp->rhsp(), entryVtxp, intp->fileline()); + return buildVarLenIntersect(intp, entryVtxp, isTopLevelStep); } if (AstSWithin* const withinp = VN_CAST(nodep, SWithin)) { return buildSWithin(withinp, entryVtxp, isTopLevelStep); diff --git a/test_regress/t/t_sequence_intersect.v b/test_regress/t/t_sequence_intersect.v index 1e1b754b5..4e278a177 100644 --- a/test_regress/t/t_sequence_intersect.v +++ b/test_regress/t/t_sequence_intersect.v @@ -79,6 +79,10 @@ module t ( assert property (@(posedge clk) (1'b1 ##1 1'b1) intersect (1'b1 ##1 1'b1)); + // Leading-delay operands (no offset-0 check): conjoin first offset > 0. + assert property (@(posedge clk) + (##2 1'b1) intersect (##2 1'b1)); + // Intersect with `throughout` on one side: exercises fixedLength's // SThroughout branch (recurses into rhs to compute the length). cover property (@(posedge clk) diff --git a/test_regress/t/t_sequence_intersect_len_warn.out b/test_regress/t/t_sequence_intersect_len_warn.out deleted file mode 100644 index f8239fae1..000000000 --- a/test_regress/t/t_sequence_intersect_len_warn.out +++ /dev/null @@ -1,10 +0,0 @@ -%Error: t/t_sequence_intersect_len_warn.v:16:17: Intersect sequence length mismatch: left 1 cycles, right 3 cycles (IEEE 1800-2023 16.9.6) - : ... note: In instance 't' - 16 | (a ##1 b) intersect (c ##3 d)); - | ^~~~~~~~~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_sequence_intersect_len_warn.v:20:17: Intersect sequence length mismatch: left 3 cycles, right 1 cycles (IEEE 1800-2023 16.9.6) - : ... note: In instance 't' - 20 | (a ##3 b) intersect (c ##1 d)); - | ^~~~~~~~~ -%Error: Exiting due to diff --git a/test_regress/t/t_sequence_intersect_len_warn.v b/test_regress/t/t_sequence_intersect_len_warn.v deleted file mode 100644 index e544c8926..000000000 --- a/test_regress/t/t_sequence_intersect_len_warn.v +++ /dev/null @@ -1,22 +0,0 @@ -// 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 -// verilog_lint: off -// verilog_format: on - -module t (input clk); - logic a, b, c, d; - - // LHS length 2, RHS length 4 -- WIDTHTRUNC (left < right) - assert property (@(posedge clk) - (a ##1 b) intersect (c ##3 d)); - - // LHS length 4, RHS length 2 -- WIDTHEXPAND (left > right) - assert property (@(posedge clk) - (a ##3 b) intersect (c ##1 d)); - -endmodule diff --git a/test_regress/t/t_sequence_intersect_len_warn.py b/test_regress/t/t_sequence_intersect_nevermatch.py similarity index 71% rename from test_regress/t/t_sequence_intersect_len_warn.py rename to test_regress/t/t_sequence_intersect_nevermatch.py index 58fc7aeba..ddef50cab 100755 --- a/test_regress/t/t_sequence_intersect_len_warn.py +++ b/test_regress/t/t_sequence_intersect_nevermatch.py @@ -9,10 +9,10 @@ import vltest_bootstrap -test.scenarios('vlt_all') +test.scenarios('simulator') -test.compile(verilator_flags2=['--assert', '--timing', '--lint-only'], - fails=True, - expect_filename=test.golden_filename) +test.compile(verilator_flags2=['--assert --timing']) + +test.execute() test.passes() diff --git a/test_regress/t/t_sequence_intersect_nevermatch.v b/test_regress/t/t_sequence_intersect_nevermatch.v new file mode 100644 index 000000000..17887e184 --- /dev/null +++ b/test_regress/t/t_sequence_intersect_nevermatch.v @@ -0,0 +1,57 @@ +// 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 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 + +module t ( + input clk +); + integer cyc = 0; + reg [63:0] crc = 64'h5aef0c8d_d70a4497; + + wire a = crc[0]; + wire b = crc[1]; + wire c = crc[2]; + wire d = crc[3]; + + int f_fix = 0; + int f_dis = 0; + + always_ff @(posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + default clocking @(posedge clk); + endclocking + + // An intersect whose operands share no common length compiles and never + // matches (IEEE 1800-2023 16.9.6 requires a window of equal length). Each + // form is the antecedent of `|-> 1'b0`: it stays vacuous while it never + // matches, so the else fires once per match -- pinned to 0. A spurious match + // would both fail the assertion and bump the counter. + + // Unequal fixed lengths: {2} vs {3}. + ap_fix : + assert property (disable iff (cyc < 2) ((a ##2 b) intersect (c ##3 d)) |-> 1'b0) + else f_fix <= f_fix + 1; + + // Disjoint ranges: {1,2} vs {4,5}. + ap_dis : + assert property (disable iff (cyc < 2) ((a ##[1:2] b) intersect (c ##[4:5] d)) |-> 1'b0) + else f_dis <= f_dis + 1; + + final begin + `checkd(f_fix, 0); // Questa: 0 + `checkd(f_dis, 0); // Questa: 0 + end +endmodule diff --git a/test_regress/t/t_sequence_intersect_range_unsup.out b/test_regress/t/t_sequence_intersect_range_unsup.out index 3d71a9f29..8788a6735 100644 --- a/test_regress/t/t_sequence_intersect_range_unsup.out +++ b/test_regress/t/t_sequence_intersect_range_unsup.out @@ -1,14 +1,18 @@ -%Error: t/t_sequence_intersect_range_unsup.v:12:10: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 't' - 12 | (a ##[1:5] b) intersect (c ##2 d)); - | ^~ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. -%Error: t/t_sequence_intersect_range_unsup.v:12:34: Usage of cycle delays requires default clocking (IEEE 1800-2023 14.11) - : ... note: In instance 't' - 12 | (a ##[1:5] b) intersect (c ##2 d)); - | ^~ -%Error: Internal Error: t/t_sequence_intersect_range_unsup.v:12:10: ../V3Ast.cpp:#: AstSExpr must have non-nullptr delayp() - : ... note: In instance 't' - 12 | (a ##[1:5] b) intersect (c ##2 d)); - | ^~ - ... This fatal error may be caused by the earlier error(s); resolve those first. +%Error-UNSUPPORTED: t/t_sequence_intersect_range_unsup.v:16:44: Unsupported: intersect with this variable-length operand + : ... note: In instance 't' + 16 | assert property ((a ##[1:3] b ##[1:2] c) intersect (d ##2 e)); + | ^~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_sequence_intersect_range_unsup.v:19:45: Unsupported: intersect operand is not a plain boolean sequence + : ... note: In instance 't' + 19 | assert property ((a throughout (b ##1 c)) intersect (d ##[0:2] e)); + | ^~~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_intersect_range_unsup.v:22:42: Unsupported: intersect of two sequences that each vary in length over a range with internal structure + : ... note: In instance 't' + 22 | assert property ((a ##[1:3] (b ##1 c)) intersect (d ##[2:4] e)); + | ^~~~~~~~~ +%Error-UNSUPPORTED: t/t_sequence_intersect_range_unsup.v:25:42: Unsupported: intersect of two sequences that each vary in length over a range with internal structure + : ... note: In instance 't' + 25 | assert property ((a ##2 (b ##[1:3] c)) intersect (d ##[3:5] e)); + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_sequence_intersect_range_unsup.v b/test_regress/t/t_sequence_intersect_range_unsup.v index 837f9b146..567496a47 100644 --- a/test_regress/t/t_sequence_intersect_range_unsup.v +++ b/test_regress/t/t_sequence_intersect_range_unsup.v @@ -4,11 +4,24 @@ // SPDX-FileCopyrightText: 2026 PlanV GmbH // SPDX-License-Identifier: CC0-1.0 -module t (input clk); - logic a, b, c, d; +module t ( + input clk +); + logic a, b, c, d, e; - // Range delay in intersect operand is unsupported - assert property (@(posedge clk) - (a ##[1:5] b) intersect (c ##2 d)); + default clocking @(posedge clk); + endclocking + + // Two ranged cycle delays in one intersect operand is unsupported + assert property ((a ##[1:3] b ##[1:2] c) intersect (d ##2 e)); + + // Single common length, but an operand is not a plain boolean sequence + assert property ((a throughout (b ##1 c)) intersect (d ##[0:2] e)); + + // Both operands vary over a range and carry internal structure + assert property ((a ##[1:3] (b ##1 c)) intersect (d ##[2:4] e)); + + // Operand top-level delay fixed but length varies via a nested range + assert property ((a ##2 (b ##[1:3] c)) intersect (d ##[3:5] e)); endmodule diff --git a/test_regress/t/t_sequence_intersect_varlen.py b/test_regress/t/t_sequence_intersect_varlen.py new file mode 100755 index 000000000..ddef50cab --- /dev/null +++ b/test_regress/t/t_sequence_intersect_varlen.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=['--assert --timing']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_sequence_intersect_varlen.v b/test_regress/t/t_sequence_intersect_varlen.v new file mode 100644 index 000000000..402560af3 --- /dev/null +++ b/test_regress/t/t_sequence_intersect_varlen.v @@ -0,0 +1,71 @@ +// 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 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 + +module t ( + input clk +); + integer cyc = 0; + reg [63:0] crc = 64'h5aef0c8d_d70a4497; + + wire a = crc[0]; + wire b = crc[1]; + wire c = crc[2]; + wire d = crc[3]; + wire e = crc[4]; + + int f_var = 0; + int f_ieee = 0; + int f_collapse = 0; + int f_over = 0; + + always_ff @(posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + default clocking @(posedge clk); + endclocking + + // Both operands vary in length: lhs in [1,3], rhs in [2,5], common [2,3]. + // Variable-length intersect (IEEE 1800-2023 16.9.6): both match the same + // window with equal start and end, length chosen from each operand's range. + ap_var : + assert property (disable iff (cyc < 2) (a & c) |-> ((a ##[1:3] b) intersect (c ##[2:5] d))) + else f_var <= f_var + 1; + + // IEEE 16.9.6 canonical: one variable + one fixed operand, common length {4}. + ap_ieee : + assert property (disable iff (cyc < 2) a |-> ((a ##[1:5] b) intersect (c ##2 d ##2 e))) + else f_ieee <= f_ieee + 1; + + // Common length collapses to {0}: (a ##[0:3] b) intersect c == a & b & c, + // so this implication never fails (exercises the bare-boolean lowering path). + ap_collapse : + assert property (disable iff (cyc < 2) (a & b & c) |-> ((a ##[0:3] b) intersect c)) + else f_collapse <= f_collapse + 1; + + // Equal fixed length, one operand carrying an internal check: lowered through + // the per-cycle conjoin, not the done-latch combiner (which conflates + // concurrent attempts and over-accepts). + ap_over : + assert property (disable iff (cyc < 2) (a ##4 b) intersect (c ##2 d ##2 e)) + else f_over <= f_over + 1; + + final begin + `checkd(f_var, 7); // Questa: 7 + `checkd(f_ieee, 41); // Questa: 41 + `checkd(f_collapse, 0); // Questa: 0 + `checkd(f_over, 84); // Questa: 84 + end +endmodule From b73a897db3c89f8301c34051d412a993f98183cb Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 25 Jun 2026 14:14:15 +0100 Subject: [PATCH 11/41] Optimize module inlining heuristic (#7837) Rewrite module inlining decision to be based on a bipartite Module/Cell graph, similar to V3InlineCFuncs. Preserved all old heuristics, but added 2 new ones: - If a module, and all the sub-hierarchy below it, is less than 10% the total flattened size of the design, then flatten the contents of that module (but the module itself is not necessarily inlined). - If the flattened size of all instances of a module is less than 20% of the total flattened size of the design, then inline all instances of that module. These are both relative to the total size of the design, so they auto-scale with complexity. The net effect is that large shared instances are preserved, but their contents are flattened out. E.g. in a multi-core CPU this would keep the cores non-inlined but flatten out most everything else. This still enables V3Combining and sharing those later, but avoids potentially big overheads e.g. with small widely used library modules. Empirically this yields less generated C++ than the previous version (due to removing lots of small functions), and can improve performance 10-20% while still having meaningful combining relative to the size of the design. --- src/V3Inline.cpp | 603 ++++++++++++------ .../t_cover_fsm_if_unknown_enum_multi_bad.out | 46 +- ..._cover_fsm_plain_always_warn_multi_bad.out | 8 +- test_regress/t/t_dpi_2exparg_bad.out | 10 +- .../t/t_fsmmulti_combo_multi_warn_bad.out | 52 +- test_regress/t/t_gen_upscope.out | 8 +- test_regress/t/t_genfor_signed.out | 6 +- test_regress/t/t_inst_tree_inl1_pub0.py | 4 +- test_regress/t/t_json_only_flat.out | 124 ++-- .../t/t_lint_unusedloop_removed_bad.out | 12 +- test_regress/t/t_opt_dedupe_clk_gate.py | 2 +- test_regress/t/t_unoptflat_simple_3_bad.out | 6 +- test_regress/t/t_x_rand_mt_stability.out | 2 +- test_regress/t/t_x_rand_mt_stability_add.out | 2 +- .../t/t_x_rand_mt_stability_add_trace.out | 2 +- .../t/t_x_rand_mt_stability_trace.out | 2 +- .../t/t_x_rand_mt_stability_zeros.out | 2 +- test_regress/t/t_x_rand_stability.out | 2 +- test_regress/t/t_x_rand_stability_add.out | 2 +- .../t/t_x_rand_stability_add_trace.out | 2 +- test_regress/t/t_x_rand_stability_trace.out | 2 +- test_regress/t/t_x_rand_stability_zeros.out | 2 +- 22 files changed, 550 insertions(+), 351 deletions(-) diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index 87b9e0b26..7b0a4ecd3 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -29,6 +29,7 @@ #include "V3Inline.h" #include "V3AstUserAllocator.h" +#include "V3Graph.h" #include "V3Inst.h" #include "V3Stats.h" @@ -41,208 +42,334 @@ VL_DEFINE_DEBUG_FUNCTIONS; static const int INLINE_MODS_SMALLER = 100; // If a mod is < this # nodes, can always inline it //###################################################################### -// Inlining state. Kept as AstNodeModule::user1p via AstUser1Allocator +// Bipartite module instantiation graph containing module and cell vertices -namespace { +class InlineModModuleVertex; +class InlineModCellVertex; -struct ModuleState final { - bool m_inlined = false; // Whether to inline this module - unsigned m_cellRefs = 0; // Number of AstCells instantiating this module - std::vector m_childCells; // AstCells under this module (to speed up traversal) -}; - -using ModuleStateUser1Allocator = AstUser1Allocator; - -} // namespace - -//###################################################################### -// Visitor that determines which modules will be inlined - -class InlineMarkVisitor final : public VNVisitor { +class InlineModGraph final : public V3Graph { // NODE STATE - // Output - // AstNodeModule::user1() // OUTPUT: ModuleState instance (via m_moduleState) - // Internal state (can be cleared after this visit completes) - // AstNodeModule::user2() // CIL_*. Allowed to automatically inline module - // AstNodeModule::user4() // int. Statements in module - const VNUser2InUse m_inuser2; - const VNUser4InUse m_inuser4; + // AstNodeModule::user4p() -> InlineModModuleVertex*, the module vertex + // AstCell::user4p() -> InlineModCellVertex*, the cell vertex - ModuleStateUser1Allocator& m_moduleState; + VNUser4InUse m_user4InUse; - // For the user2 field: - enum : uint8_t { - CIL_NOTHARD = 0, // Inline not supported - CIL_NOTSOFT, // Don't inline unless user overrides - CIL_MAYBE, // Might inline - CIL_USER - }; // Pragma suggests inlining - - // STATE - AstNodeModule* m_modp = nullptr; // Current module - VDouble0 m_statUnsup; // Statistic tracking - std::vector m_allMods; // All modules, in top-down order. - - // Within the context of a given module, LocalInstanceMap maps - // from child modules to the count of each child's local instantiations. - using LocalInstanceMap = std::unordered_map; - - // We keep a LocalInstanceMap for each module in the design - std::unordered_map m_instances; +public: + InlineModGraph() + : V3Graph{} {} + ~InlineModGraph() override = default; // METHODS - void cantInline(const char* reason, bool hard) { - if (hard) { - if (m_modp->user2() != CIL_NOTHARD) { - UINFO(4, " No inline hard: " << reason << " " << m_modp); - m_modp->user2(CIL_NOTHARD); - ++m_statUnsup; - } - } else { - if (m_modp->user2() == CIL_MAYBE) { - UINFO(4, " No inline soft: " << reason << " " << m_modp); - m_modp->user2(CIL_NOTSOFT); + InlineModModuleVertex* getInlineModModuleVertexp(AstNodeModule* modp); + InlineModCellVertex* getInlineModCellVertexp(AstCell* cellp); + void addEdge(InlineModModuleVertex& from, InlineModCellVertex& to); + void addEdge(InlineModCellVertex& from, InlineModModuleVertex& to); + + // debug + std::string dotRankDir() const override { return "LR"; } +}; + +class InlineModEitherVertex VL_NOT_FINAL : public V3GraphVertex { + VL_RTTI_IMPL(InlineModEitherVertex, V3GraphVertex) +protected: + explicit InlineModEitherVertex(InlineModGraph& graph) + : V3GraphVertex{&graph} {} +}; + +class InlineModModuleVertex final : public InlineModEitherVertex { + VL_RTTI_IMPL(InlineModModuleVertex, InlineModEitherVertex) + AstNodeModule* const m_modp; // The module + const char* m_noInlineHardWyp = nullptr; // First reason the module can never be inlined + const char* m_noInlineSoftWyp = nullptr; // First reason not to inline unless forced + const char* m_shouldInlineWhyp = nullptr; // First reason why this module should be inlined + size_t m_size = 0; // The size (statement count) of the module + size_t mutable m_flattenedSize = 0; // The size of the module if flattened + bool mutable m_flattenedSizeValid = false; // Whether the flattened size is valid + size_t mutable m_instanceCount = 0; // The number of total instances of this module + bool mutable m_instanceCountValid = false; // Whether the instance count is valid + +public: + InlineModModuleVertex(InlineModGraph& graph, AstNodeModule* modp) + : InlineModEitherVertex{graph} + , m_modp{modp} {} + ~InlineModModuleVertex() override = default; + + // ACCESSORS + AstNodeModule* modp() const { return m_modp; } + size_t size() const { return m_size; } + void size(size_t value) { m_size = value; } + void sizeInc(size_t value = 1) { m_size += value; } + bool noInlineHard() const { return m_noInlineHardWyp; } + void setNoInlineHard(const char* whyp) { + if (!m_noInlineHardWyp) m_noInlineHardWyp = whyp; + } + bool noInlineSoft() const { return m_noInlineSoftWyp; } + void setNoInlineSoft(const char* whyp) { + if (!m_noInlineSoftWyp) m_noInlineSoftWyp = whyp; + } + bool shouldInline() const { return m_shouldInlineWhyp; } + void setShouldInline(const char* whyp) { + if (!m_shouldInlineWhyp) m_shouldInlineWhyp = whyp; + } + // Mark every instance below this module for inlining + void setFlatten(); + + // Total size of module, with all hierarchy below flattened + size_t flattenedSize() const { + if (!m_flattenedSizeValid) { + m_flattenedSizeValid = true; + m_flattenedSize = m_size; + for (const V3GraphEdge& e1 : outEdges()) { + for (const V3GraphEdge& e2 : e1.top()->outEdges()) { + InlineModModuleVertex* const mVtxp = e2.top()->as(); + m_flattenedSize += mVtxp->flattenedSize(); + } } } + return m_flattenedSize; } + // Total number of instances of this module in the whole hierarchy of the design + size_t instanceCount() const { + if (!m_instanceCountValid) { + m_instanceCountValid = true; + m_instanceCount = 0; + for (const V3GraphEdge& e1 : inEdges()) { + for (const V3GraphEdge& e2 : e1.fromp()->inEdges()) { + InlineModModuleVertex* const mVtxp = e2.fromp()->as(); + m_instanceCount += mVtxp->instanceCount(); + } + } + if (!m_instanceCount) { + UASSERT_OBJ(m_modp->isTop(), m_modp, "non-top level module should have instances"); + m_instanceCount = 1; + } + } + return m_instanceCount; + } + + // debug + FileLine* fileline() const override { return m_modp->fileline(); } + std::string dotShape() const override { return "box"; } + std::string dotColor() const override { + return m_noInlineHardWyp ? "red" + : m_shouldInlineWhyp ? "blue" + : m_noInlineSoftWyp ? "orange" + : "black"; + } + std::string name() const override VL_MT_STABLE { + std::string str = m_modp->typeName() + " "s + cvtToHex(m_modp); + str += "\n" + m_modp->name() + " @ " + fileline()->ascii(); + str += "\ninstanceCount: " + std::to_string(instanceCount()); + str += "\nsize: " + std::to_string(m_size); + str += "\nflattenedSize: " + std::to_string(flattenedSize()); + if (m_shouldInlineWhyp) str += "\nShouldInline: "s + m_shouldInlineWhyp; + if (m_noInlineHardWyp) str += "\nNoInlineHard: "s + m_noInlineHardWyp; + if (m_noInlineSoftWyp) str += "\nNoInlineSoft: "s + m_noInlineSoftWyp; + str += "\n"; + return str; + } +}; + +class InlineModCellVertex final : public InlineModEitherVertex { + VL_RTTI_IMPL(InlineModCellVertex, InlineModEitherVertex) + AstCell* const m_cellp; // The cell (instance) + const char* m_doInlineWyp = nullptr; // First reason this instance should be inlined + bool m_flatten = false; // Whether this cell and below already flattened (avoid O(n^2)) + +public: + InlineModCellVertex(InlineModGraph& graph, AstCell* cellp) + : InlineModEitherVertex{graph} + , m_cellp{cellp} {} + ~InlineModCellVertex() override = default; + + // ACCESSORS + AstCell* cellp() const { return m_cellp; } + bool doInline() const { return m_doInlineWyp; } + void setDoInline(const char* whyp) { + if (!m_doInlineWyp) m_doInlineWyp = whyp; + } + bool flatten() const { return m_flatten; } + void setFlatten() { m_flatten = true; } + + // The module vertx this cell is instantiating + InlineModModuleVertex& instanceOf() const { + UASSERT_OBJ(outSize1(), this, "Cell should have exactly one outgoing edge"); + return *outEdges().frontp()->top()->as(); + } + // The module vertex this cell is instantiated in + InlineModModuleVertex& instanceIn() const { + UASSERT_OBJ(inSize1(), this, "Cell should have exactly one incoming edge"); + return *inEdges().frontp()->fromp()->as(); + } + + // debug + FileLine* fileline() const override { return m_cellp->fileline(); } + std::string dotColor() const override { return m_doInlineWyp ? "green" : "black"; } + std::string dotShape() const override { return "ellipse"; } + std::string name() const override VL_MT_STABLE { + std::string str = m_cellp->typeName() + " "s + cvtToHex(m_cellp); + str += "\n" + m_cellp->name() + " @ " + fileline()->ascii(); + if (m_doInlineWyp) str += "\nDoInline: "s + m_doInlineWyp; + str += "\n"; + return str; + } +}; + +InlineModModuleVertex* InlineModGraph::getInlineModModuleVertexp(AstNodeModule* modp) { + if (!modp->user4p()) modp->user4p(new InlineModModuleVertex{*this, modp}); + return modp->user4u().to(); +} +InlineModCellVertex* InlineModGraph::getInlineModCellVertexp(AstCell* cellp) { + if (!cellp->user4p()) cellp->user4p(new InlineModCellVertex{*this, cellp}); + return cellp->user4u().to(); +} + +void InlineModGraph::addEdge(InlineModModuleVertex& parent, InlineModCellVertex& cell) { + UASSERT_OBJ(cell.inEmpty(), &cell, "Cell should have at most one incoming edge"); + new V3GraphEdge{this, &parent, &cell, 1, /* cutable: */ false}; +} +void InlineModGraph::addEdge(InlineModCellVertex& cell, InlineModModuleVertex& submodule) { + UASSERT_OBJ(cell.outEmpty(), &cell, "Cell should have at most one outgoing edge"); + new V3GraphEdge{this, &cell, &submodule, 1, /* cutable: */ false}; +} + +void InlineModModuleVertex::setFlatten() { + for (V3GraphEdge& edge : outEdges()) { + InlineModCellVertex& cVtx = *edge.top()->as(); + if (cVtx.flatten()) continue; + cVtx.setFlatten(); + InlineModModuleVertex& iVtx = cVtx.instanceOf(); + if (!iVtx.noInlineHard() && !iVtx.noInlineSoft()) cVtx.setDoInline("flatten parent"); + iVtx.setFlatten(); + } +} + +//###################################################################### +// Visitor that builds the bipartite module instantiation graph + +class InlineModGraphBuilder final : public VNVisitor { + // STATE + std::unique_ptr m_graphp{new InlineModGraph}; // The graph being built + InlineModModuleVertex* m_modVtxp = nullptr; // Vertex of module currently being iterated // VISITORS void visit(AstNodeModule* nodep) override { - UASSERT_OBJ(!m_modp, nodep, "Unsupported: Nested modules"); - VL_RESTORER(m_modp); - m_modp = nodep; - m_allMods.push_back(nodep); - m_modp->user2(CIL_MAYBE); - m_modp->user4(0); // statement count - if (VN_IS(m_modp, Iface)) { - // Inlining an interface means we no longer have a cell handle to resolve to. - // If inlining moves post-scope this can perhaps be relaxed. - cantInline("modIface", true); - } - if (m_modp->modPublic() && (m_modp->isTop() || !v3Global.opt.flatten())) { - cantInline("modPublic", false); - } - // If the instance is a --lib-create library stub instance, and need tracing, - // then don't inline as we need to know its a lib stub for sepecial handling - // in V3TraceDecl. See #7001. - if (m_modp->verilatorLib() && v3Global.opt.trace()) { - cantInline("verilatorLib with --trace", false); + if (nodep == v3Global.rootp()->constPoolp()->modp()) return; // Ignore const pool module + + UASSERT_OBJ(!m_modVtxp, nodep, "Unsupported: Nested modules"); + + // Create the module vertex + InlineModModuleVertex* const vtxp = m_graphp->getInlineModModuleVertexp(nodep); + + // Check if the module itself is not inlineable + + // Inlining an interface means we no longer have a cell handle to resolve to. + // If inlining moves post-scope this can perhaps be relaxed. + if (VN_IS(nodep, Iface)) vtxp->setNoInlineHard("Interface"); + // Never inline packages - TODO: conceptually fine, but why not? + if (VN_IS(nodep, Package)) vtxp->setNoInlineHard("Package"); + // A --lib-create library stub instance that needs tracing must not be + // inlined, so we still know it is a lib stub in V3TraceDecl (see #7001) + if (nodep->verilatorLib() && v3Global.opt.trace()) { + vtxp->setNoInlineHard("verilatorLib with --trace"); } - iterateChildren(nodep); + // Don't inline public modules by default + if (nodep->modPublic()) vtxp->setNoInlineSoft("Public module"); + + // Iterate children + VL_RESTORER(m_modVtxp); + m_modVtxp = vtxp; + iterateChildrenConst(nodep); } + void visit(AstClass* nodep) override { - // TODO allow inlining of modules that have classes - // (Probably wait for new inliner scheme) - cantInline("class", true); - iterateChildren(nodep); + // TODO allow inlining of modules that contain classes + if (m_modVtxp) m_modVtxp->setNoInlineHard("Contains class"); + iterateChildrenConst(nodep); // TODO: this is only needed or FTaskRef cleanup } + + // Cells instantiate modules void visit(AstCell* nodep) override { - m_moduleState(nodep->modp()).m_cellRefs++; - m_moduleState(m_modp).m_childCells.push_back(nodep); - m_instances[m_modp][nodep->modp()]++; - iterateChildren(nodep); + UASSERT_OBJ(m_modVtxp, nodep, "Cell should be under a module"); + + // Create the cell vertex + InlineModCellVertex* const vtxp = m_graphp->getInlineModCellVertexp(nodep); + + // Add containing-module/instantiated-module edges + m_graphp->addEdge(*m_modVtxp, *vtxp); + m_graphp->addEdge(*vtxp, *m_graphp->getInlineModModuleVertexp(nodep->modp())); + + // Iterate children + iterateChildrenConst(nodep); } + void visit(AstPragma* nodep) override { if (nodep->pragType() == VPragmaType::INLINE_MODULE) { - if (!m_modp) { + if (!m_modVtxp) { nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE - } else if (m_modp->user2() == CIL_MAYBE || m_modp->user2() == CIL_NOTSOFT) { - m_modp->user2(CIL_USER); + } else { + m_modVtxp->setShouldInline("Pragma INLINE_MODULE"); } - // Remove so it does not propagate to upper cell - VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); - } else if (nodep->pragType() == VPragmaType::NO_INLINE_MODULE) { - if (!m_modp) { - nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE - } else if (!v3Global.opt.flatten()) { - cantInline("Pragma NO_INLINE_MODULE", false); - } - // Remove so it does not propagate to upper cell - // Remove so don't propagate to upper cell... VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return; } + + if (nodep->pragType() == VPragmaType::NO_INLINE_MODULE) { + if (!m_modVtxp) { + nodep->v3error("Inline pragma not under a module"); // LCOV_EXCL_LINE + } else { + m_modVtxp->setNoInlineSoft("Pragma NO_INLINE_MODULE"); + } + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return; + } + + iterateChildrenConst(nodep); } - void visit(AstVarXRef* nodep) override { - // Keep varp - V3Const::constifyEdit is called during pinReconnectSimple - // which needs varp to be set. V3LinkDot will re-resolve after inlining. - } + + // TODO: Bit nasty to do this here, but historically present, and still necessary void visit(AstNodeFTaskRef* nodep) override { + if (m_modVtxp) m_modVtxp->sizeInc(); // Remove link. V3LinkDot will reestablish it after inlining. // MethodCalls not currently supported by inliner, so keep linked if (!nodep->classOrPackagep() && !VN_IS(nodep, MethodCall)) { nodep->taskp(nullptr); VIsCached::clearCacheTree(); } - iterateChildren(nodep); + iterateChildrenConst(nodep); } - void visit(AstAlways* nodep) override { - if (nodep->keyword() != VAlwaysKwd::CONT_ASSIGN) nodep->user4Inc(); // statement count - iterateChildren(nodep); - } - void visit(AstNodeAssign* nodep) override { - // Don't count assignments, as they'll likely flatten out - // Still need to iterate though to nullify VarXRefs - const int oldcnt = m_modp->user4(); - iterateChildren(nodep); - m_modp->user4(oldcnt); - } - void visit(AstNetlist* nodep) override { - // Build ModuleState, user2, and user4 for all modules. - // Also build m_allMods and m_instances. - iterateChildren(nodep); - // Iterate through all modules in bottom-up order. - // Make a final inlining decision for each. - for (AstNodeModule* const modp : vlstd::reverse_view(m_allMods)) { - - // If we're going to inline some modules into this one, - // update user4 (statement count) to reflect that: - int statements = modp->user4(); - for (const auto& pair : m_instances[modp]) { - const AstNodeModule* const childp = pair.first; - if (m_moduleState(childp).m_inlined) { // inlining child - statements += childp->user4() * pair.second; - } - } - modp->user4(statements); - - const int allowed = modp->user2(); - const int refs = m_moduleState(modp).m_cellRefs; - - // Should we automatically inline this module? - // If --flatten is specified, then force everything to be inlined that can be. - // inlineMult = 2000 by default. - // If a mod*#refs is < this # nodes, can inline it - // Packages aren't really "under" anything so they confuse this algorithm - const bool doit = !VN_IS(modp, Package) // - && allowed != CIL_NOTHARD // - && allowed != CIL_NOTSOFT // - && (allowed == CIL_USER // - || v3Global.opt.flatten() // - || refs == 1 // - || statements < INLINE_MODS_SMALLER // - || v3Global.opt.inlineMult() < 1 // - || refs * statements < v3Global.opt.inlineMult()); - m_moduleState(modp).m_inlined = doit; - UINFO(4, " Inline=" << doit << " Possible=" << allowed << " Refs=" << refs - << " Stmts=" << statements << " " << modp); - } - } - //-------------------- + // Base node void visit(AstNode* nodep) override { - if (m_modp) m_modp->user4Inc(); // Inc statement count - iterateChildren(nodep); + if (m_modVtxp) m_modVtxp->sizeInc(); + iterateChildrenConst(nodep); } + // CONSTRUCTORS + explicit InlineModGraphBuilder(AstNetlist* nodep) { + // Build the module instantiation graph + iterateConst(nodep); + // Order vertices (any topological order is fine), can't be cyclic at this point + m_graphp->order(); + // Check that the first vertex is the top level module (everything, including packages, + // have a corresponding AstCell under the top level at this point). + UASSERT_OBJ(m_graphp->vertices().frontp()->as()->modp()->isTop(), + nodep, "First vertex should be top level module"); +#ifdef VL_DEBUG + for (const V3GraphVertex& vtx : m_graphp->vertices()) { + // First vertex is the top levelmodule, we checked above + if (&vtx == m_graphp->vertices().frontp()) continue; + // Otherwise it should have instantiations + UASSERT_OBJ(!vtx.inEmpty(), &vtx, "Should have edges from root"); + } +#endif + } + ~InlineModGraphBuilder() override = default; + public: - // CONSTRUCTORS - explicit InlineMarkVisitor(AstNode* nodep, ModuleStateUser1Allocator& moduleState) - : m_moduleState{moduleState} { - iterate(nodep); - } - ~InlineMarkVisitor() override { - V3Stats::addStat("Optimizations, Inline unsupported", m_statUnsup); + static std::unique_ptr apply(AstNetlist* nodep) { + return std::move(InlineModGraphBuilder{nodep}.m_graphp); } }; @@ -264,8 +391,12 @@ class InlineRelinkVisitor final : public VNVisitor { m_pinSubstitutedXRefs; // VarXRefs created by pin substitution in this relink pass. // Their dotted/inlinedDots already represent the parent's scope // and must not be rewritten on the immediate visit. + InlineModGraph& m_graph; // The instance graph AstNodeModule* const m_modp; // The module we are inlining into + // The vertex of the module we are inlining into, for updating the graph + InlineModModuleVertex* const m_mVtxp = m_graph.getInlineModModuleVertexp(m_modp); const AstCell* const m_cellp; // The cell being inlined + size_t m_nPlaceholders = 0; // Unique identifier sequence number for placeholder variables // VISITORS @@ -282,6 +413,11 @@ class InlineRelinkVisitor final : public VNVisitor { void visit(AstCell* nodep) override { // Cell under the inline cell, need to rename to avoid conflicts nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); + // Need to update graph + nodep->user4p(nullptr); // clone copied user4p, reset to make new vertex + InlineModCellVertex* const vtxp = m_graph.getInlineModCellVertexp(nodep); + m_graph.addEdge(*m_mVtxp, *vtxp); + m_graph.addEdge(*vtxp, *m_graph.getInlineModModuleVertexp(nodep->modp())); iterateChildren(nodep); } void visit(AstClass* nodep) override { @@ -464,8 +600,10 @@ class InlineRelinkVisitor final : public VNVisitor { public: // CONSTRUCTORS - InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp) - : m_modp{oldModp} + InlineRelinkVisitor(AstNodeModule* cloneModp, AstNodeModule* oldModp, AstCell* cellp, + InlineModGraph& graph) + : m_graph{graph} + , m_modp{oldModp} , m_cellp{cellp} { // CellInlines added by V3Begin for generate/named blocks have origModName // "__BEGIN__"; only those added by prior V3Inline passes carry a real module @@ -607,7 +745,7 @@ void connectPort(AstNodeModule* modp, AstVar* nodep, AstNodeExpr* pinExprp) { } // Inline 'cellp' into 'modp'. 'last' indicatest this is tha last instance of the inlined module -void inlineCell(AstNodeModule* modp, AstCell* cellp, bool last) { +void inlineCell(AstNodeModule* modp, AstCell* cellp, bool last, InlineModGraph& graph) { UINFO(5, " Inline Cell " << cellp); UINFO(5, " into Module " << modp); @@ -662,8 +800,8 @@ void inlineCell(AstNodeModule* modp, AstCell* cellp, bool last) { connectPort(modp, newModVarp, pinExprp); } - // Cleanup var names, etc, to not conflict, relink replaced variables - { InlineRelinkVisitor{inlinedp, modp, cellp}; } + // Cleanup var names, etc, to not conflict, relink replaced variables, adjust graph + { InlineRelinkVisitor{inlinedp, modp, cellp, graph}; } // Move statements from the inlined module into the module we are inlining into if (AstNode* const stmtsp = inlinedp->stmtsp()) { modp->addStmtsp(stmtsp->unlinkFrBackWithNext()); @@ -675,39 +813,60 @@ void inlineCell(AstNodeModule* modp, AstCell* cellp, bool last) { } // Apply all inlining decisions -void process(AstNetlist* netlistp, ModuleStateUser1Allocator& moduleStates) { +void process(AstNetlist* netlistp, InlineModGraph& graph) { // NODE STATE - // Input: - // AstNodeModule::user1p() // ModuleState instance (via moduleState) - // // Cleared entire netlist // AstIfaceRefDType::user1() // Whether the cell pointed to by this // // AstIfaceRefDType has been inlined // AstCell::user3p() // AstCell*, the clone - // AstVar::user3p() // AstVar*, the clone clone + // AstVar::user3p() // AstVar*, the clone // Cleared each cell // AstVar::user2p() // AstVarRef*/AstConst* This port is connected to (AstPin::expr()) - const VNUser3InUse m_user3InUse; + const VNUser1InUse user1InUse; + const VNUser3InUse user3InUse; // Number of inlined instances, for statistics VDouble0 m_nInlined; - // We want to inline bottom up. The modules under the netlist are in - // dependency order (top first, leaves last), so find the end of the list. - AstNode* nodep = netlistp->modulesp(); - while (nodep->nextp()) nodep = nodep->nextp(); + // Gather all cells that need to be inlined (this is in topological order) + std::vector cVtxps; + for (V3GraphVertex& vtx : graph.vertices()) { + InlineModCellVertex* const cVtxp = vtx.cast(); + if (!cVtxp) continue; + if (!cVtxp->doInline()) continue; + cVtxps.push_back(cVtxp); + } - // Iterate module list backwards (stop when we get back to the Netlist) - while (AstNodeModule* const modp = VN_CAST(nodep, NodeModule)) { - nodep = nodep->backp(); + // Inline cells bottom up (leaves into roots) + for (InlineModCellVertex* const cVtxp : vlstd::reverse_view(cVtxps)) { + // Pick up parts before deleting + InlineModModuleVertex& mVtx = cVtxp->instanceIn(); + InlineModModuleVertex* const iVtxp = &cVtxp->instanceOf(); + AstCell* const cellp = cVtxp->cellp(); + const bool last = iVtxp->inSize1(); + UASSERT_OBJ(!iVtxp->noInlineHard(), cellp, "Should not be inlining if not possible"); - // Consider each cell inside the current module for inlining - for (AstCell* const cellp : moduleStates(modp).m_childCells) { - ModuleState& childState = moduleStates(cellp->modp()); - if (!childState.m_inlined) continue; - ++m_nInlined; - inlineCell(modp, cellp, --childState.m_cellRefs == 0); + // Update + ++m_nInlined; + mVtx.sizeInc(iVtxp->size()); // For debug dump only + + // Delete the cell we are inlining + VL_DO_DANGLING(cVtxp->unlinkDelete(&graph), cVtxp); + // Delete the module we are inlining if this is the last instance + if (last) { + while (!iVtxp->outEmpty()) { + InlineModCellVertex* const tVtxp + = iVtxp->outEdges().frontp()->top()->as(); + // Bottom up ordering ensures this + UASSERT_OBJ(!tVtxp->doInline(), tVtxp, "Should have been inlined"); + VL_DO_DANGLING(tVtxp->unlinkDelete(&graph), tVtxp); + } + VL_DO_DANGLING(iVtxp->unlinkDelete(&graph), iVtxp); } + + // Do it + inlineCell(mVtx.modp(), cellp, last, graph); + if (dumpGraphLevel() >= 9) graph.dumpDotFilePrefixed("inlinemod-cell"); } V3Stats::addStat("Optimizations, Inlined instances", m_nInlined); @@ -729,25 +888,65 @@ void process(AstNetlist* netlistp, ModuleStateUser1Allocator& moduleStates) { void V3Inline::inlineAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); - { - const VNUser1InUse m_inuser1; // output of InlineMarkVisitor, input to InlineVisitor. - ModuleStateUser1Allocator moduleState; // AstUser1Allocator + // Build the bipartite module instantiation graph + std::unique_ptr graphp = InlineModGraphBuilder::apply(nodep); + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("inlinemod-graph"); - // Scoped to clean up temp userN's - { InlineMarkVisitor{nodep, moduleState}; } - - // Inline the modles we decided to inline - ModuleInliner::process(nodep, moduleState); - - // Check inlined modules have been removed during traversal. Otherwise we might have blown - // up Verilator memory consumption. - for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp; - modp = VN_AS(modp->nextp(), NodeModule)) { - UASSERT_OBJ(!moduleState(modp).m_inlined, modp, - "Inlined module should have been deleted when the last instance " - "referencing it was inlined"); + // Decide which instances to inline + const size_t designSize + = graphp->vertices().frontp()->as()->flattenedSize(); + for (V3GraphVertex& vtx : graphp->vertices()) { + if (InlineModModuleVertex* const mVtxp = vtx.cast()) { + // If this module is less than 10% of the design, flatten this module + if (mVtxp->flattenedSize() * 10 < designSize) mVtxp->setFlatten(); + // Don't inline if can't inline + if (mVtxp->noInlineHard()) continue; + // Don't inline if soft off + if (mVtxp->noInlineSoft()) continue; + // If all instances of this module combined are less than 20% of the design, inline all + size_t totalSize = mVtxp->flattenedSize() * mVtxp->instanceCount(); + if (totalSize * 5 < designSize) { + for (V3GraphEdge& edge : mVtxp->inEdges()) { + InlineModCellVertex* const cVtxp = edge.fromp()->as(); + cVtxp->setDoInline("< 20% of design"); + } + } + // No more decisions based on module vertex + continue; } + + // The instantiation + InlineModCellVertex& cVtx = *vtx.as(); + // The module instantiated by this cell + InlineModModuleVertex& mVtx = cVtx.instanceOf(); + + // Don't inline if can't inline, duh! + if (mVtx.noInlineHard()) continue; + + // If it should be inlined, inlined it + if (mVtx.shouldInline()) cVtx.setDoInline("should inline"); + // If --flatten, inline it + if (v3Global.opt.flatten()) cVtx.setDoInline("--flatten"); + + // Don't inline for other reasons if soft off + if (mVtx.noInlineSoft()) continue; + + // If instatiated in exactly one static site, inline it + if (mVtx.inSize1()) cVtx.setDoInline("Single static instance"); + // If small, inline it + if (mVtx.size() < INLINE_MODS_SMALLER) cVtx.setDoInline("Small"); + // If inlineMult is 0, inline it + if (v3Global.opt.inlineMult() < 1) cVtx.setDoInline("inlineMult < 1"); + // If it would yield less than the given number of ops, inline it + const size_t inlinedSize = mVtx.inEdges().size() * mVtx.size(); + const size_t limit = v3Global.opt.inlineMult(); + if (inlinedSize < limit) cVtx.setDoInline("inlinedSize < inlineMult"); } + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("inlinemod-decision"); + + // Inline the modles we decided to inline + ModuleInliner::process(nodep, *graphp); + if (dumpGraphLevel() >= 6) graphp->dumpDotFilePrefixed("inlinemod-inlined"); V3Global::dumpCheckGlobalTree("inline", 0, dumpTreeEitherLevel() >= 3); } diff --git a/test_regress/t/t_cover_fsm_if_unknown_enum_multi_bad.out b/test_regress/t/t_cover_fsm_if_unknown_enum_multi_bad.out index 7416b7aa4..8cf3712f6 100644 --- a/test_regress/t/t_cover_fsm_if_unknown_enum_multi_bad.out +++ b/test_regress/t/t_cover_fsm_if_unknown_enum_multi_bad.out @@ -1,33 +1,33 @@ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:26:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_then_u.state_q': assigned value 3 is not present in the declared enum - 26 | S0: state_d = sel ? 2'd3 : S1; +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:274:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_wide_direct_u.state_q': assigned value 40'hffffffffff is not present in the declared enum + 274 | S0: state_d = 40'hffff_ffff_ff; | ^ ... For warning description see https://verilator.org/warn/COVERIGN?v=latest ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:53:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_else_u.state_q': assigned value 3 is not present in the declared enum - 53 | S0: state_d = sel ? S1 : 2'd3; - | ^ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:79:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_direct_u.state_q': assigned value 3 is not present in the declared enum - 79 | S0: state_d = 2'd3; - | ^ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:126:15: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_reset_u.state_q': assigned value 3 is not present in the declared enum - 126 | state_q <= 2'd3; - | ^~ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:150:7: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_source_u.state_q': case item value 3 is not present in the declared enum - 150 | 2'd3: state_d = S0; - | ^~~~ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:175:5: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_if_source_u.state_q': case item value 3 is not present in the declared enum - 175 | if (state_q == 2'd3) state_d = S0; - | ^~ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:199:32: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_if_direct_target_u.state_q': assigned value 3 is not present in the declared enum - 199 | if (state_q == S0) state_d = 2'd3; +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:249:32: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_if_else_target_u.state_q': assigned value 3 is not present in the declared enum + 249 | if (state_q == S0) state_d = sel ? S1 : 2'd3; | ^ %Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:224:32: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_if_then_target_u.state_q': assigned value 3 is not present in the declared enum 224 | if (state_q == S0) state_d = sel ? 2'd3 : S1; | ^ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:249:32: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_if_else_target_u.state_q': assigned value 3 is not present in the declared enum - 249 | if (state_q == S0) state_d = sel ? S1 : 2'd3; +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:199:32: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_if_direct_target_u.state_q': assigned value 3 is not present in the declared enum + 199 | if (state_q == S0) state_d = 2'd3; | ^ -%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:274:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_wide_direct_u.state_q': assigned value 40'hffffffffff is not present in the declared enum - 274 | S0: state_d = 40'hffff_ffff_ff; +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:175:5: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_if_source_u.state_q': case item value 3 is not present in the declared enum + 175 | if (state_q == 2'd3) state_d = S0; + | ^~ +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:150:7: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_source_u.state_q': case item value 3 is not present in the declared enum + 150 | 2'd3: state_d = S0; + | ^~~~ +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:126:15: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_reset_u.state_q': assigned value 3 is not present in the declared enum + 126 | state_q <= 2'd3; + | ^~ +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:79:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_direct_u.state_q': assigned value 3 is not present in the declared enum + 79 | S0: state_d = 2'd3; + | ^ +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:53:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_else_u.state_q': assigned value 3 is not present in the declared enum + 53 | S0: state_d = sel ? S1 : 2'd3; + | ^ +%Warning-COVERIGN: t/t_cover_fsm_if_unknown_enum_multi_bad.v:26:19: Ignoring unsupported: FSM coverage on enum state variable 't.unknown_then_u.state_q': assigned value 3 is not present in the declared enum + 26 | S0: state_d = sel ? 2'd3 : S1; | ^ %Error: Exiting due to diff --git a/test_regress/t/t_cover_fsm_plain_always_warn_multi_bad.out b/test_regress/t/t_cover_fsm_plain_always_warn_multi_bad.out index 064ef2ff1..976c00e07 100644 --- a/test_regress/t/t_cover_fsm_plain_always_warn_multi_bad.out +++ b/test_regress/t/t_cover_fsm_plain_always_warn_multi_bad.out @@ -1,12 +1,12 @@ -%Warning-COVERIGN: t/t_cover_fsm_plain_always_warn_multi_bad.v:25:5: Ignoring unsupported: FSM coverage on non-clocked always blocks requires a combinational sensitivity list or always_comb - 25 | case (state_q) +%Warning-COVERIGN: t/t_cover_fsm_plain_always_warn_multi_bad.v:86:5: Ignoring unsupported: FSM coverage on non-clocked always blocks requires a combinational sensitivity list or always_comb + 86 | case (state_q) | ^~~~ ... For warning description see https://verilator.org/warn/COVERIGN?v=latest ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. %Warning-COVERIGN: t/t_cover_fsm_plain_always_warn_multi_bad.v:55:5: Ignoring unsupported: FSM coverage on non-clocked always blocks requires a combinational sensitivity list or always_comb 55 | case (state_d) | ^~~~ -%Warning-COVERIGN: t/t_cover_fsm_plain_always_warn_multi_bad.v:86:5: Ignoring unsupported: FSM coverage on non-clocked always blocks requires a combinational sensitivity list or always_comb - 86 | case (state_q) +%Warning-COVERIGN: t/t_cover_fsm_plain_always_warn_multi_bad.v:25:5: Ignoring unsupported: FSM coverage on non-clocked always blocks requires a combinational sensitivity list or always_comb + 25 | case (state_q) | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_dpi_2exparg_bad.out b/test_regress/t/t_dpi_2exparg_bad.out index e908a6f57..812511578 100644 --- a/test_regress/t/t_dpi_2exparg_bad.out +++ b/test_regress/t/t_dpi_2exparg_bad.out @@ -10,12 +10,12 @@ | ^ ... For warning description see https://verilator.org/warn/WIDTHTRUNC?v=latest ... Use "/* verilator lint_off WIDTHTRUNC */" and lint_on around source to disable this message. -%Error: t/t_dpi_2exparg_bad.v:21:8: Duplicate declaration of DPI function with different signature: 'dpix_twice' - 21 | task dpix_twice(input int i, output [63:0] o); - | ^~~~~~~~~~ - : ... New signature: void dpix_twice (int, svLogicVecVal* /* logic[63:0] */ ) - t/t_dpi_2exparg_bad.v:12:8: ... Original signature: void dpix_twice (int, svLogicVecVal* /* logic[2:0] */ ) +%Error: t/t_dpi_2exparg_bad.v:12:8: Duplicate declaration of DPI function with different signature: 'dpix_twice' 12 | task dpix_twice(input int i, output [2:0] o); + | ^~~~~~~~~~ + : ... New signature: void dpix_twice (int, svLogicVecVal* /* logic[2:0] */ ) + t/t_dpi_2exparg_bad.v:21:8: ... Original signature: void dpix_twice (int, svLogicVecVal* /* logic[63:0] */ ) + 21 | task dpix_twice(input int i, output [63:0] o); | ^~~~~~~~~~ ... 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_fsmmulti_combo_multi_warn_bad.out b/test_regress/t/t_fsmmulti_combo_multi_warn_bad.out index 8985f2c91..367f70f55 100644 --- a/test_regress/t/t_fsmmulti_combo_multi_warn_bad.out +++ b/test_regress/t/t_fsmmulti_combo_multi_warn_bad.out @@ -1,29 +1,3 @@ -%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:36:21: FSM coverage: multiple supported transition candidates found in the same combinational always block. Only the first candidate will be instrumented. - 36 | B0: state_b_d = B1; - | ^ - t/t_fsmmulti_combo_multi_warn_bad.v:32:21: ... Location of first supported candidate for 't.same_u.state_a_q' - 32 | A0: state_a_d = A1; - | ^ - ... For warning description see https://verilator.org/warn/FSMMULTI?v=latest - ... Use "/* verilator lint_off FSMMULTI */" and lint_on around source to disable this message. -%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:73:19: FSM coverage: multiple supported transition candidates found for the same FSM in combinational always blocks. Only the first candidate will be instrumented. - 73 | S0: state_d = S1; - | ^ - t/t_fsmmulti_combo_multi_warn_bad.v:65:19: ... Location of first supported candidate for 't.split_u.state_q' - 65 | S0: state_d = S1; - | ^ -%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:107:5: FSM coverage: multiple supported transition candidates found in the same combinational always block. Only the first candidate will be instrumented. - 107 | if (state_b_q == B0) state_b_d = B1; - | ^~ - t/t_fsmmulti_combo_multi_warn_bad.v:105:5: ... Location of first supported candidate for 't.same_if_u.state_a_q' - 105 | if (state_a_q == A0) state_a_d = A1; - | ^~ -%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:140:5: FSM coverage: multiple supported transition candidates found for the same FSM in combinational always blocks. Only the first candidate will be instrumented. - 140 | if (state_q == S0) state_d = S1; - | ^~ - t/t_fsmmulti_combo_multi_warn_bad.v:134:5: ... Location of first supported candidate for 't.split_if_u.state_q' - 134 | if (state_q == S0) state_d = S1; - | ^~ %Warning-COVERIGN: t/t_fsmmulti_combo_multi_warn_bad.v:165:5: Ignoring unsupported: FSM coverage on multiple supported if-chain statements found in the same combinational always block. Only the first candidate will be instrumented. 165 | if (state_q == S1) state_d = S0; | ^~ @@ -32,4 +6,30 @@ | ^~ ... For warning description see https://verilator.org/warn/COVERIGN?v=latest ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:140:5: FSM coverage: multiple supported transition candidates found for the same FSM in combinational always blocks. Only the first candidate will be instrumented. + 140 | if (state_q == S0) state_d = S1; + | ^~ + t/t_fsmmulti_combo_multi_warn_bad.v:134:5: ... Location of first supported candidate for 't.split_if_u.state_q' + 134 | if (state_q == S0) state_d = S1; + | ^~ + ... For warning description see https://verilator.org/warn/FSMMULTI?v=latest + ... Use "/* verilator lint_off FSMMULTI */" and lint_on around source to disable this message. +%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:107:5: FSM coverage: multiple supported transition candidates found in the same combinational always block. Only the first candidate will be instrumented. + 107 | if (state_b_q == B0) state_b_d = B1; + | ^~ + t/t_fsmmulti_combo_multi_warn_bad.v:105:5: ... Location of first supported candidate for 't.same_if_u.state_a_q' + 105 | if (state_a_q == A0) state_a_d = A1; + | ^~ +%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:73:19: FSM coverage: multiple supported transition candidates found for the same FSM in combinational always blocks. Only the first candidate will be instrumented. + 73 | S0: state_d = S1; + | ^ + t/t_fsmmulti_combo_multi_warn_bad.v:65:19: ... Location of first supported candidate for 't.split_u.state_q' + 65 | S0: state_d = S1; + | ^ +%Warning-FSMMULTI: t/t_fsmmulti_combo_multi_warn_bad.v:36:21: FSM coverage: multiple supported transition candidates found in the same combinational always block. Only the first candidate will be instrumented. + 36 | B0: state_b_d = B1; + | ^ + t/t_fsmmulti_combo_multi_warn_bad.v:32:21: ... Location of first supported candidate for 't.same_u.state_a_q' + 32 | A0: state_a_d = A1; + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_gen_upscope.out b/test_regress/t/t_gen_upscope.out index 3765ed4ca..bb8b8e714 100644 --- a/test_regress/t/t_gen_upscope.out +++ b/test_regress/t/t_gen_upscope.out @@ -1,12 +1,12 @@ -created tag with scope = top.t.tag -created tag with scope = top.t.b.gen[0].tag created tag with scope = top.t.b.gen[1].tag +created tag with scope = top.t.b.gen[0].tag +created tag with scope = top.t.tag mod a has scope = top.t mod a has tag = top.t.tag mod b has scope = top.t.b mod b has tag = top.t.tag -mod c has scope = top.t.b.gen[0].c -mod c has tag = top.t.b.gen[0].tag mod c has scope = top.t.b.gen[1].c mod c has tag = top.t.b.gen[1].tag +mod c has scope = top.t.b.gen[0].c +mod c has tag = top.t.b.gen[0].tag *-* All Finished *-* diff --git a/test_regress/t/t_genfor_signed.out b/test_regress/t/t_genfor_signed.out index 7f921d5a9..b99a494fc 100644 --- a/test_regress/t/t_genfor_signed.out +++ b/test_regress/t/t_genfor_signed.out @@ -1,9 +1,9 @@ -top.t.u_sub1.unnamedblk1 1..1 i=1 +top.t.SUB_PIPE[0].u_sub.unnamedblk1 1..0 i=1 +top.t.SUB_PIPE[0].u_sub.unnamedblk1 1..0 i=0 top.t.u_sub0.unnamedblk1 1..0 i=1 top.t.u_sub0.unnamedblk1 1..0 i=0 top.t.SUB_PIPE[-1].u_sub.unnamedblk1 1..-1 i=1 top.t.SUB_PIPE[-1].u_sub.unnamedblk1 1..-1 i=0 top.t.SUB_PIPE[-1].u_sub.unnamedblk1 1..-1 i=-1 -top.t.SUB_PIPE[0].u_sub.unnamedblk1 1..0 i=1 -top.t.SUB_PIPE[0].u_sub.unnamedblk1 1..0 i=0 +top.t.u_sub1.unnamedblk1 1..1 i=1 *-* All Finished *-* diff --git a/test_regress/t/t_inst_tree_inl1_pub0.py b/test_regress/t/t_inst_tree_inl1_pub0.py index 443c7b9b1..036210aaa 100755 --- a/test_regress/t/t_inst_tree_inl1_pub0.py +++ b/test_regress/t/t_inst_tree_inl1_pub0.py @@ -20,11 +20,11 @@ test.compile( if test.vlt_all: test.file_grep( out_filename, - r'{"type":"VAR","name":"t.u.u0.u0.z1",.*"loc":"\w,70:[^"]*",.*"origName":"z1",.*"dtypeName":"logic"' + r'{"type":"VAR","name":"t.u.u1.u0.z1",.*"loc":"\w,70:[^"]*",.*"origName":"z1",.*"dtypeName":"logic"' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.u.u0.u1.z1",.*"loc":"\w,70:[^"]*",.*"origName":"z1",.*"dtypeName":"logic"' + r'{"type":"VAR","name":"t.u.u1.u1.z1",.*"loc":"\w,70:[^"]*",.*"origName":"z1",.*"dtypeName":"logic"' ) test.file_grep( out_filename, diff --git a/test_regress/t/t_json_only_flat.out b/test_regress/t/t_json_only_flat.out index e90bba08b..6b3d6ca6c 100644 --- a/test_regress/t/t_json_only_flat.out +++ b/test_regress/t/t_json_only_flat.out @@ -24,20 +24,20 @@ "valuep": [ {"type":"CONST","name":"2'h2","addr":"(Z)","loc":"d,25:43,25:44","dtypep":"(T)"} ],"attrsp": []}, - {"type":"VAR","name":"t.cell1.WIDTH","addr":"(AB)","loc":"d,48:15,48:20","dtypep":"(BB)","origName":"WIDTH","verilogName":"WIDTH","direction":"NONE","isConst":true,"lifetime":"VSTATICI","varType":"GPARAM","dtypeName":"logic","isGParam":true,"isParam":true,"hasUserInit":true,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [], + {"type":"VAR","name":"t.cell2.clk","addr":"(AB)","loc":"d,62:11,62:14","dtypep":"(J)","origName":"clk","verilogName":"clk","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"t.cell2.d","addr":"(BB)","loc":"d,63:17,63:18","dtypep":"(H)","origName":"d","verilogName":"d","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"t.cell2.q","addr":"(CB)","loc":"d,64:23,64:24","dtypep":"(H)","origName":"q","verilogName":"q","direction":"NONE","icoMaybeWritten":true,"lifetime":"VSTATICI","varType":"WIRE","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"t.cell1.WIDTH","addr":"(DB)","loc":"d,48:15,48:20","dtypep":"(EB)","origName":"WIDTH","verilogName":"WIDTH","direction":"NONE","isConst":true,"lifetime":"VSTATICI","varType":"GPARAM","dtypeName":"logic","isGParam":true,"isParam":true,"hasUserInit":true,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [], "valuep": [ - {"type":"CONST","name":"32'sh4","addr":"(CB)","loc":"d,32:14,32:15","dtypep":"(DB)"} + {"type":"CONST","name":"32'sh4","addr":"(FB)","loc":"d,32:14,32:15","dtypep":"(GB)"} ],"attrsp": []}, - {"type":"VAR","name":"t.cell1.clk","addr":"(EB)","loc":"d,50:11,50:14","dtypep":"(J)","origName":"clk","verilogName":"clk","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"t.cell1.d","addr":"(FB)","loc":"d,51:23,51:24","dtypep":"(H)","origName":"d","verilogName":"d","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"t.cell1.q","addr":"(GB)","loc":"d,52:30,52:31","dtypep":"(H)","origName":"q","verilogName":"q","direction":"NONE","icoMaybeWritten":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"t.cell1.IGNORED","addr":"(HB)","loc":"d,55:14,55:21","dtypep":"(BB)","origName":"IGNORED","verilogName":"IGNORED","direction":"NONE","isConst":true,"lifetime":"VSTATICI","varType":"LPARAM","dtypeName":"logic","isParam":true,"hasUserInit":true,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [], + {"type":"VAR","name":"t.cell1.clk","addr":"(HB)","loc":"d,50:11,50:14","dtypep":"(J)","origName":"clk","verilogName":"clk","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"t.cell1.d","addr":"(IB)","loc":"d,51:23,51:24","dtypep":"(H)","origName":"d","verilogName":"d","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"t.cell1.q","addr":"(JB)","loc":"d,52:30,52:31","dtypep":"(H)","origName":"q","verilogName":"q","direction":"NONE","icoMaybeWritten":true,"lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"t.cell1.IGNORED","addr":"(KB)","loc":"d,55:14,55:21","dtypep":"(EB)","origName":"IGNORED","verilogName":"IGNORED","direction":"NONE","isConst":true,"lifetime":"VSTATICI","varType":"LPARAM","dtypeName":"logic","isParam":true,"hasUserInit":true,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [], "valuep": [ - {"type":"CONST","name":"32'sh1","addr":"(IB)","loc":"d,55:24,55:25","dtypep":"(DB)"} + {"type":"CONST","name":"32'sh1","addr":"(LB)","loc":"d,55:24,55:25","dtypep":"(GB)"} ],"attrsp": []}, - {"type":"VAR","name":"t.cell2.clk","addr":"(JB)","loc":"d,62:11,62:14","dtypep":"(J)","origName":"clk","verilogName":"clk","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"t.cell2.d","addr":"(KB)","loc":"d,63:17,63:18","dtypep":"(H)","origName":"d","verilogName":"d","direction":"NONE","lifetime":"VSTATICI","varType":"PORT","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"VAR","name":"t.cell2.q","addr":"(LB)","loc":"d,64:23,64:24","dtypep":"(H)","origName":"q","verilogName":"q","direction":"NONE","icoMaybeWritten":true,"lifetime":"VSTATICI","varType":"WIRE","dtypeName":"logic","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"TOPSCOPE","name":"","addr":"(E)","loc":"d,7:8,7:9","senTreesp": [], "scopep": [ {"type":"SCOPE","name":"TOP","addr":"(MB)","loc":"d,7:8,7:9","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(F)", @@ -85,74 +85,74 @@ {"type":"VARSCOPE","name":"t.S_IDLE","addr":"(JC)","loc":"d,23:26,23:32","dtypep":"(T)","isTrace":true,"scopep":"(MB)","varp":"(S)"}, {"type":"VARSCOPE","name":"t.S_FETCH","addr":"(KC)","loc":"d,24:26,24:33","dtypep":"(T)","isTrace":true,"scopep":"(MB)","varp":"(W)"}, {"type":"VARSCOPE","name":"t.S_EXEC","addr":"(LC)","loc":"d,25:26,25:32","dtypep":"(T)","isTrace":true,"scopep":"(MB)","varp":"(Y)"}, - {"type":"VARSCOPE","name":"t.cell1.WIDTH","addr":"(MC)","loc":"d,48:15,48:20","dtypep":"(BB)","isTrace":true,"scopep":"(MB)","varp":"(AB)"}, - {"type":"VARSCOPE","name":"t.cell1.clk","addr":"(NC)","loc":"d,50:11,50:14","dtypep":"(J)","isTrace":true,"scopep":"(MB)","varp":"(EB)"}, - {"type":"ALWAYS","name":"","addr":"(OC)","loc":"d,50:11,50:14","keyword":"cont_assign","sentreep": [], + {"type":"VARSCOPE","name":"t.cell2.clk","addr":"(MC)","loc":"d,62:11,62:14","dtypep":"(J)","isTrace":true,"scopep":"(MB)","varp":"(AB)"}, + {"type":"ALWAYS","name":"","addr":"(NC)","loc":"d,62:11,62:14","keyword":"cont_assign","sentreep": [], "stmtsp": [ - {"type":"ASSIGNW","name":"","addr":"(PC)","loc":"d,50:11,50:14","dtypep":"(J)", + {"type":"ASSIGNW","name":"","addr":"(OC)","loc":"d,62:11,62:14","dtypep":"(J)", "rhsp": [ - {"type":"VARREF","name":"clk","addr":"(QC)","loc":"d,50:11,50:14","dtypep":"(J)","access":"RD","varp":"(I)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"clk","addr":"(PC)","loc":"d,62:11,62:14","dtypep":"(J)","access":"RD","varp":"(I)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.clk","addr":"(RC)","loc":"d,50:11,50:14","dtypep":"(J)","access":"WR","varp":"(EB)","varScopep":"(NC)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.clk","addr":"(QC)","loc":"d,62:11,62:14","dtypep":"(J)","access":"WR","varp":"(AB)","varScopep":"(MC)","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} ]}, - {"type":"VARSCOPE","name":"t.cell1.d","addr":"(SC)","loc":"d,51:23,51:24","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(FB)"}, - {"type":"ALWAYS","name":"","addr":"(TC)","loc":"d,51:23,51:24","keyword":"cont_assign","sentreep": [], + {"type":"VARSCOPE","name":"t.cell2.d","addr":"(RC)","loc":"d,63:17,63:18","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(BB)"}, + {"type":"ALWAYS","name":"","addr":"(SC)","loc":"d,63:17,63:18","keyword":"cont_assign","sentreep": [], "stmtsp": [ - {"type":"ASSIGNW","name":"","addr":"(UC)","loc":"d,51:23,51:24","dtypep":"(H)", + {"type":"ASSIGNW","name":"","addr":"(TC)","loc":"d,63:17,63:18","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"d","addr":"(VC)","loc":"d,51:23,51:24","dtypep":"(H)","access":"RD","varp":"(K)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.between","addr":"(UC)","loc":"d,63:17,63:18","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.d","addr":"(WC)","loc":"d,51:23,51:24","dtypep":"(H)","access":"WR","varp":"(FB)","varScopep":"(SC)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.d","addr":"(VC)","loc":"d,63:17,63:18","dtypep":"(H)","access":"WR","varp":"(BB)","varScopep":"(RC)","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} ]}, - {"type":"VARSCOPE","name":"t.cell1.q","addr":"(XC)","loc":"d,52:30,52:31","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(GB)"}, - {"type":"ALWAYS","name":"","addr":"(YC)","loc":"d,52:30,52:31","keyword":"cont_assign","sentreep": [], + {"type":"VARSCOPE","name":"t.cell2.q","addr":"(WC)","loc":"d,64:23,64:24","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(CB)"}, + {"type":"ALWAYS","name":"","addr":"(XC)","loc":"d,64:23,64:24","keyword":"cont_assign","sentreep": [], "stmtsp": [ - {"type":"ASSIGNW","name":"","addr":"(ZC)","loc":"d,52:30,52:31","dtypep":"(H)", + {"type":"ASSIGNW","name":"","addr":"(YC)","loc":"d,64:23,64:24","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.between","addr":"(AD)","loc":"d,52:30,52:31","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"q","addr":"(ZC)","loc":"d,64:23,64:24","dtypep":"(H)","access":"RD","varp":"(G)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.q","addr":"(BD)","loc":"d,52:30,52:31","dtypep":"(H)","access":"WR","varp":"(GB)","varScopep":"(XC)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.q","addr":"(AD)","loc":"d,64:23,64:24","dtypep":"(H)","access":"WR","varp":"(CB)","varScopep":"(WC)","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} ]}, - {"type":"VARSCOPE","name":"t.cell1.IGNORED","addr":"(CD)","loc":"d,55:14,55:21","dtypep":"(BB)","isTrace":true,"scopep":"(MB)","varp":"(HB)"}, - {"type":"VARSCOPE","name":"t.cell2.clk","addr":"(DD)","loc":"d,62:11,62:14","dtypep":"(J)","isTrace":true,"scopep":"(MB)","varp":"(JB)"}, - {"type":"ALWAYS","name":"","addr":"(ED)","loc":"d,62:11,62:14","keyword":"cont_assign","sentreep": [], + {"type":"VARSCOPE","name":"t.cell1.WIDTH","addr":"(BD)","loc":"d,48:15,48:20","dtypep":"(EB)","isTrace":true,"scopep":"(MB)","varp":"(DB)"}, + {"type":"VARSCOPE","name":"t.cell1.clk","addr":"(CD)","loc":"d,50:11,50:14","dtypep":"(J)","isTrace":true,"scopep":"(MB)","varp":"(HB)"}, + {"type":"ALWAYS","name":"","addr":"(DD)","loc":"d,50:11,50:14","keyword":"cont_assign","sentreep": [], "stmtsp": [ - {"type":"ASSIGNW","name":"","addr":"(FD)","loc":"d,62:11,62:14","dtypep":"(J)", + {"type":"ASSIGNW","name":"","addr":"(ED)","loc":"d,50:11,50:14","dtypep":"(J)", "rhsp": [ - {"type":"VARREF","name":"clk","addr":"(GD)","loc":"d,62:11,62:14","dtypep":"(J)","access":"RD","varp":"(I)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"clk","addr":"(FD)","loc":"d,50:11,50:14","dtypep":"(J)","access":"RD","varp":"(I)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.clk","addr":"(HD)","loc":"d,62:11,62:14","dtypep":"(J)","access":"WR","varp":"(JB)","varScopep":"(DD)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.clk","addr":"(GD)","loc":"d,50:11,50:14","dtypep":"(J)","access":"WR","varp":"(HB)","varScopep":"(CD)","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} ]}, - {"type":"VARSCOPE","name":"t.cell2.d","addr":"(ID)","loc":"d,63:17,63:18","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(KB)"}, - {"type":"ALWAYS","name":"","addr":"(JD)","loc":"d,63:17,63:18","keyword":"cont_assign","sentreep": [], + {"type":"VARSCOPE","name":"t.cell1.d","addr":"(HD)","loc":"d,51:23,51:24","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(IB)"}, + {"type":"ALWAYS","name":"","addr":"(ID)","loc":"d,51:23,51:24","keyword":"cont_assign","sentreep": [], "stmtsp": [ - {"type":"ASSIGNW","name":"","addr":"(KD)","loc":"d,63:17,63:18","dtypep":"(H)", + {"type":"ASSIGNW","name":"","addr":"(JD)","loc":"d,51:23,51:24","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.between","addr":"(LD)","loc":"d,63:17,63:18","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"d","addr":"(KD)","loc":"d,51:23,51:24","dtypep":"(H)","access":"RD","varp":"(K)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.d","addr":"(MD)","loc":"d,63:17,63:18","dtypep":"(H)","access":"WR","varp":"(KB)","varScopep":"(ID)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.d","addr":"(LD)","loc":"d,51:23,51:24","dtypep":"(H)","access":"WR","varp":"(IB)","varScopep":"(HD)","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} ]}, - {"type":"VARSCOPE","name":"t.cell2.q","addr":"(ND)","loc":"d,64:23,64:24","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(LB)"}, - {"type":"ALWAYS","name":"","addr":"(OD)","loc":"d,64:23,64:24","keyword":"cont_assign","sentreep": [], + {"type":"VARSCOPE","name":"t.cell1.q","addr":"(MD)","loc":"d,52:30,52:31","dtypep":"(H)","isTrace":true,"scopep":"(MB)","varp":"(JB)"}, + {"type":"ALWAYS","name":"","addr":"(ND)","loc":"d,52:30,52:31","keyword":"cont_assign","sentreep": [], "stmtsp": [ - {"type":"ASSIGNW","name":"","addr":"(PD)","loc":"d,64:23,64:24","dtypep":"(H)", + {"type":"ASSIGNW","name":"","addr":"(OD)","loc":"d,52:30,52:31","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"q","addr":"(QD)","loc":"d,64:23,64:24","dtypep":"(H)","access":"RD","varp":"(G)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.between","addr":"(PD)","loc":"d,52:30,52:31","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.q","addr":"(RD)","loc":"d,64:23,64:24","dtypep":"(H)","access":"WR","varp":"(LB)","varScopep":"(ND)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.q","addr":"(QD)","loc":"d,52:30,52:31","dtypep":"(H)","access":"WR","varp":"(JB)","varScopep":"(MD)","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} - ]} + ]}, + {"type":"VARSCOPE","name":"t.cell1.IGNORED","addr":"(RD)","loc":"d,55:14,55:21","dtypep":"(EB)","isTrace":true,"scopep":"(MB)","varp":"(KB)"} ], "blocksp": [ {"type":"ALWAYS","name":"","addr":"(SD)","loc":"d,27:23,27:24","keyword":"cont_assign","sentreep": [], @@ -221,34 +221,34 @@ {"type":"VARREF","name":"t.anonymous_expr","addr":"(RE)","loc":"d,29:10,29:24","dtypep":"(J)","access":"WR","varp":"(R)","varScopep":"(IC)","classOrPackagep":"UNLINKED"} ],"timingControlp": [],"strengthSpecp": []} ]}, - {"type":"ALWAYS","name":"","addr":"(SE)","loc":"d,57:3,57:9","keyword":"always", + {"type":"ALWAYS","name":"","addr":"(SE)","loc":"d,67:12,67:13","keyword":"cont_assign","sentreep": [], + "stmtsp": [ + {"type":"ASSIGNW","name":"","addr":"(TE)","loc":"d,67:12,67:13","dtypep":"(H)", + "rhsp": [ + {"type":"VARREF","name":"t.between","addr":"(UE)","loc":"d,67:14,67:15","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} + ], + "lhsp": [ + {"type":"VARREF","name":"q","addr":"(VE)","loc":"d,67:10,67:11","dtypep":"(H)","access":"WR","varp":"(G)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} + ],"timingControlp": [],"strengthSpecp": []} + ]}, + {"type":"ALWAYS","name":"","addr":"(WE)","loc":"d,57:3,57:9","keyword":"always", "sentreep": [ - {"type":"SENTREE","name":"","addr":"(TE)","loc":"d,57:10,57:11", + {"type":"SENTREE","name":"","addr":"(XE)","loc":"d,57:10,57:11", "sensesp": [ - {"type":"SENITEM","name":"","addr":"(UE)","loc":"d,57:12,57:19","edgeType":"POS", + {"type":"SENITEM","name":"","addr":"(YE)","loc":"d,57:12,57:19","edgeType":"POS", "sensp": [ - {"type":"VARREF","name":"clk","addr":"(VE)","loc":"d,57:20,57:23","dtypep":"(J)","access":"RD","varp":"(I)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"clk","addr":"(ZE)","loc":"d,57:20,57:23","dtypep":"(J)","access":"RD","varp":"(I)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} ],"condp": []} ]} ], "stmtsp": [ - {"type":"ASSIGNDLY","name":"","addr":"(WE)","loc":"d,57:27,57:29","dtypep":"(H)", + {"type":"ASSIGNDLY","name":"","addr":"(AF)","loc":"d,57:27,57:29","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"d","addr":"(XE)","loc":"d,57:30,57:31","dtypep":"(H)","access":"RD","varp":"(K)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"d","addr":"(BF)","loc":"d,57:30,57:31","dtypep":"(H)","access":"RD","varp":"(K)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.between","addr":"(YE)","loc":"d,57:25,57:26","dtypep":"(H)","access":"WR","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.between","addr":"(CF)","loc":"d,57:25,57:26","dtypep":"(H)","access":"WR","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} ],"timingControlp": []} - ]}, - {"type":"ALWAYS","name":"","addr":"(ZE)","loc":"d,67:12,67:13","keyword":"cont_assign","sentreep": [], - "stmtsp": [ - {"type":"ASSIGNW","name":"","addr":"(AF)","loc":"d,67:12,67:13","dtypep":"(H)", - "rhsp": [ - {"type":"VARREF","name":"t.between","addr":"(BF)","loc":"d,67:14,67:15","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(FC)","classOrPackagep":"UNLINKED"} - ], - "lhsp": [ - {"type":"VARREF","name":"q","addr":"(CF)","loc":"d,67:10,67:11","dtypep":"(H)","access":"WR","varp":"(G)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} - ],"timingControlp": [],"strengthSpecp": []} ]} ],"inlinesp": []} ]} @@ -260,11 +260,11 @@ {"type":"BASICDTYPE","name":"bit","addr":"(V)","loc":"d,23:35,23:40","dtypep":"(V)","keyword":"bit","range":"1:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(J)","loc":"d,27:32,27:34","dtypep":"(J)","keyword":"logic","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(T)","loc":"d,23:14,23:19","dtypep":"(T)","keyword":"logic","range":"1:0","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"logic","addr":"(BB)","loc":"d,48:15,48:20","dtypep":"(BB)","keyword":"logic","range":"31:0","generic":true,"signed":true,"rangep": []}, + {"type":"BASICDTYPE","name":"logic","addr":"(EB)","loc":"d,48:15,48:20","dtypep":"(EB)","keyword":"logic","range":"31:0","generic":true,"signed":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(H)","loc":"d,16:15,16:16","dtypep":"(H)","keyword":"logic","range":"3:0","generic":true,"rangep": []}, {"type":"BASICDTYPE","name":"logic","addr":"(AE)","loc":"d,27:26,27:27","dtypep":"(AE)","keyword":"logic","range":"1:0","generic":true,"signed":true,"rangep": []}, {"type":"BASICDTYPE","name":"bit","addr":"(VD)","loc":"d,27:32,27:34","dtypep":"(VD)","keyword":"bit","generic":true,"rangep": []}, - {"type":"BASICDTYPE","name":"bit","addr":"(DB)","loc":"d,29:48,29:49","dtypep":"(DB)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []} + {"type":"BASICDTYPE","name":"bit","addr":"(GB)","loc":"d,29:48,29:49","dtypep":"(GB)","keyword":"bit","range":"31:0","generic":true,"signed":true,"rangep": []} ]}, {"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0", "modulep": [ diff --git a/test_regress/t/t_lint_unusedloop_removed_bad.out b/test_regress/t/t_lint_unusedloop_removed_bad.out index 792568e78..16b88ada9 100644 --- a/test_regress/t/t_lint_unusedloop_removed_bad.out +++ b/test_regress/t/t_lint_unusedloop_removed_bad.out @@ -21,12 +21,6 @@ %Warning-UNUSEDLOOP: t/t_lint_unusedloop_removed_bad.v:280:5: Loop is not used and will be optimized out 280 | while (m_2_ticked); | ^~~~~ -%Warning-UNUSEDLOOP: t/t_lint_unusedloop_removed_bad.v:114:5: Loop condition is always false - 114 | while(always_zero < 0) begin - | ^~~~~ -%Warning-UNUSEDLOOP: t/t_lint_unusedloop_removed_bad.v:171:5: Loop condition is always false - 171 | while(always_false) begin - | ^~~~~ %Warning-UNUSEDLOOP: t/t_lint_unusedloop_removed_bad.v:181:5: Loop condition is always false 181 | while(always_zero) begin | ^~~~~ @@ -36,4 +30,10 @@ %Warning-UNUSEDLOOP: t/t_lint_unusedloop_removed_bad.v:190:5: Loop condition is always false 190 | for (int i = 0; i < always_zero; i++) | ^~~ +%Warning-UNUSEDLOOP: t/t_lint_unusedloop_removed_bad.v:171:5: Loop condition is always false + 171 | while(always_false) begin + | ^~~~~ +%Warning-UNUSEDLOOP: t/t_lint_unusedloop_removed_bad.v:114:5: Loop condition is always false + 114 | while(always_zero < 0) begin + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_opt_dedupe_clk_gate.py b/test_regress/t/t_opt_dedupe_clk_gate.py index 02778fcec..0fbd122ba 100755 --- a/test_regress/t/t_opt_dedupe_clk_gate.py +++ b/test_regress/t/t_opt_dedupe_clk_gate.py @@ -18,7 +18,7 @@ test.compile(verilator_flags2=["--no-json-edit-nums", "--stats"]) if test.vlt_all: test.file_grep( out_filename, - r'{"type":"VAR","name":"t.f0.clock_gate.clken_latched","addr":"[^"]*","loc":"\w,44:[^"]*","dtypep":"\(\w+\)",.*"origName":"clken_latched",.*"isLatched":true,.*"dtypeName":"logic"' + r'{"type":"VAR","name":"t.f1.clock_gate.clken_latched","addr":"[^"]*","loc":"\w,44:[^"]*","dtypep":"\(\w+\)",.*"origName":"clken_latched",.*"isLatched":true,.*"dtypeName":"logic"' ) test.file_grep(test.stats, r'Optimizations, Gate sigs deduped\s+(\d+)', 2) diff --git a/test_regress/t/t_unoptflat_simple_3_bad.out b/test_regress/t/t_unoptflat_simple_3_bad.out index f5ef439ae..7e31eb84d 100644 --- a/test_regress/t/t_unoptflat_simple_3_bad.out +++ b/test_regress/t/t_unoptflat_simple_3_bad.out @@ -4,8 +4,8 @@ ... For warning description see https://verilator.org/warn/UNOPTFLAT?v=latest ... Use "/* verilator lint_off UNOPTFLAT */" and lint_on around source to disable this message. t/t_unoptflat_simple_3.v:16:14: Example path: t.x - t/t_unoptflat_simple_3.v:58:18: Example path: ASSIGNW - t/t_unoptflat_simple_3.v:56:21: Example path: t.__Vcellout__test1i__xvecout - t/t_unoptflat_simple_3.v:21:8: Example path: ASSIGNW + t/t_unoptflat_simple_3.v:76:18: Example path: ASSIGNW + t/t_unoptflat_simple_3.v:74:21: Example path: t.__Vcellout__test2i__xvecout + t/t_unoptflat_simple_3.v:27:8: Example path: ASSIGNW t/t_unoptflat_simple_3.v:16:14: Example path: t.x %Error: Exiting due to diff --git a/test_regress/t/t_x_rand_mt_stability.out b/test_regress/t/t_x_rand_mt_stability.out index 30d041b29..cf0f962d9 100644 --- a/test_regress/t/t_x_rand_mt_stability.out +++ b/test_regress/t/t_x_rand_mt_stability.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x5fa24450 diff --git a/test_regress/t/t_x_rand_mt_stability_add.out b/test_regress/t/t_x_rand_mt_stability_add.out index 30d041b29..cf0f962d9 100644 --- a/test_regress/t/t_x_rand_mt_stability_add.out +++ b/test_regress/t/t_x_rand_mt_stability_add.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x5fa24450 diff --git a/test_regress/t/t_x_rand_mt_stability_add_trace.out b/test_regress/t/t_x_rand_mt_stability_add_trace.out index 30d041b29..cf0f962d9 100644 --- a/test_regress/t/t_x_rand_mt_stability_add_trace.out +++ b/test_regress/t/t_x_rand_mt_stability_add_trace.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x5fa24450 diff --git a/test_regress/t/t_x_rand_mt_stability_trace.out b/test_regress/t/t_x_rand_mt_stability_trace.out index 30d041b29..cf0f962d9 100644 --- a/test_regress/t/t_x_rand_mt_stability_trace.out +++ b/test_regress/t/t_x_rand_mt_stability_trace.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x5fa24450 diff --git a/test_regress/t/t_x_rand_mt_stability_zeros.out b/test_regress/t/t_x_rand_mt_stability_zeros.out index 2b3a3a882..eaac43644 100644 --- a/test_regress/t/t_x_rand_mt_stability_zeros.out +++ b/test_regress/t/t_x_rand_mt_stability_zeros.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0x00000000 big = 0x0000000000000000000000000000000000000000000000000000000000000000 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x0 top.t.the_sub_yes_inline_2 no_init 0x0 +top.t.the_sub_yes_inline_1 no_init 0x0 top.t.the_sub_no_inline_1 no_init 0x0 top.t.the_sub_no_inline_2 no_init 0x0 rand = 0x5fa24450 diff --git a/test_regress/t/t_x_rand_stability.out b/test_regress/t/t_x_rand_stability.out index 1db7b9396..eaa53dd1f 100644 --- a/test_regress/t/t_x_rand_stability.out +++ b/test_regress/t/t_x_rand_stability.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x24800459 diff --git a/test_regress/t/t_x_rand_stability_add.out b/test_regress/t/t_x_rand_stability_add.out index 1db7b9396..eaa53dd1f 100644 --- a/test_regress/t/t_x_rand_stability_add.out +++ b/test_regress/t/t_x_rand_stability_add.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x24800459 diff --git a/test_regress/t/t_x_rand_stability_add_trace.out b/test_regress/t/t_x_rand_stability_add_trace.out index 1db7b9396..eaa53dd1f 100644 --- a/test_regress/t/t_x_rand_stability_add_trace.out +++ b/test_regress/t/t_x_rand_stability_add_trace.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x24800459 diff --git a/test_regress/t/t_x_rand_stability_trace.out b/test_regress/t/t_x_rand_stability_trace.out index 1db7b9396..eaa53dd1f 100644 --- a/test_regress/t/t_x_rand_stability_trace.out +++ b/test_regress/t/t_x_rand_stability_trace.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0xdf5768de big = 0x355d75711c3bf0ca3e65805db585c43beae34b336b9dbe44a2d040cbe101a665 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_yes_inline_2 no_init 0xa36c65459f4b9f46 +top.t.the_sub_yes_inline_1 no_init 0x842cd8b1033a58 top.t.the_sub_no_inline_1 no_init 0x42d55205d10c58f8 top.t.the_sub_no_inline_2 no_init 0xac98037e5042d96d rand = 0x24800459 diff --git a/test_regress/t/t_x_rand_stability_zeros.out b/test_regress/t/t_x_rand_stability_zeros.out index f0fc3e106..c6dc6b6ab 100644 --- a/test_regress/t/t_x_rand_stability_zeros.out +++ b/test_regress/t/t_x_rand_stability_zeros.out @@ -3,8 +3,8 @@ x_assigned (initial) = 0x00000000 uninitialized2 = 0x00000000 big = 0x0000000000000000000000000000000000000000000000000000000000000000 random_init = 0x5fa24450 -top.t.the_sub_yes_inline_1 no_init 0x0 top.t.the_sub_yes_inline_2 no_init 0x0 +top.t.the_sub_yes_inline_1 no_init 0x0 top.t.the_sub_no_inline_1 no_init 0x0 top.t.the_sub_no_inline_2 no_init 0x0 rand = 0x24800459 From f0f1c44dd69a4dd17f923c9ca2f85dda8c006820 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Thu, 25 Jun 2026 15:30:05 +0200 Subject: [PATCH 12/41] Fix object randomization skipped by an unrelated global constraint (#7833) (#7838) Fixes #7833. --- src/V3Randomize.cpp | 78 ++++++-- test_regress/t/t_constraint_global_subobj.py | 21 +++ test_regress/t/t_constraint_global_subobj.v | 178 +++++++++++++++++++ 3 files changed, 258 insertions(+), 19 deletions(-) create mode 100755 test_regress/t/t_constraint_global_subobj.py create mode 100644 test_regress/t/t_constraint_global_subobj.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 36699526d..95a4d35aa 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1171,16 +1171,18 @@ class ConstraintExprVisitor final : public VNVisitor { // else: Global constraints keep nodep alive for write_var processing relinker.relink(exprp); - // For global constraints: check shared path-level set - // For inline constraints: check per-instance set (each __Vrandwith has own randomizer) - // For class-level constraints: check varp->user3() + // Global / inline / class-level member-select refs key on the full path + // (so same-type sub-objects c1.x, c2.x stay distinct); a plain class-level + // variable keys on user3(). const bool alreadyWritten = isGlobalConstrained ? m_writtenVars.count(smtName) > 0 : m_inlineInitTaskp ? m_inlineWrittenVars.count(smtName) > 0 + : membersel ? m_writtenVars.count(smtName) > 0 : varp->user3(); const bool shouldWriteVar = !alreadyWritten; if (shouldWriteVar) { // Track this variable path as written - if (isGlobalConstrained) m_writtenVars.insert(smtName); + if (isGlobalConstrained || (membersel && !m_inlineInitTaskp)) + m_writtenVars.insert(smtName); if (m_inlineInitTaskp) m_inlineWrittenVars.insert(smtName); // For global constraints, delete nodep after processing if (isGlobalConstrained && !nodep->backp()) VL_DO_DANGLING(pushDeletep(nodep), nodep); @@ -3882,6 +3884,22 @@ class RandomizeVisitor final : public VNVisitor { return VN_IS(dtypep, ClassRefDType); }); } + // True if this class owns a global constraint: a member-select chain rooted + // at a rand class-typed handle reaching into a sub-object. + bool classOwnsGlobalConstraint(const AstClass* classp) const { + return classp->existsMember([](const AstClass*, const AstConstraint* constrp) { + bool owns = false; + constrp->foreach([&](const AstMemberSel* memberSelp) { + const AstNode* rootp = memberSelp->fromp(); + while (const AstMemberSel* const sp = VN_CAST(rootp, MemberSel)) + rootp = sp->fromp(); + if (const AstVarRef* const refp = VN_CAST(rootp, VarRef)) { + if (VN_IS(refp->varp()->dtypep()->skipRefp(), ClassRefDType)) owns = true; + } + }); + return owns; + }); + } // Get or create __VrandCb_pre/__VrandCb_post task for nested callbacks AstTask* getCreateNestedCallbackTask(AstClass* classp, const string& suffix) { const string name = "__VrandCb_" + suffix; @@ -4773,6 +4791,10 @@ class RandomizeVisitor final : public VNVisitor { UINFO(9, "Define randomize() for " << nodep); nodep->baseMostClassp()->needRNG(true); + // Detect global-constraint ownership BEFORE the constraint items are + // unlinked into setup tasks below (after that the member-selects are gone). + const bool basicFirst = classOwnsGlobalConstraint(nodep); + FileLine* fl = nodep->fileline(); AstFunc* const randomizep = V3Randomize::newRandomizeFunc(m_memberMap, nodep); AstVar* const fvarp = VN_AS(randomizep->fvarp(), Var); @@ -5081,29 +5103,41 @@ class RandomizeVisitor final : public VNVisitor { beginValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1}; } + AstFunc* const basicRandomizep + = V3Randomize::newRandomizeFunc(m_memberMap, nodep, BASIC_RANDOMIZE_FUNC_NAME); + addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp); + // A basicFirst owner basic-randomizes first, then the solver overrides the + // constrained leaves, so a globally-constrained leaf (not user3) is still + // basic-randomized when its type is randomized standalone. AstVarRef* const fvarRefp = new AstVarRef{fl, fvarp, VAccess::WRITE}; - randomizep->addStmtsp(new AstAssign{fl, fvarRefp, beginValp}); + randomizep->addStmtsp(new AstAssign{ + fl, fvarRefp, basicFirst ? new AstFuncRef{fl, basicRandomizep} : beginValp}); const auto sizeArraysIt = m_sizeConstrainedArrays.find(nodep); const bool needsSizePhase = sizeArraysIt != m_sizeConstrainedArrays.end() && !sizeArraysIt->second.empty(); - if (!needsSizePhase) { + // Size-only resize fallback (no element constraint, so no two-pass phase). + // Must run after the solver .next() that sets the size variable; emit it + // after whichever assignment below holds the solver call. + const auto emitResizeFallback = [&]() { + if (needsSizePhase) return; if (AstTask* const resizeAllTaskp = VN_AS(m_memberMap.findMember(nodep, "__Vresize_constrained_arrays"), Task)) { AstTaskRef* const resizeTaskRefp = new AstTaskRef{fl, resizeAllTaskp}; randomizep->addStmtsp(resizeTaskRefp->makeStmt()); } - } + }; + if (!basicFirst) emitResizeFallback(); AstVarRef* const fvarRefReadp = fvarRefp->cloneTree(false); fvarRefReadp->access(VAccess::READ); - AstFunc* const basicRandomizep - = V3Randomize::newRandomizeFunc(m_memberMap, nodep, BASIC_RANDOMIZE_FUNC_NAME); - addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp); - AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, basicRandomizep}; + AstNodeExpr* const secondHalfp + = basicFirst ? beginValp : new AstFuncRef{fl, basicRandomizep}; randomizep->addStmtsp(new AstAssign{fl, fvarRefp->cloneTree(false), - new AstAnd{fl, fvarRefReadp, basicRandomizeCallp}}); + new AstAnd{fl, fvarRefReadp, secondHalfp}}); + + if (basicFirst) emitResizeFallback(); // Call nested post_randomize on rand class-type members (IEEE 18.4.1) if (classHasRandClassMembers(nodep)) { @@ -5643,15 +5677,21 @@ public: explicit RandomizeVisitor(AstNetlist* nodep) : m_inlineUniqueNames{"__Vrandwith"} { createRandomizeClassVars(nodep); - // Mark variables in global constraints - // These should not be randomized in nested class's __VBasicRand - nodep->foreach([&](AstConstraint* constrp) { - constrp->foreach([&](AstMemberSel* memberSelp) { - // Only mark if this MemberSel was created during constraint cloning - if (memberSelp->user2p()) { + // Flag local constraint leaves as solver-owned so __VBasicRand skips them. + // Runs before any class is lowered. Only a randomized class counts, and + // only a leaf owned by the constraint's own class: a leaf reached through + // a global constraint stays basic-randomized so a standalone randomize() + // of its type still randomizes it (issue #7833); a class-typed sub-object + // is skipped so its own members are still basic-randomized. + nodep->foreach([&](AstClass* const classp) { + if (!classp->user1()) return; + classp->foreachMember([&](AstClass* const ownerClassp, AstConstraint* const constrp) { + constrp->foreach([&](AstMemberSel* const memberSelp) { AstVar* const varp = memberSelp->varp(); + if (VN_IS(varp->dtypep()->skipRefp(), ClassRefDType)) return; + if (VN_AS(varp->user2p(), NodeModule) != ownerClassp) return; if (!varp->user3()) varp->user3(true); - } + }); }); }); diff --git a/test_regress/t/t_constraint_global_subobj.py b/test_regress/t/t_constraint_global_subobj.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_global_subobj.py @@ -0,0 +1,21 @@ +#!/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') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_global_subobj.v b/test_regress/t/t_constraint_global_subobj.v new file mode 100644 index 000000000..333695f2d --- /dev/null +++ b/test_regress/t/t_constraint_global_subobj.v @@ -0,0 +1,178 @@ +// 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 + +// Scenario 1 (issue #7833 literal): owner SA1 declares a global constraint on a +// sub-object's type but is NEVER randomized; a standalone randomize() of the +// holder must still randomize the nested fields. +class C1; + rand int x; + rand int y; +endclass + +class B1; + rand C1 c; + function new(); + c = new(); + endfunction +endclass + +class SA1; + rand B1 b; + constraint c_foo {b.c.x == 123;} +endclass + +// Scenario 2 (combined): the SAME design randomizes both the constraint owner +// and a standalone holder of the constrained type. +class C2; + rand int x; + rand int y; +endclass + +class B2; + rand C2 c; + function new(); + c = new(); + endfunction +endclass + +class SA2; + rand B2 b; + constraint c_foo {b.c.x == 123;} + function new(); + b = new(); + endfunction +endclass + +// Scenario 3 (multiple same-type sub-objects): two sub-objects of one type are +// each pinned by a global constraint while that type is also randomized +// standalone; the two share one underlying rand variable. +class C3; + rand int x; +endclass + +class M3; + rand C3 c1; + rand C3 c2; + constraint c { + c1.x == 11; + c2.x == 22; + } + function new(); + c1 = new(); + c2 = new(); + endfunction +endclass + +// Scenario 4 (global constraint owner with its own size-constrained array): the +// owner basic-randomizes first, the solver overrides last, and the size-only +// resize fallback still resizes from the solver-determined size. +class C4; + rand int x; +endclass + +class A4; + rand C4 c; + rand int arr[]; + constraint c_sub {c.x == 5;} + constraint c_size {arr.size() == 4;} + function new(); + c = new(); + endfunction +endclass + +module t_constraint_global_subobj; + B1 b1; + SA2 a2; + B2 b2; + M3 m3; + C3 s3; + A4 a4; + int prevx, prevy, p3; + bit varyx, varyy, vary3; + + initial begin + // Scenario 1: SA1 never randomized; standalone B1 must vary both fields. + b1 = new(); + varyx = 0; + varyy = 0; + if (b1.randomize() != 1) $stop; + prevx = b1.c.x; + prevy = b1.c.y; + for (int i = 0; i < 20; i++) begin + if (b1.randomize() != 1) $stop; + if (b1.c.x != prevx) varyx = 1; + if (b1.c.y != prevy) varyy = 1; + prevx = b1.c.x; + prevy = b1.c.y; + end + if (!varyx || !varyy) begin + $display("ERROR: standalone holder fields stuck (varyx=%0d varyy=%0d)", varyx, varyy); + $stop; + end + + // Scenario 2: randomize the owner (constraint applies) and a standalone + // holder (fields random) in the same design. + a2 = new(); + if (a2.randomize() != 1) $stop; + if (a2.b.c.x != 123) begin + $display("ERROR: owner constraint not applied, a2.b.c.x=%0d", a2.b.c.x); + $stop; + end + b2 = new(); + varyx = 0; + if (b2.randomize() != 1) $stop; + prevx = b2.c.x; + for (int i = 0; i < 20; i++) begin + if (b2.randomize() != 1) $stop; + if (b2.c.x != prevx) varyx = 1; + prevx = b2.c.x; + end + if (!varyx) begin + $display("ERROR: standalone holder x stuck while owner also randomized"); + $stop; + end + + // Scenario 3: two same-type sub-objects each pinned, plus standalone vary. + m3 = new(); + if (m3.randomize() != 1) $stop; + if (m3.c1.x != 11) begin + $display("ERROR: m3.c1.x should be 11, got %0d", m3.c1.x); + $stop; + end + if (m3.c2.x != 22) begin + $display("ERROR: m3.c2.x should be 22, got %0d", m3.c2.x); + $stop; + end + s3 = new(); + vary3 = 0; + if (s3.randomize() != 1) $stop; + p3 = s3.x; + for (int i = 0; i < 20; i++) begin + if (s3.randomize() != 1) $stop; + if (s3.x != p3) vary3 = 1; + p3 = s3.x; + end + if (!vary3) begin + $display("ERROR: standalone same-type x stuck"); + $stop; + end + + // Scenario 4: owner with a global constraint AND its own size constraint. + a4 = new(); + if (a4.randomize() != 1) $stop; + if (a4.c.x != 5) begin + $display("ERROR: a4.c.x should be 5, got %0d", a4.c.x); + $stop; + end + if (a4.arr.size() != 4) begin + $display("ERROR: a4.arr.size() should be 4, got %0d", a4.arr.size()); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 2d157b29b0d155650c9a41805195b74db27b46dd Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 25 Jun 2026 20:12:49 +0100 Subject: [PATCH 13/41] Optimize assetOn checks furter --- src/V3Assert.cpp | 34 +++++++++++++--------------- test_regress/t/t_assert_opt_check.py | 4 ++-- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 8d4bf5275..09f6d1126 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -278,30 +278,26 @@ class AssertVisitor final : public VNVisitor { } VL_UNREACHABLE; } - static string assertActionControlPrefix(VAssertDirectiveType directiveType) { - const int controlled = !!(static_cast(directiveType) - & (static_cast(VAssertDirectiveType::ASSERT) - | static_cast(VAssertDirectiveType::COVER) - | static_cast(VAssertDirectiveType::ASSUME))); - const int checkRuntime = controlled & static_cast(v3Global.opt.assertOn()); - return "("s + std::to_string(controlled ^ 1) + " || ("s + std::to_string(checkRuntime) - + " && "s; + + static bool isControlled(VAssertDirectiveType directiveType) { + return (static_cast(directiveType) + & (static_cast(VAssertDirectiveType::ASSERT) + | static_cast(VAssertDirectiveType::COVER) + | static_cast(VAssertDirectiveType::ASSUME))); } static AstNodeExpr* assertPassOnCond(FileLine* fl, VAssertType type, VAssertDirectiveType directiveType, bool vacuous) { + if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}}; + if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}}; return new AstCExpr{fl, AstCExpr::Pure{}, - assertActionControlPrefix(directiveType) - + assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType) - + "))"s, - 1}; + assertCtlGetCall(assertPassOnQuery(vacuous), type, directiveType), 1}; } static AstNodeExpr* assertFailOnCond(FileLine* fl, VAssertType type, VAssertDirectiveType directiveType) { + if (!isControlled(directiveType)) return new AstConst{fl, AstConst::BitTrue{}}; + if (!v3Global.opt.assertOn()) return new AstConst{fl, AstConst::BitFalse{}}; return new AstCExpr{fl, AstCExpr::Pure{}, - assertActionControlPrefix(directiveType) - + assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType) - + "))"s, - 1}; + assertCtlGetCall("ASSERT_CTL_FAIL_ON", type, directiveType), 1}; } string assertDisplayMessage(const AstNode* nodep, const string& prefix, const string& message, VDisplayType severity) { @@ -374,6 +370,7 @@ class AssertVisitor final : public VNVisitor { AstNodeIf* const newp = new AstIf{fl, condp, bodyp}; newp->isBoundsCheck(true); // To avoid LATCH warning newp->user1(true); // Don't assert/cover this if + newp->user2(true); // Mark as an assertOn() check return newp; } static AstNodeStmt* newIfAssertFailOn(AstNode* bodyp, VAssertDirectiveType directiveType, @@ -385,6 +382,7 @@ class AssertVisitor final : public VNVisitor { AstNodeIf* const newp = new AstIf{fl, condp, bodyp}; newp->isBoundsCheck(true); // To avoid LATCH warning newp->user1(true); // Don't assert/cover this if + newp->user2(true); // Mark as an assertOn() check return newp; } @@ -696,10 +694,10 @@ class AssertVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); return; } - - iterateChildren(nodep); } + iterateChildren(nodep); + if (nodep->user2()) { // Combine consecutive assertOn checks if possible if (AstIf* const backp = VN_CAST(nodep->backp(), If)) { diff --git a/test_regress/t/t_assert_opt_check.py b/test_regress/t/t_assert_opt_check.py index 62b5a95d0..0e970d46e 100755 --- a/test_regress/t/t_assert_opt_check.py +++ b/test_regress/t/t_assert_opt_check.py @@ -15,7 +15,7 @@ test.compile(verilator_flags2=['--binary', '--stats']) test.execute(check_finished=True) -test.file_grep(test.stats, r'Assertions, assertOn checks combined\s+(\d+)', 3) -test.file_grep(test.stats, r'Assertions, assertOn checks hoisted\s+(\d+)', 16) +test.file_grep(test.stats, r'Assertions, assertOn checks combined\s+(\d+)', 4) +test.file_grep(test.stats, r'Assertions, assertOn checks hoisted\s+(\d+)', 42) test.passes() From 38533013670b950ff207fb192b3ea60ad8b0fa8a Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Fri, 26 Jun 2026 12:00:44 +0200 Subject: [PATCH 14/41] Fix disable iff ignored when its condition is held continuously true (#7841) --- src/V3AssertNfa.cpp | 12 +++- test_regress/t/t_cover_sequence.v | 2 +- test_regress/t/t_property_accept_reject_on.v | 20 +++--- test_regress/t/t_property_disable_iff_held.py | 18 +++++ test_regress/t/t_property_disable_iff_held.v | 65 +++++++++++++++++++ test_regress/t/t_sequence_within.v | 8 +-- 6 files changed, 109 insertions(+), 16 deletions(-) create mode 100755 test_regress/t/t_property_disable_iff_held.py create mode 100644 test_regress/t/t_property_disable_iff_held.v diff --git a/src/V3AssertNfa.cpp b/src/V3AssertNfa.cpp index 7949f8563..6a35273a5 100644 --- a/src/V3AssertNfa.cpp +++ b/src/V3AssertNfa.cpp @@ -1983,8 +1983,18 @@ class SvaNfaLowering final { AstNodeExpr*& sigp = c.vtx[i]->datap()->stateSigp; if (sigp) VL_DO_DANGLING(sigp->deleteTree(), sigp); } - // Disable iff gating on throughout/required-step rejects (IEEE 16.12). + // Disable iff gating (IEEE 1800-2023 16.12). The edge counter misses a + // continuously-true disable, so gate on the current level value too. if (c.disableExprp) { + // terminalActivep is always set, so gate it unconditionally. + AstNodeExpr* const notTermp + = new AstLogNot{c.flp, c.disableExprp->cloneTreePure(false)}; + sigs.terminalActivep = new AstLogAnd{c.flp, sigs.terminalActivep, notTermp}; + if (sigs.rejectBasep) { + AstNodeExpr* const notDisp + = new AstLogNot{c.flp, c.disableExprp->cloneTreePure(false)}; + sigs.rejectBasep = new AstLogAnd{c.flp, sigs.rejectBasep, notDisp}; + } if (sigs.throughoutRejectp) { AstNodeExpr* const notDisp = new AstLogNot{c.flp, c.disableExprp->cloneTreePure(false)}; diff --git a/test_regress/t/t_cover_sequence.v b/test_regress/t/t_cover_sequence.v index 719a3c1ba..42f39949e 100644 --- a/test_regress/t/t_cover_sequence.v +++ b/test_regress/t/t_cover_sequence.v @@ -79,7 +79,7 @@ module t ( `endif `checkd(hit_simple, 96); // Questa: 95 `checkd(hit_clocked, 149); // Questa: 149 - `checkd(hit_clocked_disable, 28); // Questa: 27 + `checkd(hit_clocked_disable, 27); // Questa: 27 `checkd(hit_default_disable, 30); // Questa: 30 `checkd(hit_consrep_2, 30); // Questa: 29 `checkd(hit_consrep_3, 14); // Questa: 13 diff --git a/test_regress/t/t_property_accept_reject_on.v b/test_regress/t/t_property_accept_reject_on.v index 020e754f2..1affe217c 100644 --- a/test_regress/t/t_property_accept_reject_on.v +++ b/test_regress/t/t_property_accept_reject_on.v @@ -94,16 +94,16 @@ module t ( end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - `checkd(count_fail1, 29); // Questa: 14 - `checkd(count_fail2, 65); // Questa: 64 - `checkd(count_fail3, 29); // Questa: 14 - `checkd(count_fail4, 65); // Questa: 64 - `checkd(count_fail5, 46); // Questa: 31 - `checkd(count_fail6, 65); // Questa: 59 - `checkd(count_fail7, 29); // Questa: 14 - `checkd(count_fail8, 14); // Questa: 10 - `checkd(count_fail9, 29); // Questa: 14 - `checkd(count_fail10, 29); // Questa: 14 + `checkd(count_fail1, 28); // Questa: 14 + `checkd(count_fail2, 64); // Questa: 64 + `checkd(count_fail3, 28); // Questa: 14 + `checkd(count_fail4, 64); // Questa: 64 + `checkd(count_fail5, 45); // Questa: 31 + `checkd(count_fail6, 64); // Questa: 59 + `checkd(count_fail7, 28); // Questa: 14 + `checkd(count_fail8, 13); // Questa: 10 + `checkd(count_fail9, 28); // Questa: 14 + `checkd(count_fail10, 28); // Questa: 14 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_property_disable_iff_held.py b/test_regress/t/t_property_disable_iff_held.py new file mode 100755 index 000000000..8c5881f1b --- /dev/null +++ b/test_regress/t/t_property_disable_iff_held.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(timing_loop=True, verilator_flags2=['--assert --timing --coverage']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_property_disable_iff_held.v b/test_regress/t/t_property_disable_iff_held.v new file mode 100644 index 000000000..04530fc6e --- /dev/null +++ b/test_regress/t/t_property_disable_iff_held.v @@ -0,0 +1,65 @@ +// 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\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +// IEEE 1800-2023 16.12: a disable iff condition held continuously true must +// disable every attempt of a multi-cycle property (verilator/verilator#7792). +// en_held is a plain non-$sampled, non-constant signal held 1, so it exercises +// the NFA disable-counter path. The held assert/cover must never fire; the +// `disable iff (1'b0)` controls prove the same assert/cover do fire when enabled. + +module t ( + input clk +); + int cyc = 0; + reg [63:0] crc = 64'h5aef0c8d_d70a4497; + wire a = crc[0]; + wire b = crc[4]; + + bit en_held = 1'b1; + + int n_held_assert = 0; + int n_held_cover = 0; + int n_ctrl_assert = 0; + int n_ctrl_cover = 0; + + // Held-true disable: assert + cover must be fully suppressed. + assert property (@(posedge clk) disable iff (en_held) (a ##1 b)) + else n_held_assert <= n_held_assert + 1; + cover property (@(posedge clk) disable iff (en_held) (a ##1 b)) + n_held_cover <= n_held_cover + 1; + + // Enabled control (disable iff 1'b0): same assert + cover must fire. + assert property (@(posedge clk) disable iff (1'b0) (a ##1 b)) + else n_ctrl_assert <= n_ctrl_assert + 1; + cover property (@(posedge clk) disable iff (1'b0) (a ##1 b)) + n_ctrl_cover <= n_ctrl_cover + 1; + + always @(posedge clk) begin + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + if (cyc == 99) begin + `checkd(n_held_assert, 0); // Questa: 0 + `checkd(n_held_cover, 0); // Questa: 0 + `checkd(n_ctrl_assert, 58); // Questa: 58 + `checkd(n_ctrl_cover, 26); // Questa: 26 + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +`ifndef VERILATOR +module wrap; + logic clk = 0; + always #5 clk = ~clk; + t inst (.clk(clk)); +endmodule +`endif diff --git a/test_regress/t/t_sequence_within.v b/test_regress/t/t_sequence_within.v index 093b7afcb..dcffaae56 100644 --- a/test_regress/t/t_sequence_within.v +++ b/test_regress/t/t_sequence_within.v @@ -107,14 +107,14 @@ module t ( // engine-wide behavior, not within-specific. `checkd(count_p1, 23); // Questa: 23 `checkd(count_p2, 44); // Questa: 44 - `checkd(count_p3, 26); // Questa: 20 - `checkd(count_p4, 24); // Questa: 22 + `checkd(count_p3, 25); // Questa: 20 + `checkd(count_p4, 23); // Questa: 22 `checkd(count_p5, 26); // Questa: 26 `checkd(count_p6, 21); // Questa: 16 `checkd(count_p7, 15); // Questa: 9 `checkd(count_p8, 15); // Questa: 4 - `checkd(count_p9, 17); // Questa: 10 - `checkd(count_p10, 24); // Questa: 15 + `checkd(count_p9, 15); // Questa: 10 + `checkd(count_p10, 23); // Questa: 15 $write("*-* All Finished *-*\n"); $finish; end From d023b3b075b2632bc02ac834eee54d0b466232fc Mon Sep 17 00:00:00 2001 From: Matthew Ballance Date: Sun, 28 Jun 2026 06:28:09 -0700 Subject: [PATCH 15/41] Support dynamic loading of VPI extensions (#7727) --- bin/verilator | 1 + configure.ac | 28 ++++++ docs/guide/exe_sim.rst | 14 +++ include/verilated.cpp | 69 ++++++++++++++ include/verilated.h | 3 + include/verilated.mk.in | 4 + src/V3EmitCMain.cpp | 2 + src/V3EmitMk.cpp | 17 ++++ test_regress/t/t_flag_main_vpi.cpp | 94 ++++++++++++++++++++ test_regress/t/t_flag_main_vpi.out | 2 + test_regress/t/t_flag_main_vpi.py | 30 +++++++ test_regress/t/t_flag_main_vpi.v | 28 ++++++ test_regress/t/t_flag_main_vpi_badentry.out | 2 + test_regress/t/t_flag_main_vpi_badentry.py | 25 ++++++ test_regress/t/t_flag_main_vpi_badlib.out | 2 + test_regress/t/t_flag_main_vpi_badlib.py | 26 ++++++ test_regress/t/t_flag_main_vpi_bootstrap.out | 2 + test_regress/t/t_flag_main_vpi_bootstrap.py | 25 ++++++ test_regress/t/t_flag_main_vpi_lib2.cpp | 25 ++++++ test_regress/t/t_flag_main_vpi_multi.out | 3 + test_regress/t/t_flag_main_vpi_multi.py | 42 +++++++++ test_regress/t/t_flag_main_vpi_noarray.cpp | 21 +++++ test_regress/t/t_flag_main_vpi_noarray.out | 2 + test_regress/t/t_flag_main_vpi_noarray.py | 25 ++++++ test_regress/t/t_flag_main_vpi_noexe.py | 30 +++++++ test_regress/t/t_flag_main_vpi_nowarn.out | 3 + test_regress/t/t_flag_main_vpi_nowarn.py | 24 +++++ 27 files changed, 549 insertions(+) create mode 100644 test_regress/t/t_flag_main_vpi.cpp create mode 100644 test_regress/t/t_flag_main_vpi.out create mode 100755 test_regress/t/t_flag_main_vpi.py create mode 100644 test_regress/t/t_flag_main_vpi.v create mode 100644 test_regress/t/t_flag_main_vpi_badentry.out create mode 100755 test_regress/t/t_flag_main_vpi_badentry.py create mode 100644 test_regress/t/t_flag_main_vpi_badlib.out create mode 100755 test_regress/t/t_flag_main_vpi_badlib.py create mode 100644 test_regress/t/t_flag_main_vpi_bootstrap.out create mode 100755 test_regress/t/t_flag_main_vpi_bootstrap.py create mode 100644 test_regress/t/t_flag_main_vpi_lib2.cpp create mode 100644 test_regress/t/t_flag_main_vpi_multi.out create mode 100755 test_regress/t/t_flag_main_vpi_multi.py create mode 100644 test_regress/t/t_flag_main_vpi_noarray.cpp create mode 100644 test_regress/t/t_flag_main_vpi_noarray.out create mode 100755 test_regress/t/t_flag_main_vpi_noarray.py create mode 100755 test_regress/t/t_flag_main_vpi_noexe.py create mode 100644 test_regress/t/t_flag_main_vpi_nowarn.out create mode 100755 test_regress/t/t_flag_main_vpi_nowarn.py diff --git a/bin/verilator b/bin/verilator index 57fd60507..cb866e243 100755 --- a/bin/verilator +++ b/bin/verilator @@ -653,6 +653,7 @@ description of these arguments. +verilator+solver+file+ Set random solver log filename +verilator+V Show verbose version and config +verilator+version Show version and exit + +verilator+vpi+[:] Load VPI shared library +verilator+wno+unsatconstr+ Disable constraint warnings diff --git a/configure.ac b/configure.ac index 6917f0c4b..00d8ac713 100644 --- a/configure.ac +++ b/configure.ac @@ -589,6 +589,34 @@ m4_foreach([ldflag], [ AC_SUBST(CFG_LDLIBS_THREADS) AC_SUBST(CFG_LDFLAGS_THREADS_CMAKE) +# Find link flags for runtime VPI library loading (+verilator+vpi+). +# The model executable must export its VPI symbols so the dlopen'd library can +# resolve them: -rdynamic (GNU ld) or -Wl,-export_dynamic (Darwin); the first the +# linker accepts wins. -ldl provides dlopen/dlsym where it is a separate library. +_MY_LDLIBS_CHECK_SET(CFG_LDFLAGS_DYNAMIC, -rdynamic) +# -Wl,-export_dynamic contains a comma, so probe it directly rather than through +# the _MY_LDLIBS_CHECK_* macros (which re-expand their flag argument unquoted). +if test "$CFG_LDFLAGS_DYNAMIC" = ""; then + ACO_SAVE_LIBS="$LIBS" + LIBS="$LIBS -Wl,-export_dynamic" + AC_MSG_CHECKING([whether $CXX linker accepts -Wl,-export_dynamic]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[]])], + [_my_result=yes + if test -s conftest.err; then + if grep -e "-export_dynamic" conftest.err >/dev/null; then + _my_result=no + fi + fi], + [_my_result=no]) + AC_MSG_RESULT($_my_result) + LIBS="$ACO_SAVE_LIBS" + if test "$_my_result" = "yes"; then CFG_LDFLAGS_DYNAMIC="-Wl,-export_dynamic"; fi +fi +AC_SUBST(CFG_LDFLAGS_DYNAMIC) +_MY_LDLIBS_CHECK_OPT(CFG_LDLIBS_DYNAMIC, -ldl) +AC_SUBST(CFG_LDLIBS_DYNAMIC) + # If 'mold' is installed, use it to link for faster buildtimes _MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -fuse-ld=mold) _MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_VERILATED, -fuse-ld=mold) diff --git a/docs/guide/exe_sim.rst b/docs/guide/exe_sim.rst index ac2b73f16..190367346 100644 --- a/docs/guide/exe_sim.rst +++ b/docs/guide/exe_sim.rst @@ -139,6 +139,20 @@ Options: Displays program version and exits. +.. option:: +verilator+vpi+[:] + + Load a VPI shared library before simulation starts. Only available when the + model was Verilated with :vlopt:`--vpi` and :vlopt:`--main` (or + :vlopt:`--binary`). ```` is the path to the shared library. If + ``:`` is given, that named no-argument function is called; + otherwise the library's ``vlog_startup_routines`` array (IEEE 1800 38.37.2) is + invoked. May be repeated to load multiple libraries. + + Runtime loading is supported on POSIX platforms only (it relies on the + executable exporting its VPI symbols to the loaded library); on Windows the + argument is rejected and the VPI code must instead be statically linked + into the model. + .. option:: +verilator+wno+unsatconstr+ Disable unsatisfied constraint warnings at simulation runtime. When set to diff --git a/include/verilated.cpp b/include/verilated.cpp index 9cbb58ffc..a92c6ca5f 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -89,6 +89,12 @@ # include # define _VL_HAVE_GETRLIMIT #endif +#if VM_VPI +# include +# ifndef _WIN32 +# include // dlopen +# endif +#endif #include "verilated_threads.h" // clang-format on @@ -260,6 +266,58 @@ void VL_WARN_MT(const char* filename, int linenum, const char* hier, const char* }}); } +//=========================================================================== +// Runtime VPI shared library loading (--vpi) + +// Load one VPI shared library named by a +verilator+vpi+[:] argument. +// 'arg' is the payload after the prefix: either "" (invoke the library's +// vlog_startup_routines array) or ":" (invoke the named bootstrap). +void Verilated::loadVpiLib(const std::string& arg) VL_MT_UNSAFE { +#if VM_VPI + if (arg.empty()) return; +#ifdef _WIN32 + VL_FATAL_MT("", 0, "", + "+verilator+vpi+: runtime VPI library loading is not supported on" + " Windows; link the VPI code into the model instead"); +#else + using vlog_startup_t = void (*)(); + // Split : on the last ':' + const std::string::size_type colon_pos = arg.rfind(':'); + const bool has_entry = (colon_pos != std::string::npos); + const std::string libpath = has_entry ? arg.substr(0, colon_pos) : arg; + const std::string entry_name = has_entry ? arg.substr(colon_pos + 1) : std::string{}; + void* handle = dlopen(libpath.c_str(), RTLD_LAZY); + if (!handle) + // The library path is stable; the dlerror() text is platform-specific, so put it on + // a separate "- " line (test golden files strip "- " lines, keeping output portable). + VL_FATAL_MT( + "", 0, "", + (std::string{"Cannot load VPI library: "} + libpath + "\n- dlerror: " + dlerror()) + .c_str()); + if (has_entry) { + vlog_startup_t bsp = reinterpret_cast(dlsym(handle, entry_name.c_str())); + if (!bsp) + VL_FATAL_MT( + "", 0, "", + (std::string{"Cannot find VPI bootstrap '"} + entry_name + "' in: " + libpath) + .c_str()); + bsp(); + } else { + vlog_startup_t* routinesp + = reinterpret_cast(dlsym(handle, "vlog_startup_routines")); + if (!routinesp) + VL_FATAL_MT( + "", 0, "", + (std::string{"Cannot find 'vlog_startup_routines' in: "} + libpath).c_str()); + for (int j = 0; routinesp[j]; ++j) routinesp[j](); + } +#endif +#else + // Never reached: the command-line handler only calls this when compiled with --vpi. + (void)arg; +#endif +} + //=========================================================================== // Debug prints @@ -3544,6 +3602,17 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) { // and the run can be reproduced by passing +verilator+seed+. if (u64 == 0) u64 = pickRandomSeed(); randSeed(static_cast(u64)); + } else if (commandArgVlString(arg, "+verilator+vpi+", str)) { + // With --vpi, load the requested shared library now. Without --vpi there is + // no VPI runtime, so warn the argument is ignored. +#if VM_VPI + Verilated::loadVpiLib(str); +#else + VL_WARN_MT( + "COMMAND_LINE", 0, "", + ("+verilator+vpi+ ignored: simulation was not compiled with --vpi '" + arg + "'") + .c_str()); // LCOV_EXCL_LINE (gcov zeroes this wrapped continuation line) +#endif } else if (arg == "+verilator+V") { VerilatedImp::versionDump(); // Someday more info too VL_FATAL_MT("COMMAND_LINE", 0, "", diff --git a/include/verilated.h b/include/verilated.h index 59ae1ad8c..73c7e5b2a 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -1036,6 +1036,9 @@ public: static void scTraceBeforeElaborationError() VL_ATTR_NORETURN VL_MT_SAFE; static void stackCheck(QData needSize) VL_MT_UNSAFE; + // Internal: Load a VPI shared library (+verilator+vpi+[:]) + static void loadVpiLib(const std::string& arg) VL_MT_UNSAFE; + // Internal: Get and set DPI context static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; } static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep = scopep; } diff --git a/include/verilated.mk.in b/include/verilated.mk.in index 9d3f3b2e7..a90907755 100644 --- a/include/verilated.mk.in +++ b/include/verilated.mk.in @@ -52,6 +52,9 @@ CFG_GCH_IF_CLANG = @CFG_GCH_IF_CLANG@ CFG_LDFLAGS_VERILATED = @CFG_LDFLAGS_VERILATED@ # Linker libraries for multithreading CFG_LDLIBS_THREADS = @CFG_LDLIBS_THREADS@ +# Linker flags/libraries for runtime VPI library loading (+verilator+vpi+) +CFG_LDFLAGS_DYNAMIC = @CFG_LDFLAGS_DYNAMIC@ +CFG_LDLIBS_DYNAMIC = @CFG_LDLIBS_DYNAMIC@ ###################################################################### # Programs @@ -93,6 +96,7 @@ VK_CPPFLAGS_ALWAYS += \ -DVM_TRACE_FST=$(VM_TRACE_FST) \ -DVM_TRACE_VCD=$(VM_TRACE_VCD) \ -DVM_TRACE_SAIF=$(VM_TRACE_SAIF) \ + -DVM_VPI=$(VM_VPI) \ $(CFG_CXXFLAGS_NO_UNUSED) \ ifeq ($(CFG_WITH_CCWARN),yes) # Local... Else don't burden users diff --git a/src/V3EmitCMain.cpp b/src/V3EmitCMain.cpp index 000567eaf..0d3fd0b6d 100644 --- a/src/V3EmitCMain.cpp +++ b/src/V3EmitCMain.cpp @@ -104,6 +104,8 @@ private: puts("\n"); if (v3Global.opt.vpi()) { + // VPI shared libraries requested via +verilator+vpi+ are loaded by + // contextp->commandArgs() above, before the statically-linked startup routines. puts("// Hook VPI startup routines and invoke callback\n"); puts("if (vlog_startup_routines) {\n"); puts(/**/ "for (auto routinep = &vlog_startup_routines[0]; *routinep; routinep++)" diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index f76945279..a63d328f3 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -567,6 +567,12 @@ public: of.puts("VM_TRACE_VCD = "); of.puts(v3Global.opt.traceEnabledVcd() ? "1" : "0"); of.puts("\n"); + of.puts("# VPI enabled? 0/1 (from --vpi)\n"); + of.puts("VM_VPI = "); + of.puts(v3Global.opt.vpi() ? "1" : "0"); + of.puts("\n"); + // Link flags for runtime VPI library loading are emitted by emitOverallMake() after + // verilated.mk is included (so $(CFG_LDFLAGS_DYNAMIC)/$(CFG_LDLIBS_DYNAMIC) are defined). of.puts("\n### Object file lists...\n"); for (int support = 0; support < 3; ++support) { @@ -729,6 +735,17 @@ public: of.puts("\n### Executable rules... (from --exe)\n"); of.puts("VPATH += $(VM_USER_DIR)\n"); of.puts("\n"); + + if (v3Global.opt.vpi()) { + // Runtime VPI library loading (+verilator+vpi+) needs the executable to + // export its VPI symbols so the dlopen'd library can resolve them, plus the + // dl library for dlopen/dlsym. The exact flags are probed at configure time + // (CFG_LDFLAGS_DYNAMIC / CFG_LDLIBS_DYNAMIC in verilated.mk). + of.puts("# Runtime VPI library loading (+verilator+vpi+) link requirements\n"); + of.puts("LDFLAGS += $(CFG_LDFLAGS_DYNAMIC)\n"); + of.puts("LDLIBS += $(CFG_LDLIBS_DYNAMIC)\n"); + of.puts("\n"); + } } const string compilerIncludePch diff --git a/test_regress/t/t_flag_main_vpi.cpp b/test_regress/t/t_flag_main_vpi.cpp new file mode 100644 index 000000000..1be31452a --- /dev/null +++ b/test_regress/t/t_flag_main_vpi.cpp @@ -0,0 +1,94 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: VPI test library for t_flag_main_vpi +// +// Loaded at runtime via +verilator+vpi+ to verify that --binary --vpi +// correctly loads shared libraries and invokes vlog_startup_routines[] (or a +// named bootstrap). The design drives its own clock; this library only +// observes 'count' via a cbValueChange callback and calls $finish after +// MAX_TICKS edges -- so a successful $finish proves the library was loaded +// and is able to register callbacks and reach signals by name. +// +// 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: 2025 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#include "vpi_user.h" + +#include +#include + +// Number of count increments to observe before calling $finish +static const int MAX_TICKS = 10; +static vpiHandle s_count_handle = nullptr; + +static PLI_INT32 count_change_cb(p_cb_data /*cb_data*/) { + if (!s_count_handle) return 0; + s_vpi_value val; + val.format = vpiIntVal; + vpi_get_value(s_count_handle, &val); + if (val.value.integer >= MAX_TICKS) { + vpi_printf(const_cast("*-* All Finished *-*\n")); + vpi_control(vpiFinish, 0); + } + return 0; +} + +static PLI_INT32 start_of_sim_cb(p_cb_data /*cb_data*/) { + s_count_handle = vpi_handle_by_name(const_cast("t.count"), nullptr); + if (!s_count_handle) { + vpi_printf(const_cast("ERROR: cannot find t.count\n")); + vpi_control(vpiFinish, 1); + return 0; + } + // Observe count: fire a callback whenever it changes + t_cb_data cb_data; + s_vpi_time t; + s_vpi_value val; + t.type = vpiSuppressTime; + val.format = vpiSuppressVal; + cb_data.reason = cbValueChange; + cb_data.cb_rtn = count_change_cb; + cb_data.obj = s_count_handle; + cb_data.time = &t; + cb_data.value = &val; + cb_data.user_data = nullptr; + vpi_register_cb(&cb_data); + return 0; +} + +static PLI_INT32 end_of_sim_cb(p_cb_data /*cb_data*/) { + vpi_printf(const_cast("VPI end of simulation\n")); + return 0; +} + +static void register_callbacks() { + // cbStartOfSimulation + t_cb_data cb_data; + s_vpi_time t; + t.type = vpiSuppressTime; + cb_data.reason = cbStartOfSimulation; + cb_data.cb_rtn = start_of_sim_cb; + cb_data.obj = nullptr; + cb_data.time = &t; + cb_data.value = nullptr; + cb_data.user_data = nullptr; + vpi_register_cb(&cb_data); + + // cbEndOfSimulation + cb_data.reason = cbEndOfSimulation; + cb_data.cb_rtn = end_of_sim_cb; + vpi_register_cb(&cb_data); +} + +// IEEE 1800 section 37: vlog_startup_routines[] -- null-terminated array of startup functions +extern "C" { +void (*vlog_startup_routines[])() = {register_callbacks, nullptr}; + +// Named bootstrap entrypoint -- used when library is loaded as :my_vpi_bootstrap +void my_vpi_bootstrap() { register_callbacks(); } +} diff --git a/test_regress/t/t_flag_main_vpi.out b/test_regress/t/t_flag_main_vpi.out new file mode 100644 index 000000000..6a9a7f37b --- /dev/null +++ b/test_regress/t/t_flag_main_vpi.out @@ -0,0 +1,2 @@ +*-* All Finished *-* +VPI end of simulation diff --git a/test_regress/t/t_flag_main_vpi.py b/test_regress/t/t_flag_main_vpi.py new file mode 100755 index 000000000..695b70e76 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi.py @@ -0,0 +1,30 @@ +#!/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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +# Compile with --binary --vpi to exercise the VPI-aware generated main. +# Also compile a VPI shared library to be loaded at runtime via +verilator+vpi+. +test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"]) + +# Run the generated binary; load the VPI library via the +verilator+vpi+ plusarg. +# The VPI library's output (observed 'count' reaching MAX_TICKS, then end-of-sim) is +# checked against the golden .out file. +# Also pass a non-VPI plusarg (skipped by the loader's prefix check) and a bare +# +verilator+vpi+ with an empty payload (skipped by the empty-arg check), so both +# loader-skip branches are exercised alongside the real library load. +test.execute(all_run_flags=[ + "+othertest", "+verilator+vpi+", "+verilator+vpi+" + test.obj_dir + "/libvpi.so" +], + check_finished=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_flag_main_vpi.v b/test_regress/t/t_flag_main_vpi.v new file mode 100644 index 000000000..c688405ca --- /dev/null +++ b/test_regress/t/t_flag_main_vpi.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2025 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +// Test for --binary --vpi runtime library loading. The design provides its +// own clock (so the simulation has Verilog event activity); the VPI library +// (t_flag_main_vpi.cpp), loaded at runtime via +verilator+vpi+, observes +// 'count' via a cbValueChange callback and calls $finish after MAX_TICKS +// edges. Signals are public so the library can reach them by name +// (requires --public-flat-rw). +module t; + + reg clk /*verilator public_flat_rw*/; + reg [31:0] count /*verilator public_flat_rw*/; + + initial begin + clk = 0; + count = 0; + end + + // Self-driving clock: the design itself keeps the simulation alive + always #5 clk = ~clk; + + always @(posedge clk) count <= count + 1; + +endmodule diff --git a/test_regress/t/t_flag_main_vpi_badentry.out b/test_regress/t/t_flag_main_vpi_badentry.out new file mode 100644 index 000000000..6283fa03f --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_badentry.out @@ -0,0 +1,2 @@ +%Error: Cannot find VPI bootstrap 'no_such_fn' in: obj_vlt/t_flag_main_vpi_badentry/libvpi.so +Aborting... diff --git a/test_regress/t/t_flag_main_vpi_badentry.py b/test_regress/t/t_flag_main_vpi_badentry.py new file mode 100755 index 000000000..15398a3e5 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_badentry.py @@ -0,0 +1,25 @@ +#!/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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +# A valid library loaded with a : entry that does not exist must +# fail with a clear error (the missing-named-bootstrap branch of the loader). +test.top_filename = 't/t_flag_main_vpi.v' +test.pli_filename = 't/t_flag_main_vpi.cpp' + +test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"]) + +test.execute(fails=True, + all_run_flags=["+verilator+vpi+" + test.obj_dir + "/libvpi.so:no_such_fn"], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_flag_main_vpi_badlib.out b/test_regress/t/t_flag_main_vpi_badlib.out new file mode 100644 index 000000000..752708e9e --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_badlib.out @@ -0,0 +1,2 @@ +%Error: Cannot load VPI library: obj_vlt/t_flag_main_vpi_badlib/nonexistent.so +Aborting... diff --git a/test_regress/t/t_flag_main_vpi_badlib.py b/test_regress/t/t_flag_main_vpi_badlib.py new file mode 100755 index 000000000..2ac211d49 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_badlib.py @@ -0,0 +1,26 @@ +#!/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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +# +verilator+vpi+ pointing at a non-existent library must fail with a clear +# error (the dlopen-failure branch of the runtime loader). +test.top_filename = 't/t_flag_main_vpi.v' + +test.compile(verilator_flags2=["--binary --vpi --public-flat-rw"]) + +# The fatal names the (stable, relative) library path; the platform-specific dlerror() +# detail is emitted on a "- " line, which golden comparison strips, so the .out is portable. +test.execute(fails=True, + all_run_flags=["+verilator+vpi+" + test.obj_dir + "/nonexistent.so"], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_flag_main_vpi_bootstrap.out b/test_regress/t/t_flag_main_vpi_bootstrap.out new file mode 100644 index 000000000..6a9a7f37b --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_bootstrap.out @@ -0,0 +1,2 @@ +*-* All Finished *-* +VPI end of simulation diff --git a/test_regress/t/t_flag_main_vpi_bootstrap.py b/test_regress/t/t_flag_main_vpi_bootstrap.py new file mode 100755 index 000000000..4c5c21e70 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_bootstrap.py @@ -0,0 +1,25 @@ +#!/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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +# Same design and VPI library as t_flag_main_vpi, but loaded via the +# +verilator+vpi+: named-bootstrap syntax instead of vlog_startup_routines[]. +test.top_filename = 't/t_flag_main_vpi.v' +test.pli_filename = 't/t_flag_main_vpi.cpp' + +test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"]) + +test.execute(all_run_flags=["+verilator+vpi+" + test.obj_dir + "/libvpi.so:my_vpi_bootstrap"], + check_finished=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_flag_main_vpi_lib2.cpp b/test_regress/t/t_flag_main_vpi_lib2.cpp new file mode 100644 index 000000000..8f5bba74b --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_lib2.cpp @@ -0,0 +1,25 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Second VPI test library for t_flag_main_vpi_multi +// +// A second, independent VPI library loaded alongside t_flag_main_vpi.cpp via a +// repeated +verilator+vpi+ argument, to verify multiple libraries are loaded. +// Its startup routine prints a marker proving it was loaded; it does not drive +// or finish the simulation (the first library does that). +// +// 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: 2025 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#include "vpi_user.h" + +static void lib2_startup() { vpi_printf(const_cast("second VPI library loaded\n")); } + +// IEEE 1800 section 37: vlog_startup_routines[] -- null-terminated array of startup functions +extern "C" { +void (*vlog_startup_routines[])() = {lib2_startup, nullptr}; +} diff --git a/test_regress/t/t_flag_main_vpi_multi.out b/test_regress/t/t_flag_main_vpi_multi.out new file mode 100644 index 000000000..04f3f5e6d --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_multi.out @@ -0,0 +1,3 @@ +second VPI library loaded +*-* All Finished *-* +VPI end of simulation diff --git a/test_regress/t/t_flag_main_vpi_multi.py b/test_regress/t/t_flag_main_vpi_multi.py new file mode 100755 index 000000000..af3e301ee --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_multi.py @@ -0,0 +1,42 @@ +#!/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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import os +import platform +import vltest_bootstrap + +test.scenarios('vlt') + +# Two +verilator+vpi+ arguments load two independent libraries: the first +# (t_flag_main_vpi.cpp) observes the design and calls $finish; the second +# (t_flag_main_vpi_lib2.cpp) prints a marker proving it too was loaded. +test.top_filename = 't/t_flag_main_vpi.v' +test.pli_filename = 't/t_flag_main_vpi.cpp' + +test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"]) + +# Build the second VPI library (make_pli only builds libvpi.so), mirroring the +# driver's own pli flags. +root = os.environ['VERILATOR_ROOT'] +pli2_cmd = [ + os.environ['CXX'], "-I" + root + "/include/vltstd", "-I" + root + "/include", "-fPIC", + "-shared" +] +pli2_cmd += (["-Wl,-undefined,dynamic_lookup"] if platform.system() == 'Darwin' else ["-rdynamic"]) +pli2_cmd += ["-o", test.obj_dir + "/libvpi2.so", "t/t_flag_main_vpi_lib2.cpp"] +test.run(logfile=test.obj_dir + "/pli2_compile.log", cmd=pli2_cmd) + +test.execute(all_run_flags=[ + "+verilator+vpi+" + test.obj_dir + "/libvpi.so", + "+verilator+vpi+" + test.obj_dir + "/libvpi2.so" +], + check_finished=True, + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_flag_main_vpi_noarray.cpp b/test_regress/t/t_flag_main_vpi_noarray.cpp new file mode 100644 index 000000000..02568d282 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_noarray.cpp @@ -0,0 +1,21 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: VPI test library lacking vlog_startup_routines +// +// Loaded via +verilator+vpi+ with no : entry, to exercise +// the loader's error path when a library defines neither a named bootstrap +// nor the vlog_startup_routines[] array. +// +// 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: 2025 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#include "vpi_user.h" + +// Intentionally no vlog_startup_routines and no bootstrap; just some symbol so +// the shared object is non-empty and loads successfully. +extern "C" void t_flag_main_vpi_noarray_present() {} diff --git a/test_regress/t/t_flag_main_vpi_noarray.out b/test_regress/t/t_flag_main_vpi_noarray.out new file mode 100644 index 000000000..a7f54a6af --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_noarray.out @@ -0,0 +1,2 @@ +%Error: Cannot find 'vlog_startup_routines' in: obj_vlt/t_flag_main_vpi_noarray/libvpi.so +Aborting... diff --git a/test_regress/t/t_flag_main_vpi_noarray.py b/test_regress/t/t_flag_main_vpi_noarray.py new file mode 100755 index 000000000..9dd2ac853 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_noarray.py @@ -0,0 +1,25 @@ +#!/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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +# A library loaded with no : entry that lacks vlog_startup_routines +# must fail with a clear error (the missing-array branch of the loader). +test.top_filename = 't/t_flag_main_vpi.v' +test.pli_filename = 't/t_flag_main_vpi_noarray.cpp' + +test.compile(make_pli=True, verilator_flags2=["--binary --vpi --public-flat-rw"]) + +test.execute(fails=True, + all_run_flags=["+verilator+vpi+" + test.obj_dir + "/libvpi.so"], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_flag_main_vpi_noexe.py b/test_regress/t/t_flag_main_vpi_noexe.py new file mode 100755 index 000000000..1f347105b --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_noexe.py @@ -0,0 +1,30 @@ +#!/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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +# --vpi without --exe (no generated main, library-only output): the Makefile +# must still report VM_VPI = 1, but must NOT add the runtime-VPI link flags since +# there is no executable to export VPI symbols from. Exercises the exe()==false +# branch of the VPI link-flag gate in V3EmitMk. +test.top_filename = 't/t_flag_main_vpi.v' + +test.compile(make_main=False, verilator_make_gmake=False, verilator_flags2=["--vpi --timing"]) + +test.file_grep(test.obj_dir + "/V" + test.name + "_classes.mk", r'VM_VPI = 1') +# Without --exe there is no executable to export symbols from or to dlopen into, +# so the runtime-VPI link flags (CFG_LDFLAGS_DYNAMIC/CFG_LDLIBS_DYNAMIC, probed at +# configure time) must not be referenced in the generated Makefile. +mk = test.obj_dir + "/V" + test.name + ".mk" +test.file_grep_not(mk, r'CFG_LDFLAGS_DYNAMIC') +test.file_grep_not(mk, r'CFG_LDLIBS_DYNAMIC') + +test.passes() diff --git a/test_regress/t/t_flag_main_vpi_nowarn.out b/test_regress/t/t_flag_main_vpi_nowarn.out new file mode 100644 index 000000000..e51042279 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_nowarn.out @@ -0,0 +1,3 @@ +%Warning: COMMAND_LINE:0: +verilator+vpi+ ignored: simulation was not compiled with --vpi '+verilator+vpi+/nonexistent.so' +[0] Hello +*-* All Finished *-* diff --git a/test_regress/t/t_flag_main_vpi_nowarn.py b/test_regress/t/t_flag_main_vpi_nowarn.py new file mode 100755 index 000000000..ab077f4f5 --- /dev/null +++ b/test_regress/t/t_flag_main_vpi_nowarn.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: 2025 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('vlt') + +# Compile with --binary but WITHOUT --vpi. +# Passing +verilator+vpi+ at runtime should emit a warning, not load anything. +test.compile(top_filename='t/t_flag_main.v', verilator_flags2=["--binary"]) + +# Without --vpi there is no loader, so the plusarg is ignored with a warning (checked via +# the golden .out). The plusarg value is fixed, so the warning text is portable. +test.execute(all_run_flags=["+verilator+vpi+/nonexistent.so"], + check_finished=True, + expect_filename=test.golden_filename) + +test.passes() From 59b85f670b0d379f84469ed4bc620e9a31c5c1ea Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Sun, 28 Jun 2026 09:30:48 -0400 Subject: [PATCH 16/41] Fix constant pool recache after dead scope removal (#7845) --- src/V3AstNodeOther.h | 4 +- src/V3AstNodes.cpp | 25 ++++++-- src/V3Dead.cpp | 2 +- test_regress/t/t_opt_constpool_recache.py | 21 +++++++ test_regress/t/t_opt_constpool_recache.v | 75 +++++++++++++++++++++++ 5 files changed, 119 insertions(+), 8 deletions(-) create mode 100755 test_regress/t/t_opt_constpool_recache.py create mode 100644 test_regress/t/t_opt_constpool_recache.v diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 666a3ec22..3c25a9992 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1000,8 +1000,8 @@ public: // this matters, the caller must handle the dtype difference as appropriate. If 'mergeDType' is // false, the returned VarScope will have _->dtypep()->sameTree(initp->dtypep()) return true. AstVarScope* findConst(AstConst* initp, bool mergeDType); - // Rebuild hashes after potential removals - void reCache(); + // Rebuild hashes and missing variable scopes after potential removals + void rebuildVarScopesAndCache(); }; class AstConstraint final : public AstNode { // Constraint diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index ec26f24a9..aba8a9d0f 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include // Routines for dumping dict fields (NOTE: due to leading ',' they can't be used for first field in @@ -1779,14 +1780,28 @@ AstVarScope* AstConstPool::findConst(AstConst* initp, bool mergeDType) { return varScopep; } -void AstConstPool::reCache() { +void AstConstPool::rebuildVarScopesAndCache() { m_tables.clear(); m_consts.clear(); + std::unordered_map varScopeps; for (AstVarScope* vscp = m_scopep->varsp(); vscp; vscp = VN_CAST(vscp->nextp(), VarScope)) { - AstNode* const valuep = vscp->varp()->valuep(); - const V3Hash hash = V3Hasher::uncachedHash(valuep); - if (VN_IS(valuep, InitArray)) m_tables.emplace(hash.value(), vscp); - if (VN_IS(valuep, Const)) m_consts.emplace(hash.value(), vscp); + varScopeps.emplace(vscp->varp(), vscp); + } + for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) { + AstVar* const varp = VN_CAST(nodep, Var); + if (!varp) continue; + AstNode* const valuep = varp->valuep(); + if (!valuep) continue; + const bool isTable = VN_IS(valuep, InitArray); + const AstConst* const constp = VN_CAST(valuep, Const); + if (!isTable && !constp) continue; + AstVarScope*& vscp = varScopeps[varp]; + if (!vscp) { + vscp = new AstVarScope{varp->fileline(), m_scopep, varp}; + m_scopep->addVarsp(vscp); + } + if (isTable) m_tables.emplace(V3Hasher::uncachedHash(valuep).value(), vscp); + if (constp) m_consts.emplace(constp->num().toHash().value(), vscp); } } diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 677f0b2aa..411ec57eb 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -597,7 +597,7 @@ public: // We may have removed some datatypes, cleanup nodep->typeTablep()->repairCache(); VIsCached::clearCacheTree(); // Removing assignments may affect isPure - nodep->constPoolp()->reCache(); + nodep->constPoolp()->rebuildVarScopesAndCache(); } ~DeadVisitor() override { V3Stats::addStatSum("Optimizations, deadified FTasks", m_statFTasksDeadified); diff --git a/test_regress/t/t_opt_constpool_recache.py b/test_regress/t/t_opt_constpool_recache.py new file mode 100755 index 000000000..ac30d3a16 --- /dev/null +++ b/test_regress/t/t_opt_constpool_recache.py @@ -0,0 +1,21 @@ +#!/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=['--binary', '--stats']) + +test.execute() + +test.file_grep(test.stats, r'Optimizations, Cases table normal\s+(\d+)', 1) +test.file_grep(test.stats, r'ConstPool, Constants emitted\s+(\d+)', 1) + +test.passes() diff --git a/test_regress/t/t_opt_constpool_recache.v b/test_regress/t/t_opt_constpool_recache.v new file mode 100644 index 000000000..35ca38a27 --- /dev/null +++ b/test_regress/t/t_opt_constpool_recache.v @@ -0,0 +1,75 @@ +// 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 + +// 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 = 1'b0; + always #5 clk = ~clk; + + logic [31:0] cyc = 0; + logic [3:0] idx; + assign idx = cyc[3:0]; + + // V3Case lowers this to a 512-bit constant-pool lookup table before V3Dead + // calls AstConstPool::rebuildVarScopesAndCache(). + logic [31:0] case_word; + always_comb + case (idx) + 4'h0: case_word = 32'h00000000; + 4'h1: case_word = 32'h00000001; + 4'h2: case_word = 32'h00000002; + 4'h3: case_word = 32'h00000003; + 4'h4: case_word = 32'h00000004; + 4'h5: case_word = 32'h00000005; + 4'h6: case_word = 32'h00000006; + 4'h7: case_word = 32'h00000007; + 4'h8: case_word = 32'h00000008; + 4'h9: case_word = 32'h00000009; + 4'ha: case_word = 32'h0000000a; + 4'hb: case_word = 32'h0000000b; + 4'hc: case_word = 32'h0000000c; + 4'hd: case_word = 32'h0000000d; + 4'he: case_word = 32'h0000000e; + default: case_word = 32'h0000000f; + endcase + + localparam logic [511:0] TABLE = { + 32'h0000000f, + 32'h0000000e, + 32'h0000000d, + 32'h0000000c, + 32'h0000000b, + 32'h0000000a, + 32'h00000009, + 32'h00000008, + 32'h00000007, + 32'h00000006, + 32'h00000005, + 32'h00000004, + 32'h00000003, + 32'h00000002, + 32'h00000001, + 32'h00000000 + }; + + // V3Premit extracts this matching wide constant after V3Dead recached the + // const-pool contents created by V3Case. + logic [31:0] static_word; + assign static_word = TABLE[{idx, 5'b0} +: 32]; + + always @(posedge clk) begin + `checkh(case_word, static_word); + cyc <= cyc + 32'd1; + if (cyc == 32'd32) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule From 9448e4366ca6aaaf7ca2eefe9f29a2a302971f90 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 28 Jun 2026 09:42:01 -0400 Subject: [PATCH 17/41] Fix MSVC warning. No functional change. --- include/verilated.cpp | 1 - src/V3LinkInc.cpp | 1 + src/V3Randomize.cpp | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index a92c6ca5f..a5fdc91ac 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -377,7 +377,6 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE { } void VL_FFLUSH_MT() VL_MT_SAFE { - va_list ap; VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // Verilated::runFlushCallbacks(); }}); diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index f8e3c6138..e473325cd 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -307,6 +307,7 @@ class LinkIncVisitor final : public VNVisitor { return new AstSub{nodep->fileline(), lhsp, rhsp}; case AstAssignCompound::operation::Xor: return new AstXor{nodep->fileline(), lhsp, rhsp}; + default:; // Error below // LCOV_EXCL_LINE } } nodep->v3fatalSrc("Unhandled compound assignment operation"); diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 95a4d35aa..2786ea305 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1053,7 +1053,7 @@ class ConstraintExprVisitor final : public VNVisitor { nodep->user1(false); return; } - bool anyChild = false; + int anyChild = false; // Used as bool if (AstNodeExpr* const cp = VN_CAST(nodep->op1p(), NodeExpr)) { propagateUser1InlineRecurse(cp); anyChild |= cp->user1(); From 657d5f6abf269f6dd8b4957bb009fdfd3ced6cd2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 28 Jun 2026 15:34:50 -0400 Subject: [PATCH 18/41] Tests: Add t_initial_dlyass2 (#5210). Fixes #5210. (Confirms fixed in master earlier). --- test_regress/t/t_initial_dlyass2.py | 18 +++++++++++ test_regress/t/t_initial_dlyass2.v | 50 +++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100755 test_regress/t/t_initial_dlyass2.py create mode 100644 test_regress/t/t_initial_dlyass2.v diff --git a/test_regress/t/t_initial_dlyass2.py b/test_regress/t/t_initial_dlyass2.py new file mode 100755 index 000000000..46d1fe4c0 --- /dev/null +++ b/test_regress/t/t_initial_dlyass2.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_initial_dlyass2.v b/test_regress/t/t_initial_dlyass2.v new file mode 100644 index 000000000..02e25a323 --- /dev/null +++ b/test_regress/t/t_initial_dlyass2.v @@ -0,0 +1,50 @@ +// 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 + +// 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 + +module t; + + reg clk = 1; + reg [7:0] d = 0; + reg [7:0] q = 0; + + // Clock generation + always #0.5 clk = ~clk; + + // Input signal generation + initial begin + // verilator lint_off INITIALDLY + d <= 0; + repeat (5) @(posedge clk); + d <= 1; + @(posedge clk); + d <= 2; + @(posedge clk); + d <= 3; + @(posedge clk); + d <= 4; + @(posedge clk); + d <= 0; + repeat (5) @(posedge clk); + $finish; + end + + // Unit under test (flip-flop) + always @(posedge clk) q <= d; + + always @(negedge clk) begin + $display("[%0t] d=%x q=%x", $time, d, q); + if (d == 1) `checkd(q, 0); + if (d == 2) `checkd(q, 1); + if (d == 3) `checkd(q, 2); + if (d == 4) `checkd(q, 3); + end + +endmodule From c4f63583bcbe2436621995436d9661a4be7aa8a4 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 28 Jun 2026 15:40:10 -0400 Subject: [PATCH 19/41] Tests: Fix t_cover_expr_fork data location --- test_regress/t/t_cover_expr_fork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_regress/t/t_cover_expr_fork.py b/test_regress/t/t_cover_expr_fork.py index 8b6c049d2..57b6e30b1 100755 --- a/test_regress/t/t_cover_expr_fork.py +++ b/test_regress/t/t_cover_expr_fork.py @@ -13,6 +13,6 @@ test.scenarios('simulator') test.compile(verilator_flags2=['--coverage-expr --binary']) -test.execute() +test.execute(all_run_flags=[" +verilator+coverage+file+" + test.obj_dir + "/coverage.dat"]) test.passes() From 6006f91e2edb21d68625d07a2858471d0aeb218a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 28 Jun 2026 16:07:37 -0400 Subject: [PATCH 20/41] Tests: Add t_queue_array (#6921 test) --- test_regress/t/t_queue_array.py | 18 +++++++ test_regress/t/t_queue_array.v | 87 +++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100755 test_regress/t/t_queue_array.py create mode 100644 test_regress/t/t_queue_array.v diff --git a/test_regress/t/t_queue_array.py b/test_regress/t/t_queue_array.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_queue_array.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_queue_array.v b/test_regress/t/t_queue_array.v new file mode 100644 index 000000000..c729a4654 --- /dev/null +++ b/test_regress/t/t_queue_array.v @@ -0,0 +1,87 @@ +// 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 + +// 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; + + int normal_queue[$]; + int array_of_queues[3][$]; + int aa_of_queues[int][$]; + + function void test_normal_queue(); + $display("[%0t] %m: Testing single queue (int normal_queue [$])", $realtime); + `checkd(normal_queue.size(), 0); + repeat (4) normal_queue.push_back($urandom); + `checkd(normal_queue.size(), 4); + repeat (4) void'(normal_queue.pop_front()); + `checkd(normal_queue.size(), 0); + endfunction + + function void test_array_of_queues(); + $display("[%0t] %m: Testing array of queues (int array_of_queues [3][$])", $realtime); + array_of_queues[0] = {}; + array_of_queues[1] = {}; + array_of_queues[2] = {}; + + for (int i = 0; i < 3; i++) begin + `checkd(array_of_queues[i].size(), 0); + end + + for (int i = 0; i < 3; i++) begin + for (int j = 0; j < 4; j++) begin : push_4_items + array_of_queues[i].push_back($urandom); + $display("[%0t] %m: array_of_queues, pushed item to queue %0d: [0]=%p [1]=%p [2]=%p", + $realtime, i, array_of_queues[0], array_of_queues[1], array_of_queues[2]); + `checkd(array_of_queues[i].size(), j + 1); + end + end + + for (int i = 0; i < 3; i++) begin : pop_4_items_from_each + repeat (4) void'(array_of_queues[i].pop_front()); + `checkd(array_of_queues[i].size(), 0); + end + endfunction + + function void test_aa_of_queues(); + $display("[%0t] %m: Testing associative-array of queues (int aa_of_queues [int][$])", + $realtime); + + aa_of_queues[0] = {}; + aa_of_queues[1] = {}; + aa_of_queues[2] = {}; + + for (int i = 0; i < 3; i++) begin + `checkd(aa_of_queues[i].size(), 0); + end + + for (int i = 0; i < 3; i++) begin + for (int j = 0; j < 4; j++) begin : push_4_items + aa_of_queues[i].push_back($urandom); + $display("[%0t] %m: aa_of_queues, pushed item to queue %0d: [0]=%p [1]=%p [2]=%p", + $realtime, i, aa_of_queues[0], aa_of_queues[1], aa_of_queues[2]); + `checkd(aa_of_queues[i].size(), j + 1); + end + end + + for (int i = 0; i < 3; i++) begin + repeat (4) void'(aa_of_queues[i].pop_front()); + `checkd(aa_of_queues[i].size(), 0); + end + endfunction + + + initial begin + test_normal_queue(); + test_aa_of_queues(); + test_array_of_queues(); + $finish; + end + +endmodule From 276f2f344d88d186af5615afa5679d64ca174d30 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 28 Jun 2026 16:15:54 -0400 Subject: [PATCH 21/41] Commentary: Changes update --- Changes | 32 ++++++++-- docs/spelling.txt | 2 + test_regress/t/t_assert_ctl_pass_actions.v | 55 +++++++++------- .../t/t_assert_ctl_type_runtime_bad.v | 2 +- test_regress/t/t_assert_disabled.v | 6 +- test_regress/t/t_assertcontrol.v | 7 +-- test_regress/t/t_class_member_shadow_type.v | 2 +- test_regress/t/t_constraint_dist_foreach_if.v | 50 +++++++++------ test_regress/t/t_constraint_dist_range.v | 63 ++++++++++++------- test_regress/t/t_cuse_struct_pkg.v | 12 ++-- test_regress/t/t_flag_main_vpi.v | 4 +- test_regress/t/t_opt_constpool_recache.v | 2 +- test_regress/t/t_opt_inline_cfuncs_args.v | 2 +- test_regress/t/t_opt_inline_cfuncs_dup.v | 2 +- .../t/t_virtual_interface_method_sched.v | 12 +++- 15 files changed, 161 insertions(+), 92 deletions(-) diff --git a/Changes b/Changes index 438a8c265..08d60bba0 100644 --- a/Changes +++ b/Changes @@ -32,7 +32,7 @@ Verilator 5.049 devel * Improve `--coverage-fsm` (#7490) (#7529) (#7561) (#7573) (#7619). [Yogish Sekhar] * Change `+verilator+seed` to default to 1, and 0 to randomly select (#7325) (#7516). [Miguel] * Change JSON to include parameter constant mnemonics for FSM Coverage (#7531). [Yogish Sekhar] -* Support assert property 'default disable iff` (#4848) (#7723). [Artur Bieniek, Antmicro Ltd.] +* Support assert property `default disable iff` (#4848) (#7723). [Artur Bieniek, Antmicro Ltd.] * Support printing enum names for %p and %s (#5523) (#7338 repair) (#7521) (#7527). [Nick Brereton] * Support weak `until` / `until_with` property operators (#7290) (#7548) (#7685). [Yilou Wang] * Support `s_eventually` (#7291) (#7508). [Bartłomiej Chmiel, Antmicro Ltd.] @@ -67,6 +67,7 @@ Verilator 5.049 devel * Support property case (#7682) (#7721). [Artur Bieniek, Antmicro Ltd.] * Support `s_until` and `s_until_with`(#7722). [Artur Bieniek, Antmicro Ltd.] * Support covergroup runtime model Phase A1 (#7728). [Matthew Ballance] +* Support hierarchical reference cross members (#7749) (#7820). [Matthew Ballance] * Support reduction XOR/AND operations in constraints (#7753). [Kornel Uriasz, Antmicro Ltd.] * Support NBAs in initial blocks (#7754). [Igor Zaworski, Antmicro Ltd.] * Support assertion control system tasks in classes and interfaces (#7761). [Yilou Wang] @@ -75,6 +76,11 @@ Verilator 5.049 devel * Support $assertcontrol control_type from lock to kill (#7788). [Yilou Wang] * Support unbounded always [m:$] and strong s_always liveness (#7798). [Yilou Wang] * Support method calls on a sub-interface via a virtual interface (#7800). [Yilou Wang] +* Support global $assertcontrol (#7807). [Artur Bieniek, Antmicro Ltd.] +* Support VPI access to unpacked struct members (#7823). [Nick Brereton] +* Support variable-length intersect in SVA sequences (#7835). [Yilou Wang] +* Support dynamic loading of VPI extensions (#7727). [Matthew Ballance] +* Optimize DFG cycle breaking to do less work (#7210). [Geza Lore, Testorrent USA, Inc.] * Optimize emitting to_string() for compiler speedup (#7468). [Jakub Michalski, Antmicro Ltd.] * Optimize additional DFG peephole cases (#7553). [Varun Koyyalagunta, Testorrent USA, Inc.] * Optimize forced signal handling (#7554 partial) (#7572) (#7594) (#7596). [Krzysztof Bieganski, Artur Bieniek, Antmicro Ltd.] @@ -97,7 +103,12 @@ Verilator 5.049 devel * Optimize input combinational logic by change detection (#7784). [Geza Lore, Testorrent USA, Inc.] * Optimize decoder case statements into lookup tables (#7795). [Geza Lore, Testorrent USA, Inc.] * Optimize wide decoder case statements into decoder expressions (#7804). [Geza Lore, Testorrent USA, Inc.] -* Optimize DFG cycle breaking to do less work (#7210). [Geza Lore, Testorrent USA, Inc.] +* Optimize statically known oversize shifts (#7806). [Geza Lore, Testorrent USA, Inc.] +* Optimize generated function inlining (#7811). [Geza Lore, Testorrent USA, Inc.] +* Optimize bit-scan loops into most-set-bit or $countones (#7822). [Thomas Santerre] +* Optimize additional expression patterns (#7824). [Geza Lore, Testorrent USA, Inc.] +* Optimize module inlining heuristic (#7837). [Geza Lore, Testorrent USA, Inc.] +* Fix `$bits` on unpacked structs (#4521) (#7796). [Nick Brereton] * Fix TSP variable ordering for mtasks (#5342) (#7610). [Muzaffer Kal] * Fix inlining static initializer in V3Gate (#5381) (#7503). [Andrew Nolte] [Geza Lore, Testorrent USA, Inc.] * Fix timed nested fork block with disable (#6720) (#7743). [Marco Bartoli] @@ -147,6 +158,7 @@ Verilator 5.049 devel * Fix reference counting for modport task references (#7628). [Nick Brereton] * Fix internal error when handling typedefs containing parameterized class type members (#7635) (#7661). [em2machine] * Fix forceable signal with a procedural continuous assign (#7638) (#7639). [Zubin Jain] +* Fix scheduling of virtual interface method writes (#7641). [Artur Bieniek, Antmicro Ltd.] * Fix implicit conversions of VlWide (#7642). [Geza Lore, Testorrent USA, Inc.] * Fix CASEINCOMPLETE to not warn on `unique0 case` (#7647). * Fix hierarchical coverage counts for duplicate no-inline module instances (#7649). [Yogish Sekhar] @@ -173,17 +185,27 @@ Verilator 5.049 devel * Fix s_eventually in parameterized interfaces (#7741). [Nick Brereton] * Fix dpi export pointers (#7742) (#7751). [Yilin Li] * Fix force on unpacked bit select (#7744) (#7745). [Nikolai Kumar] +* Fix `$` as unsupported coverpoint-bin range bounds (#7750) (#7825). [Matthew Ballance] * Fix FSM detect unchecked casts and variable redeclaration (#7758). [Adam Kostrzewski, Antmicro Ltd.] * Fix no-scope internal error on virtual interface method calls (#7759). [Yilou Wang] -* Fix 'case (_) inside' with x wildcards (#7766). [Geza Lore, Testorrent USA, Inc.] +* Fix `case (_) inside` with x wildcards (#7766). [Geza Lore, Testorrent USA, Inc.] * Fix not failing assertion when RHS of a range window rejects once (#7773). [Artur Bieniek, Antmicro Ltd.] * Fix $fflush and autoflush with --threads (#7782). * Fix out-of-bounds read value for 2-state types (#7785). [Jakub Michalski] * Fix `cover property` of an implication counting vacuous matches (#7789). [Yilou Wang] * Fix randomization of dynamic arrays of objects (#7790). [Ryszard Rozak, Antmicro Ltd.] -* Fix `$bits` on unpacked structs (#4521) (#7796). [Nick Brereton] * Fix randomize() with skipping derived pre/post_randomize (#7799). [Yilou Wang] +* Fix skewed dist operator for arrays (#7802). [Jakub Wasilewski, Antmicro Ltd.] * Fix assertion when loop unrolling failed (#7810). [Geza Lore, Testorrent USA, Inc.] +* Fix CASEINCOMPLETE for all uncovered enum items (#7815) (#7817). [Saksham] +* Fix split optimization nested-class crash (#7826). [Igor Zaworski, Antmicro Ltd.] +* Fix class/var named identically to an enclosing-scope type (#7827) (#7828). [Tom Jackson] +* Fix performance on large package-scoped structs (#7830). [Wolfgang Mayerwieser] +* Fix unclocked concurrent assertion misreported as unsupported (#7831). [Yilou Wang] +* Fix insertion of expression coverage statement (#7832). [Ryszard Rozak, Antmicro Ltd.] +* Fix lifetime of expression coverage variable (#7834). [Ryszard Rozak, Antmicro Ltd.] +* Fix disable iff ignored when its condition is held continuously true (#7841). [Yilou Wang] +* Fix constant pool cache after dead scope removal (#7845). [Nick Brereton] Verilator 5.048 2026-04-26 @@ -520,7 +542,7 @@ Verilator 5.044 2026-01-01 * Support clocking output delay `1step` (#6681). [Ondrej Ille] * Support parsing of dotted `bins_expression` (#6683). [Pawel Kojma, Antmicro Ltd.] * Support constant expression cycle delays in sequences (#6691). [Ryszard Rozak, Antmicro Ltd.] -* Support general global constraints (#6709) (#6711). [Yilou Wang] +* Support general global constraints (#6709) (#6711) (#7833) (#7838). [Yilou Wang] * Support complex std::randomize patterns (#6736) (#6737). [Yilou Wang] * Support `rand_mode` in global constraint gathering (#6740) (#6752). [Yilou Wang] * Support reduction or in constraints (#6840). [Pawel Kojma, Antmicro Ltd.] diff --git a/docs/spelling.txt b/docs/spelling.txt index fa7fae5e9..a9d91f52b 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -684,6 +684,7 @@ countones cout covergroup covergroups +coverpoint coverpoints cpp cppstyle @@ -1207,6 +1208,7 @@ typename uint un unbased +unclocked uncomment undef undefineall diff --git a/test_regress/t/t_assert_ctl_pass_actions.v b/test_regress/t/t_assert_ctl_pass_actions.v index 3768f4bd1..3a7065569 100644 --- a/test_regress/t/t_assert_ctl_pass_actions.v +++ b/test_regress/t/t_assert_ctl_pass_actions.v @@ -36,7 +36,8 @@ interface AssertCtlIface; $assertcontrol(3, 2, 1); endfunction function void fail_check(); - assert (0) `stop; else fails++; + assert (0) `stop; + else fails++; endfunction function void run_checks(); assert_off(); @@ -69,12 +70,13 @@ module t; $assertcontrol(3, 2, 1); endfunction function void fail_check(); - assert (0) `stop; else class_fails++; + assert (0) `stop; + else class_fails++; endfunction endclass AssertCtlClass assert_ctl_class; - AssertCtlIface assert_ctl_iface(); + AssertCtlIface assert_ctl_iface (); virtual AssertCtlIface v_assert_ctl_iface = assert_ctl_iface; always #5 clk = !clk; @@ -84,17 +86,17 @@ module t; assert property (@(posedge clk) 1'b0 |-> ##1 1'b1) begin vacuous_passes++; - end else - `stop; + end + else `stop; assert property (@(posedge clk) 1'b1 |-> ##1 1'b1) begin nonvacuous_passes++; - end else - `stop; + end + else `stop; assert property (@(posedge clk) 1'b1 |-> ##1 1'b0) begin - end else - concurrent_fails++; + end + else concurrent_fails++; task automatic tick_and_check(input int exp_vacuous, input int exp_nonvacuous, input int exp_concurrent_fails); @@ -110,62 +112,73 @@ module t; $assertcontrol(4, 16, 1); $assertcontrol(5, 16, 1); - $assertcontrol(3*`IMPURE_ONE, 2*`IMPURE_ONE); + $assertcontrol(3 * `IMPURE_ONE, 2 * `IMPURE_ONE); $assertcontrol(3, 255, 7); $assertcontrol(6, 255, 7); $assertcontrol(8, 255, 7); - assert (1) imm_passes++; else `stop; + assert (1) imm_passes++; + else `stop; `checkd(imm_passes, 1); $assertcontrol(1, 2, 1); $assertcontrol(4, 2, 1); - assert (1) imm_passes++; else `stop; + assert (1) imm_passes++; + else `stop; `checkd(imm_passes, 2); $assertcontrol(2, 2, 1); $assertcontrol(4, 2, 1); - assert (1) imm_passes++; else `stop; + assert (1) imm_passes++; + else `stop; `checkd(imm_passes, 2); $assertcontrol(3, 2, 1); $assertcontrol(1, 2, 1); $assertcontrol(7, 2, 1); - assert (1) imm_passes++; else `stop; + assert (1) imm_passes++; + else `stop; `checkd(imm_passes, 3); $assertcontrol(2, 2, 1); $assertcontrol(7, 2, 1); - assert (1) imm_passes++; else `stop; + assert (1) imm_passes++; + else `stop; `checkd(imm_passes, 3); $assertcontrol(10, 2, 1); - assert (1) imm_passes++; else `stop; + assert (1) imm_passes++; + else `stop; `checkd(imm_passes, 4); $assertcontrol(11, 2, 1); - assert (1) imm_passes++; else `stop; + assert (1) imm_passes++; + else `stop; `checkd(imm_passes, 5); $assertcontrol(6, 2, 1); - assert (0) `stop; else imm_fails++; + assert (0) `stop; + else imm_fails++; `checkd(imm_fails, 1); $assertcontrol(1, 2, 1); $assertcontrol(9, 2, 1); - assert (0) `stop; else imm_fails++; + assert (0) `stop; + else imm_fails++; `checkd(imm_fails, 2); $assertcontrol(2, 2, 1); $assertcontrol(9, 2, 1); - assert (0) `stop; else imm_fails++; + assert (0) `stop; + else imm_fails++; `checkd(imm_fails, 2); $assertcontrol(8, 2, 1); - assert (0) `stop; else imm_fails++; + assert (0) `stop; + else imm_fails++; `checkd(imm_fails, 3); assert_ctl_class.assert_off(); diff --git a/test_regress/t/t_assert_ctl_type_runtime_bad.v b/test_regress/t/t_assert_ctl_type_runtime_bad.v index d35421daf..ab48ce35f 100644 --- a/test_regress/t/t_assert_ctl_type_runtime_bad.v +++ b/test_regress/t/t_assert_ctl_type_runtime_bad.v @@ -14,7 +14,7 @@ module t; initial begin - $assertcontrol(100*`IMPURE_ONE); + $assertcontrol(100 * `IMPURE_ONE); $finish; end endmodule diff --git a/test_regress/t/t_assert_disabled.v b/test_regress/t/t_assert_disabled.v index 447253c68..b09690d85 100644 --- a/test_regress/t/t_assert_disabled.v +++ b/test_regress/t/t_assert_disabled.v @@ -16,10 +16,8 @@ module t ( integer action_hits = 0; integer cyc = 0; - assert property (@(posedge clk) ##1 1'b1) - action_hits++; - else - action_hits--; + assert property (@(posedge clk) ##1 1'b1) action_hits++; + else action_hits--; always @(posedge clk) begin cyc++; diff --git a/test_regress/t/t_assertcontrol.v b/test_regress/t/t_assertcontrol.v index 26f9d360b..5088aa280 100644 --- a/test_regress/t/t_assertcontrol.v +++ b/test_regress/t/t_assertcontrol.v @@ -33,13 +33,12 @@ module t; task automatic run_pass(); assert (1) begin pass_count++; - end else - `stop; + end + else `stop; endtask task automatic run_fail(); - assert (0) - `stop; + assert (0) `stop; else begin fail_count++; end diff --git a/test_regress/t/t_class_member_shadow_type.v b/test_regress/t/t_class_member_shadow_type.v index e0d11a0c7..3e56ba5cd 100644 --- a/test_regress/t/t_class_member_shadow_type.v +++ b/test_regress/t/t_class_member_shadow_type.v @@ -21,7 +21,7 @@ class C; endclass class D; - my_t a; // second use of the type name after the shadowing variable... + my_t a; // second use of the type name after the shadowing variable... my_t my_t; // ... is also legal; both resolve to the $unit typedef endclass diff --git a/test_regress/t/t_constraint_dist_foreach_if.v b/test_regress/t/t_constraint_dist_foreach_if.v index 3ae315711..a8eaedb24 100644 --- a/test_regress/t/t_constraint_dist_foreach_if.v +++ b/test_regress/t/t_constraint_dist_foreach_if.v @@ -13,12 +13,15 @@ // foreach (a[i]) if (gate) a[i] dist {...} class ClsIf; - rand bit [3:0] a [4]; + rand bit [3:0] a[4]; bit gate; constraint c { foreach (a[i]) { if (gate == 1'b1) { - a[i] dist { 4'd0 := 3, [4'd1:4'd4] := 1 }; + a[i] dist { + 4'd0 := 3, + [4'd1 : 4'd4] := 1 + }; } } } @@ -26,22 +29,30 @@ endclass // foreach (a[i]) gate -> a[i] dist {...} class ClsImpl; - rand bit [3:0] a [4]; + rand bit [3:0] a[4]; bit gate; constraint c { foreach (a[i]) { - gate -> (a[i] dist { 4'd0 := 3, [4'd1:4'd4] := 1 }); + gate -> + (a[i] dist { + 4'd0 := 3, + [4'd1 : 4'd4] := 1 + }); } } endclass // foreach (a[i]) gateA -> (gateB -> a[i] dist {...}) -- doubly-nested implication class ClsImplChained; - rand bit [3:0] a [4]; + rand bit [3:0] a[4]; bit gateA, gateB; constraint c { foreach (a[i]) { - gateA -> (gateB -> (a[i] dist { 4'd0 := 3, [4'd1:4'd4] := 1 })); + gateA -> + (gateB -> (a[i] dist { + 4'd0 := 3, + [4'd1 : 4'd4] := 1 + })); } } endclass @@ -59,8 +70,8 @@ module t; `checkd(obj.randomize(), 1) foreach (obj.a[i]) begin if (obj.a[i] > 4) begin - $write("%%Error: %s:%0d: if: value out of dist range: %0d\n", - `__FILE__, `__LINE__, obj.a[i]); + $write("%%Error: %s:%0d: if: value out of dist range: %0d\n", `__FILE__, `__LINE__, + obj.a[i]); $stop; end if (obj.a[i] == 0) seen_zero++; @@ -68,8 +79,9 @@ module t; end end if (seen_zero == 0 || seen_nonzero == 0) begin - $write("%%Error: %s:%0d: dist inside foreach+if: not all buckets hit (zero=%0d nonzero=%0d)\n", - `__FILE__, `__LINE__, seen_zero, seen_nonzero); + $write( + "%%Error: %s:%0d: dist inside foreach+if: not all buckets hit (zero=%0d nonzero=%0d)\n", + `__FILE__, `__LINE__, seen_zero, seen_nonzero); $stop; end end @@ -85,8 +97,8 @@ module t; `checkd(obj.randomize(), 1) foreach (obj.a[i]) begin if (obj.a[i] > 4) begin - $write("%%Error: %s:%0d: ->: value out of dist range: %0d\n", - `__FILE__, `__LINE__, obj.a[i]); + $write("%%Error: %s:%0d: ->: value out of dist range: %0d\n", `__FILE__, `__LINE__, + obj.a[i]); $stop; end if (obj.a[i] == 0) seen_zero++; @@ -94,8 +106,9 @@ module t; end end if (seen_zero == 0 || seen_nonzero == 0) begin - $write("%%Error: %s:%0d: dist inside foreach+->: not all buckets hit (zero=%0d nonzero=%0d)\n", - `__FILE__, `__LINE__, seen_zero, seen_nonzero); + $write( + "%%Error: %s:%0d: dist inside foreach+->: not all buckets hit (zero=%0d nonzero=%0d)\n", + `__FILE__, `__LINE__, seen_zero, seen_nonzero); $stop; end end @@ -112,8 +125,8 @@ module t; `checkd(obj.randomize(), 1) foreach (obj.a[i]) begin if (obj.a[i] > 4) begin - $write("%%Error: %s:%0d: ->->: value out of dist range: %0d\n", - `__FILE__, `__LINE__, obj.a[i]); + $write("%%Error: %s:%0d: ->->: value out of dist range: %0d\n", `__FILE__, `__LINE__, + obj.a[i]); $stop; end if (obj.a[i] == 0) seen_zero++; @@ -121,8 +134,9 @@ module t; end end if (seen_zero == 0 || seen_nonzero == 0) begin - $write("%%Error: %s:%0d: dist inside foreach+->->: not all buckets hit (zero=%0d nonzero=%0d)\n", - `__FILE__, `__LINE__, seen_zero, seen_nonzero); + $write( + "%%Error: %s:%0d: dist inside foreach+->->: not all buckets hit (zero=%0d nonzero=%0d)\n", + `__FILE__, `__LINE__, seen_zero, seen_nonzero); $stop; end end diff --git a/test_regress/t/t_constraint_dist_range.v b/test_regress/t/t_constraint_dist_range.v index fbddaf0a1..88ffe3e0b 100644 --- a/test_regress/t/t_constraint_dist_range.v +++ b/test_regress/t/t_constraint_dist_range.v @@ -14,13 +14,20 @@ // Scalar: uniform over [0:9] (10% each) class DistScalarRange; rand bit [3:0] x; - constraint c { x dist {[4'd0:4'd9] := 1}; } + constraint c { + x dist { + [4'd0 : 4'd9] := 1 + }; + } endclass // Array foreach: uniform over [0:4] (20% each per element) class DistForeachUniform; rand bit [2:0] a[5]; - constraint c { foreach (a[i]) a[i] dist {[3'd0:3'd4] := 1}; } + constraint c { + foreach (a[i]) + a[i] dist {[3'd0 : 3'd4] := 1}; + } endclass // Array foreach: mixed single + range @@ -28,32 +35,50 @@ endclass // 0: 5/14 ~= 35.7%, 1..9: 1/14 ~= 7.1% each class DistForeachMixed; rand bit [3:0] a[5]; - constraint c { foreach (a[i]) a[i] dist {4'd0 := 5, [4'd1:4'd9] := 1}; } + constraint c { + foreach (a[i]) + a[i] dist { + 4'd0 := 5, + [4'd1 : 4'd9] := 1 + }; + } endclass // Scalar signed int: uniform over negative range [-9:0] (10% each) class DistNegRange; rand int x; - constraint c { x dist {[-9:0] := 1}; } + constraint c { + x dist { + [-9 : 0] := 1 + }; + } endclass // Non-constant unsigned range bounds: uniform over [lo_val:hi_val] = [2:7] (6 values) class DistVarRangeUnsigned; rand bit [3:0] x; bit [3:0] lo_val = 4'd2, hi_val = 4'd7; - constraint c { x dist {[lo_val:hi_val] := 1}; } + constraint c { + x dist { + [lo_val : hi_val] := 1 + }; + } endclass // Mixed const/non-const bounds: lo is constant, hi is a variable [1:hi_val] = [1:7] class DistMixedBounds; rand bit [3:0] x; bit [3:0] hi_val = 4'd7; - constraint c { x dist {[4'd1:hi_val] := 1}; } + constraint c { + x dist { + [4'd1 : hi_val] := 1 + }; + } endclass module t; - parameter int N = 2000; // randomize() calls per test - parameter int TOL_PCT = 30; // +-% tolerance on expected counts + parameter int N = 2000; // randomize() calls per test + parameter int TOL_PCT = 30; // +-% tolerance on expected counts initial begin @@ -71,8 +96,7 @@ module t; end cnt[obj.x]++; end - foreach (cnt[v]) - `check_tol(cnt[v], N/10) + foreach (cnt[v]) `check_tol(cnt[v], N / 10) end // --- T2: array foreach uniform [0:4], 5 elements * N calls --- @@ -91,8 +115,7 @@ module t; cnt[obj.a[i]]++; end end - foreach (cnt[v]) - `check_tol(cnt[v], N) + foreach (cnt[v]) `check_tol(cnt[v], N) end // --- T3: array foreach mixed {0:=5, [1:9]:=1}, 5 elements * N calls --- @@ -113,9 +136,8 @@ module t; cnt[obj.a[i]]++; end end - `check_tol(cnt[0], N*5*5/14) - for (int v = 1; v <= 9; v++) - `check_tol(cnt[v], N*5*1/14) + `check_tol(cnt[0], N * 5 * 5 / 14) + for (int v = 1; v <= 9; v++) `check_tol(cnt[v], N * 5 * 1 / 14) end // --- T4: signed int, uniform over negative range [-9:0] --- @@ -131,10 +153,9 @@ module t; $write("%%Error: x=%0d outside valid range [-9:0]\n", obj.x); `stop; end - cnt[obj.x + 9]++; + cnt[obj.x+9]++; end - foreach (cnt[v]) - `check_tol(cnt[v], N/10) + foreach (cnt[v]) `check_tol(cnt[v], N / 10) end // --- T5: non-constant unsigned range bounds [lo_val:hi_val] = [2:7] --- @@ -151,8 +172,7 @@ module t; end cnt[obj.x]++; end - for (int v = 2; v <= 7; v++) - `check_tol(cnt[v], N/6) + for (int v = 2; v <= 7; v++) `check_tol(cnt[v], N / 6) end // --- T6: mixed const/non-const bounds [4'd1:hi_val] = [1:7] --- @@ -169,8 +189,7 @@ module t; end cnt[obj.x]++; end - for (int v = 1; v <= 7; v++) - `check_tol(cnt[v], N/7) + for (int v = 1; v <= 7; v++) `check_tol(cnt[v], N / 7) end $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_cuse_struct_pkg.v b/test_regress/t/t_cuse_struct_pkg.v index 197bbe62a..2ff97c8b2 100644 --- a/test_regress/t/t_cuse_struct_pkg.v +++ b/test_regress/t/t_cuse_struct_pkg.v @@ -5,9 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 package pkg; - typedef struct { - logic [7:0] value; - } field_t; + typedef struct {logic [7:0] value;} field_t; typedef struct { field_t f0; field_t f1; @@ -19,11 +17,9 @@ package pkg; } hwif_t; endpackage -module t (/*AUTOARG*/ - // Inputs - clk - ); - input clk; +module t ( + input clk +); import pkg::*; diff --git a/test_regress/t/t_flag_main_vpi.v b/test_regress/t/t_flag_main_vpi.v index c688405ca..6ac03e26e 100644 --- a/test_regress/t/t_flag_main_vpi.v +++ b/test_regress/t/t_flag_main_vpi.v @@ -12,8 +12,8 @@ // (requires --public-flat-rw). module t; - reg clk /*verilator public_flat_rw*/; - reg [31:0] count /*verilator public_flat_rw*/; + reg clk /*verilator public_flat_rw*/; + reg [31:0] count /*verilator public_flat_rw*/; initial begin clk = 0; diff --git a/test_regress/t/t_opt_constpool_recache.v b/test_regress/t/t_opt_constpool_recache.v index 35ca38a27..af77d7409 100644 --- a/test_regress/t/t_opt_constpool_recache.v +++ b/test_regress/t/t_opt_constpool_recache.v @@ -62,7 +62,7 @@ module t; // V3Premit extracts this matching wide constant after V3Dead recached the // const-pool contents created by V3Case. logic [31:0] static_word; - assign static_word = TABLE[{idx, 5'b0} +: 32]; + assign static_word = TABLE[{idx, 5'b0}+:32]; always @(posedge clk) begin `checkh(case_word, static_word); diff --git a/test_regress/t/t_opt_inline_cfuncs_args.v b/test_regress/t/t_opt_inline_cfuncs_args.v index d94ac40c4..74c049939 100644 --- a/test_regress/t/t_opt_inline_cfuncs_args.v +++ b/test_regress/t/t_opt_inline_cfuncs_args.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 module t ( - input wire clk + input wire clk ); integer cyc = 0; diff --git a/test_regress/t/t_opt_inline_cfuncs_dup.v b/test_regress/t/t_opt_inline_cfuncs_dup.v index 2a39fa47e..2d93e20fe 100644 --- a/test_regress/t/t_opt_inline_cfuncs_dup.v +++ b/test_regress/t/t_opt_inline_cfuncs_dup.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 module t ( - input wire clk + input wire clk ); integer cyc = 0; diff --git a/test_regress/t/t_virtual_interface_method_sched.v b/test_regress/t/t_virtual_interface_method_sched.v index 48dc01565..b6586f268 100644 --- a/test_regress/t/t_virtual_interface_method_sched.v +++ b/test_regress/t/t_virtual_interface_method_sched.v @@ -20,7 +20,10 @@ package helper_pkg; endfunction endpackage -interface intf(output logic a, output logic b); +interface intf ( + output logic a, + output logic b +); msg m; event e; @@ -34,7 +37,7 @@ interface intf(output logic a, output logic b); // verilator no_inline_task m.set_context("go_helper"); helper_pkg::bump(); - -> e; + ->e; a <= 1; endtask endinterface @@ -56,7 +59,10 @@ module t; wire b; virtual intf vif; - intf i(a, b); + intf i ( + a, + b + ); initial begin driver d; From cf7e3f791a21904c387235de5add20bcf7bae3c5 Mon Sep 17 00:00:00 2001 From: Joshua Leahy <36183+jleahy@users.noreply.github.com> Date: Mon, 29 Jun 2026 15:56:59 +0100 Subject: [PATCH 22/41] Fix covergroups without --coverage (#7848) (#7849) Fixes #7848. --- docs/CONTRIBUTORS | 1 + include/verilated_covergroup.cpp | 9 +++++++-- src/V3EmitCHeaders.cpp | 3 ++- src/V3EmitCModel.cpp | 3 ++- src/V3Global.cpp | 3 ++- .../t/t_covergroup_cross_no_coverage.py | 20 +++++++++++++++++++ .../t/t_covergroup_option_no_coverage.py | 20 +++++++++++++++++++ 7 files changed, 54 insertions(+), 5 deletions(-) create mode 100755 test_regress/t/t_covergroup_cross_no_coverage.py create mode 100755 test_regress/t/t_covergroup_option_no_coverage.py diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 10db04fcc..f0e8b1c98 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -146,6 +146,7 @@ Jose Loyola Josep Sans Joseph Nwabueze Josh Redford +Joshua Leahy Julian Carrier Julian Daube Julie Schwartz diff --git a/include/verilated_covergroup.cpp b/include/verilated_covergroup.cpp index 6dc7006f1..c872d5557 100644 --- a/include/verilated_covergroup.cpp +++ b/include/verilated_covergroup.cpp @@ -14,7 +14,8 @@ /// \file /// \brief Verilated functional-coverage collection runtime implementation /// -/// Compiled and linked when "verilator --coverage" is used with covergroups. +/// Linked when covergroups are present. The coverage-database registration +/// is compiled only with "verilator --coverage". /// //============================================================================= @@ -22,7 +23,9 @@ #include "verilated_covergroup.h" -#include "verilated_cov.h" +#if VM_COVERAGE +# include "verilated_cov.h" +#endif void VlCoverpoint::init(const char* hier, uint32_t atLeast, int nBins) { m_hier = hier; @@ -54,6 +57,7 @@ std::string VlCoverpoint::binName(int i) const { return name; } +#if VM_COVERAGE void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* page) { for (int i = 0; i < binCount(); ++i) { const VlCovNamer& nm = namerFor(i); @@ -76,3 +80,4 @@ void VlCoverpoint::registerBins(VerilatedCovContext* covcontextp, const char* pa } } } +#endif // VM_COVERAGE diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 2a3197112..8b2f2add0 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -683,7 +683,8 @@ class EmitCHeader final : public EmitCConstInit { if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); - if (v3Global.opt.coverage()) puts("#include \"verilated_covergroup.h\"\n"); + if (v3Global.opt.coverage() || v3Global.useCovergroup()) + puts("#include \"verilated_covergroup.h\"\n"); if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n"); if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n"); if (v3Global.usesForce()) puts("#include \"verilated_force.h\"\n"); diff --git a/src/V3EmitCModel.cpp b/src/V3EmitCModel.cpp index 6ddd53e77..034b1e807 100644 --- a/src/V3EmitCModel.cpp +++ b/src/V3EmitCModel.cpp @@ -70,7 +70,8 @@ class EmitCModel final : public EmitCFunc { if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n"); if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n"); if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n"); - if (v3Global.opt.coverage()) puts("#include \"verilated_covergroup.h\"\n"); + if (v3Global.opt.coverage() || v3Global.useCovergroup()) + puts("#include \"verilated_covergroup.h\"\n"); if (v3Global.dpi()) puts("#include \"svdpi.h\"\n"); // Declare foreign instances up front to make C++ happy diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 2e7c25fc7..3fd207be3 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -245,7 +245,8 @@ std::vector V3Global::verilatedCppFiles() { if (v3Global.opt.vpi()) result.emplace_back("verilated_vpi.cpp"); if (v3Global.opt.savable()) result.emplace_back("verilated_save.cpp"); if (v3Global.opt.coverage()) result.emplace_back("verilated_cov.cpp"); - if (v3Global.opt.coverage()) result.emplace_back("verilated_covergroup.cpp"); + if (v3Global.opt.coverage() || v3Global.useCovergroup()) + result.emplace_back("verilated_covergroup.cpp"); for (const string& base : v3Global.opt.traceSourceBases()) result.emplace_back(base + "_c.cpp"); if (v3Global.usesProbDist()) result.emplace_back("verilated_probdist.cpp"); diff --git a/test_regress/t/t_covergroup_cross_no_coverage.py b/test_regress/t/t_covergroup_cross_no_coverage.py new file mode 100755 index 000000000..699c061b8 --- /dev/null +++ b/test_regress/t/t_covergroup_cross_no_coverage.py @@ -0,0 +1,20 @@ +#!/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_all') +test.top_filename = 't/t_covergroup_cross.v' + +# runs without --coverage +test.compile(verilator_flags2=['--Wno-COVERIGN']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_covergroup_option_no_coverage.py b/test_regress/t/t_covergroup_option_no_coverage.py new file mode 100755 index 000000000..5f3bb6c6d --- /dev/null +++ b/test_regress/t/t_covergroup_option_no_coverage.py @@ -0,0 +1,20 @@ +#!/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.top_filename = 't/t_covergroup_option.v' + +# runs without --coverage +test.compile(verilator_flags2=['--Wno-COVERIGN']) + +test.execute() + +test.passes() From 514fa120f590c1c7914385f29d3139f6d45a7748 Mon Sep 17 00:00:00 2001 From: github action Date: Mon, 29 Jun 2026 14:58:00 +0000 Subject: [PATCH 23/41] Apply 'make format' [ci skip] --- include/verilated_covergroup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/verilated_covergroup.cpp b/include/verilated_covergroup.cpp index c872d5557..9bf0fc53f 100644 --- a/include/verilated_covergroup.cpp +++ b/include/verilated_covergroup.cpp @@ -24,7 +24,7 @@ #include "verilated_covergroup.h" #if VM_COVERAGE -# include "verilated_cov.h" +#include "verilated_cov.h" #endif void VlCoverpoint::init(const char* hier, uint32_t atLeast, int nBins) { From e80329a57bfdff59331ae37429953bd254dccf8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2026 11:03:22 -0400 Subject: [PATCH 24/41] CI: Bump actions/cache from 5.0.5 to 6.1.0 in the everything group (#7854) --- .github/workflows/build-test.yml | 2 +- .github/workflows/reusable-build.yml | 2 +- .github/workflows/reusable-rtlmeter-build.yml | 2 +- .github/workflows/reusable-rtlmeter-run.yml | 2 +- .github/workflows/reusable-test.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index d0dfeccb7..7817a3bf6 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -158,7 +158,7 @@ jobs: with: path: repo - name: Cache $CCACHE_DIR - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5 with: path: ${{ env.CCACHE_DIR }} key: msbuild-msvc-cmake diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index 5d053cadb..87a4052d7 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -68,7 +68,7 @@ jobs: fetch-depth: ${{ inputs.dev-gcov && '0' || '1' }} # Coverage flow needs full history - name: Cache $CCACHE_DIR - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5 env: CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache with: diff --git a/.github/workflows/reusable-rtlmeter-build.yml b/.github/workflows/reusable-rtlmeter-build.yml index cdfee423a..b38ed62cf 100644 --- a/.github/workflows/reusable-rtlmeter-build.yml +++ b/.github/workflows/reusable-rtlmeter-build.yml @@ -50,7 +50,7 @@ jobs: sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev - name: Use saved ccache - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5 with: path: ccache key: rtlmeter-build-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.sha }}-${{ github.run_id }}-${{ github.run_attempt }} diff --git a/.github/workflows/reusable-rtlmeter-run.yml b/.github/workflows/reusable-rtlmeter-run.yml index 81aaad405..ab66647db 100644 --- a/.github/workflows/reusable-rtlmeter-run.yml +++ b/.github/workflows/reusable-rtlmeter-run.yml @@ -84,7 +84,7 @@ jobs: - name: Use saved ccache if: ${{ env.CCACHE_DISABLE == 0 }} - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5 with: path: ${{ env.CCACHE_DIR }} key: rtlmeter-run-ccache-${{ inputs.runs-on }}-${{ inputs.cc }}-${{ inputs.cases }}-${{ inputs.compileArgs }}-${{ github.run_id }}-${{ github.run_attempt }} diff --git a/.github/workflows/reusable-test.yml b/.github/workflows/reusable-test.yml index efc9c50fe..ffec26b84 100644 --- a/.github/workflows/reusable-test.yml +++ b/.github/workflows/reusable-test.yml @@ -67,7 +67,7 @@ jobs: ls -lsha - name: Cache $CCACHE_DIR - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5 env: CACHE_KEY: ${{ env.CACHE_BASE_KEY }}-ccache2 with: From 6b3e2ce971c3d76fc36295a7c3603922e49deb91 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 29 Jun 2026 22:00:10 -0400 Subject: [PATCH 25/41] Commentary: Changes update --- Changes | 1 + test_regress/t/t_config_libmap_inc.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Changes b/Changes index 08d60bba0..9428c6eac 100644 --- a/Changes +++ b/Changes @@ -206,6 +206,7 @@ Verilator 5.049 devel * Fix lifetime of expression coverage variable (#7834). [Ryszard Rozak, Antmicro Ltd.] * Fix disable iff ignored when its condition is held continuously true (#7841). [Yilou Wang] * Fix constant pool cache after dead scope removal (#7845). [Nick Brereton] +* Fix covergroups without --coverage (#7848) (#7849). [Joshua Leahy] Verilator 5.048 2026-04-26 diff --git a/test_regress/t/t_config_libmap_inc.py b/test_regress/t/t_config_libmap_inc.py index 10db367a5..bbe991e93 100755 --- a/test_regress/t/t_config_libmap_inc.py +++ b/test_regress/t/t_config_libmap_inc.py @@ -9,7 +9,6 @@ import vltest_bootstrap -test.scenarios('simulator') test.scenarios('linter') test.lint(verilator_flags2=["-libmap t/t_config_libmap_inc.map"], From 1ea10ba71c2e598687c43d571909b0f81418ceba Mon Sep 17 00:00:00 2001 From: Sergey Chusov <44680536+serge0699@users.noreply.github.com> Date: Wed, 1 Jul 2026 22:58:35 +0300 Subject: [PATCH 26/41] Fix class scope '::' reference through an inherited type parameter (#7844) --- docs/CONTRIBUTORS | 1 + src/V3LinkDot.cpp | 17 +++++-- test_regress/t/t_class_extends_param_scope.py | 18 +++++++ test_regress/t/t_class_extends_param_scope.v | 47 +++++++++++++++++++ 4 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 test_regress/t/t_class_extends_param_scope.py create mode 100644 test_regress/t/t_class_extends_param_scope.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index f0e8b1c98..763ac8ac0 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -258,6 +258,7 @@ Samuel Riedel Sean Cross Sebastien Van Cauwenberghe Secturion +Sergey Chusov Sergey Fedorov Sergi Granell Seth Pellegrino diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 7dd5fe3ea..33e2d38b5 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1013,18 +1013,23 @@ public: if (checkUnresolvedRef(VN_CAST(dtypep, RefDType))) return true; } else if (const AstParamTypeDType* const paramTypep = VN_CAST(symp->nodep(), ParamTypeDType)) { - // ParamTypeDType child may be wrapped in RequireDType or unwrapped + // Before V3Param the declared default is in childDTypep (possibly + // wrapped in a RequireDType); after V3Param it is consumed and the + // bound type is the resolved data type, e.g. a type parameter + // inherited from a specialized base class (REQ #(Item) -> class Item). AstNode* childp = paramTypep->childDTypep(); if (const AstRequireDType* const reqp = VN_CAST(childp, RequireDType)) { childp = reqp->lhsp(); } - if (isValidTypeNode(childp)) return true; - if (checkUnresolvedRef(VN_CAST(childp, RefDType))) return true; + const AstNode* const checkp = childp ? childp : paramTypep->skipRefp(); + if (isValidTypeNode(checkp)) return true; + if (checkUnresolvedRef(VN_CAST(checkp, RefDType))) return true; } return false; } VSymEnt* resolveClassOrPackage(VSymEnt* lookSymp, AstClassOrPackageRef* nodep, bool fallback, - bool classOnly, const string& forWhat) { + bool classOnly, const string& forWhat, + bool deferIfUnresolved = false) { if (nodep->classOrPackageSkipp()) return getNodeSym(nodep->classOrPackageSkipp()); VSymEnt* foundp; VSymEnt* searchSymp = lookSymp; @@ -1050,6 +1055,7 @@ public: nodep->classOrPackageNodep(foundp->nodep()); return foundp; } + if (deferIfUnresolved) return nullptr; const string suggest = suggestSymFallback(lookSymp, nodep->name(), LinkNodeMatcherClassOrPackage{}); nodep->v3error((classOnly ? "Class" : "Package/class") @@ -4758,8 +4764,9 @@ class LinkDotResolveVisitor final : public VNVisitor { VL_RESTORER(m_pinSymp); if (!nodep->classOrPackageSkipp() && nodep->name() != "local::") { + const bool deferIfUnresolved = m_statep->forPrimary() && m_insideClassExtParam; m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, m_ds.m_dotPos != DP_PACKAGE, - false, ":: reference"); + false, ":: reference", deferIfUnresolved); } // ClassRef's have pins, so track diff --git a/test_regress/t/t_class_extends_param_scope.py b/test_regress/t/t_class_extends_param_scope.py new file mode 100644 index 000000000..3cc73805c --- /dev/null +++ b/test_regress/t/t_class_extends_param_scope.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: 2024 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_extends_param_scope.v b/test_regress/t/t_class_extends_param_scope.v new file mode 100644 index 000000000..d60ac9381 --- /dev/null +++ b/test_regress/t/t_class_extends_param_scope.v @@ -0,0 +1,47 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Sergey Chusov +// SPDX-License-Identifier: CC0-1.0 + +// A factory-like static method, as used by UVM's type_id::create(). +class registry #(type T = int); + static function T create(); + T r = new; + return r; + endfunction +endclass + +class item; + int val; + typedef registry#(item) type_id; + function new(); + val = 42; + endfunction +endclass + +class seq_base #(type REQ = int, type RSP = REQ); + REQ req; +endclass + +// Non-parameterized class extending a parameterized base. +// REQ is a type parameter inherited from the (specialized) base class and is +// here used as a '::' class scope: REQ::type_id::create(). Resolving REQ in +// this position requires deferring until after V3Param has bound REQ to item. +class seq extends seq_base #(item); + function int make(); + REQ r; + r = REQ::type_id::create(); + return r.val; + endfunction +endclass + +module t; + seq s; + initial begin + s = new; + if (s.make() != 42) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 9c655d379a5c2e952573cc3495dc0ee512a4c08a Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 1 Jul 2026 19:59:37 +0000 Subject: [PATCH 27/41] Apply 'make format' [ci skip] --- test_regress/t/t_class_extends_param_scope.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test_regress/t/t_class_extends_param_scope.py diff --git a/test_regress/t/t_class_extends_param_scope.py b/test_regress/t/t_class_extends_param_scope.py old mode 100644 new mode 100755 From bb9cad507f74feb8f1ac6d07adcfaa076bc8f369 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Jul 2026 18:15:56 -0400 Subject: [PATCH 28/41] Commentary: Changes update --- Changes | 1 + 1 file changed, 1 insertion(+) diff --git a/Changes b/Changes index 9428c6eac..2963deb07 100644 --- a/Changes +++ b/Changes @@ -207,6 +207,7 @@ Verilator 5.049 devel * Fix disable iff ignored when its condition is held continuously true (#7841). [Yilou Wang] * Fix constant pool cache after dead scope removal (#7845). [Nick Brereton] * Fix covergroups without --coverage (#7848) (#7849). [Joshua Leahy] +* Fix class scope '::' reference through an inherited type parameter (#7844). [Sergey Chusov] Verilator 5.048 2026-04-26 From 848d926ebd4addacacd294dc84e35d9d4ae8078c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Jul 2026 18:17:03 -0400 Subject: [PATCH 29/41] Version bump --- CMakeLists.txt | 2 +- Changes | 2 +- configure.ac | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb5682e7b..ce2e8beca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime project( Verilator - VERSION 5.049 + VERSION 5.050 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index 2963deb07..2774b4ad4 100644 --- a/Changes +++ b/Changes @@ -10,7 +10,7 @@ The changes in each Verilator version are described below. The contributors that suggested or implemented a given issue are shown in []. Thanks! -Verilator 5.049 devel +Verilator 5.050 2026-07-01 ========================== **Important:** diff --git a/configure.ac b/configure.ac index 00d8ac713..81329841c 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.049 devel], +AC_INIT([Verilator],[5.050 2026-07-01], [https://verilator.org], [verilator],[https://verilator.org]) From ce4be92a6733329ef1077e13c6357f8e5b525a45 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Jul 2026 18:31:23 -0400 Subject: [PATCH 30/41] devel release --- CMakeLists.txt | 2 +- Changes | 6 ++++++ configure.ac | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ce2e8beca..6fa5e2ffc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime project( Verilator - VERSION 5.050 + VERSION 5.051 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index 2774b4ad4..1edeaf179 100644 --- a/Changes +++ b/Changes @@ -10,6 +10,12 @@ The changes in each Verilator version are described below. The contributors that suggested or implemented a given issue are shown in []. Thanks! +Verilator 5.051 devel +========================== + +**Other:** + + Verilator 5.050 2026-07-01 ========================== diff --git a/configure.ac b/configure.ac index 81329841c..6376593ad 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,7 @@ # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.050 2026-07-01], +AC_INIT([Verilator],[5.051 devel], [https://verilator.org], [verilator],[https://verilator.org]) From 964474837f02728cb60e032b860fa07e39a7399d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Jul 2026 19:37:12 -0400 Subject: [PATCH 31/41] Commentary --- docs/guide/connecting.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/connecting.rst b/docs/guide/connecting.rst index 1d714ce4e..35ec9742b 100644 --- a/docs/guide/connecting.rst +++ b/docs/guide/connecting.rst @@ -179,7 +179,7 @@ DPI Example In the SYSTEMC example above, if you wanted to import C++ functions into Verilog, put in our.v: -.. code-block:: sv +.. code-block:: import "DPI-C" function int add (input int a, input int b); @@ -211,7 +211,7 @@ Verilator extends the DPI format to allow using the same scheme to efficiently add system functions. Use a dollar-sign prefixed system function name for the import, but note it must be escaped. -.. code-block:: sv +.. code-block:: import "DPI-C" function integer \$myRand; From 853ee5df1796354ff24de19cb7b6cba49a0530f7 Mon Sep 17 00:00:00 2001 From: Jakub Michalski <143384197+jmichalski-ant@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:33:38 +0200 Subject: [PATCH 32/41] Fix DFG misoptimizing bound checks (#7755) Signed-off-by: Jakub Michalski --- src/V3Dfg.cpp | 6 ++++++ src/V3Dfg.h | 2 ++ src/V3DfgPeephole.cpp | 24 +++++++++++---------- test_regress/t/t_dfg_oob_array_access.py | 18 ++++++++++++++++ test_regress/t/t_dfg_oob_array_access.v | 27 ++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 11 deletions(-) create mode 100755 test_regress/t/t_dfg_oob_array_access.py create mode 100644 test_regress/t/t_dfg_oob_array_access.v diff --git a/src/V3Dfg.cpp b/src/V3Dfg.cpp index fc6be54ce..2fd5afb95 100644 --- a/src/V3Dfg.cpp +++ b/src/V3Dfg.cpp @@ -620,6 +620,12 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataT dfg.addVertex(*this); } +bool DfgVertex::unsafe() const { + if (is()) return true; + if (is()) return !as()->bitp()->is(); + return false; +} + void DfgVertex::typeCheck(const DfgGraph& dfg) const { #define CHECK(cond, msg) \ diff --git a/src/V3Dfg.h b/src/V3Dfg.h index b93c1c716..d80be4d84 100644 --- a/src/V3Dfg.h +++ b/src/V3Dfg.h @@ -195,6 +195,8 @@ public: UASSERT_OBJ(m_dtype.isPacked(), this, "Non packed vertex has no 'width'"); return m_dtype.size(); } + // Has terminating side-effect + bool unsafe() const; // Type check vertex (for debugging) void typeCheck(const DfgGraph& dfg) const; diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index de92790a8..fa29339fe 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -2269,7 +2269,7 @@ class V3DfgPeephole final : public DfgVisitor { } void visit(DfgLogAnd* const vtxp) override { - if (binary(vtxp)) return; + if (binary(vtxp) || vtxp->rhsp()->unsafe()) return; DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -2287,11 +2287,11 @@ class V3DfgPeephole final : public DfgVisitor { } void visit(DfgLogIf* const vtxp) override { - if (binary(vtxp)) return; + if (binary(vtxp) || vtxp->rhsp()->unsafe()) return; } void visit(DfgLogOr* const vtxp) override { - if (binary(vtxp)) return; + if (binary(vtxp) || vtxp->rhsp()->unsafe()) return; DfgVertex* const lhsp = vtxp->lhsp(); DfgVertex* const rhsp = vtxp->rhsp(); @@ -2890,13 +2890,13 @@ class V3DfgPeephole final : public DfgVisitor { } if (vtxp->dtype() == m_bitDType) { - if (isSame(condp, thenp)) { // a ? a : b becomes a | b + if (isSame(condp, thenp) && !elsep->unsafe()) { // a ? a : b becomes a | b APPLYING(REPLACE_COND_WITH_THEN_BRANCH_COND) { replace(make(vtxp, condp, elsep)); return; } } - if (isSame(condp, elsep)) { // a ? b : a becomes a & b + if (isSame(condp, elsep) && !thenp->unsafe()) { // a ? b : a becomes a & b APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_COND) { replace(make(vtxp, condp, thenp)); return; @@ -2905,28 +2905,28 @@ class V3DfgPeephole final : public DfgVisitor { } if (vtxp->width() <= VL_QUADSIZE) { - if (isZero(thenp)) { // a ? 0 : b becomes ~a & b + if (isZero(thenp) && !elsep->unsafe()) { // a ? 0 : b becomes ~a & b APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) { DfgVertex* const maskp = replicate(vtxp, make(condp, condp)); replace(make(vtxp, maskp, elsep)); return; } } - if (isOnes(thenp)) { // a ? 1 : b becomes a | b + if (isOnes(thenp) && !elsep->unsafe()) { // a ? 1 : b becomes a | b APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) { DfgVertex* const maskp = replicate(vtxp, condp); replace(make(vtxp, maskp, elsep)); return; } } - if (isZero(elsep)) { // a ? b : 0 becomes a & b + if (isZero(elsep) && !thenp->unsafe()) { // a ? b : 0 becomes a & b APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) { DfgVertex* const maskp = replicate(vtxp, condp); replace(make(vtxp, maskp, thenp)); return; } } - if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b + if (isOnes(elsep) && !thenp->unsafe()) { // a ? b : 1 becomes ~a | b APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) { DfgVertex* const maskp = replicate(vtxp, make(condp, condp)); replace(make(vtxp, maskp, thenp)); @@ -2935,7 +2935,8 @@ class V3DfgPeephole final : public DfgVisitor { } if (DfgOr* const tOrp = thenp->cast()) { - if (isSame(tOrp->lhsp(), elsep)) { // a ? b | c : b becomes b | (a & c) + if (isSame(tOrp->lhsp(), elsep) + && !tOrp->rhsp()->unsafe()) { // a ? b | c : b becomes b | (a & c) APPLYING(REPLACE_COND_THEN_OR_LHS) { DfgVertex* const maskp = replicate(vtxp, condp); DfgAnd* const andp = make(vtxp, maskp, tOrp->rhsp()); @@ -2943,7 +2944,8 @@ class V3DfgPeephole final : public DfgVisitor { return; } } - if (isSame(tOrp->rhsp(), elsep)) { // a ? b | c : c becomes c | (a & b) + if (isSame(tOrp->rhsp(), elsep) + && !tOrp->lhsp()->unsafe()) { // a ? b | c : c becomes c | (a & b) APPLYING(REPLACE_COND_THEN_OR_RHS) { DfgVertex* const maskp = replicate(vtxp, condp); DfgAnd* const andp = make(vtxp, maskp, tOrp->lhsp()); diff --git a/test_regress/t/t_dfg_oob_array_access.py b/test_regress/t/t_dfg_oob_array_access.py new file mode 100755 index 000000000..b7bc5e342 --- /dev/null +++ b/test_regress/t/t_dfg_oob_array_access.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=["-runtime-debug"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_dfg_oob_array_access.v b/test_regress/t/t_dfg_oob_array_access.v new file mode 100644 index 000000000..39b3f4d17 --- /dev/null +++ b/test_regress/t/t_dfg_oob_array_access.v @@ -0,0 +1,27 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + + +module t ( + input clk +); + wire mem_wire; + bit [15:0] idx = 65535; + bit mem_reg[0:34000]; + assign mem_wire = mem_reg[idx]; + + always @(posedge clk) begin + if (idx < 65533) begin + $display("oob_val %d", mem_wire); + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + idx <= idx - 1; + mem_reg[idx] <= 0; + end + end +endmodule From 506f703da5ec43ae1709d90b952f50fd36bfcd5e Mon Sep 17 00:00:00 2001 From: Jakub Michalski <143384197+jmichalski-ant@users.noreply.github.com> Date: Thu, 2 Jul 2026 13:16:42 +0200 Subject: [PATCH 33/41] Internals: Remove unused variables in verilated_funcs.h (#7862) Signed-off-by: Jakub Michalski --- include/verilated_funcs.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 88da40387..16ed18728 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -1025,14 +1025,10 @@ static inline IData VL_EQ_R(int words, VlQueue q, WDataInP const rwp) VL_PURE } else if (sizeof(T) == 4) { for (int i = 0; (i < wordsInQ + 1); ++i) { nequal |= (q.at(wordsInQ - i) ^ rwp[i]); } } else if (sizeof(T) == 8) { - QData temp = 0; int qSize = q.size() - 1; for (int i = 0; (i < qSize); i += 2) { - temp = q.at(qSize - i); nequal |= (static_cast(q.at(qSize - i)) >> 32 ^ rwp[i + 1]); - temp = rwp[i + 1]; nequal |= (static_cast(q.at(qSize - i)) ^ rwp[i]); - temp = rwp[i]; } } return (nequal == 0); @@ -1042,7 +1038,6 @@ template static inline IData VL_EQ_R(int words, const VlQueue>& q, WDataInP const rwp) VL_PURE { EData nequal = 0; - const int wordsInQ = q.size() * N_Words; if ((q.size() * N_Words) != words) { return false; } int count = 0; for (int qIndex = q.size() - 1; qIndex >= 0; qIndex--) { @@ -1953,7 +1948,6 @@ static inline void VL_STREAMR_RRI(int lbits, VlQueue& to_q, const VlQueue>& from_q, IData rd) VL_MT_SAFE { to_q.clear(); - VL_CONSTEXPR_CXX17 size_t otherSize = 4 * N_Words; VL_CONSTEXPR_CXX17 size_t sizeOfThis = sizeof(T_Value); T_Value temp = 0; for (auto val : from_q) { From 1106e89c84de1f6fef6ef39bc2e84e32cf40d8e1 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 2 Jul 2026 14:50:55 +0100 Subject: [PATCH 34/41] Optimize random initialization Fetching a thread-local is relatively expensive. Random initializing wides used to do it once or twice per word, and on short runs for large designs can be noticeably expensive, so fetch once per variable instead. Also remove unused VL_RAND_RESET_{Q,W} functions. --- include/verilated.cpp | 47 +++++++++++++++++---------------------- include/verilated_funcs.h | 4 ---- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index a5fdc91ac..d981f7c0a 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -563,9 +563,10 @@ IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE { } IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE { - if (Verilated::threadContextp()->randReset() == 0) return 0; + const int randReset = Verilated::threadContextp()->randReset(); + if (randReset == 0) return 0; IData data = ~0; - if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize + if (randReset != 1) { // if 2, randomize VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; data = rng.rand64(); } @@ -574,9 +575,10 @@ IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT } QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE { - if (Verilated::threadContextp()->randReset() == 0) return 0; + const int randReset = Verilated::threadContextp()->randReset(); + if (randReset == 0) return 0; QData data = ~0ULL; - if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize + if (randReset != 1) { // if 2, randomize VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; data = rng.rand64(); } @@ -586,10 +588,17 @@ QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE { - if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); } - VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; - for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64(); - outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits); + const int words = VL_WORDS_I(obits); + const int randReset = Verilated::threadContextp()->randReset(); + if (randReset == 0) { + VL_MEMSET_ZERO_W(outwp, words); + } else if (randReset == 1) { + VL_MEMSET_ONES_W(outwp, words); + } else { + VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; + for (int i = 0; i < words; ++i) outwp[i] = rng.rand64(); + } + outwp[words - 1] &= VL_MASK_E(obits); return outwp; } @@ -614,30 +623,14 @@ WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t sco } IData VL_RAND_RESET_I(int obits) VL_MT_SAFE { - if (Verilated::threadContextp()->randReset() == 0) return 0; + const int randReset = Verilated::threadContextp()->randReset(); + if (randReset == 0) return 0; IData data = ~0; - if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize - data = VL_RANDOM_I(); - } + if (randReset != 1) data = VL_RANDOM_I(); // if 2, randomize data &= VL_MASK_I(obits); return data; } -QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE { - if (Verilated::threadContextp()->randReset() == 0) return 0; - QData data = ~0ULL; - if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize - data = VL_RANDOM_Q(); - } - data &= VL_MASK_Q(obits); - return data; -} - -WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE { - for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32); - outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits); - return outwp; -} WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE { // Not inlined to speed up compilation of slowpath code return VL_ZERO_W(obits, outwp); diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 16ed18728..349fe44da 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -124,10 +124,6 @@ extern WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint6 /// Random reset a signal of given width (init time only) extern IData VL_RAND_RESET_I(int obits) VL_MT_SAFE; -/// Random reset a signal of given width (init time only) -extern QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE; -/// Random reset a signal of given width (init time only) -extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE; /// Zero reset a signal (slow - else use VL_ZERO_W) extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE; From 0e371d6e5caf16c7f6a8c49c8fd49d3115f104eb Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 2 Jul 2026 17:47:09 +0100 Subject: [PATCH 35/41] Fix cleaning purity cache after assertions --- src/V3Assert.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 09f6d1126..6c507c22c 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -1180,6 +1180,8 @@ public: V3Stats::addStat("Assertions, $past variables", m_statPastVars); V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined); V3Stats::addStat("Assertions, assertOn checks hoisted", m_statAssertOnHoisted); + // Rewrites can change purity, e.g. by compiling out assertion statements with --no-assert + VIsCached::clearCacheTree(); } }; From b97df914ddcbff470c5a37d3c1bd99d9813f4698 Mon Sep 17 00:00:00 2001 From: Pawel Kojma Date: Fri, 3 Jul 2026 12:37:48 +0200 Subject: [PATCH 36/41] Fix clang++ ambiguous overload of '==' operator (#7863) --- include/verilated_types.h | 18 +++++++++++++- test_regress/t/t_clang_overload.py | 16 ++++++++++++ test_regress/t/t_clang_overload.v | 39 ++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_clang_overload.py create mode 100644 test_regress/t/t_clang_overload.v diff --git a/include/verilated_types.h b/include/verilated_types.h index 40847b65e..fd0341a2d 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -2069,8 +2069,24 @@ struct VlNull final { operator T*() const { return nullptr; } + template + bool operator==(T* rhs) const { + return !rhs; + } + template + bool operator==(const T* rhs) const { + return !rhs; + } }; -inline bool operator==(const void* ptr, VlNull) { return !ptr; } + +template +inline bool operator==(T* lhs, VlNull) { + return !lhs; +} +template +inline bool operator==(const T* lhs, VlNull) { + return !lhs; +} //=================================================================== // Verilog class reference container diff --git a/test_regress/t/t_clang_overload.py b/test_regress/t/t_clang_overload.py new file mode 100755 index 000000000..0f1f5d0a0 --- /dev/null +++ b/test_regress/t/t_clang_overload.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.compile(verilator_flags2=['--binary', '-Wno-WIDTHTRUNC']) + +test.passes() diff --git a/test_regress/t/t_clang_overload.v b/test_regress/t/t_clang_overload.v new file mode 100644 index 000000000..02f67f60f --- /dev/null +++ b/test_regress/t/t_clang_overload.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +interface irq_if ( + input logic clk, + input logic resetn +); + logic irq; + logic te; + logic halted; + logic fault; + logic wfi; + clocking cb @(posedge clk); + default input #1step output #2ns; + output irq; + output te; + input halted; + input fault; + input wfi; + endclocking + modport DUT_IRQ_PORT(input clk, resetn, output halted, fault, wfi); +endinterface +class base_test_class; + function int foo(); + endfunction + virtual irq_if.DUT_IRQ_PORT irq_vif; + function new(string name); + endfunction + virtual function void build_phase(); + if (irq_vif == null) begin + if (foo()) $display(); + end + endfunction +endclass +module tb_top; +endmodule From 58bd13b623dbb1a8db231aba7d68b07baf0f3d4f Mon Sep 17 00:00:00 2001 From: Dragon-Git <42856316+Dragon-Git@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:38:48 +0800 Subject: [PATCH 37/41] Fix heap-use-after-free in `VlRNG::VlRNG()` (#7865) --- docs/CONTRIBUTORS | 1 + include/verilated_types.h | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 763ac8ac0..81765f325 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -61,6 +61,7 @@ David Turner Dercury Diego Roux Dominick Grochowina +Dragon-Git Don Williamson Drew Ranck Drew Taussig diff --git a/include/verilated_types.h b/include/verilated_types.h index fd0341a2d..a48b99c4c 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -337,6 +337,7 @@ public: ~VlProcess() { if (m_parentp) m_parentp->detach(this); + if (t_currentp == this) t_currentp = m_parentp.get(); } void attach(VlProcess* childp) { m_children.insert(childp); } From a64234e897f43d07c752ed6fe025de5c32b5bd48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eryk=20Szpota=C5=84ski?= <129167091+eszpotanski@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:18:29 +0200 Subject: [PATCH 38/41] Add comments as a branch description in coverage .info files (#7843) Signed-off-by: Eryk Szpotanski --- docs/CONTRIBUTORS | 1 + src/VlcTop.cpp | 12 +- test_regress/t/t_cover_line_cc.info.out | 166 ++++----- test_regress/t/t_cover_toggle_min.info.out | 24 +- test_regress/t/t_vlcov_data_e.dat | 1 + test_regress/t/t_vlcov_hier_report.out | 2 +- test_regress/t/t_vlcov_opt_branch.info.out | 128 +++---- test_regress/t/t_vlcov_opt_expr.info.out | 24 +- test_regress/t/t_vlcov_opt_line.info.out | 15 +- test_regress/t/t_vlcov_opt_toggle.info.out | 188 +++++------ test_regress/t/t_vlcov_opt_user.info.out | 300 ++++++++--------- test_regress/t/t_vlcov_opt_wild.info.out | 369 +++++++++++---------- test_regress/t/t_vlcov_summary_typed.out | 2 +- 13 files changed, 621 insertions(+), 611 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 81765f325..24e3ced87 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -70,6 +70,7 @@ Edgar E. Iglesias Eric Mejdrich Eric Müller Eric Rippey +Eryk Szpotański Eunseo Song Ethan Sifferman Eyck Jentzsch diff --git a/src/VlcTop.cpp b/src/VlcTop.cpp index ccaec527e..de5160c2d 100644 --- a/src/VlcTop.cpp +++ b/src/VlcTop.cpp @@ -240,7 +240,7 @@ void VlcTop::writeInfo(const string& filename) { // FNF: // FNH: // Branches: - // BRDA:,,, + // BRDA:,,, // BRF: // BRH: // Line counts: @@ -273,8 +273,14 @@ void VlcTop::writeInfo(const string& filename) { for (const VlcPoint* point : infoPoints) { os << "BRDA:" << sc.lineno() << ","; os << "0,"; - os << point_num << ","; - os << point->count() << "\n"; + if (point->comment().empty()) { + os << point_num; + } else { + std::string comment(point->comment()); + std::replace(comment.begin(), comment.end(), ',', '_'); + os << comment; + } + os << "," << point->count() << "\n"; branchesHit += opt.countOk(point->count()); ++point_num; diff --git a/test_regress/t/t_cover_line_cc.info.out b/test_regress/t/t_cover_line_cc.info.out index 6a04c28bf..fbba38865 100644 --- a/test_regress/t/t_cover_line_cc.info.out +++ b/test_regress/t/t_cover_line_cc.info.out @@ -4,32 +4,32 @@ DA:15,1 DA:18,1 DA:55,10 DA:56,10 -BRDA:56,0,0,10 -BRDA:56,0,1,0 +BRDA:56,0,if,10 +BRDA:56,0,else,0 DA:57,10 DA:58,10 DA:60,9 -BRDA:60,0,0,1 -BRDA:60,0,1,9 +BRDA:60,0,if,1 +BRDA:60,0,else,9 DA:61,9 -BRDA:61,0,0,1 -BRDA:61,0,1,9 +BRDA:61,0,if,1 +BRDA:61,0,else,9 DA:62,1 DA:63,1 DA:66,9 -BRDA:66,0,0,1 -BRDA:66,0,1,9 +BRDA:66,0,if,1 +BRDA:66,0,else,9 DA:67,9 -BRDA:67,0,0,1 -BRDA:67,0,1,9 +BRDA:67,0,if,1 +BRDA:67,0,else,9 DA:69,9 DA:70,9 DA:73,9 -BRDA:73,0,0,1 -BRDA:73,0,1,9 +BRDA:73,0,if,1 +BRDA:73,0,else,9 DA:74,9 -BRDA:74,0,0,1 -BRDA:74,0,1,9 +BRDA:74,0,if,1 +BRDA:74,0,else,9 DA:75,1 DA:76,1 DA:79,9 @@ -41,8 +41,8 @@ DA:87,1 DA:88,1 DA:89,1 DA:91,7 -BRDA:91,0,0,1 -BRDA:91,0,1,7 +BRDA:91,0,if,1 +BRDA:91,0,else,7 DA:92,1 DA:93,1 DA:96,7 @@ -52,67 +52,67 @@ DA:101,0 DA:102,0 DA:104,0 DA:105,10 -BRDA:105,0,0,0 -BRDA:105,0,1,10 +BRDA:105,0,block,0 +BRDA:105,0,block,10 DA:106,10 DA:107,10 -BRDA:107,0,0,0 -BRDA:107,0,1,10 +BRDA:107,0,block,0 +BRDA:107,0,block,10 DA:110,1 DA:111,1 DA:113,1 DA:115,1 DA:120,7 -BRDA:120,0,0,1 -BRDA:120,0,1,7 +BRDA:120,0,if,1 +BRDA:120,0,else,7 DA:121,1 DA:122,1 DA:127,1 DA:129,1 DA:140,20 DA:141,18 -BRDA:141,0,0,2 -BRDA:141,0,1,18 +BRDA:141,0,if,2 +BRDA:141,0,else,18 DA:142,2 DA:145,18 DA:164,20 DA:165,20 DA:166,20 -BRDA:166,0,0,0 -BRDA:166,0,1,20 +BRDA:166,0,if,0 +BRDA:166,0,else,20 DA:168,0 DA:170,18 -BRDA:170,0,0,2 -BRDA:170,0,1,18 +BRDA:170,0,if,2 +BRDA:170,0,else,18 DA:172,2 DA:174,18 DA:188,11 DA:189,11 DA:190,11 -BRDA:190,0,0,11 -BRDA:190,0,1,0 +BRDA:190,0,if,11 +BRDA:190,0,else,0 DA:191,11 DA:194,11 DA:195,11 -BRDA:195,0,0,11 -BRDA:195,0,1,0 +BRDA:195,0,if,11 +BRDA:195,0,else,0 DA:196,11 DA:199,11 DA:200,11 -BRDA:200,0,0,11 -BRDA:200,0,1,0 +BRDA:200,0,if,11 +BRDA:200,0,else,0 DA:201,11 DA:215,10 DA:216,10 DA:219,11 DA:221,11 DA:222,10 -BRDA:222,0,0,1 -BRDA:222,0,1,10 +BRDA:222,0,if,1 +BRDA:222,0,else,10 DA:223,1 DA:225,10 -BRDA:225,0,0,1 -BRDA:225,0,1,10 +BRDA:225,0,if,1 +BRDA:225,0,else,10 DA:226,1 DA:229,11 DA:230,11 @@ -121,12 +121,12 @@ DA:232,11 DA:233,11 DA:253,10 DA:254,9 -BRDA:254,0,0,1 -BRDA:254,0,1,9 +BRDA:254,0,if,1 +BRDA:254,0,else,9 DA:256,1 DA:257,1 -BRDA:257,0,0,0 -BRDA:257,0,1,1 +BRDA:257,0,if,0 +BRDA:257,0,else,1 DA:266,10 DA:267,10 DA:268,1 @@ -139,8 +139,8 @@ DA:277,10 DA:278,10 DA:288,0 DA:289,0 -BRDA:289,0,0,0 -BRDA:289,0,1,0 +BRDA:289,0,if,0 +BRDA:289,0,else,0 DA:290,0 DA:292,0 DA:293,0 @@ -157,66 +157,66 @@ DA:328,10 DA:329,10 DA:330,10 DA:333,31 -BRDA:333,0,0,0 -BRDA:333,0,1,31 +BRDA:333,0,cond_then,0 +BRDA:333,0,cond_else,31 DA:334,28 -BRDA:334,0,0,3 -BRDA:334,0,1,28 +BRDA:334,0,cond_then,3 +BRDA:334,0,cond_else,28 DA:335,1 -BRDA:335,0,0,1 -BRDA:335,0,1,0 +BRDA:335,0,cond_then,1 +BRDA:335,0,cond_else,0 DA:336,10 DA:337,10 -BRDA:337,0,0,10 -BRDA:337,0,1,3 -BRDA:337,0,2,7 +BRDA:337,0,block,10 +BRDA:337,0,cond_then,3 +BRDA:337,0,cond_else,7 DA:338,10 -BRDA:338,0,0,10 -BRDA:338,0,1,0 -BRDA:338,0,2,10 +BRDA:338,0,block,10 +BRDA:338,0,cond_then,0 +BRDA:338,0,cond_else,10 DA:340,19 -BRDA:340,0,0,12 -BRDA:340,0,1,19 -BRDA:340,0,2,7 -BRDA:340,0,3,5 +BRDA:340,0,cond_then,12 +BRDA:340,0,cond_else,19 +BRDA:340,0,cond_then,7 +BRDA:340,0,cond_else,5 DA:343,11 -BRDA:343,0,0,11 -BRDA:343,0,1,0 +BRDA:343,0,cond_then,11 +BRDA:343,0,cond_else,0 DA:349,22 -BRDA:349,0,0,20 -BRDA:349,0,1,22 +BRDA:349,0,cond_then,20 +BRDA:349,0,cond_else,22 DA:352,11 DA:353,10 -BRDA:353,0,0,0 -BRDA:353,0,1,1 -BRDA:353,0,2,1 -BRDA:353,0,3,10 +BRDA:353,0,cond_then,0 +BRDA:353,0,cond_else,1 +BRDA:353,0,if,1 +BRDA:353,0,else,10 DA:354,10 DA:356,11 -BRDA:356,0,0,11 -BRDA:356,0,1,1 -BRDA:356,0,2,10 +BRDA:356,0,block,11 +BRDA:356,0,cond_then,1 +BRDA:356,0,cond_else,10 DA:359,55 -BRDA:359,0,0,11 -BRDA:359,0,1,55 +BRDA:359,0,block,11 +BRDA:359,0,block,55 DA:360,55 DA:362,44 -BRDA:362,0,0,11 -BRDA:362,0,1,11 -BRDA:362,0,2,33 -BRDA:362,0,3,44 +BRDA:362,0,block,11 +BRDA:362,0,cond_then,11 +BRDA:362,0,cond_else,33 +BRDA:362,0,block,44 DA:363,44 DA:366,11 -BRDA:366,0,0,0 -BRDA:366,0,1,11 +BRDA:366,0,if,0 +BRDA:366,0,else,11 DA:367,11 DA:370,10 -BRDA:370,0,0,1 -BRDA:370,0,1,10 +BRDA:370,0,cond_then,1 +BRDA:370,0,cond_else,10 DA:373,10 DA:374,9 -BRDA:374,0,0,1 -BRDA:374,0,1,9 +BRDA:374,0,if,1 +BRDA:374,0,else,9 BRF:83 BRH:32 end_of_record diff --git a/test_regress/t/t_cover_toggle_min.info.out b/test_regress/t/t_cover_toggle_min.info.out index 975b502e0..6cdc747be 100644 --- a/test_regress/t/t_cover_toggle_min.info.out +++ b/test_regress/t/t_cover_toggle_min.info.out @@ -1,20 +1,20 @@ TN:verilator_coverage SF:t/t_cover_toggle_min.v DA:10,1 -BRDA:10,0,0,1 -BRDA:10,0,1,0 -BRDA:10,0,2,0 -BRDA:10,0,3,0 +BRDA:10,0,a[0]:0->1,1 +BRDA:10,0,a[0]:1->0,0 +BRDA:10,0,a[1]:0->1,0 +BRDA:10,0,a[1]:1->0,0 DA:11,1 -BRDA:11,0,0,0 -BRDA:11,0,1,0 -BRDA:11,0,2,1 -BRDA:11,0,3,0 +BRDA:11,0,b[0]:0->1,0 +BRDA:11,0,b[0]:1->0,0 +BRDA:11,0,b[1]:0->1,1 +BRDA:11,0,b[1]:1->0,0 DA:12,1 -BRDA:12,0,0,1 -BRDA:12,0,1,1 -BRDA:12,0,2,1 -BRDA:12,0,3,0 +BRDA:12,0,c[0]:0->1,1 +BRDA:12,0,c[0]:1->0,1 +BRDA:12,0,c[1]:0->1,1 +BRDA:12,0,c[1]:1->0,0 BRF:12 BRH:0 end_of_record diff --git a/test_regress/t/t_vlcov_data_e.dat b/test_regress/t/t_vlcov_data_e.dat index 32c705c59..114d03f03 100644 --- a/test_regress/t/t_vlcov_data_e.dat +++ b/test_regress/t/t_vlcov_data_e.dat @@ -224,3 +224,4 @@ C 'ft/t_cover_line.vl83n10tlinepagev_line/toelsifS83-85htop.t' 1 C 'ft/t_cover_line.vl87n15tlinepagev_line/toelsifS87-89htop.t' 1 C 'ft/t_cover_line.vl91n15tlinepagev_line/toifS91-93htop.t' 1 C 'ft/t_cover_line.vl91n16tlinepagev_line/toelseS96-97htop.t' 7 +C 'ft/t_cover_line.vl91n16tlinepagev_line/totest,testS91-91htop.t' 5 diff --git a/test_regress/t/t_vlcov_hier_report.out b/test_regress/t/t_vlcov_hier_report.out index bc70aa8a1..1c37b8e6a 100644 --- a/test_regress/t/t_vlcov_hier_report.out +++ b/test_regress/t/t_vlcov_hier_report.out @@ -1,6 +1,6 @@ $ verilator_coverage --report summary t/t_vlcov_data_e.dat Coverage Summary: - line : 88.6% ( 39/ 44) + line : 88.9% ( 40/ 45) toggle : 33.3% ( 35/105) branch : 78.1% ( 50/ 64) expr : 66.7% ( 8/ 12) diff --git a/test_regress/t/t_vlcov_opt_branch.info.out b/test_regress/t/t_vlcov_opt_branch.info.out index 45d5b4627..f7850b8c9 100644 --- a/test_regress/t/t_vlcov_opt_branch.info.out +++ b/test_regress/t/t_vlcov_opt_branch.info.out @@ -1,32 +1,32 @@ TN:verilator_coverage SF:t/t_cover_line.v DA:56,10 -BRDA:56,0,0,10 -BRDA:56,0,1,0 +BRDA:56,0,if,10 +BRDA:56,0,else,0 DA:57,10 DA:58,10 DA:60,9 -BRDA:60,0,0,1 -BRDA:60,0,1,9 +BRDA:60,0,if,1 +BRDA:60,0,else,9 DA:61,9 -BRDA:61,0,0,1 -BRDA:61,0,1,9 +BRDA:61,0,if,1 +BRDA:61,0,else,9 DA:62,1 DA:63,1 DA:66,9 -BRDA:66,0,0,1 -BRDA:66,0,1,9 +BRDA:66,0,if,1 +BRDA:66,0,else,9 DA:67,9 -BRDA:67,0,0,1 -BRDA:67,0,1,9 +BRDA:67,0,if,1 +BRDA:67,0,else,9 DA:69,9 DA:70,9 DA:73,9 -BRDA:73,0,0,1 -BRDA:73,0,1,9 +BRDA:73,0,if,1 +BRDA:73,0,else,9 DA:74,9 -BRDA:74,0,0,1 -BRDA:74,0,1,9 +BRDA:74,0,if,1 +BRDA:74,0,else,9 DA:75,1 DA:76,1 DA:79,9 @@ -34,93 +34,93 @@ DA:80,9 DA:105,10 DA:106,10 DA:120,7 -BRDA:120,0,0,1 -BRDA:120,0,1,7 +BRDA:120,0,if,1 +BRDA:120,0,else,7 DA:121,1 DA:122,1 DA:141,18 -BRDA:141,0,0,2 -BRDA:141,0,1,18 +BRDA:141,0,if,2 +BRDA:141,0,else,18 DA:142,2 DA:166,20 -BRDA:166,0,0,0 -BRDA:166,0,1,20 +BRDA:166,0,if,0 +BRDA:166,0,else,20 DA:168,0 DA:170,18 -BRDA:170,0,0,2 -BRDA:170,0,1,18 +BRDA:170,0,if,2 +BRDA:170,0,else,18 DA:172,2 DA:190,1 -BRDA:190,0,0,1 -BRDA:190,0,1,0 +BRDA:190,0,if,1 +BRDA:190,0,else,0 DA:191,1 DA:195,11 -BRDA:195,0,0,11 -BRDA:195,0,1,0 +BRDA:195,0,if,11 +BRDA:195,0,else,0 DA:196,11 DA:200,11 -BRDA:200,0,0,11 -BRDA:200,0,1,0 +BRDA:200,0,if,11 +BRDA:200,0,else,0 DA:201,11 DA:222,10 -BRDA:222,0,0,1 -BRDA:222,0,1,10 +BRDA:222,0,if,1 +BRDA:222,0,else,10 DA:223,1 DA:225,10 -BRDA:225,0,0,1 -BRDA:225,0,1,10 +BRDA:225,0,if,1 +BRDA:225,0,else,10 DA:226,1 DA:253,9 -BRDA:253,0,0,1 -BRDA:253,0,1,9 +BRDA:253,0,if,1 +BRDA:253,0,else,9 DA:255,1 DA:256,1 -BRDA:256,0,0,0 -BRDA:256,0,1,1 +BRDA:256,0,if,0 +BRDA:256,0,else,1 DA:288,0 -BRDA:288,0,0,0 -BRDA:288,0,1,0 +BRDA:288,0,if,0 +BRDA:288,0,else,0 DA:289,0 DA:291,0 DA:292,0 DA:327,31 -BRDA:327,0,0,0 -BRDA:327,0,1,31 +BRDA:327,0,cond_then,0 +BRDA:327,0,cond_else,31 DA:328,28 -BRDA:328,0,0,3 -BRDA:328,0,1,28 +BRDA:328,0,cond_then,3 +BRDA:328,0,cond_else,28 DA:329,21 -BRDA:329,0,0,21 -BRDA:329,0,1,0 +BRDA:329,0,cond_then,21 +BRDA:329,0,cond_else,0 DA:331,7 -BRDA:331,0,0,3 -BRDA:331,0,1,7 +BRDA:331,0,cond_then,3 +BRDA:331,0,cond_else,7 DA:332,10 -BRDA:332,0,0,0 -BRDA:332,0,1,10 +BRDA:332,0,cond_then,0 +BRDA:332,0,cond_else,10 DA:334,19 -BRDA:334,0,0,12 -BRDA:334,0,1,19 -BRDA:334,0,2,7 -BRDA:334,0,3,5 +BRDA:334,0,cond_then,12 +BRDA:334,0,cond_else,19 +BRDA:334,0,cond_then,7 +BRDA:334,0,cond_else,5 DA:337,11 -BRDA:337,0,0,11 -BRDA:337,0,1,0 +BRDA:337,0,cond_then,11 +BRDA:337,0,cond_else,0 DA:343,11 -BRDA:343,0,0,10 -BRDA:343,0,1,11 +BRDA:343,0,cond_then,10 +BRDA:343,0,cond_else,11 DA:347,10 -BRDA:347,0,0,0 -BRDA:347,0,1,1 -BRDA:347,0,2,1 -BRDA:347,0,3,10 +BRDA:347,0,cond_then,0 +BRDA:347,0,cond_else,1 +BRDA:347,0,if,1 +BRDA:347,0,else,10 DA:348,10 DA:350,10 -BRDA:350,0,0,1 -BRDA:350,0,1,10 +BRDA:350,0,cond_then,1 +BRDA:350,0,cond_else,10 DA:360,11 -BRDA:360,0,0,0 -BRDA:360,0,1,11 +BRDA:360,0,if,0 +BRDA:360,0,else,11 DA:361,11 BRF:64 BRH:20 diff --git a/test_regress/t/t_vlcov_opt_expr.info.out b/test_regress/t/t_vlcov_opt_expr.info.out index 33c3c1c89..c83249652 100644 --- a/test_regress/t/t_vlcov_opt_expr.info.out +++ b/test_regress/t/t_vlcov_opt_expr.info.out @@ -1,23 +1,23 @@ TN:verilator_coverage SF:t/t_cover_line.v DA:331,7 -BRDA:331,0,0,7 -BRDA:331,0,1,3 +BRDA:331,0,(((cyc %25 32'sh3) == 32'sh0)==0) => 0,7 +BRDA:331,0,(((cyc %25 32'sh3) == 32'sh0)==1) => 1,3 DA:347,1 -BRDA:347,0,0,1 -BRDA:347,0,1,0 +BRDA:347,0,((cyc > 32'sh5)==0) => 0,1 +BRDA:347,0,((cyc > 32'sh5)==1) => 1,0 DA:350,10 -BRDA:350,0,0,10 -BRDA:350,0,1,1 +BRDA:350,0,((cyc == 32'sh2)==0) => 0,10 +BRDA:350,0,((cyc == 32'sh2)==1) => 1,1 DA:353,11 -BRDA:353,0,0,11 -BRDA:353,0,1,0 +BRDA:353,0,((i < 32'sh5)==0) => 0,11 +BRDA:353,0,((i < 32'sh5)==1) => 1,0 DA:356,11 -BRDA:356,0,0,0 -BRDA:356,0,1,11 +BRDA:356,0,((i > 32'sh4)==0) => 0,0 +BRDA:356,0,((i > 32'sh4)==1) => 1,11 DA:360,11 -BRDA:360,0,0,11 -BRDA:360,0,1,0 +BRDA:360,0,(k==0) => 0,11 +BRDA:360,0,(k==1) => 1,0 BRF:12 BRH:4 end_of_record diff --git a/test_regress/t/t_vlcov_opt_line.info.out b/test_regress/t/t_vlcov_opt_line.info.out index eaba1df5d..c744548c6 100644 --- a/test_regress/t/t_vlcov_opt_line.info.out +++ b/test_regress/t/t_vlcov_opt_line.info.out @@ -10,8 +10,9 @@ DA:87,1 DA:88,1 DA:89,1 DA:91,7 -BRDA:91,0,0,1 -BRDA:91,0,1,7 +BRDA:91,0,if,1 +BRDA:91,0,else,7 +BRDA:91,0,test_test,5 DA:92,1 DA:93,1 DA:96,7 @@ -75,13 +76,13 @@ DA:332,10 DA:346,11 DA:350,11 DA:353,55 -BRDA:353,0,0,11 -BRDA:353,0,1,55 +BRDA:353,0,block,11 +BRDA:353,0,block,55 DA:354,55 DA:356,44 -BRDA:356,0,0,11 -BRDA:356,0,1,44 +BRDA:356,0,block,11 +BRDA:356,0,block,44 DA:357,44 -BRF:6 +BRF:7 BRH:4 end_of_record diff --git a/test_regress/t/t_vlcov_opt_toggle.info.out b/test_regress/t/t_vlcov_opt_toggle.info.out index 7740e44dc..e44fbbbbe 100644 --- a/test_regress/t/t_vlcov_opt_toggle.info.out +++ b/test_regress/t/t_vlcov_opt_toggle.info.out @@ -3,14 +3,14 @@ SF:t/t_cover_line.v DA:12,19 DA:14,2 DA:20,11 -BRDA:20,0,0,11 -BRDA:20,0,1,5 -BRDA:20,0,2,2 -BRDA:20,0,3,1 -BRDA:20,0,4,0 -BRDA:20,0,5,0 -BRDA:20,0,6,0 -BRDA:20,0,7,0 +BRDA:20,0,cyc_copy[0],11 +BRDA:20,0,cyc_copy[1],5 +BRDA:20,0,cyc_copy[2],2 +BRDA:20,0,cyc_copy[3],1 +BRDA:20,0,cyc_copy[4],0 +BRDA:20,0,cyc_copy[5],0 +BRDA:20,0,cyc_copy[6],0 +BRDA:20,0,cyc_copy[7],0 DA:138,38 DA:139,4 DA:159,38 @@ -21,96 +21,96 @@ DA:241,19 DA:242,2 DA:261,19 DA:262,10 -BRDA:262,0,0,10 -BRDA:262,0,1,5 -BRDA:262,0,2,2 -BRDA:262,0,3,1 +BRDA:262,0,cyc4[0],10 +BRDA:262,0,cyc4[1],5 +BRDA:262,0,cyc4[2],2 +BRDA:262,0,cyc4[3],1 DA:309,19 -BRDA:309,0,0,19 -BRDA:309,0,1,11 -BRDA:309,0,2,0 -BRDA:309,0,3,0 -BRDA:309,0,4,0 -BRDA:309,0,5,0 -BRDA:309,0,6,0 -BRDA:309,0,7,0 -BRDA:309,0,8,0 -BRDA:309,0,9,0 -BRDA:309,0,10,0 -BRDA:309,0,11,0 -BRDA:309,0,12,5 -BRDA:309,0,13,0 -BRDA:309,0,14,0 -BRDA:309,0,15,0 -BRDA:309,0,16,0 -BRDA:309,0,17,0 -BRDA:309,0,18,0 -BRDA:309,0,19,0 -BRDA:309,0,20,0 -BRDA:309,0,21,0 -BRDA:309,0,22,0 -BRDA:309,0,23,2 -BRDA:309,0,24,0 -BRDA:309,0,25,0 -BRDA:309,0,26,1 -BRDA:309,0,27,0 -BRDA:309,0,28,0 -BRDA:309,0,29,0 -BRDA:309,0,30,0 -BRDA:309,0,31,0 -BRDA:309,0,32,0 +BRDA:309,0,clk,19 +BRDA:309,0,cyc[0],11 +BRDA:309,0,cyc[10],0 +BRDA:309,0,cyc[11],0 +BRDA:309,0,cyc[12],0 +BRDA:309,0,cyc[13],0 +BRDA:309,0,cyc[14],0 +BRDA:309,0,cyc[15],0 +BRDA:309,0,cyc[16],0 +BRDA:309,0,cyc[17],0 +BRDA:309,0,cyc[18],0 +BRDA:309,0,cyc[19],0 +BRDA:309,0,cyc[1],5 +BRDA:309,0,cyc[20],0 +BRDA:309,0,cyc[21],0 +BRDA:309,0,cyc[22],0 +BRDA:309,0,cyc[23],0 +BRDA:309,0,cyc[24],0 +BRDA:309,0,cyc[25],0 +BRDA:309,0,cyc[26],0 +BRDA:309,0,cyc[27],0 +BRDA:309,0,cyc[28],0 +BRDA:309,0,cyc[29],0 +BRDA:309,0,cyc[2],2 +BRDA:309,0,cyc[30],0 +BRDA:309,0,cyc[31],0 +BRDA:309,0,cyc[3],1 +BRDA:309,0,cyc[4],0 +BRDA:309,0,cyc[5],0 +BRDA:309,0,cyc[6],0 +BRDA:309,0,cyc[7],0 +BRDA:309,0,cyc[8],0 +BRDA:309,0,cyc[9],0 DA:310,19 -BRDA:310,0,0,0 -BRDA:310,0,1,2 -BRDA:310,0,2,19 -BRDA:310,0,3,6 -BRDA:310,0,4,7 -BRDA:310,0,5,1 -BRDA:310,0,6,19 -BRDA:310,0,7,3 -BRDA:310,0,8,0 -BRDA:310,0,9,0 -BRDA:310,0,10,0 +BRDA:310,0,a,0 +BRDA:310,0,b,2 +BRDA:310,0,c,19 +BRDA:310,0,d,6 +BRDA:310,0,e,7 +BRDA:310,0,f,1 +BRDA:310,0,g,19 +BRDA:310,0,h,3 +BRDA:310,0,k,0 +BRDA:310,0,l,0 +BRDA:310,0,m,0 DA:311,1 -BRDA:311,0,0,1 -BRDA:311,0,1,1 -BRDA:311,0,2,0 -BRDA:311,0,3,0 -BRDA:311,0,4,0 -BRDA:311,0,5,0 +BRDA:311,0,tab[0],1 +BRDA:311,0,tab[1],1 +BRDA:311,0,tab[2],0 +BRDA:311,0,tab[3],0 +BRDA:311,0,tab[4],0 +BRDA:311,0,tab[5],0 DA:313,2 -BRDA:313,0,0,0 -BRDA:313,0,1,2 -BRDA:313,0,2,0 -BRDA:313,0,3,0 -BRDA:313,0,4,0 -BRDA:313,0,5,0 -BRDA:313,0,6,0 -BRDA:313,0,7,0 -BRDA:313,0,8,2 -BRDA:313,0,9,0 -BRDA:313,0,10,0 -BRDA:313,0,11,0 -BRDA:313,0,12,0 -BRDA:313,0,13,0 -BRDA:313,0,14,0 -BRDA:313,0,15,0 -BRDA:313,0,16,0 -BRDA:313,0,17,0 -BRDA:313,0,18,0 -BRDA:313,0,19,0 -BRDA:313,0,20,0 -BRDA:313,0,21,0 -BRDA:313,0,22,0 -BRDA:313,0,23,0 -BRDA:313,0,24,0 -BRDA:313,0,25,0 -BRDA:313,0,26,0 -BRDA:313,0,27,0 -BRDA:313,0,28,0 -BRDA:313,0,29,0 -BRDA:313,0,30,0 -BRDA:313,0,31,0 +BRDA:313,0,data[0][0][0],0 +BRDA:313,0,data[0][0][1],2 +BRDA:313,0,data[0][0][2],0 +BRDA:313,0,data[0][0][3],0 +BRDA:313,0,data[0][0][4],0 +BRDA:313,0,data[0][0][5],0 +BRDA:313,0,data[0][0][6],0 +BRDA:313,0,data[0][0][7],0 +BRDA:313,0,data[0][1][0],2 +BRDA:313,0,data[0][1][1],0 +BRDA:313,0,data[0][1][2],0 +BRDA:313,0,data[0][1][3],0 +BRDA:313,0,data[0][1][4],0 +BRDA:313,0,data[0][1][5],0 +BRDA:313,0,data[0][1][6],0 +BRDA:313,0,data[0][1][7],0 +BRDA:313,0,data[1][0][0],0 +BRDA:313,0,data[1][0][1],0 +BRDA:313,0,data[1][0][2],0 +BRDA:313,0,data[1][0][3],0 +BRDA:313,0,data[1][0][4],0 +BRDA:313,0,data[1][0][5],0 +BRDA:313,0,data[1][0][6],0 +BRDA:313,0,data[1][0][7],0 +BRDA:313,0,data[1][1][0],0 +BRDA:313,0,data[1][1][1],0 +BRDA:313,0,data[1][1][2],0 +BRDA:313,0,data[1][1][3],0 +BRDA:313,0,data[1][1][4],0 +BRDA:313,0,data[1][1][5],0 +BRDA:313,0,data[1][1][6],0 +BRDA:313,0,data[1][1][7],0 BRF:94 BRH:6 end_of_record diff --git a/test_regress/t/t_vlcov_opt_user.info.out b/test_regress/t/t_vlcov_opt_user.info.out index e1b47061b..45fd8205a 100644 --- a/test_regress/t/t_vlcov_opt_user.info.out +++ b/test_regress/t/t_vlcov_opt_user.info.out @@ -1,180 +1,180 @@ TN:verilator_coverage SF:t/t_assert_ctl_arg.v DA:49,1 -BRDA:49,0,0,1 -BRDA:49,0,1,1 -BRDA:49,0,2,0 -BRDA:49,0,3,0 -BRDA:49,0,4,0 -BRDA:49,0,5,0 +BRDA:49,0,cover_simple_immediate_49,1 +BRDA:49,0,cover_simple_immediate_stmt_49,1 +BRDA:49,0,cover_final_deferred_immediate_49,0 +BRDA:49,0,cover_observed_deferred_immediate_49,0 +BRDA:49,0,cover_final_deferred_immediate_stmt_49,0 +BRDA:49,0,cover_observed_deferred_immediate_stmt_49,0 DA:51,0 -BRDA:51,0,0,0 -BRDA:51,0,1,0 -BRDA:51,0,2,0 -BRDA:51,0,3,0 -BRDA:51,0,4,0 -BRDA:51,0,5,0 +BRDA:51,0,cover_simple_immediate_51,0 +BRDA:51,0,cover_simple_immediate_stmt_51,0 +BRDA:51,0,cover_final_deferred_immediate_51,0 +BRDA:51,0,cover_observed_deferred_immediate_51,0 +BRDA:51,0,cover_final_deferred_immediate_stmt_51,0 +BRDA:51,0,cover_observed_deferred_immediate_stmt_51,0 DA:56,1 -BRDA:56,0,0,0 -BRDA:56,0,1,0 -BRDA:56,0,2,0 -BRDA:56,0,3,1 -BRDA:56,0,4,0 -BRDA:56,0,5,1 +BRDA:56,0,cover_simple_immediate_56,0 +BRDA:56,0,cover_simple_immediate_stmt_56,0 +BRDA:56,0,cover_final_deferred_immediate_56,0 +BRDA:56,0,cover_observed_deferred_immediate_56,1 +BRDA:56,0,cover_final_deferred_immediate_stmt_56,0 +BRDA:56,0,cover_observed_deferred_immediate_stmt_56,1 DA:58,0 -BRDA:58,0,0,0 -BRDA:58,0,1,0 -BRDA:58,0,2,0 -BRDA:58,0,3,0 -BRDA:58,0,4,0 -BRDA:58,0,5,0 +BRDA:58,0,cover_simple_immediate_58,0 +BRDA:58,0,cover_simple_immediate_stmt_58,0 +BRDA:58,0,cover_final_deferred_immediate_58,0 +BRDA:58,0,cover_observed_deferred_immediate_58,0 +BRDA:58,0,cover_final_deferred_immediate_stmt_58,0 +BRDA:58,0,cover_observed_deferred_immediate_stmt_58,0 DA:63,1 -BRDA:63,0,0,0 -BRDA:63,0,1,0 -BRDA:63,0,2,1 -BRDA:63,0,3,0 -BRDA:63,0,4,1 -BRDA:63,0,5,0 +BRDA:63,0,cover_simple_immediate_63,0 +BRDA:63,0,cover_simple_immediate_stmt_63,0 +BRDA:63,0,cover_final_deferred_immediate_63,1 +BRDA:63,0,cover_observed_deferred_immediate_63,0 +BRDA:63,0,cover_final_deferred_immediate_stmt_63,1 +BRDA:63,0,cover_observed_deferred_immediate_stmt_63,0 DA:65,0 -BRDA:65,0,0,0 -BRDA:65,0,1,0 -BRDA:65,0,2,0 -BRDA:65,0,3,0 -BRDA:65,0,4,0 -BRDA:65,0,5,0 +BRDA:65,0,cover_simple_immediate_65,0 +BRDA:65,0,cover_simple_immediate_stmt_65,0 +BRDA:65,0,cover_final_deferred_immediate_65,0 +BRDA:65,0,cover_observed_deferred_immediate_65,0 +BRDA:65,0,cover_final_deferred_immediate_stmt_65,0 +BRDA:65,0,cover_observed_deferred_immediate_stmt_65,0 DA:69,0 -BRDA:69,0,0,0 -BRDA:69,0,1,0 -BRDA:69,0,2,0 -BRDA:69,0,3,0 -BRDA:69,0,4,0 -BRDA:69,0,5,0 +BRDA:69,0,cover_simple_immediate_69,0 +BRDA:69,0,cover_simple_immediate_stmt_69,0 +BRDA:69,0,cover_final_deferred_immediate_69,0 +BRDA:69,0,cover_observed_deferred_immediate_69,0 +BRDA:69,0,cover_final_deferred_immediate_stmt_69,0 +BRDA:69,0,cover_observed_deferred_immediate_stmt_69,0 DA:71,1 -BRDA:71,0,0,1 -BRDA:71,0,1,1 -BRDA:71,0,2,1 -BRDA:71,0,3,1 -BRDA:71,0,4,1 -BRDA:71,0,5,1 +BRDA:71,0,cover_simple_immediate_71,1 +BRDA:71,0,cover_simple_immediate_stmt_71,1 +BRDA:71,0,cover_final_deferred_immediate_71,1 +BRDA:71,0,cover_observed_deferred_immediate_71,1 +BRDA:71,0,cover_final_deferred_immediate_stmt_71,1 +BRDA:71,0,cover_observed_deferred_immediate_stmt_71,1 DA:73,0 -BRDA:73,0,0,0 -BRDA:73,0,1,0 -BRDA:73,0,2,0 -BRDA:73,0,3,0 -BRDA:73,0,4,0 -BRDA:73,0,5,0 +BRDA:73,0,cover_simple_immediate_73,0 +BRDA:73,0,cover_simple_immediate_stmt_73,0 +BRDA:73,0,cover_final_deferred_immediate_73,0 +BRDA:73,0,cover_observed_deferred_immediate_73,0 +BRDA:73,0,cover_final_deferred_immediate_stmt_73,0 +BRDA:73,0,cover_observed_deferred_immediate_stmt_73,0 DA:76,1 -BRDA:76,0,0,1 -BRDA:76,0,1,1 -BRDA:76,0,2,0 -BRDA:76,0,3,1 -BRDA:76,0,4,0 -BRDA:76,0,5,1 +BRDA:76,0,cover_simple_immediate_76,1 +BRDA:76,0,cover_simple_immediate_stmt_76,1 +BRDA:76,0,cover_final_deferred_immediate_76,0 +BRDA:76,0,cover_observed_deferred_immediate_76,1 +BRDA:76,0,cover_final_deferred_immediate_stmt_76,0 +BRDA:76,0,cover_observed_deferred_immediate_stmt_76,1 DA:78,1 -BRDA:78,0,0,1 -BRDA:78,0,1,1 -BRDA:78,0,2,1 -BRDA:78,0,3,1 -BRDA:78,0,4,1 -BRDA:78,0,5,1 +BRDA:78,0,cover_simple_immediate_78,1 +BRDA:78,0,cover_simple_immediate_stmt_78,1 +BRDA:78,0,cover_final_deferred_immediate_78,1 +BRDA:78,0,cover_observed_deferred_immediate_78,1 +BRDA:78,0,cover_final_deferred_immediate_stmt_78,1 +BRDA:78,0,cover_observed_deferred_immediate_stmt_78,1 DA:80,1 -BRDA:80,0,0,1 -BRDA:80,0,1,1 -BRDA:80,0,2,0 -BRDA:80,0,3,0 -BRDA:80,0,4,0 -BRDA:80,0,5,0 +BRDA:80,0,cover_simple_immediate_80,1 +BRDA:80,0,cover_simple_immediate_stmt_80,1 +BRDA:80,0,cover_final_deferred_immediate_80,0 +BRDA:80,0,cover_observed_deferred_immediate_80,0 +BRDA:80,0,cover_final_deferred_immediate_stmt_80,0 +BRDA:80,0,cover_observed_deferred_immediate_stmt_80,0 DA:82,1 -BRDA:82,0,0,1 -BRDA:82,0,1,1 -BRDA:82,0,2,0 -BRDA:82,0,3,0 -BRDA:82,0,4,0 -BRDA:82,0,5,0 +BRDA:82,0,cover_simple_immediate_82,1 +BRDA:82,0,cover_simple_immediate_stmt_82,1 +BRDA:82,0,cover_final_deferred_immediate_82,0 +BRDA:82,0,cover_observed_deferred_immediate_82,0 +BRDA:82,0,cover_final_deferred_immediate_stmt_82,0 +BRDA:82,0,cover_observed_deferred_immediate_stmt_82,0 DA:84,0 -BRDA:84,0,0,0 -BRDA:84,0,1,0 -BRDA:84,0,2,0 -BRDA:84,0,3,0 -BRDA:84,0,4,0 -BRDA:84,0,5,0 +BRDA:84,0,cover_simple_immediate_84,0 +BRDA:84,0,cover_simple_immediate_stmt_84,0 +BRDA:84,0,cover_final_deferred_immediate_84,0 +BRDA:84,0,cover_observed_deferred_immediate_84,0 +BRDA:84,0,cover_final_deferred_immediate_stmt_84,0 +BRDA:84,0,cover_observed_deferred_immediate_stmt_84,0 DA:86,1 -BRDA:86,0,0,1 -BRDA:86,0,1,1 -BRDA:86,0,2,0 -BRDA:86,0,3,0 -BRDA:86,0,4,0 -BRDA:86,0,5,0 +BRDA:86,0,cover_simple_immediate_86,1 +BRDA:86,0,cover_simple_immediate_stmt_86,1 +BRDA:86,0,cover_final_deferred_immediate_86,0 +BRDA:86,0,cover_observed_deferred_immediate_86,0 +BRDA:86,0,cover_final_deferred_immediate_stmt_86,0 +BRDA:86,0,cover_observed_deferred_immediate_stmt_86,0 DA:88,0 -BRDA:88,0,0,0 -BRDA:88,0,1,0 -BRDA:88,0,2,0 -BRDA:88,0,3,0 -BRDA:88,0,4,0 -BRDA:88,0,5,0 +BRDA:88,0,cover_simple_immediate_88,0 +BRDA:88,0,cover_simple_immediate_stmt_88,0 +BRDA:88,0,cover_final_deferred_immediate_88,0 +BRDA:88,0,cover_observed_deferred_immediate_88,0 +BRDA:88,0,cover_final_deferred_immediate_stmt_88,0 +BRDA:88,0,cover_observed_deferred_immediate_stmt_88,0 DA:90,1 -BRDA:90,0,0,1 -BRDA:90,0,1,1 -BRDA:90,0,2,1 -BRDA:90,0,3,1 -BRDA:90,0,4,1 -BRDA:90,0,5,1 +BRDA:90,0,cover_simple_immediate_90,1 +BRDA:90,0,cover_simple_immediate_stmt_90,1 +BRDA:90,0,cover_final_deferred_immediate_90,1 +BRDA:90,0,cover_observed_deferred_immediate_90,1 +BRDA:90,0,cover_final_deferred_immediate_stmt_90,1 +BRDA:90,0,cover_observed_deferred_immediate_stmt_90,1 DA:92,0 -BRDA:92,0,0,0 -BRDA:92,0,1,0 -BRDA:92,0,2,0 -BRDA:92,0,3,0 -BRDA:92,0,4,0 -BRDA:92,0,5,0 +BRDA:92,0,cover_simple_immediate_92,0 +BRDA:92,0,cover_simple_immediate_stmt_92,0 +BRDA:92,0,cover_final_deferred_immediate_92,0 +BRDA:92,0,cover_observed_deferred_immediate_92,0 +BRDA:92,0,cover_final_deferred_immediate_stmt_92,0 +BRDA:92,0,cover_observed_deferred_immediate_stmt_92,0 DA:97,0 -BRDA:97,0,0,0 -BRDA:97,0,1,0 -BRDA:97,0,2,0 -BRDA:97,0,3,0 -BRDA:97,0,4,0 -BRDA:97,0,5,0 +BRDA:97,0,cover_simple_immediate_97,0 +BRDA:97,0,cover_simple_immediate_stmt_97,0 +BRDA:97,0,cover_final_deferred_immediate_97,0 +BRDA:97,0,cover_observed_deferred_immediate_97,0 +BRDA:97,0,cover_final_deferred_immediate_stmt_97,0 +BRDA:97,0,cover_observed_deferred_immediate_stmt_97,0 DA:100,1 -BRDA:100,0,0,1 -BRDA:100,0,1,1 -BRDA:100,0,2,1 -BRDA:100,0,3,1 -BRDA:100,0,4,1 -BRDA:100,0,5,1 +BRDA:100,0,cover_simple_immediate_100,1 +BRDA:100,0,cover_simple_immediate_stmt_100,1 +BRDA:100,0,cover_final_deferred_immediate_100,1 +BRDA:100,0,cover_observed_deferred_immediate_100,1 +BRDA:100,0,cover_final_deferred_immediate_stmt_100,1 +BRDA:100,0,cover_observed_deferred_immediate_stmt_100,1 DA:103,0 -BRDA:103,0,0,0 -BRDA:103,0,1,0 -BRDA:103,0,2,0 -BRDA:103,0,3,0 -BRDA:103,0,4,0 -BRDA:103,0,5,0 +BRDA:103,0,cover_simple_immediate_103,0 +BRDA:103,0,cover_simple_immediate_stmt_103,0 +BRDA:103,0,cover_final_deferred_immediate_103,0 +BRDA:103,0,cover_observed_deferred_immediate_103,0 +BRDA:103,0,cover_final_deferred_immediate_stmt_103,0 +BRDA:103,0,cover_observed_deferred_immediate_stmt_103,0 DA:106,1 -BRDA:106,0,0,1 -BRDA:106,0,1,1 -BRDA:106,0,2,1 -BRDA:106,0,3,1 -BRDA:106,0,4,1 -BRDA:106,0,5,1 +BRDA:106,0,cover_simple_immediate_106,1 +BRDA:106,0,cover_simple_immediate_stmt_106,1 +BRDA:106,0,cover_final_deferred_immediate_106,1 +BRDA:106,0,cover_observed_deferred_immediate_106,1 +BRDA:106,0,cover_final_deferred_immediate_stmt_106,1 +BRDA:106,0,cover_observed_deferred_immediate_stmt_106,1 DA:108,1 -BRDA:108,0,0,1 -BRDA:108,0,1,1 -BRDA:108,0,2,1 -BRDA:108,0,3,1 -BRDA:108,0,4,1 -BRDA:108,0,5,1 +BRDA:108,0,cover_simple_immediate_108,1 +BRDA:108,0,cover_simple_immediate_stmt_108,1 +BRDA:108,0,cover_final_deferred_immediate_108,1 +BRDA:108,0,cover_observed_deferred_immediate_108,1 +BRDA:108,0,cover_final_deferred_immediate_stmt_108,1 +BRDA:108,0,cover_observed_deferred_immediate_stmt_108,1 DA:110,0 -BRDA:110,0,0,0 -BRDA:110,0,1,0 -BRDA:110,0,2,0 -BRDA:110,0,3,0 -BRDA:110,0,4,0 -BRDA:110,0,5,0 +BRDA:110,0,cover_simple_immediate_110,0 +BRDA:110,0,cover_simple_immediate_stmt_110,0 +BRDA:110,0,cover_final_deferred_immediate_110,0 +BRDA:110,0,cover_observed_deferred_immediate_110,0 +BRDA:110,0,cover_final_deferred_immediate_stmt_110,0 +BRDA:110,0,cover_observed_deferred_immediate_stmt_110,0 DA:112,1 -BRDA:112,0,0,1 -BRDA:112,0,1,1 -BRDA:112,0,2,1 -BRDA:112,0,3,0 -BRDA:112,0,4,1 -BRDA:112,0,5,0 +BRDA:112,0,cover_simple_immediate_112,1 +BRDA:112,0,cover_simple_immediate_stmt_112,1 +BRDA:112,0,cover_final_deferred_immediate_112,1 +BRDA:112,0,cover_observed_deferred_immediate_112,0 +BRDA:112,0,cover_final_deferred_immediate_stmt_112,1 +BRDA:112,0,cover_observed_deferred_immediate_stmt_112,0 DA:192,0 DA:193,0 BRF:150 diff --git a/test_regress/t/t_vlcov_opt_wild.info.out b/test_regress/t/t_vlcov_opt_wild.info.out index efcf737f6..8d8af4f6f 100644 --- a/test_regress/t/t_vlcov_opt_wild.info.out +++ b/test_regress/t/t_vlcov_opt_wild.info.out @@ -5,42 +5,42 @@ DA:14,2 DA:15,1 DA:18,1 DA:20,11 -BRDA:20,0,0,11 -BRDA:20,0,1,5 -BRDA:20,0,2,2 -BRDA:20,0,3,1 -BRDA:20,0,4,0 -BRDA:20,0,5,0 -BRDA:20,0,6,0 -BRDA:20,0,7,0 +BRDA:20,0,cyc_copy[0],11 +BRDA:20,0,cyc_copy[1],5 +BRDA:20,0,cyc_copy[2],2 +BRDA:20,0,cyc_copy[3],1 +BRDA:20,0,cyc_copy[4],0 +BRDA:20,0,cyc_copy[5],0 +BRDA:20,0,cyc_copy[6],0 +BRDA:20,0,cyc_copy[7],0 DA:55,10 DA:56,10 -BRDA:56,0,0,10 -BRDA:56,0,1,0 +BRDA:56,0,if,10 +BRDA:56,0,else,0 DA:57,10 DA:58,10 DA:60,9 -BRDA:60,0,0,1 -BRDA:60,0,1,9 +BRDA:60,0,if,1 +BRDA:60,0,else,9 DA:61,9 -BRDA:61,0,0,1 -BRDA:61,0,1,9 +BRDA:61,0,if,1 +BRDA:61,0,else,9 DA:62,1 DA:63,1 DA:66,9 -BRDA:66,0,0,1 -BRDA:66,0,1,9 +BRDA:66,0,if,1 +BRDA:66,0,else,9 DA:67,9 -BRDA:67,0,0,1 -BRDA:67,0,1,9 +BRDA:67,0,if,1 +BRDA:67,0,else,9 DA:69,9 DA:70,9 DA:73,9 -BRDA:73,0,0,1 -BRDA:73,0,1,9 +BRDA:73,0,if,1 +BRDA:73,0,else,9 DA:74,9 -BRDA:74,0,0,1 -BRDA:74,0,1,9 +BRDA:74,0,if,1 +BRDA:74,0,else,9 DA:75,1 DA:76,1 DA:79,9 @@ -52,8 +52,9 @@ DA:87,1 DA:88,1 DA:89,1 DA:91,7 -BRDA:91,0,0,1 -BRDA:91,0,1,7 +BRDA:91,0,if,1 +BRDA:91,0,else,7 +BRDA:91,0,test_test,5 DA:92,1 DA:93,1 DA:96,7 @@ -63,19 +64,19 @@ DA:101,0 DA:102,0 DA:104,0 DA:105,10 -BRDA:105,0,0,0 -BRDA:105,0,1,10 +BRDA:105,0,block,0 +BRDA:105,0,if,10 DA:106,10 -BRDA:106,0,0,0 -BRDA:106,0,1,10 +BRDA:106,0,block,0 +BRDA:106,0,if,10 DA:107,0 DA:110,1 DA:111,1 DA:113,1 DA:115,1 DA:120,7 -BRDA:120,0,0,1 -BRDA:120,0,1,7 +BRDA:120,0,if,1 +BRDA:120,0,else,7 DA:121,1 DA:122,1 DA:127,1 @@ -84,8 +85,8 @@ DA:138,38 DA:139,4 DA:140,20 DA:141,18 -BRDA:141,0,0,2 -BRDA:141,0,1,18 +BRDA:141,0,if,2 +BRDA:141,0,else,18 DA:142,2 DA:145,18 DA:159,38 @@ -93,29 +94,29 @@ DA:160,4 DA:164,20 DA:165,20 DA:166,20 -BRDA:166,0,0,0 -BRDA:166,0,1,20 +BRDA:166,0,if,0 +BRDA:166,0,else,20 DA:168,0 DA:170,18 -BRDA:170,0,0,2 -BRDA:170,0,1,18 +BRDA:170,0,if,2 +BRDA:170,0,else,18 DA:172,2 DA:174,18 DA:188,1 DA:189,1 DA:190,1 -BRDA:190,0,0,1 -BRDA:190,0,1,0 +BRDA:190,0,if,1 +BRDA:190,0,else,0 DA:191,1 DA:194,11 DA:195,11 -BRDA:195,0,0,11 -BRDA:195,0,1,0 +BRDA:195,0,if,11 +BRDA:195,0,else,0 DA:196,11 DA:199,11 DA:200,11 -BRDA:200,0,0,11 -BRDA:200,0,1,0 +BRDA:200,0,if,11 +BRDA:200,0,else,0 DA:201,11 DA:210,19 DA:211,2 @@ -124,12 +125,12 @@ DA:216,10 DA:219,11 DA:221,11 DA:222,10 -BRDA:222,0,0,1 -BRDA:222,0,1,10 +BRDA:222,0,if,1 +BRDA:222,0,else,10 DA:223,1 DA:225,10 -BRDA:225,0,0,1 -BRDA:225,0,1,10 +BRDA:225,0,if,1 +BRDA:225,0,else,10 DA:226,1 DA:229,11 DA:230,1 @@ -139,18 +140,18 @@ DA:241,19 DA:242,2 DA:252,10 DA:253,9 -BRDA:253,0,0,1 -BRDA:253,0,1,9 +BRDA:253,0,if,1 +BRDA:253,0,else,9 DA:255,1 DA:256,1 -BRDA:256,0,0,0 -BRDA:256,0,1,1 +BRDA:256,0,if,0 +BRDA:256,0,else,1 DA:261,19 DA:262,10 -BRDA:262,0,0,10 -BRDA:262,0,1,5 -BRDA:262,0,2,2 -BRDA:262,0,3,1 +BRDA:262,0,cyc4[0],10 +BRDA:262,0,cyc4[1],5 +BRDA:262,0,cyc4[2],2 +BRDA:262,0,cyc4[3],1 DA:265,10 DA:266,10 DA:267,1 @@ -163,8 +164,8 @@ DA:276,10 DA:277,10 DA:287,0 DA:288,0 -BRDA:288,0,0,0 -BRDA:288,0,1,0 +BRDA:288,0,if,0 +BRDA:288,0,else,0 DA:289,0 DA:291,0 DA:292,0 @@ -174,91 +175,91 @@ DA:303,1 DA:304,20 DA:305,20 DA:309,19 -BRDA:309,0,0,19 -BRDA:309,0,1,11 -BRDA:309,0,2,0 -BRDA:309,0,3,0 -BRDA:309,0,4,0 -BRDA:309,0,5,0 -BRDA:309,0,6,0 -BRDA:309,0,7,0 -BRDA:309,0,8,0 -BRDA:309,0,9,0 -BRDA:309,0,10,0 -BRDA:309,0,11,0 -BRDA:309,0,12,5 -BRDA:309,0,13,0 -BRDA:309,0,14,0 -BRDA:309,0,15,0 -BRDA:309,0,16,0 -BRDA:309,0,17,0 -BRDA:309,0,18,0 -BRDA:309,0,19,0 -BRDA:309,0,20,0 -BRDA:309,0,21,0 -BRDA:309,0,22,0 -BRDA:309,0,23,2 -BRDA:309,0,24,0 -BRDA:309,0,25,0 -BRDA:309,0,26,1 -BRDA:309,0,27,0 -BRDA:309,0,28,0 -BRDA:309,0,29,0 -BRDA:309,0,30,0 -BRDA:309,0,31,0 -BRDA:309,0,32,0 +BRDA:309,0,clk,19 +BRDA:309,0,cyc[0],11 +BRDA:309,0,cyc[10],0 +BRDA:309,0,cyc[11],0 +BRDA:309,0,cyc[12],0 +BRDA:309,0,cyc[13],0 +BRDA:309,0,cyc[14],0 +BRDA:309,0,cyc[15],0 +BRDA:309,0,cyc[16],0 +BRDA:309,0,cyc[17],0 +BRDA:309,0,cyc[18],0 +BRDA:309,0,cyc[19],0 +BRDA:309,0,cyc[1],5 +BRDA:309,0,cyc[20],0 +BRDA:309,0,cyc[21],0 +BRDA:309,0,cyc[22],0 +BRDA:309,0,cyc[23],0 +BRDA:309,0,cyc[24],0 +BRDA:309,0,cyc[25],0 +BRDA:309,0,cyc[26],0 +BRDA:309,0,cyc[27],0 +BRDA:309,0,cyc[28],0 +BRDA:309,0,cyc[29],0 +BRDA:309,0,cyc[2],2 +BRDA:309,0,cyc[30],0 +BRDA:309,0,cyc[31],0 +BRDA:309,0,cyc[3],1 +BRDA:309,0,cyc[4],0 +BRDA:309,0,cyc[5],0 +BRDA:309,0,cyc[6],0 +BRDA:309,0,cyc[7],0 +BRDA:309,0,cyc[8],0 +BRDA:309,0,cyc[9],0 DA:310,19 -BRDA:310,0,0,0 -BRDA:310,0,1,2 -BRDA:310,0,2,19 -BRDA:310,0,3,6 -BRDA:310,0,4,7 -BRDA:310,0,5,1 -BRDA:310,0,6,19 -BRDA:310,0,7,3 -BRDA:310,0,8,0 -BRDA:310,0,9,0 -BRDA:310,0,10,0 +BRDA:310,0,a,0 +BRDA:310,0,b,2 +BRDA:310,0,c,19 +BRDA:310,0,d,6 +BRDA:310,0,e,7 +BRDA:310,0,f,1 +BRDA:310,0,g,19 +BRDA:310,0,h,3 +BRDA:310,0,k,0 +BRDA:310,0,l,0 +BRDA:310,0,m,0 DA:311,1 -BRDA:311,0,0,1 -BRDA:311,0,1,1 -BRDA:311,0,2,0 -BRDA:311,0,3,0 -BRDA:311,0,4,0 -BRDA:311,0,5,0 +BRDA:311,0,tab[0],1 +BRDA:311,0,tab[1],1 +BRDA:311,0,tab[2],0 +BRDA:311,0,tab[3],0 +BRDA:311,0,tab[4],0 +BRDA:311,0,tab[5],0 DA:313,2 -BRDA:313,0,0,0 -BRDA:313,0,1,2 -BRDA:313,0,2,0 -BRDA:313,0,3,0 -BRDA:313,0,4,0 -BRDA:313,0,5,0 -BRDA:313,0,6,0 -BRDA:313,0,7,0 -BRDA:313,0,8,2 -BRDA:313,0,9,0 -BRDA:313,0,10,0 -BRDA:313,0,11,0 -BRDA:313,0,12,0 -BRDA:313,0,13,0 -BRDA:313,0,14,0 -BRDA:313,0,15,0 -BRDA:313,0,16,0 -BRDA:313,0,17,0 -BRDA:313,0,18,0 -BRDA:313,0,19,0 -BRDA:313,0,20,0 -BRDA:313,0,21,0 -BRDA:313,0,22,0 -BRDA:313,0,23,0 -BRDA:313,0,24,0 -BRDA:313,0,25,0 -BRDA:313,0,26,0 -BRDA:313,0,27,0 -BRDA:313,0,28,0 -BRDA:313,0,29,0 -BRDA:313,0,30,0 -BRDA:313,0,31,0 +BRDA:313,0,data[0][0][0],0 +BRDA:313,0,data[0][0][1],2 +BRDA:313,0,data[0][0][2],0 +BRDA:313,0,data[0][0][3],0 +BRDA:313,0,data[0][0][4],0 +BRDA:313,0,data[0][0][5],0 +BRDA:313,0,data[0][0][6],0 +BRDA:313,0,data[0][0][7],0 +BRDA:313,0,data[0][1][0],2 +BRDA:313,0,data[0][1][1],0 +BRDA:313,0,data[0][1][2],0 +BRDA:313,0,data[0][1][3],0 +BRDA:313,0,data[0][1][4],0 +BRDA:313,0,data[0][1][5],0 +BRDA:313,0,data[0][1][6],0 +BRDA:313,0,data[0][1][7],0 +BRDA:313,0,data[1][0][0],0 +BRDA:313,0,data[1][0][1],0 +BRDA:313,0,data[1][0][2],0 +BRDA:313,0,data[1][0][3],0 +BRDA:313,0,data[1][0][4],0 +BRDA:313,0,data[1][0][5],0 +BRDA:313,0,data[1][0][6],0 +BRDA:313,0,data[1][0][7],0 +BRDA:313,0,data[1][1][0],0 +BRDA:313,0,data[1][1][1],0 +BRDA:313,0,data[1][1][2],0 +BRDA:313,0,data[1][1][3],0 +BRDA:313,0,data[1][1][4],0 +BRDA:313,0,data[1][1][5],0 +BRDA:313,0,data[1][1][6],0 +BRDA:313,0,data[1][1][7],0 DA:314,1 DA:317,21 DA:318,21 @@ -266,69 +267,69 @@ DA:319,21 DA:322,10 DA:324,10 DA:327,31 -BRDA:327,0,0,0 -BRDA:327,0,1,31 +BRDA:327,0,cond_then,0 +BRDA:327,0,cond_else,31 DA:328,28 -BRDA:328,0,0,3 -BRDA:328,0,1,28 +BRDA:328,0,cond_then,3 +BRDA:328,0,cond_else,28 DA:329,21 -BRDA:329,0,0,21 -BRDA:329,0,1,0 +BRDA:329,0,cond_then,21 +BRDA:329,0,cond_else,0 DA:330,10 DA:331,10 -BRDA:331,0,0,10 -BRDA:331,0,1,7 -BRDA:331,0,2,3 -BRDA:331,0,3,3 -BRDA:331,0,4,7 +BRDA:331,0,block,10 +BRDA:331,0,(((cyc %25 32'sh3) == 32'sh0)==0) => 0,7 +BRDA:331,0,(((cyc %25 32'sh3) == 32'sh0)==1) => 1,3 +BRDA:331,0,cond_then,3 +BRDA:331,0,cond_else,7 DA:332,10 -BRDA:332,0,0,10 -BRDA:332,0,1,0 -BRDA:332,0,2,10 +BRDA:332,0,block,10 +BRDA:332,0,cond_then,0 +BRDA:332,0,cond_else,10 DA:334,19 -BRDA:334,0,0,12 -BRDA:334,0,1,19 -BRDA:334,0,2,7 -BRDA:334,0,3,5 +BRDA:334,0,cond_then,12 +BRDA:334,0,cond_else,19 +BRDA:334,0,cond_then,7 +BRDA:334,0,cond_else,5 DA:337,11 -BRDA:337,0,0,11 -BRDA:337,0,1,0 +BRDA:337,0,cond_then,11 +BRDA:337,0,cond_else,0 DA:343,11 -BRDA:343,0,0,10 -BRDA:343,0,1,11 +BRDA:343,0,cond_then,10 +BRDA:343,0,cond_else,11 DA:346,11 DA:347,10 -BRDA:347,0,0,1 -BRDA:347,0,1,0 -BRDA:347,0,2,0 -BRDA:347,0,3,1 -BRDA:347,0,4,1 -BRDA:347,0,5,10 +BRDA:347,0,((cyc > 32'sh5)==0) => 0,1 +BRDA:347,0,((cyc > 32'sh5)==1) => 1,0 +BRDA:347,0,cond_then,0 +BRDA:347,0,cond_else,1 +BRDA:347,0,if,1 +BRDA:347,0,else,10 DA:348,10 DA:350,11 -BRDA:350,0,0,11 -BRDA:350,0,1,10 -BRDA:350,0,2,1 -BRDA:350,0,3,1 -BRDA:350,0,4,10 +BRDA:350,0,block,11 +BRDA:350,0,((cyc == 32'sh2)==0) => 0,10 +BRDA:350,0,((cyc == 32'sh2)==1) => 1,1 +BRDA:350,0,cond_then,1 +BRDA:350,0,cond_else,10 DA:353,55 -BRDA:353,0,0,11 -BRDA:353,0,1,11 -BRDA:353,0,2,0 -BRDA:353,0,3,55 +BRDA:353,0,block,11 +BRDA:353,0,((i < 32'sh5)==0) => 0,11 +BRDA:353,0,((i < 32'sh5)==1) => 1,0 +BRDA:353,0,block,55 DA:354,55 DA:356,44 -BRDA:356,0,0,11 -BRDA:356,0,1,0 -BRDA:356,0,2,11 -BRDA:356,0,3,44 +BRDA:356,0,block,11 +BRDA:356,0,((i > 32'sh4)==0) => 0,0 +BRDA:356,0,((i > 32'sh4)==1) => 1,11 +BRDA:356,0,block,44 DA:357,44 DA:360,11 -BRDA:360,0,0,11 -BRDA:360,0,1,0 -BRDA:360,0,2,0 -BRDA:360,0,3,11 +BRDA:360,0,(k==0) => 0,11 +BRDA:360,0,(k==1) => 1,0 +BRDA:360,0,if,0 +BRDA:360,0,else,11 DA:361,11 -BRF:183 +BRF:184 BRH:39 end_of_record diff --git a/test_regress/t/t_vlcov_summary_typed.out b/test_regress/t/t_vlcov_summary_typed.out index e4b3d14f6..f9700d96d 100644 --- a/test_regress/t/t_vlcov_summary_typed.out +++ b/test_regress/t/t_vlcov_summary_typed.out @@ -1,5 +1,5 @@ Coverage Summary: - line : 88.6% ( 39/ 44) + line : 88.9% ( 40/ 45) toggle : 33.3% ( 35/105) branch : 78.1% ( 50/ 64) expr : 66.7% ( 8/ 12) From 7ece66d06e19f21d14c38d0946cf2060a19f0544 Mon Sep 17 00:00:00 2001 From: Pawel Klopotek Date: Fri, 3 Jul 2026 18:49:21 +0200 Subject: [PATCH 39/41] Fix unique0 case side effects (#7787) --- src/V3Assert.cpp | 27 +++++++++++++++ test_regress/t/t_case_call_count.py | 3 +- test_regress/t/t_case_inside_call_count.py | 3 +- test_regress/t/t_unique0_case.py | 20 +++++++++++ test_regress/t/t_unique0_case.v | 39 ++++++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_unique0_case.py create mode 100644 test_regress/t/t_unique0_case.v diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 6c507c22c..ee18b24b4 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -20,6 +20,7 @@ #include "V3AstUserAllocator.h" #include "V3Stats.h" +#include "V3UniqueNames.h" VL_DEFINE_DEBUG_FUNCTIONS; @@ -228,6 +229,9 @@ class AssertVisitor final : public VNVisitor { AstNode* m_failsp = nullptr; // Current fail statement AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion AstFinal* m_finalp = nullptr; // Current final block + VDouble0 m_statLiftedCaseExprs; // Count of purified case expressions + AstNodeFTask* m_ftaskp = nullptr; // Current function/task + V3UniqueNames m_caseTempNames{"__VCase"}; // Map from (expression, senTree) to AstAlways that computes delayed values of the expression std::unordered_map, std::unordered_map, AstAlways*>> m_modExpr2Sen2DelayedAlwaysp; @@ -755,6 +759,28 @@ class AssertVisitor final : public VNVisitor { //========== Case assertions void visit(AstCase* nodep) override { + // Introduce temporary variable for AstCase if needed - it is done here and not in V3Case + // because this phase is before V3Scope and V3Case is not. Doing it before V3Scope ensures + // that V3Scope will take care of a scope creation + // We also need to do it before V3Begin, co that pragmas like `unique0` also work correctly + if (!nodep->exprp()->isPure()) { + ++m_statLiftedCaseExprs; + FileLine* const fl = nodep->exprp()->fileline(); + AstVar* const varp = new AstVar{fl, VVarType::BLOCKTEMP, m_caseTempNames.get(nodep), + nodep->exprp()->dtypep()}; + AstNodeExpr* const origp = nodep->exprp()->unlinkFrBack(); + nodep->addHereThisAsNext( + new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, origp}); + nodep->exprp(new AstVarRef{fl, varp, VAccess::READ}); + if (m_ftaskp) { + varp->funcLocal(true); + varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT); + m_ftaskp->stmtsp()->addHereThisAsNext(varp); + } else { + m_modp->stmtsp()->addHereThisAsNext(varp); + } + VIsCached::clearCacheTree(); + } iterateChildren(nodep); if (!nodep->user1SetOnce()) { bool has_default = false; @@ -1180,6 +1206,7 @@ public: V3Stats::addStat("Assertions, $past variables", m_statPastVars); V3Stats::addStat("Assertions, assertOn checks combined", m_statAssertOnCombined); V3Stats::addStat("Assertions, assertOn checks hoisted", m_statAssertOnHoisted); + V3Stats::addStat("Assertions, lifted impure case expressions", m_statLiftedCaseExprs); // Rewrites can change purity, e.g. by compiling out assertion statements with --no-assert VIsCached::clearCacheTree(); } diff --git a/test_regress/t/t_case_call_count.py b/test_regress/t/t_case_call_count.py index df91d0e3c..bf14a54dd 100755 --- a/test_regress/t/t_case_call_count.py +++ b/test_regress/t/t_case_call_count.py @@ -15,6 +15,7 @@ test.compile(verilator_flags2=['--stats']) test.execute() -test.file_grep(test.stats, r'LiftExpr, lifted calls\s+(\d+)', 3) +test.file_grep(test.stats, r'LiftExpr, lifted calls\s+(\d+)', 2) +test.file_grep(test.stats, r'Assertions, lifted impure case expressions\s+(\d+)', 2) test.passes() diff --git a/test_regress/t/t_case_inside_call_count.py b/test_regress/t/t_case_inside_call_count.py index df91d0e3c..bf14a54dd 100755 --- a/test_regress/t/t_case_inside_call_count.py +++ b/test_regress/t/t_case_inside_call_count.py @@ -15,6 +15,7 @@ test.compile(verilator_flags2=['--stats']) test.execute() -test.file_grep(test.stats, r'LiftExpr, lifted calls\s+(\d+)', 3) +test.file_grep(test.stats, r'LiftExpr, lifted calls\s+(\d+)', 2) +test.file_grep(test.stats, r'Assertions, lifted impure case expressions\s+(\d+)', 2) test.passes() diff --git a/test_regress/t/t_unique0_case.py b/test_regress/t/t_unique0_case.py new file mode 100755 index 000000000..3626b338d --- /dev/null +++ b/test_regress/t/t_unique0_case.py @@ -0,0 +1,20 @@ +#!/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=["--stats"]) + +test.execute() + +test.file_grep(test.stats, r"Assertions, lifted impure case expressions\s+(\d+)", 1) + +test.passes() diff --git a/test_regress/t/t_unique0_case.v b/test_regress/t/t_unique0_case.v new file mode 100644 index 000000000..54cedb901 --- /dev/null +++ b/test_regress/t/t_unique0_case.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +`define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) != (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); +// verilog_format: on + +module t; + int x; + int y; + int z; + initial begin + y = 0; + x = 0; + z = 0; + unique0 case(increment_x()) + 1: begin + y = 1; + end + default: begin + z = 1; + end + endcase + `checkh(x, 32'h1); + `checkh(y, 32'h1); + `checkh(z, 32'h0); + $finish; + end + + function automatic integer increment_x(); + x = x + 1; + return x; + endfunction +endmodule From 12fab5d5d75f6b12598ceccba7d075cfebb177eb Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 3 Jul 2026 13:00:23 -0400 Subject: [PATCH 40/41] Tests: Cleanup some cross-simulator test differences --- Changes | 2 +- src/V3Options.cpp | 2 +- test_regress/t/t_array_packed_sysfunct.v | 2 - test_regress/t/t_assert_always_unbounded.v | 12 +- test_regress/t/t_assert_bang_in_seq.v | 12 +- test_regress/t/t_assert_consec_rep.v | 28 +-- test_regress/t/t_assert_consec_rep_large.v | 4 +- test_regress/t/t_assert_ctl_lock.v | 11 +- test_regress/t/t_assert_goto_rep.v | 16 +- test_regress/t/t_assert_nonconsec_rep.v | 8 +- test_regress/t/t_assert_procedural_gated.v | 5 +- test_regress/t/t_assert_seq_clocking.v | 5 +- test_regress/t/t_constraint_dist_foreach_if.v | 3 +- test_regress/t/t_cover_property.v | 19 +- test_regress/t/t_cover_sequence.v | 17 +- test_regress/t/t_forceable_unpacked.v | 3 +- test_regress/t/t_gen_for.v | 4 - test_regress/t/t_gen_intdot.v | 98 ++++++---- test_regress/t/t_inst_signed.v | 6 +- test_regress/t/t_interface2.v | 2 +- test_regress/t/t_interface_array_nocolon.v | 1 - .../t/t_interface_virtual_sub_iface_method.v | 3 +- test_regress/t/t_math_pow.v | 2 - test_regress/t/t_math_pow3.v | 27 ++- test_regress/t/t_math_signed4.v | 6 +- test_regress/t/t_math_signed5.v | 19 +- test_regress/t/t_math_svl2.v | 4 - test_regress/t/t_modport_export_task.v | 3 +- test_regress/t/t_param_first.v | 3 +- test_regress/t/t_prop_always.v | 20 +- test_regress/t/t_prop_followed_by.v | 15 +- test_regress/t/t_property_accept_reject_on.v | 20 +- test_regress/t/t_property_disable_iff_held.v | 16 +- test_regress/t/t_property_until_implication.v | 6 +- test_regress/t/t_randsequence.v | 5 +- test_regress/t/t_randsequence_func.v | 3 +- test_regress/t/t_randsequence_randjoin.v | 5 +- test_regress/t/t_randsequence_svtests.v | 3 +- .../t/t_sequence_intersect_nevermatch.v | 8 +- test_regress/t/t_sequence_intersect_varlen.v | 13 +- test_regress/t/t_sequence_sexpr_throughout.v | 14 +- test_regress/t/t_sequence_within.v | 37 ++-- test_regress/t/t_sv_cpu.py | 33 ++-- test_regress/t/t_sys_file_scan_delay.v | 2 +- test_regress/t/t_tagged_case.out | 184 +++++++++--------- test_regress/t/t_tagged_case.v | 12 +- test_regress/t/t_tagged_if.out | 16 +- test_regress/t/t_tagged_if.v | 14 +- test_regress/t/t_tagged_union.out | 182 ++++++++--------- test_regress/t/t_tagged_union.v | 6 + test_regress/t/t_vpi_force.v | 9 +- 51 files changed, 473 insertions(+), 477 deletions(-) diff --git a/Changes b/Changes index 1edeaf179..3e4174b62 100644 --- a/Changes +++ b/Changes @@ -5744,7 +5744,7 @@ Verilator 3.104 2003-04-30 **Major:** * Indicate direction of ports with VL_IN and VL_OUT. -* Allow $c32, etc, to specify width of the $c statement for VCS. +* Allow $c32, etc, to specify width of the $c statement. * Numerous performance improvements, worth about 25% **Minor:** diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 7a85c848d..f9502e00c 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -542,7 +542,7 @@ bool V3Options::fileStatNormal(const string& filename) { } string V3Options::fileExists(const string& filename) { - // Surprisingly, for VCS and other simulators, this process + // Surprisingly, for some other simulators, this process // is quite slow; presumably because of re-reading each directory // many times. So we read a whole dir at once and cache it diff --git a/test_regress/t/t_array_packed_sysfunct.v b/test_regress/t/t_array_packed_sysfunct.v index e31d38929..c380fa898 100644 --- a/test_regress/t/t_array_packed_sysfunct.v +++ b/test_regress/t/t_array_packed_sysfunct.v @@ -33,9 +33,7 @@ module t ( initial begin `checkh($dimensions (array_unpk), 3); -`ifndef VCS `checkh($unpacked_dimensions (array_unpk), 2); // IEEE 2009 -`endif `checkh($bits (array_unpk), 2*2*2); `checkh($low (array_unpk), 2); `checkh($high (array_unpk), 3); diff --git a/test_regress/t/t_assert_always_unbounded.v b/test_regress/t/t_assert_always_unbounded.v index 9f41a2627..f408c5885 100644 --- a/test_regress/t/t_assert_always_unbounded.v +++ b/test_regress/t/t_assert_always_unbounded.v @@ -35,7 +35,7 @@ module t ( assert property (@(posedge clk) always [2:$] a_low) else low2_fail_q.push_back(cyc); // a_drop is high then drops at cyc 5 and stays low: deterministic single - // transition, so Verilator and Questa agree on the failing ticks exactly. + // transition, so Verilator and others agree on the failing ticks exactly. assert property (@(posedge clk) always [2:$] a_drop) else drop_fail_q.push_back(cyc); always @(posedge clk) begin @@ -43,12 +43,12 @@ module t ( if (cyc >= 4) a_drop <= 1'b0; if (cyc == 19) begin // Counts pinned to Verilator (NFA per-cycle reject). For all-fail windows - // Questa is one lower (it does not fire the end-of-sim tick); see the sva + // others are one lower (it does not fire the end-of-sim tick); see the sva // lessons "multi-cycle end-of-simulation offset" note. - `checkd(high_fail_q.size(), 0); // Questa: 0 - `checkd(low0_fail_q.size(), 20); // Questa: 19 - `checkd(low2_fail_q.size(), 18); // Questa: 17 - `checkd(drop_fail_q[0], 5); // first fail tick: a_drop sampled low from cyc 5 + `checkd(high_fail_q.size(), 0); + `checkd(low0_fail_q.size(), 20); // All others: 19 + `checkd(low2_fail_q.size(), 18); // All others: 17 + `checkd(drop_fail_q[0], 5); // All others: 6; first fail tick: a_drop sampled low from cyc 5 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_bang_in_seq.v b/test_regress/t/t_assert_bang_in_seq.v index b0c831229..784dd338a 100644 --- a/test_regress/t/t_assert_bang_in_seq.v +++ b/test_regress/t/t_assert_bang_in_seq.v @@ -66,12 +66,12 @@ module t ( end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - `checkd(count_fail1, 66); // Questa: 66 - `checkd(count_fail2, 69); // Questa: 69 - `checkd(count_fail3, 26); // Questa: 26 - `checkd(count_fail4, 66); // Questa: 66 - `checkd(count_fail5, 80); // Questa: 80 - `checkd(count_fail6, 27); // Questa: 27 + `checkd(count_fail1, 66); + `checkd(count_fail2, 69); + `checkd(count_fail3, 26); + `checkd(count_fail4, 66); + `checkd(count_fail5, 80); + `checkd(count_fail6, 27); $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_consec_rep.v b/test_regress/t/t_assert_consec_rep.v index 8a4d87040..206b49886 100644 --- a/test_regress/t/t_assert_consec_rep.v +++ b/test_regress/t/t_assert_consec_rep.v @@ -95,23 +95,23 @@ module t ( end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - `checkd(count_fail1, 5); // Questa: 5 - `checkd(count_fail2, 25); // Questa: 25 - `checkd(count_fail3, 9); // Questa: 9 - `checkd(count_fail4, 49); // Questa: 49 - `checkd(count_fail5, 0); // Questa: 0 - // NFA merge-node range [*M:N] over-counts rejects (Questa: 51); match + `checkd(count_fail1, 5); + `checkd(count_fail2, 25); // One other sim: 19 + `checkd(count_fail3, 9); + `checkd(count_fail4, 49); + `checkd(count_fail5, 0); + // NFA merge-node range [*M:N] over-counts rejects; match // detection is correct, only reject counting is imprecise - `checkd(count_fail6, 59); - `checkd(count_fail7, 51); // Questa: 51 - `checkd(count_fail8, 20); // Questa: 20 + `checkd(count_fail6, 59); // All other sims: 51 + `checkd(count_fail7, 51); + `checkd(count_fail8, 20); // IEEE 1800-2023 16.9.2 permits empty match of [*0]; NFA reports - // rejects on each tick while Questa suppresses (Questa: 20) - `checkd(count_fail9, 49); - `checkd(count_fail10, 59); // Questa: 59 + // rejects on each tick while others suppress + `checkd(count_fail9, 49); // Most others: 20, one other 49 + `checkd(count_fail10, 59); // a[*] ##1 b: NFA treats unbounded [*] as liveness (no reject); - // Questa treats as definite antecedent (Questa: 29) - `checkd(count_fail11, 0); + // Should be definite antecedent + `checkd(count_fail11, 0); // All other sims: 29 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_consec_rep_large.v b/test_regress/t/t_assert_consec_rep_large.v index 359537ce7..4df36f7bc 100644 --- a/test_regress/t/t_assert_consec_rep_large.v +++ b/test_regress/t/t_assert_consec_rep_large.v @@ -40,8 +40,8 @@ module t ( else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); `checkd(count_fail_257, 0); - // Questa: 31 -- pre-existing ~26.5% NFA reject gap on |-> ##1 [*N] - `checkd(count_fail_513, 23); + // Mismatch due to pre-existing ~26.5% NFA reject gap on |-> ##1 [*N] + `checkd(count_fail_513, 23); // All other sims: 31 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_ctl_lock.v b/test_regress/t/t_assert_ctl_lock.v index 59c98a006..c47e3b341 100644 --- a/test_regress/t/t_assert_ctl_lock.v +++ b/test_regress/t/t_assert_ctl_lock.v @@ -5,6 +5,8 @@ // 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\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); `ifdef verilator `define no_optimize(v) $c(v) `else @@ -12,7 +14,7 @@ `endif // verilog_format: on -module t ( /*AUTOARG*/); +module t; logic clk = 0; int imm_fails = 0, conc_fails = 0; logic a = 1'b1; // antecedent always true @@ -58,11 +60,8 @@ module t ( /*AUTOARG*/); end final begin - // Concrete counts cross-checked against Questa 2022.3: imm_fails=5 conc_fails=7. - if (imm_fails != 5 || conc_fails != 7) begin - $display("%%Error: imm_fails=%0d (exp 5) conc_fails=%0d (exp 7)", imm_fails, conc_fails); - $stop; - end + `checkd(imm_fails, 5); + `checkd(conc_fails, 7); // Other sims: 7 or 1 $write("*-* All Finished *-*\n"); end endmodule diff --git a/test_regress/t/t_assert_goto_rep.v b/test_regress/t/t_assert_goto_rep.v index f926a3b5b..1a209444b 100644 --- a/test_regress/t/t_assert_goto_rep.v +++ b/test_regress/t/t_assert_goto_rep.v @@ -75,14 +75,14 @@ module t ( end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - `checkd(count_fail1, 20); // Questa: 20 - `checkd(count_fail2, 25); // Questa: 25 - `checkd(count_fail3, 19); // Questa: 19 - `checkd(count_fail4, 0); // Questa: 0 - `checkd(count_fail5, 20); // Questa: 20 - `checkd(count_fail6, 25); // Questa: 25 - `checkd(count_fail7, 20); // Questa: 20 - `checkd(count_fail8, 20); // Questa: 20 + `checkd(count_fail1, 20); + `checkd(count_fail2, 25); + `checkd(count_fail3, 19); + `checkd(count_fail4, 0); + `checkd(count_fail5, 20); + `checkd(count_fail6, 25); + `checkd(count_fail7, 20); + `checkd(count_fail8, 20); $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_nonconsec_rep.v b/test_regress/t/t_assert_nonconsec_rep.v index 6d759b9c2..c69db8bf9 100644 --- a/test_regress/t/t_assert_nonconsec_rep.v +++ b/test_regress/t/t_assert_nonconsec_rep.v @@ -56,10 +56,10 @@ module t ( end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - `checkd(count_fail1, 34); // Questa: 29 - `checkd(count_fail2, 27); // Questa: 32 - `checkd(count_fail3, 25); // Questa: 29 - `checkd(count_fail4, 0); // Questa: 0 + `checkd(count_fail1, 34); // Other sims: 29, one other: 20 + `checkd(count_fail2, 27); // Other sims: 32, one other: 25 + `checkd(count_fail3, 25); // Other sims: 29, one other: 25 + `checkd(count_fail4, 0); $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_procedural_gated.v b/test_regress/t/t_assert_procedural_gated.v index 9ed88121f..56e38ed41 100644 --- a/test_regress/t/t_assert_procedural_gated.v +++ b/test_regress/t/t_assert_procedural_gated.v @@ -57,9 +57,8 @@ module t ( end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - // Questa 2022.3 golden: count_gated=5, count_ref=12. - `checkd(count_gated, 5); - `checkd(count_ref, 12); + `checkd(count_gated, 5); // Other sims same, one other: 4 + `checkd(count_ref, 12); // Other sims same, one other: 10 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_assert_seq_clocking.v b/test_regress/t/t_assert_seq_clocking.v index b835acb85..9c59d7287 100644 --- a/test_regress/t/t_assert_seq_clocking.v +++ b/test_regress/t/t_assert_seq_clocking.v @@ -44,10 +44,9 @@ module t ( end // Counts read in final (Postponed) to avoid same-timestep races. - // Concrete Verilator counts; Questa: fails_single=17 fails_multi=17 final begin - `checkd(fails_single, 17); - `checkd(fails_multi, 17); + `checkd(fails_single, 17); // Other sims: 0 + `checkd(fails_multi, 17); // Other sims: 0 $write("*-* All Finished *-*\n"); end endmodule diff --git a/test_regress/t/t_constraint_dist_foreach_if.v b/test_regress/t/t_constraint_dist_foreach_if.v index a8eaedb24..5d6f1b2ec 100644 --- a/test_regress/t/t_constraint_dist_foreach_if.v +++ b/test_regress/t/t_constraint_dist_foreach_if.v @@ -8,7 +8,8 @@ // values only within the declared distribution and cover all buckets. // verilog_format: off -`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); +`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 // foreach (a[i]) if (gate) a[i] dist {...} diff --git a/test_regress/t/t_cover_property.v b/test_regress/t/t_cover_property.v index 40089451c..d68118d96 100644 --- a/test_regress/t/t_cover_property.v +++ b/test_regress/t/t_cover_property.v @@ -60,18 +60,17 @@ module t ( // A cover of an implication counts only non-vacuous matches (IEEE // 1800-2023 16.15.2): the antecedent must match. So it is identical to the // corresponding sequence cover, not the vacuous implication value. - `checkd(n_imp_no, n_seq) - `checkd(n_imp_ov, n_seq0) + `checkd(n_imp_no, n_seq); // Other sims: pass, 73 + `checkd(n_imp_ov, n_seq0); // Other sims: pass, 45 // A named-property cover lowers the same implication, so it also counts // non-vacuously (regression guard for the property-inlining path). - `checkd(n_named, n_imp_no) - // Pinned Verilator counts; Questa golden cross-checked. - `checkd(n_imp_no, 28) // Questa: 28 - `checkd(n_imp_ov, 27) // Questa: 27 - `checkd(n_seq, 28) // Questa: 28 - `checkd(n_seq0, 27) // Questa: 27 - `checkd(n_bool, 55) // Questa: 54 - `checkd(n_named, 28) // Questa: 28 + `checkd(n_named, n_imp_no); + `checkd(n_imp_no, 28); + `checkd(n_imp_ov, 27); // Other sims: pass, 73 + `checkd(n_seq, 28); // Other sims: 45, 27 + `checkd(n_seq0, 27); + `checkd(n_bool, 55); // Other sims: pass, 25 + `checkd(n_named, 28); // Other sims: 73, 54, 54 end endmodule diff --git a/test_regress/t/t_cover_sequence.v b/test_regress/t/t_cover_sequence.v index 42f39949e..79516a81c 100644 --- a/test_regress/t/t_cover_sequence.v +++ b/test_regress/t/t_cover_sequence.v @@ -69,22 +69,21 @@ module t ( // Read the counters in 'final', not the clocked block: a same-cycle read of a // cover counter races the cover's increment under --threads (vltmt). Verilator - // counts one more end-of-match than Questa 2022.3 on some forms at the - // simulation boundary; the Questa value is noted per check. + // counts one more end-of-match than others on some forms. final begin `ifdef TEST_VERBOSE $write("simple=%0d clocked=%0d clk_dis=%0d def_dis=%0d range=%0d 2=%0d 3=%0d\n", hit_simple, hit_clocked, hit_clocked_disable, hit_default_disable, hit_consrep_range, hit_consrep_2, hit_consrep_3); `endif - `checkd(hit_simple, 96); // Questa: 95 - `checkd(hit_clocked, 149); // Questa: 149 - `checkd(hit_clocked_disable, 27); // Questa: 27 - `checkd(hit_default_disable, 30); // Questa: 30 - `checkd(hit_consrep_2, 30); // Questa: 29 - `checkd(hit_consrep_3, 14); // Questa: 13 + `checkd(hit_simple, 96); // Other sims: 5, 95 + `checkd(hit_clocked, 149); + `checkd(hit_clocked_disable, 27); + `checkd(hit_default_disable, 30); + `checkd(hit_consrep_2, 30); // Other sims: 29 + `checkd(hit_consrep_3, 14); // Other sims: 13 // a[*2:3] == a[*2] or a[*3] (IEEE 1800-2023 16.9.2) - `checkd(hit_consrep_range, hit_consrep_2 + hit_consrep_3); // 44; Questa: 42 + `checkd(hit_consrep_range, hit_consrep_2 + hit_consrep_3); $write("*-* All Finished *-*\n"); end endmodule diff --git a/test_regress/t/t_forceable_unpacked.v b/test_regress/t/t_forceable_unpacked.v index 5e0048563..3b3cd23a0 100644 --- a/test_regress/t/t_forceable_unpacked.v +++ b/test_regress/t/t_forceable_unpacked.v @@ -5,7 +5,8 @@ // SPDX-License-Identifier: CC0-1.0 // verilog_format: off -`define checkh(g,e) do if ((g) !==(e)) begin $write("%%Error: %s:%0d: got=%x exp=%x\n", `__FILE__,`__LINE__, (g),(e)); $stop; end while(0) +`define stop $stop +`define checkh(g,e) do if ((g) !==(e)) begin $write("%%Error: %s:%0d: got=%x exp=%x\n", `__FILE__,`__LINE__, (g),(e)); `stop; end while(0) `ifdef CMT `define FORCEABLE /*verilator forceable*/ diff --git a/test_regress/t/t_gen_for.v b/test_regress/t/t_gen_for.v index 47d7ba1a7..006b9f83a 100644 --- a/test_regress/t/t_gen_for.v +++ b/test_regress/t/t_gen_for.v @@ -97,8 +97,6 @@ module paramed ( /*AUTOARG*/ // No else endgenerate -`ifndef NC // for(genvar) unsupported -`ifndef ATSIM // for(genvar) unsupported generate // Empty loop body, local genvar for (genvar j = 0; j < 3; j = j + 1) begin @@ -107,8 +105,6 @@ module paramed ( /*AUTOARG*/ for (genvar j = 0; j < 5; j = j + 1) begin end endgenerate -`endif -`endif generate endgenerate diff --git a/test_regress/t/t_gen_intdot.v b/test_regress/t/t_gen_intdot.v index d00c7028a..248616e8e 100644 --- a/test_regress/t/t_gen_intdot.v +++ b/test_regress/t/t_gen_intdot.v @@ -10,30 +10,34 @@ module t ( input clk ); - integer cyc = 0; + integer cyc = 0; - wire out; - reg in; + wire out; + reg in; - Genit g (.clk(clk), .value(in), .result(out)); + Genit g ( + .clk(clk), + .value(in), + .result(out) + ); - always @ (posedge clk) begin + always @(posedge clk) begin //$write("[%0t] cyc==%0d %x %x\n", $time, cyc, in, out); cyc <= cyc + 1; - if (cyc==0) begin + if (cyc == 0) begin // Setup in <= 1'b1; end - else if (cyc==1) begin + else if (cyc == 1) begin in <= 1'b0; end - else if (cyc==2) begin + else if (cyc == 2) begin if (out != 1'b1) $stop; end - else if (cyc==3) begin + else if (cyc == 3) begin if (out != 1'b0) $stop; end - else if (cyc==9) begin + else if (cyc == 9) begin $write("*-* All Finished *-*\n"); $finish; end @@ -41,7 +45,11 @@ module t ( endmodule -module Generate (clk, value, result); +module Generate ( + clk, + value, + result +); input clk; input value; output result; @@ -50,63 +58,73 @@ module Generate (clk, value, result); assign result = Internal; - always @(posedge clk) - Internal <= value; + always @(posedge clk) Internal <= value; endmodule -module Checker (clk, value); +module Checker ( + clk, + value +); input clk, value; always @(posedge clk) begin - $write ("[%0t] value=%h\n", $time, value); + $write("[%0t] value=%h\n", $time, value); end endmodule -module Test (clk, value, result); +module Test ( + clk, + value, + result +); input clk; input value; output result; - Generate gen (clk, value, result); - Checker chk (clk, gen.Internal); + Generate gen ( + clk, + value, + result + ); + Checker chk ( + clk, + gen.Internal + ); endmodule -module Genit (clk, value, result); +module Genit ( + clk, + value, + result +); input clk; input value; output result; -`ifndef ATSIM // else unsupported - `ifndef NC // else unsupported - `ifndef IVERILOG // else unsupported - `define WITH_FOR_GENVAR - `endif - `endif -`endif - -`define WITH_GENERATE + `define WITH_GENERATE `ifdef WITH_GENERATE - `ifndef WITH_FOR_GENVAR genvar i; - `endif generate - for ( - `ifdef WITH_FOR_GENVAR - genvar - `endif - i = 0; i < 1; i = i + 1) - begin : foo - Test tt (clk, value, result); - end + for (genvar i = 0; i < 1; i = i + 1) begin : foo + Test tt ( + clk, + value, + result + ); + end endgenerate `else - Test tt (clk, value, result); + Test tt ( + clk, + value, + result + ); `endif wire Result2 = t.g.foo[0].tt.gen.Internal; // Works - Do not change! - always @ (posedge clk) begin + always @(posedge clk) begin $write("[%0t] Result2 = %x\n", $time, Result2); end diff --git a/test_regress/t/t_inst_signed.v b/test_regress/t/t_inst_signed.v index bd46592dd..bfb24f007 100644 --- a/test_regress/t/t_inst_signed.v +++ b/test_regress/t/t_inst_signed.v @@ -34,9 +34,9 @@ module t ( if (sgn_wide[2:0] != 3'sh7) $stop; if (unsgn_wide[2:0] != 3'h7) $stop; // Simulators differ here. - if (sgn_wide !== 8'sbzzzzz111 // z-extension - NC - && sgn_wide !== 8'sb11111111) - $stop; // sign extension - VCS + if (sgn_wide !== 8'sbzzzzz111 // z-extension - some others + && sgn_wide !== 8'sb11111111) // sign extension - some others + $stop; if (unsgn_wide !== 8'sbzzzzz111 && unsgn_wide !== 8'sb00000111) $stop; cyc <= cyc + 1; if (cyc == 3) begin diff --git a/test_regress/t/t_interface2.v b/test_regress/t/t_interface2.v index e11f71b1f..52c3ac79e 100644 --- a/test_regress/t/t_interface2.v +++ b/test_regress/t/t_interface2.v @@ -12,7 +12,7 @@ module t ( counter_io c1_data(); counter_io c2_data(); - //counter_io c3_data; // IEEE illegal, and VCS doesn't allow non-() as it does with cells + // counter_io c3_data; // IEEE illegal counter_io c3_data(); counter_ansi c1 (.clkm(clk), diff --git a/test_regress/t/t_interface_array_nocolon.v b/test_regress/t/t_interface_array_nocolon.v index a5af54af4..c8015b005 100644 --- a/test_regress/t/t_interface_array_nocolon.v +++ b/test_regress/t/t_interface_array_nocolon.v @@ -37,7 +37,6 @@ module t; initial begin // Check numbering with 0 first - // NC has a bug here if (foos[0].x !== 1'b1) $stop; if (foos[1].x !== 1'b1) $stop; if (foos[2].x !== 1'b0) $stop; diff --git a/test_regress/t/t_interface_virtual_sub_iface_method.v b/test_regress/t/t_interface_virtual_sub_iface_method.v index 6cf486f5b..d5d9a59fe 100644 --- a/test_regress/t/t_interface_virtual_sub_iface_method.v +++ b/test_regress/t/t_interface_virtual_sub_iface_method.v @@ -4,7 +4,8 @@ // SPDX-FileCopyrightText: 2026 PlanV GmbH // SPDX-License-Identifier: CC0-1.0 // verilog_format: off -`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); +`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 interface inner_if; diff --git a/test_regress/t/t_math_pow.v b/test_regress/t/t_math_pow.v index 72a617697..35aab1cb3 100644 --- a/test_regress/t/t_math_pow.v +++ b/test_regress/t/t_math_pow.v @@ -244,9 +244,7 @@ module t ( `checkh(67'h0 ** 21'h0, 67'h1); `checkh(67'sh0 ** 21'sh0, 67'sh1); `checkh(67'h10 ** 21'h0, 67'h1); -`ifndef VCS `checkh(61'h7ab3811219 ** 21'ha6e30, 61'h01ea58c703687e81); -`endif if (cyc==0) begin end else if (cyc==1) begin a <= 67'h0; b <= 67'h0; end else if (cyc==2) begin a <= 67'h0; b <= 67'h3; end diff --git a/test_regress/t/t_math_pow3.v b/test_regress/t/t_math_pow3.v index b591618b3..f0c18f8b6 100644 --- a/test_regress/t/t_math_pow3.v +++ b/test_regress/t/t_math_pow3.v @@ -16,22 +16,21 @@ module t; // verilog_format: off initial begin - // NC=67b6cfc1b29a21 VCS=c1b29a20(wrong) IV=67b6cfc1b29a21 Verilator=67b6cfc1b29a21 $display("15 ** 14 = %0x expect 67b6cfc1b29a21", 64'b1111 ** 64'b1110); - // NC=1 VCS=0 IV=0 Verilator=1 (wrong,fixed) $display("15 **-4'sd2 = %0x expect 0 (per IEEE negative power)", ((-4'd1 ** -4'sd2))); - // NC=1 VCS=0 IV=67b6cfc1b29a21(wrong) Verilator=1 $display("15 ** 14 = %0x expect 1 (LSB 4-bits of 67b6cfc1b29a21)", ((-4'd1 ** -4'd2))); - // NC=1 VCS=0 IV=67b6cfc1b29a21(wrong) Verilator=1 $display("15 ** 14 = %0x expect 1 (LSB 4-bits of 67b6cfc1b29a21)", ((4'd15 ** 4'd14))); - // NC=8765432187654321 VCS=8765432187654000(wrong) IV=8765432187654321 Verilator=8765432187654321 $display("64'big ** 1 = %0x expect %0x", 64'h8765432187654321 ** 1, 64'h8765432187654321); $display("\n"); `checkh( (64'b1111 ** 64'b1110), 64'h67b6cfc1b29a21); +`ifndef NC `checkh( (-4'd1 ** -4'sd2), 4'h0); //bug730 +`endif +`ifndef VCS `checkh( (-4'd1 ** -4'd2), 4'h1); `checkh( (4'd15 ** 4'd14), 4'h1); +`endif `checkh( (64'h8765432187654321 ** 4'h1), 64'h8765432187654321); `checkh((-8'sh3 ** 8'h3) , 8'he5 ); // a**b (-27) @@ -70,13 +69,23 @@ module t; `checkh(( 8'h3 ** -8'sh0), 8'h1 ); // a**0 always 1 `checkh(( 8'sh3 ** -8'sh0), 8'h1 ); // a**0 always 1 - `checkh((-8'sh3 ** -8'sh3), 8'h0 ); // 0 (a<-1) // NCVERILOG bug +`ifndef NC + `checkh((-8'sh3 ** -8'sh3), 8'h0 ); // 0 (a<-1) +`endif +`ifndef IVERILOG +`ifndef QUESTA +`ifndef VCS `checkh((-8'sh1 ** -8'sh2), 8'h1 ); // -1^odd=-1, -1^even=1 `checkh((-8'sh1 ** -8'sh3), 8'hff); // -1^odd=-1, -1^even=1 -// `checkh(( 8'h0 ** -8'sh3), 8'hx ); // x // NCVERILOG bug +`endif +`endif +`endif +// `checkh(( 8'h0 ** -8'sh3), 8'hx ); // x `checkh(( 8'h1 ** -8'sh3), 8'h1 ); // 1**b always 1 - `checkh(( 8'h3 ** -8'sh3), 8'h0 ); // 0 // NCVERILOG bug - `checkh(( 8'sh3 ** -8'sh3), 8'h0 ); // 0 // NCVERILOG bug +`ifndef NC + `checkh(( 8'h3 ** -8'sh3), 8'h0 ); // 0 + `checkh(( 8'sh3 ** -8'sh3), 8'h0 ); // 0 +`endif if (fail) $stop; diff --git a/test_regress/t/t_math_signed4.v b/test_regress/t/t_math_signed4.v index bb5d83602..05b7e6f7c 100644 --- a/test_regress/t/t_math_signed4.v +++ b/test_regress/t/t_math_signed4.v @@ -97,11 +97,7 @@ module t; // bug754 w5_u = 4'sb0010 << -2'sd1; // << 3 -`ifdef VCS - `checkh(w5_u, 5'b00000); // VCS E-2014.03 bug -`else - `checkh(w5_u, 5'b10000); // VCS E-2014.03 bug -`endif + `checkh(w5_u, 5'b10000); w5_u = 4'sb1000 << 0; // Sign extends `checkh(w5_u, 5'b11000); diff --git a/test_regress/t/t_math_signed5.v b/test_regress/t/t_math_signed5.v index b87b854f2..aa9a68d79 100644 --- a/test_regress/t/t_math_signed5.v +++ b/test_regress/t/t_math_signed5.v @@ -47,11 +47,7 @@ module t ( /*AUTOARG*/ initial begin // verilator lint_off STMTDLY #1; -`ifdef VCS // I-2014.03 - `checkh({a, b, c, d, e, f, g}, 7'b1101111); -`else `checkh({a, b, c, d, e, f, g}, 7'b1101011); -`endif //====================================================================== @@ -76,20 +72,11 @@ module t ( /*AUTOARG*/ w4_u = ((5'b0 == (5'sb11111 >>> 3'd7))); // Exp 0 Vlt 0 `checkh(w4_u, 4'b0001); w4_u = ((5'b01111 == (5'sb11111 / 5'sd2))); // Strength-reduces to >>> -`ifdef VCS // I-2014.03 - `checkh(w4_u, 4'b0000); // Wrong, gets 5'b0==..., unsigned does not propagate -`else - `checkh(w4_u, 4'b0001); // NC-Verilog, Modelsim, XSim, ... -`endif + `checkh(w4_u, 4'b0001); - // Does == sign propagate from lhs to rhs? Yes, but not in VCS + // Does == sign propagate from lhs to rhs? w4_u = ((5'b01010 == (5'sb11111 / 5'sd3))); // Exp 0 Vlt 0 // Must be signed result (-1/3) to make this result zero -`ifdef VCS // I-2014.03 - `checkh(w4_u, 4'b0000); // Wrong, gets 5'b0==..., unsigned does not propagate - // Somewhat questionable, as spec says division signed depends on only LHS and RHS, however differs from others -`else - `checkh(w4_u, 4'b0001); // NC-Verilog, Modelsim, XSim, ... -`endif + `checkh(w4_u, 4'b0001); w4_u = (1'b0+(5'sb11111 >>> 3'd7)); // Exp 00000 Vlt 000000 Actually the signedness of result does NOT matter `checkh(w4_u, 4'b0000); diff --git a/test_regress/t/t_math_svl2.v b/test_regress/t/t_math_svl2.v index b6049d112..1d62f7006 100644 --- a/test_regress/t/t_math_svl2.v +++ b/test_regress/t/t_math_svl2.v @@ -19,15 +19,11 @@ module t ( if ('1 !== {66{1'b1}}) $stop; if ('x !== {66{1'bx}}) $stop; if ('z !== {66{1'bz}}) $stop; -`ifndef NC // NC-Verilog 5.50-s09 chokes on this test if ("\v" != 8'd11) $stop; if ("\f" != 8'd12) $stop; if ("\a" != 8'd7) $stop; if ("\x9a" != 8'h9a) $stop; if ("\xf1" != 8'hf1) $stop; -`endif - end - if (cyc == 8) begin end if (cyc == 9) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_modport_export_task.v b/test_regress/t/t_modport_export_task.v index 848c05ccf..0016f7a3d 100644 --- a/test_regress/t/t_modport_export_task.v +++ b/test_regress/t/t_modport_export_task.v @@ -5,7 +5,8 @@ // SPDX-License-Identifier: CC0-1.0 // verilog_format: off -`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0) +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0) // verilog_format: on interface bus_if; diff --git a/test_regress/t/t_param_first.v b/test_regress/t/t_param_first.v index eea84e2c3..6ded92a93 100644 --- a/test_regress/t/t_param_first.v +++ b/test_regress/t/t_param_first.v @@ -63,12 +63,11 @@ module t ( parameter THREE_2WIDE = 2'b11; parameter ALSO_THREE_WIDE = THREE_BITS_WIDE; parameter THREEPP_32_WIDE = 2 * 8 * 2 + 3; - parameter THREEPP_3_WIDE = 3'd4 * 3'd4 * 3'd2 + 3'd3; // Yes folks VCS says 3 bits wide + parameter THREEPP_3_WIDE = 3'd4 * 3'd4 * 3'd2 + 3'd3; // Width propagation doesn't care about LHS vs RHS // But the width of a RHS/LHS on a upper node does affect lower nodes; // Thus must double-descend in width analysis. - // VCS 7.0.1 is broken on this test! parameter T10 = (3'h7 + 3'h7) + 4'h0; //initial if (T10!==4'd14) $stop; parameter T11 = 4'h0 + (3'h7 + 3'h7); //initial if (T11!==4'd14) $stop; diff --git a/test_regress/t/t_prop_always.v b/test_regress/t/t_prop_always.v index 52cfea02c..f389a3a8c 100644 --- a/test_regress/t/t_prop_always.v +++ b/test_regress/t/t_prop_always.v @@ -43,11 +43,9 @@ module t ( // Constant-false: every attempt fails. assert property (@(posedge clk) always [0:3] a_low) - ; else low_bounded_fail_q.push_back(cyc); assert property (@(posedge clk) always [0:0] a_low) - ; else low_degenerate_fail_q.push_back(cyc); // CRC-driven random input: window [cyc..cyc+3] of a_rand. @@ -76,21 +74,21 @@ module t ( crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; if (cyc == 19) begin // Constant-true window [0:3]: K=0..16 succeed at cyc K+3 = 3..19. - `checkd(high_bounded_pass_q.size(), 17); - `checkd(high_bounded_pass_q[0], 3); + `checkd(high_bounded_pass_q.size(), 17); // Other sims: 16 + `checkd(high_bounded_pass_q[0], 3); // Other sims: 4 `checkd(high_bounded_pass_q[$], 19); // Degenerate [0:0]: K=0..19 succeed at cyc K = 0..19. - `checkd(high_degenerate_pass_q.size(), 20); - `checkd(high_degenerate_pass_q[0], 0); + `checkd(high_degenerate_pass_q.size(), 20); // Other sims: 19 + `checkd(high_degenerate_pass_q[0], 0); // Other sims: 0, 1 `checkd(high_degenerate_pass_q[$], 19); // Constant-false: every attempt fails immediately. - `checkd(low_bounded_fail_q.size(), 20); - `checkd(low_degenerate_fail_q.size(), 20); - // CRC + disable streams: counts pinned (cross-checked against Questa). + `checkd(low_bounded_fail_q.size(), 20); // Other sims: 19 + `checkd(low_degenerate_fail_q.size(), 20); // Other sims: 19 + // CRC + disable streams `checkd(rand_bounded_pass_q.size(), 0); - `checkd(rand_bounded_fail_q.size(), 20); + `checkd(rand_bounded_fail_q.size(), 20); // Other sims: 19, 11 `checkd(disable_bounded_pass_q.size(), 0); - `checkd(disable_bounded_fail_q.size(), 13); + `checkd(disable_bounded_fail_q.size(), 13); // Other sims: 5, 6 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_prop_followed_by.v b/test_regress/t/t_prop_followed_by.v index c1e5778a6..6cc11045a 100644 --- a/test_regress/t/t_prop_followed_by.v +++ b/test_regress/t/t_prop_followed_by.v @@ -4,11 +4,10 @@ // SPDX-FileCopyrightText: 2026 PlanV GmbH // SPDX-License-Identifier: CC0-1.0 -`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: 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 module t ( input clk @@ -54,13 +53,11 @@ module t ( cyc <= cyc + 1; crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; if (cyc == 32) begin - // Counts are deterministic for this CRC seed. Questa reference run - // (IEEE 1800-2023 16.12.9) reports ovl=28, novl=19, impl=9, nimp=0; the // ovl/novl deltas vs Verilator are 1-cycle preponed-sampling differences. $display("ovl=%0d novl=%0d impl=%0d nimp=%0d wide=%0d", ovl_f, novl_f, impl_f, nimp_f, wide_f); - `checkd(ovl_f, 29); - `checkd(novl_f, 20); + `checkd(ovl_f, 29); // Other sims: 28, one other sim: 5 + `checkd(novl_f, 20); // Other sims: 19 `checkd(impl_f, 9); `checkd(nimp_f, 0); `checkd(wide_f, 0); diff --git a/test_regress/t/t_property_accept_reject_on.v b/test_regress/t/t_property_accept_reject_on.v index 1affe217c..080af892c 100644 --- a/test_regress/t/t_property_accept_reject_on.v +++ b/test_regress/t/t_property_accept_reject_on.v @@ -94,16 +94,16 @@ module t ( end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - `checkd(count_fail1, 28); // Questa: 14 - `checkd(count_fail2, 64); // Questa: 64 - `checkd(count_fail3, 28); // Questa: 14 - `checkd(count_fail4, 64); // Questa: 64 - `checkd(count_fail5, 45); // Questa: 31 - `checkd(count_fail6, 64); // Questa: 59 - `checkd(count_fail7, 28); // Questa: 14 - `checkd(count_fail8, 13); // Questa: 10 - `checkd(count_fail9, 28); // Questa: 14 - `checkd(count_fail10, 28); // Questa: 14 + `checkd(count_fail1, 28); // Other sims: 14, one other: 15 + `checkd(count_fail2, 64); // One other sim: 66 + `checkd(count_fail3, 28); // Other sims: 14 + `checkd(count_fail4, 64); + `checkd(count_fail5, 45); // Other sims: 31, one other: 32 + `checkd(count_fail6, 64); // Other sims: 59, one other: 60 + `checkd(count_fail7, 28); // Other sims: 14, one other: 15 + `checkd(count_fail8, 13); // Other sims: 10 + `checkd(count_fail9, 28); // Other sims: 14, one other: 15 + `checkd(count_fail10, 28); // Other sims: 14 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_property_disable_iff_held.v b/test_regress/t/t_property_disable_iff_held.v index 04530fc6e..37fa06461 100644 --- a/test_regress/t/t_property_disable_iff_held.v +++ b/test_regress/t/t_property_disable_iff_held.v @@ -46,20 +46,12 @@ module t ( cyc <= cyc + 1; crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; if (cyc == 99) begin - `checkd(n_held_assert, 0); // Questa: 0 - `checkd(n_held_cover, 0); // Questa: 0 - `checkd(n_ctrl_assert, 58); // Questa: 58 - `checkd(n_ctrl_cover, 26); // Questa: 26 + `checkd(n_held_assert, 0); + `checkd(n_held_cover, 0); + `checkd(n_ctrl_assert, 58); + `checkd(n_ctrl_cover, 26); // Others: 26, One other: 0 $write("*-* All Finished *-*\n"); $finish; end end endmodule - -`ifndef VERILATOR -module wrap; - logic clk = 0; - always #5 clk = ~clk; - t inst (.clk(clk)); -endmodule -`endif diff --git a/test_regress/t/t_property_until_implication.v b/test_regress/t/t_property_until_implication.v index 6023ac501..2f345677f 100644 --- a/test_regress/t/t_property_until_implication.v +++ b/test_regress/t/t_property_until_implication.v @@ -50,12 +50,12 @@ module t ( crc <= 64'h5aef0c8d_d70a4497; end else if (cyc == 99) begin - // Counts reflect NFA per-cycle reject aggregation, not Questa's + // Counts reflect NFA per-cycle reject aggregation, not some other sim's // per-attempt action_block firing; the two differ by a small constant // (see PR description for the model gap). Test is a regression for // "no internal error on `until` as |=> consequent" (issue #7548). - `checkd(fail_nonoverlap, 7); - `checkd(fail_overlap, 22); + `checkd(fail_nonoverlap, 7); // Other sims: 8, one other: 7 + `checkd(fail_overlap, 22); // Other sims: 24, one other: 22 $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_randsequence.v b/test_regress/t/t_randsequence.v index 739178d02..7d00d9e15 100644 --- a/test_regress/t/t_randsequence.v +++ b/test_regress/t/t_randsequence.v @@ -7,8 +7,9 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // verilog_format: off -`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); -`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); $stop; end while(0); +`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); +`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0); `define check_within_30_percent(gotv,val) `check_range((gotv), (val) * 70 / 100, (val) * 130 / 100) // verilog_format: on diff --git a/test_regress/t/t_randsequence_func.v b/test_regress/t/t_randsequence_func.v index ac06220bc..2374251af 100644 --- a/test_regress/t/t_randsequence_func.v +++ b/test_regress/t/t_randsequence_func.v @@ -7,7 +7,8 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // verilog_format: off -`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); +`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 module t; diff --git a/test_regress/t/t_randsequence_randjoin.v b/test_regress/t/t_randsequence_randjoin.v index 47bd297c2..d0d2febcd 100644 --- a/test_regress/t/t_randsequence_randjoin.v +++ b/test_regress/t/t_randsequence_randjoin.v @@ -7,8 +7,9 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // verilog_format: off -`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); -`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); $stop; end while(0); +`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); +`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0); `define check_within_30_percent(gotv,val) `check_range((gotv), (val) * 70 / 100, (val) * 130 / 100) // verilog_format: on diff --git a/test_regress/t/t_randsequence_svtests.v b/test_regress/t/t_randsequence_svtests.v index dd087d63b..b4c549148 100644 --- a/test_regress/t/t_randsequence_svtests.v +++ b/test_regress/t/t_randsequence_svtests.v @@ -4,7 +4,8 @@ // SPDX-License-Identifier: ISC // verilog_format: off -`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); +`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 module t; diff --git a/test_regress/t/t_sequence_intersect_nevermatch.v b/test_regress/t/t_sequence_intersect_nevermatch.v index 17887e184..38abde53a 100644 --- a/test_regress/t/t_sequence_intersect_nevermatch.v +++ b/test_regress/t/t_sequence_intersect_nevermatch.v @@ -5,7 +5,8 @@ // SPDX-License-Identifier: CC0-1.0 // verilog_format: off -`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); +`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 module t ( @@ -51,7 +52,8 @@ module t ( else f_dis <= f_dis + 1; final begin - `checkd(f_fix, 0); // Questa: 0 - `checkd(f_dis, 0); // Questa: 0 + // TODO need better non-zero test + `checkd(f_fix, 0); + `checkd(f_dis, 0); end endmodule diff --git a/test_regress/t/t_sequence_intersect_varlen.v b/test_regress/t/t_sequence_intersect_varlen.v index 402560af3..72eaa8601 100644 --- a/test_regress/t/t_sequence_intersect_varlen.v +++ b/test_regress/t/t_sequence_intersect_varlen.v @@ -5,7 +5,8 @@ // SPDX-License-Identifier: CC0-1.0 // verilog_format: off -`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); +`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 module t ( @@ -25,7 +26,7 @@ module t ( int f_collapse = 0; int f_over = 0; - always_ff @(posedge clk) begin + always @(posedge clk) begin cyc <= cyc + 1; crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; if (cyc == 99) begin @@ -63,9 +64,9 @@ module t ( else f_over <= f_over + 1; final begin - `checkd(f_var, 7); // Questa: 7 - `checkd(f_ieee, 41); // Questa: 41 - `checkd(f_collapse, 0); // Questa: 0 - `checkd(f_over, 84); // Questa: 84 + `checkd(f_var, 7); + `checkd(f_ieee, 41); + `checkd(f_collapse, 0); + `checkd(f_over, 84); end endmodule diff --git a/test_regress/t/t_sequence_sexpr_throughout.v b/test_regress/t/t_sequence_sexpr_throughout.v index a2b17ef46..8b9fe3cfa 100644 --- a/test_regress/t/t_sequence_sexpr_throughout.v +++ b/test_regress/t/t_sequence_sexpr_throughout.v @@ -74,7 +74,7 @@ module t ( // by the throughout-drop check. cover property (@(posedge clk) a throughout (b ##1 c)); - always_ff @(posedge clk) begin + always @(posedge clk) begin `ifdef TEST_VERBOSE $write("[%0t] cyc==%0d crc=%x cond=%b a=%b b=%b c=%b\n", $time, cyc, crc, cond, a, b, c); @@ -85,15 +85,15 @@ module t ( crc <= 64'h5aef0c8d_d70a4497; end else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); - `checkd(count_fail1, 28); // Questa: 28 - `checkd(count_fail2, 33); // Questa: 33 - `checkd(count_fail3, 31); // Questa: 31 - `checkd(count_fail4, 35); // Questa: 35 + `checkd(count_fail1, 28); + `checkd(count_fail2, 33); + `checkd(count_fail3, 31); + `checkd(count_fail4, 35); // count_fail5: NFA undercounts by 12; throughout+temporal-and first-step // rejection is a known limitation of the SAnd combiner architecture // (propagating isTopLevelStep causes double-counting; fix is future work). - `checkd(count_fail5, 25); // Questa: 36 - `checkd(count_fail6, 33); // Questa: 33 + `checkd(count_fail5, 25); // All other sims: 36 + `checkd(count_fail6, 33); $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_sequence_within.v b/test_regress/t/t_sequence_within.v index dcffaae56..e8d73ad61 100644 --- a/test_regress/t/t_sequence_within.v +++ b/test_regress/t/t_sequence_within.v @@ -11,9 +11,6 @@ // verilog_format: on // IEEE 1800-2023 16.9.10: seq1 within seq2 -// CRC-driven random stimulus. Each property has a counter; at cyc==99 we -// `checkd` against Verilator's actual count and record the Questa golden -// value in a trailing comment for cross-simulator reference. module t ( input clk @@ -93,7 +90,7 @@ module t ( initial $assertvacuousoff; - always_ff @(posedge clk) begin + always @(posedge clk) begin cyc <= cyc + 1; crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; @@ -103,30 +100,20 @@ module t ( else if (cyc == 99) begin `checkh(crc, 64'hc77bb9b3784ea091); // p1/p2/p5 use |->; the NFA currently fires the pass action on - // vacuous passes too, so counts are inflated vs. Questa. Pre-existing + // vacuous passes too, so counts are inflated vs. others. Pre-existing // engine-wide behavior, not within-specific. - `checkd(count_p1, 23); // Questa: 23 - `checkd(count_p2, 44); // Questa: 44 - `checkd(count_p3, 25); // Questa: 20 - `checkd(count_p4, 23); // Questa: 22 - `checkd(count_p5, 26); // Questa: 26 - `checkd(count_p6, 21); // Questa: 16 - `checkd(count_p7, 15); // Questa: 9 - `checkd(count_p8, 15); // Questa: 4 - `checkd(count_p9, 15); // Questa: 10 - `checkd(count_p10, 23); // Questa: 15 + `checkd(count_p1, 23); // Other sims: 23, or 16 + `checkd(count_p2, 44); // Other sims: 44, or 21 + `checkd(count_p3, 25); // Other sims: 20 + `checkd(count_p4, 23); // Other sims: 22 + `checkd(count_p5, 26); + `checkd(count_p6, 21); // Other sims: 16 + `checkd(count_p7, 15); // Other sims: 9 + `checkd(count_p8, 15); // Other sims: 4 + `checkd(count_p9, 15); // Other sims: 10 + `checkd(count_p10, 23); // Other sims: 15 $write("*-* All Finished *-*\n"); $finish; end end endmodule - -// Harness for stand-alone simulators (e.g. QuestaSim). Verilator uses -// test_regress's built-in clock shell and ignores this module. -`ifndef VERILATOR -module wrap; - logic clk = 0; - always #5 clk = ~clk; - t inst (.clk(clk)); -endmodule -`endif diff --git a/test_regress/t/t_sv_cpu.py b/test_regress/t/t_sv_cpu.py index dcc41dab2..0eb062f2a 100755 --- a/test_regress/t/t_sv_cpu.py +++ b/test_regress/t/t_sv_cpu.py @@ -14,23 +14,22 @@ test.scenarios('simulator') # 22-Mar-2012: Modifications for this test contributed by Jeremy Bennett, # Embecosm. -test.compile( - # Taken from the original VCS command line. - v_flags2=[ - "t/t_sv_cpu_code/timescale.sv", "t/t_sv_cpu_code/program_h.sv", - "t/t_sv_cpu_code/pads_h.sv", "t/t_sv_cpu_code/ports_h.sv", "t/t_sv_cpu_code/pinout_h.sv", - "t/t_sv_cpu_code/genbus_if.sv", "t/t_sv_cpu_code/pads_if.sv", "t/t_sv_cpu_code/adrdec.sv", - "t/t_sv_cpu_code/pad_gpio.sv", "t/t_sv_cpu_code/pad_vdd.sv", "t/t_sv_cpu_code/pad_gnd.sv", - "t/t_sv_cpu_code/pads.sv", "t/t_sv_cpu_code/ports.sv", "t/t_sv_cpu_code/ac_dig.sv", - "t/t_sv_cpu_code/ac_ana.sv", "t/t_sv_cpu_code/ac.sv", "t/t_sv_cpu_code/cpu.sv", - "t/t_sv_cpu_code/chip.sv" - ], - vcs_flags2=["-R -sverilog +memcbk -y t/t_sv_cpu_code +libext+.sv+ +incdir+t/t_sv_cpu_code"], - verilator_flags2=[ - "-y t/t_sv_cpu_code +libext+.sv+ +incdir+t/t_sv_cpu_code --top-module t", - "--timescale-override 1ns/1ps" - ], - iv_flags2=["-yt/t_sv_cpu_code -It/t_sv_cpu_code -Y.sv"]) +test.compile(v_flags2=[ + "t/t_sv_cpu_code/timescale.sv", "t/t_sv_cpu_code/program_h.sv", "t/t_sv_cpu_code/pads_h.sv", + "t/t_sv_cpu_code/ports_h.sv", "t/t_sv_cpu_code/pinout_h.sv", "t/t_sv_cpu_code/genbus_if.sv", + "t/t_sv_cpu_code/pads_if.sv", "t/t_sv_cpu_code/adrdec.sv", "t/t_sv_cpu_code/pad_gpio.sv", + "t/t_sv_cpu_code/pad_vdd.sv", "t/t_sv_cpu_code/pad_gnd.sv", "t/t_sv_cpu_code/pads.sv", + "t/t_sv_cpu_code/ports.sv", "t/t_sv_cpu_code/ac_dig.sv", "t/t_sv_cpu_code/ac_ana.sv", + "t/t_sv_cpu_code/ac.sv", "t/t_sv_cpu_code/cpu.sv", "t/t_sv_cpu_code/chip.sv" +], + vcs_flags2=[ + "-R -sverilog +memcbk -y t/t_sv_cpu_code +libext+.sv+ +incdir+t/t_sv_cpu_code" + ], + verilator_flags2=[ + "-y t/t_sv_cpu_code +libext+.sv+ +incdir+t/t_sv_cpu_code --top-module t", + "--timescale-override 1ns/1ps" + ], + iv_flags2=["-yt/t_sv_cpu_code -It/t_sv_cpu_code -Y.sv"]) test.execute() diff --git a/test_regress/t/t_sys_file_scan_delay.v b/test_regress/t/t_sys_file_scan_delay.v index f1db0a34e..efb4e4659 100644 --- a/test_regress/t/t_sys_file_scan_delay.v +++ b/test_regress/t/t_sys_file_scan_delay.v @@ -6,7 +6,7 @@ // verilog_format: off `define stop $stop -`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0); +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); // verilog_format: on module t; diff --git a/test_regress/t/t_tagged_case.out b/test_regress/t/t_tagged_case.out index e7965a85d..edec504d1 100644 --- a/test_regress/t/t_tagged_case.out +++ b/test_regress/t/t_tagged_case.out @@ -11,142 +11,142 @@ %Error-UNSUPPORTED: t/t_tagged_case.v:65:5: Unsupported: void (for tagged unions) 65 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:71:5: Unsupported: void (for tagged unions) - 71 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_case.v:73:5: Unsupported: void (for tagged unions) + 73 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:80:5: Unsupported: void (for tagged unions) - 80 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_case.v:82:5: Unsupported: void (for tagged unions) + 82 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:87:5: Unsupported: void (for tagged unions) - 87 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_case.v:89:5: Unsupported: void (for tagged unions) + 89 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:93:5: Unsupported: void (for tagged unions) - 93 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_case.v:95:5: Unsupported: void (for tagged unions) + 95 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:102:5: Unsupported: void (for tagged unions) - 102 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_case.v:104:5: Unsupported: void (for tagged unions) + 104 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:126:9: Unsupported: tagged union - 126 | v = tagged Invalid; +%Error-UNSUPPORTED: t/t_tagged_case.v:128:9: Unsupported: tagged union + 128 | v = tagged Invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:128:5: Unsupported: case matches (for tagged union) - 128 | case (v) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:130:5: Unsupported: case matches (for tagged union) + 130 | case (v) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:129:7: Unsupported: tagged union - 129 | tagged Invalid : result = 1; +%Error-UNSUPPORTED: t/t_tagged_case.v:131:7: Unsupported: tagged union + 131 | tagged Invalid : result = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:130:7: Unsupported: tagged pattern - 130 | tagged Valid .n : result = n; +%Error-UNSUPPORTED: t/t_tagged_case.v:132:7: Unsupported: tagged pattern + 132 | tagged Valid .n : result = n; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:130:20: Unsupported: pattern variable - 130 | tagged Valid .n : result = n; +%Error-UNSUPPORTED: t/t_tagged_case.v:132:20: Unsupported: pattern variable + 132 | tagged Valid .n : result = n; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:135:9: Unsupported: tagged union - 135 | v = tagged Valid (123); +%Error-UNSUPPORTED: t/t_tagged_case.v:137:9: Unsupported: tagged union + 137 | v = tagged Valid (123); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:137:5: Unsupported: case matches (for tagged union) - 137 | case (v) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:139:5: Unsupported: case matches (for tagged union) + 139 | case (v) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:138:7: Unsupported: tagged union - 138 | tagged Invalid : result = -1; +%Error-UNSUPPORTED: t/t_tagged_case.v:140:7: Unsupported: tagged union + 140 | tagged Invalid : result = -1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:139:7: Unsupported: tagged pattern - 139 | tagged Valid .n : result = n; +%Error-UNSUPPORTED: t/t_tagged_case.v:141:7: Unsupported: tagged pattern + 141 | tagged Valid .n : result = n; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:139:20: Unsupported: pattern variable - 139 | tagged Valid .n : result = n; +%Error-UNSUPPORTED: t/t_tagged_case.v:141:20: Unsupported: pattern variable + 141 | tagged Valid .n : result = n; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:144:10: Unsupported: tagged union - 144 | wt = tagged Wide60 (60'hFEDCBA987654321); +%Error-UNSUPPORTED: t/t_tagged_case.v:146:10: Unsupported: tagged union + 146 | wt = tagged Wide60 (60'hFEDCBA987654321); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:146:5: Unsupported: case matches (for tagged union) - 146 | case (wt) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:148:5: Unsupported: case matches (for tagged union) + 148 | case (wt) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:147:7: Unsupported: tagged union - 147 | tagged Invalid : wide60_result = 0; +%Error-UNSUPPORTED: t/t_tagged_case.v:149:7: Unsupported: tagged union + 149 | tagged Invalid : wide60_result = 0; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:148:7: Unsupported: tagged pattern - 148 | tagged Wide60 .w : wide60_result = w; +%Error-UNSUPPORTED: t/t_tagged_case.v:150:7: Unsupported: tagged pattern + 150 | tagged Wide60 .w : wide60_result = w; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:148:21: Unsupported: pattern variable - 148 | tagged Wide60 .w : wide60_result = w; +%Error-UNSUPPORTED: t/t_tagged_case.v:150:21: Unsupported: pattern variable + 150 | tagged Wide60 .w : wide60_result = w; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:154:10: Unsupported: tagged union - 154 | wt = tagged Wide90 (90'hDE_ADBEEFCA_FEBABE12_3456); +%Error-UNSUPPORTED: t/t_tagged_case.v:158:10: Unsupported: tagged union + 158 | wt = tagged Wide90 (90'hDE_ADBEEFCA_FEBABE12_3456); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:156:5: Unsupported: case matches (for tagged union) - 156 | case (wt) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:160:5: Unsupported: case matches (for tagged union) + 160 | case (wt) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:157:7: Unsupported: tagged union - 157 | tagged Invalid : wide90_result = 0; +%Error-UNSUPPORTED: t/t_tagged_case.v:161:7: Unsupported: tagged union + 161 | tagged Invalid : wide90_result = 0; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:158:7: Unsupported: tagged pattern - 158 | tagged Wide90 .w : wide90_result = w; +%Error-UNSUPPORTED: t/t_tagged_case.v:162:7: Unsupported: tagged pattern + 162 | tagged Wide90 .w : wide90_result = w; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:158:21: Unsupported: pattern variable - 158 | tagged Wide90 .w : wide90_result = w; +%Error-UNSUPPORTED: t/t_tagged_case.v:162:21: Unsupported: pattern variable + 162 | tagged Wide90 .w : wide90_result = w; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:164:10: Unsupported: tagged union - 164 | wt = tagged Byte8NonZeroLSB (8'hA5); +%Error-UNSUPPORTED: t/t_tagged_case.v:170:10: Unsupported: tagged union + 170 | wt = tagged Byte8NonZeroLSB (8'hA5); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:166:5: Unsupported: case matches (for tagged union) - 166 | case (wt) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:172:5: Unsupported: case matches (for tagged union) + 172 | case (wt) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:167:7: Unsupported: tagged pattern - 167 | tagged Byte8NonZeroLSB .b : result = b; +%Error-UNSUPPORTED: t/t_tagged_case.v:173:7: Unsupported: tagged pattern + 173 | tagged Byte8NonZeroLSB .b : result = b; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:167:30: Unsupported: pattern variable - 167 | tagged Byte8NonZeroLSB .b : result = b; +%Error-UNSUPPORTED: t/t_tagged_case.v:173:30: Unsupported: pattern variable + 173 | tagged Byte8NonZeroLSB .b : result = b; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:173:10: Unsupported: tagged union - 173 | wt = tagged Word32LittleEndian (32'hDEADBEEF); +%Error-UNSUPPORTED: t/t_tagged_case.v:179:10: Unsupported: tagged union + 179 | wt = tagged Word32LittleEndian (32'hDEADBEEF); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:175:5: Unsupported: case matches (for tagged union) - 175 | case (wt) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:181:5: Unsupported: case matches (for tagged union) + 181 | case (wt) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:176:7: Unsupported: tagged pattern - 176 | tagged Word32LittleEndian .w : result = w; +%Error-UNSUPPORTED: t/t_tagged_case.v:182:7: Unsupported: tagged pattern + 182 | tagged Word32LittleEndian .w : result = w; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:176:33: Unsupported: pattern variable - 176 | tagged Word32LittleEndian .w : result = w; +%Error-UNSUPPORTED: t/t_tagged_case.v:182:33: Unsupported: pattern variable + 182 | tagged Word32LittleEndian .w : result = w; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:182:10: Unsupported: tagged union - 182 | at = tagged Arr '{10, 20, 30, 40}; +%Error-UNSUPPORTED: t/t_tagged_case.v:188:10: Unsupported: tagged union + 188 | at = tagged Arr '{10, 20, 30, 40}; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:184:5: Unsupported: case matches (for tagged union) - 184 | case (at) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:190:5: Unsupported: case matches (for tagged union) + 190 | case (at) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:185:7: Unsupported: tagged union - 185 | tagged Invalid : result = -1; +%Error-UNSUPPORTED: t/t_tagged_case.v:191:7: Unsupported: tagged union + 191 | tagged Invalid : result = -1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:186:7: Unsupported: tagged pattern - 186 | tagged Scalar .s : result = s; +%Error-UNSUPPORTED: t/t_tagged_case.v:192:7: Unsupported: tagged pattern + 192 | tagged Scalar .s : result = s; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:186:21: Unsupported: pattern variable - 186 | tagged Scalar .s : result = s; +%Error-UNSUPPORTED: t/t_tagged_case.v:192:21: Unsupported: pattern variable + 192 | tagged Scalar .s : result = s; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:187:7: Unsupported: tagged pattern - 187 | tagged Arr .a : result = a[0] + a[1] + a[2] + a[3]; +%Error-UNSUPPORTED: t/t_tagged_case.v:193:7: Unsupported: tagged pattern + 193 | tagged Arr .a : result = a[0] + a[1] + a[2] + a[3]; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:187:18: Unsupported: pattern variable - 187 | tagged Arr .a : result = a[0] + a[1] + a[2] + a[3]; +%Error-UNSUPPORTED: t/t_tagged_case.v:193:18: Unsupported: pattern variable + 193 | tagged Arr .a : result = a[0] + a[1] + a[2] + a[3]; | ^ -%Error-UNSUPPORTED: t/t_tagged_case.v:192:13: Unsupported: tagged union - 192 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); +%Error-UNSUPPORTED: t/t_tagged_case.v:198:13: Unsupported: tagged union + 198 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:192:25: Unsupported: tagged union - 192 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); +%Error-UNSUPPORTED: t/t_tagged_case.v:198:25: Unsupported: tagged union + 198 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:194:5: Unsupported: case matches (for tagged union) - 194 | case (instr) matches +%Error-UNSUPPORTED: t/t_tagged_case.v:200:5: Unsupported: case matches (for tagged union) + 200 | case (instr) matches | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:195:7: Unsupported: tagged pattern - 195 | tagged Add .* : result = -1; +%Error-UNSUPPORTED: t/t_tagged_case.v:201:7: Unsupported: tagged pattern + 201 | tagged Add .* : result = -1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_case.v:195:18: Unsupported: pattern wildcard - 195 | tagged Add .* : result = -1; +%Error-UNSUPPORTED: t/t_tagged_case.v:201:18: Unsupported: pattern wildcard + 201 | tagged Add .* : result = -1; | ^~ -%Error-UNSUPPORTED: t/t_tagged_case.v:196:7: Unsupported: tagged union - 196 | tagged Jmp (tagged JmpU .a) : result = a; +%Error-UNSUPPORTED: t/t_tagged_case.v:202:7: Unsupported: tagged union + 202 | tagged Jmp (tagged JmpU .a) : result = a; | ^~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_tagged_case.v b/test_regress/t/t_tagged_case.v index a2ebf02fc..ebf466c59 100644 --- a/test_regress/t/t_tagged_case.v +++ b/test_regress/t/t_tagged_case.v @@ -63,7 +63,9 @@ module t; // Tagged union with chandle member typedef union tagged { void Invalid; +`ifndef QUESTA chandle Handle; +`endif } ChandleType; // Tagged union with class reference member @@ -148,7 +150,9 @@ module t; tagged Wide60 .w : wide60_result = w; default : wide60_result = 0; endcase +`ifndef QUESTA `checkh(wide60_result, 60'hFEDCBA987654321); +`endif // Test 4: Wide type case matching - 90-bit wt = tagged Wide90 (90'hDE_ADBEEFCA_FEBABE12_3456); @@ -158,7 +162,9 @@ module t; tagged Wide90 .w : wide90_result = w; default : wide90_result = 0; endcase +`ifndef QUESTA `checkh(wide90_result, 90'hDE_ADBEEFCA_FEBABE12_3456); +`endif // Test 5: Non-zero LSB case match wt = tagged Byte8NonZeroLSB (8'hA5); @@ -203,7 +209,9 @@ module t; result = 0; case (cht) matches tagged Invalid : result = 1; +`ifndef QUESTA tagged Handle .* : result = 2; // Wildcard - can't bind chandle +`endif endcase `checkh(result, 1); @@ -213,9 +221,9 @@ module t; result = 0; case (clt) matches tagged Invalid : result = -1; - tagged Obj .o : result = o.value; + tagged Obj : result = 2; endcase - `checkh(result, 42); + `checkh(result, 2); // Test 11: Real member case matching rt = tagged Invalid; diff --git a/test_regress/t/t_tagged_if.out b/test_regress/t/t_tagged_if.out index 31b7cc0bb..db7e619f6 100644 --- a/test_regress/t/t_tagged_if.out +++ b/test_regress/t/t_tagged_if.out @@ -1,14 +1,14 @@ -%Error-UNSUPPORTED: t/t_tagged_if.v:218:37: Unsupported: &&& expression - 218 | if (instr matches tagged Jmp .j &&& +%Error-UNSUPPORTED: t/t_tagged_if.v:226:37: Unsupported: &&& expression + 226 | if (instr matches tagged Jmp .j &&& | ^~~ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_tagged_if.v:228:35: Unsupported: &&& expression - 228 | if (v matches tagged Valid .n &&& (n > 50)) +%Error-UNSUPPORTED: t/t_tagged_if.v:236:35: Unsupported: &&& expression + 236 | if (v matches tagged Valid .n &&& (n > 50)) | ^~~ -%Error-UNSUPPORTED: t/t_tagged_if.v:237:35: Unsupported: &&& expression - 237 | if (v matches tagged Valid .n &&& (n > 50)) +%Error-UNSUPPORTED: t/t_tagged_if.v:245:35: Unsupported: &&& expression + 245 | if (v matches tagged Valid .n &&& (n > 50)) | ^~~ -%Error-UNSUPPORTED: t/t_tagged_if.v:266:37: Unsupported: &&& expression - 266 | if (instr matches tagged Jmp .j &&& +%Error-UNSUPPORTED: t/t_tagged_if.v:274:37: Unsupported: &&& expression + 274 | if (instr matches tagged Jmp .j &&& | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_tagged_if.v b/test_regress/t/t_tagged_if.v index 46be66135..269219f9d 100644 --- a/test_regress/t/t_tagged_if.v +++ b/test_regress/t/t_tagged_if.v @@ -61,10 +61,12 @@ module t; } Instr; // Tagged union with chandle member +`ifndef QUESTA typedef union tagged { void Invalid; chandle Handle; } ChandleType; +`endif // Tagged union with class reference member typedef union tagged { @@ -108,7 +110,9 @@ module t; WideType wt; ArrayType at; Instr instr; +`ifndef QUESTA ChandleType cht; +`endif ClassType clt; TestClass obj; RealType rt; @@ -156,7 +160,9 @@ module t; wide60_result = w; else wide60_result = 0; +`ifndef QUESTA `checkh(wide60_result, 60'hFEDCBA987654321); +`endif // Test 5: Wide type if matching - 90-bit wt = tagged Wide90 (90'hDE_ADBEEFCA_FEBABE12_3456); @@ -165,7 +171,9 @@ module t; wide90_result = w; else wide90_result = 0; +`ifndef QUESTA `checkh(wide90_result, 90'hDE_ADBEEFCA_FEBABE12_3456); +`endif // Test 6: Non-zero LSB if match wt = tagged Byte8NonZeroLSB (8'hA5); @@ -291,6 +299,7 @@ module t; result = 2; `checkh(result, 1); +`ifndef QUESTA // Test 19: Chandle member if matching cht = tagged Invalid; result = 0; @@ -307,6 +316,7 @@ module t; else result = 2; `checkh(result, 1); +`endif // Test 20: Class reference member if matching obj = new(42); @@ -320,8 +330,8 @@ module t; clt = tagged Obj (obj); result = 0; - if (clt matches tagged Obj .o) - result = o.value; + if (clt matches tagged Obj) + result = 42; else result = -1; `checkh(result, 42); diff --git a/test_regress/t/t_tagged_union.out b/test_regress/t/t_tagged_union.out index ab08889dc..d31a3fdfa 100644 --- a/test_regress/t/t_tagged_union.out +++ b/test_regress/t/t_tagged_union.out @@ -8,145 +8,145 @@ %Error-UNSUPPORTED: t/t_tagged_union.v:57:5: Unsupported: void (for tagged unions) 57 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:79:5: Unsupported: void (for tagged unions) - 79 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:80:5: Unsupported: void (for tagged unions) + 80 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:85:5: Unsupported: void (for tagged unions) - 85 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:87:5: Unsupported: void (for tagged unions) + 87 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:94:5: Unsupported: void (for tagged unions) - 94 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:96:5: Unsupported: void (for tagged unions) + 96 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:101:5: Unsupported: void (for tagged unions) - 101 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:103:5: Unsupported: void (for tagged unions) + 103 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:107:5: Unsupported: void (for tagged unions) - 107 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:109:5: Unsupported: void (for tagged unions) + 109 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:116:5: Unsupported: void (for tagged unions) - 116 | void Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:118:5: Unsupported: void (for tagged unions) + 118 | void Invalid; | ^~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:137:11: Unsupported: tagged union - 137 | vi1 = tagged Invalid; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:138:11: Unsupported: tagged union - 138 | vi2 = tagged Invalid; - | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:141:11: Unsupported: tagged union - 141 | vi1 = tagged Valid (42); + 141 | vi1 = tagged Invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:144:11: Unsupported: tagged union - 144 | vi2 = tagged Valid (23 + 34); +%Error-UNSUPPORTED: t/t_tagged_union.v:142:11: Unsupported: tagged union + 142 | vi2 = tagged Invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:148:10: Unsupported: tagged union - 148 | mt = tagged Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:145:11: Unsupported: tagged union + 145 | vi1 = tagged Valid (42); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged_union.v:148:11: Unsupported: tagged union + 148 | vi2 = tagged Valid (23 + 34); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged_union.v:152:10: Unsupported: tagged union + 152 | mt = tagged Invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:150:10: Unsupported: tagged union - 150 | mt = tagged IntVal (32'h12345678); +%Error-UNSUPPORTED: t/t_tagged_union.v:154:10: Unsupported: tagged union + 154 | mt = tagged IntVal (32'h12345678); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:153:10: Unsupported: tagged union - 153 | mt = tagged ShortVal (16'hABCD); +%Error-UNSUPPORTED: t/t_tagged_union.v:157:10: Unsupported: tagged union + 157 | mt = tagged ShortVal (16'hABCD); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:156:10: Unsupported: tagged union - 156 | mt = tagged ByteVal (8'h5A); - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:159:10: Unsupported: tagged union - 159 | mt = tagged BitVal (1'b1); +%Error-UNSUPPORTED: t/t_tagged_union.v:160:10: Unsupported: tagged union + 160 | mt = tagged ByteVal (8'h5A); | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:163:10: Unsupported: tagged union - 163 | mt = tagged Byte8NonZeroLSB (8'hA5); + 163 | mt = tagged BitVal (1'b1); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:166:10: Unsupported: tagged union - 166 | mt = tagged Word16NonZeroLSB (16'h1234); +%Error-UNSUPPORTED: t/t_tagged_union.v:167:10: Unsupported: tagged union + 167 | mt = tagged Byte8NonZeroLSB (8'hA5); | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:170:10: Unsupported: tagged union - 170 | mt = tagged Word32LittleEndian (32'hDEADBEEF); + 170 | mt = tagged Word16NonZeroLSB (16'h1234); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:173:10: Unsupported: tagged union - 173 | mt = tagged Word16LittleEndian (16'hCAFE); +%Error-UNSUPPORTED: t/t_tagged_union.v:174:10: Unsupported: tagged union + 174 | mt = tagged Word32LittleEndian (32'hDEADBEEF); | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:177:10: Unsupported: tagged union - 177 | mt = tagged Wide60 (60'hFEDCBA987654321); + 177 | mt = tagged Word16LittleEndian (16'hCAFE); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:180:10: Unsupported: tagged union - 180 | mt = tagged Wide60NonZeroLSB (60'h123456789ABCDEF); +%Error-UNSUPPORTED: t/t_tagged_union.v:181:10: Unsupported: tagged union + 181 | mt = tagged Wide60 (60'hFEDCBA987654321); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:183:10: Unsupported: tagged union - 183 | mt = tagged Wide60LittleEndian (60'hABCDEF012345678); +%Error-UNSUPPORTED: t/t_tagged_union.v:184:10: Unsupported: tagged union + 184 | mt = tagged Wide60NonZeroLSB (60'h123456789ABCDEF); | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:187:10: Unsupported: tagged union - 187 | mt = tagged Wide90 (90'hFF_FFFFFFFF_FFFFFFFF_FFFF); + 187 | mt = tagged Wide60LittleEndian (60'hABCDEF012345678); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:190:10: Unsupported: tagged union - 190 | mt = tagged Wide90NonZeroLSB (90'hDE_ADBEEFCA_FEBABE12_3456); +%Error-UNSUPPORTED: t/t_tagged_union.v:191:10: Unsupported: tagged union + 191 | mt = tagged Wide90 (90'hFF_FFFFFFFF_FFFFFFFF_FFFF); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:193:10: Unsupported: tagged union - 193 | mt = tagged Wide90LittleEndian (90'h11_11111122_22222233_3333); +%Error-UNSUPPORTED: t/t_tagged_union.v:194:10: Unsupported: tagged union + 194 | mt = tagged Wide90NonZeroLSB (90'hDE_ADBEEFCA_FEBABE12_3456); | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:197:10: Unsupported: tagged union - 197 | at = tagged Invalid; + 197 | mt = tagged Wide90LittleEndian (90'h11_11111122_22222233_3333); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:199:10: Unsupported: tagged union - 199 | at = tagged Scalar (999); +%Error-UNSUPPORTED: t/t_tagged_union.v:201:10: Unsupported: tagged union + 201 | at = tagged Invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:202:10: Unsupported: tagged union - 202 | at = tagged UnpackedArr '{100, 200, 300, 400}; +%Error-UNSUPPORTED: t/t_tagged_union.v:203:10: Unsupported: tagged union + 203 | at = tagged Scalar (999); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:208:10: Unsupported: tagged union - 208 | at = tagged UnpackedArr2D '{'{32'hA, 32'hB, 32'hC}, '{32'hD, 32'hE, 32'hF}}; +%Error-UNSUPPORTED: t/t_tagged_union.v:206:10: Unsupported: tagged union + 206 | at = tagged UnpackedArr '{100, 200, 300, 400}; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:217:13: Unsupported: tagged union - 217 | instr = tagged Add '{5'd1, 5'd2, 5'd3}; +%Error-UNSUPPORTED: t/t_tagged_union.v:212:10: Unsupported: tagged union + 212 | at = tagged UnpackedArr2D '{'{32'hA, 32'hB, 32'hC}, '{32'hD, 32'hE, 32'hF}}; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged_union.v:221:13: Unsupported: tagged union + 221 | instr = tagged Add '{5'd1, 5'd2, 5'd3}; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:223:13: Unsupported: tagged union - 223 | instr = tagged Add '{reg2:5'd10, regd:5'd20, reg1:5'd5}; +%Error-UNSUPPORTED: t/t_tagged_union.v:227:13: Unsupported: tagged union + 227 | instr = tagged Add '{reg2:5'd10, regd:5'd20, reg1:5'd5}; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:229:13: Unsupported: tagged union - 229 | instr = tagged Jmp (tagged JmpU 10'd512); - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:229:25: Unsupported: tagged union - 229 | instr = tagged Jmp (tagged JmpU 10'd512); - | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:233:13: Unsupported: tagged union - 233 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); + 233 | instr = tagged Jmp (tagged JmpU 10'd512); | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:233:25: Unsupported: tagged union - 233 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); + 233 | instr = tagged Jmp (tagged JmpU 10'd512); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:238:13: Unsupported: tagged union - 238 | instr = tagged Jmp (tagged JmpC '{cc:2'd3, addr:10'd100}); +%Error-UNSUPPORTED: t/t_tagged_union.v:237:13: Unsupported: tagged union + 237 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:238:25: Unsupported: tagged union - 238 | instr = tagged Jmp (tagged JmpC '{cc:2'd3, addr:10'd100}); +%Error-UNSUPPORTED: t/t_tagged_union.v:237:25: Unsupported: tagged union + 237 | instr = tagged Jmp (tagged JmpC '{2'd1, 10'd256}); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged_union.v:242:13: Unsupported: tagged union + 242 | instr = tagged Jmp (tagged JmpC '{cc:2'd3, addr:10'd100}); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged_union.v:242:25: Unsupported: tagged union + 242 | instr = tagged Jmp (tagged JmpC '{cc:2'd3, addr:10'd100}); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:243:11: Unsupported: tagged union - 243 | cht = tagged Invalid; - | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:244:11: Unsupported: tagged union - 244 | cht = tagged Handle (null); - | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:248:11: Unsupported: tagged union - 248 | clt = tagged Invalid; + 248 | cht = tagged Invalid; | ^~~~~~ %Error-UNSUPPORTED: t/t_tagged_union.v:249:11: Unsupported: tagged union - 249 | clt = tagged Obj (obj); + 249 | cht = tagged Handle (null); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:253:10: Unsupported: tagged union - 253 | rt = tagged Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:254:11: Unsupported: tagged union + 254 | clt = tagged Invalid; + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged_union.v:255:11: Unsupported: tagged union + 255 | clt = tagged Obj (obj); + | ^~~~~~ +%Error-UNSUPPORTED: t/t_tagged_union.v:259:10: Unsupported: tagged union + 259 | rt = tagged Invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:254:10: Unsupported: tagged union - 254 | rt = tagged RealVal (3.14159); +%Error-UNSUPPORTED: t/t_tagged_union.v:260:10: Unsupported: tagged union + 260 | rt = tagged RealVal (3.14159); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:258:10: Unsupported: tagged union - 258 | rt = tagged ShortRealVal (2.5); +%Error-UNSUPPORTED: t/t_tagged_union.v:264:10: Unsupported: tagged union + 264 | rt = tagged ShortRealVal (2.5); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:262:10: Unsupported: tagged union - 262 | st = tagged Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:268:10: Unsupported: tagged union + 268 | st = tagged Invalid; | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:263:10: Unsupported: tagged union - 263 | st = tagged StrVal ("hello"); +%Error-UNSUPPORTED: t/t_tagged_union.v:269:10: Unsupported: tagged union + 269 | st = tagged StrVal ("hello"); | ^~~~~~ -%Error-UNSUPPORTED: t/t_tagged_union.v:267:10: Unsupported: tagged union - 267 | et = tagged Invalid; +%Error-UNSUPPORTED: t/t_tagged_union.v:273:10: Unsupported: tagged union + 273 | et = tagged Invalid; | ^~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_tagged_union.v b/test_regress/t/t_tagged_union.v index 59642f503..4ce7fe866 100644 --- a/test_regress/t/t_tagged_union.v +++ b/test_regress/t/t_tagged_union.v @@ -75,10 +75,12 @@ module t; } Instr; // Tagged union with chandle member +`ifndef QUESTA typedef union tagged { void Invalid; chandle Handle; } ChandleType; +`endif // Tagged union with class reference member typedef union tagged { @@ -122,7 +124,9 @@ module t; MultiType mt; ArrayType at; Instr instr; +`ifndef QUESTA ChandleType cht; +`endif ClassType clt; TestClass obj; RealType rt; @@ -239,9 +243,11 @@ module t; `checkh(instr.Jmp.JmpC.cc, 2'd3); `checkh(instr.Jmp.JmpC.addr, 10'd100); +`ifndef QUESTA // Test 13: Chandle member cht = tagged Invalid; cht = tagged Handle (null); +`endif // Test 14: Class reference member obj = new(42); diff --git a/test_regress/t/t_vpi_force.v b/test_regress/t/t_vpi_force.v index ae4913926..fcfa1b5fb 100644 --- a/test_regress/t/t_vpi_force.v +++ b/test_regress/t/t_vpi_force.v @@ -175,8 +175,7 @@ typedef enum byte { // - Continuous assignments do not work // - vpi_handle_by_multi_index is not implemented // - vpi_handle_by_name and vpi_handle_by_index fail with negative indices - // Hence, these signals are excluded from testing with Icarus. Recommend - // Xcelium for cross-checking results. + // Hence, these signals are excluded from testing with Icarus. `ifndef IVERILOG // Force the entire packed array (no partial forcing possible, because // partial indexing only works for bits, not dimension slices) @@ -2422,10 +2421,8 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); svReleaseValues(); #8 svCheckValuesReleased(); - // Icarus does not support forcing single bits through VPI -`ifndef IVERILOG - // Xcelium supports forcing single bits through VPI, but crashes on some signals -`ifndef XRUN +`ifndef IVERILOG // Does not support forcing single bits through VPI +`ifndef XRUN // Supports forcing single bits through VPI, but crashes on some signals // Force single bit through VPI, release through VPI if (`verbose) $display("*** Forcing single bit through VPI, releasing through VPI ***"); From f82f59a0246f7e62f2f3a237446fdf10788cd09f Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 3 Jul 2026 13:01:05 -0400 Subject: [PATCH 41/41] Commentary: Changes update --- Changes | 8 ++++++++ docs/spelling.txt | 3 +++ .../t/{t_clang_overload.py => t_class_cpp_overload.py} | 0 .../t/{t_clang_overload.v => t_class_cpp_overload.v} | 0 test_regress/t/t_unique0_case.v | 3 +-- 5 files changed, 12 insertions(+), 2 deletions(-) rename test_regress/t/{t_clang_overload.py => t_class_cpp_overload.py} (100%) rename test_regress/t/{t_clang_overload.v => t_class_cpp_overload.v} (100%) diff --git a/Changes b/Changes index 3e4174b62..9ae1a40ee 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,14 @@ Verilator 5.051 devel **Other:** +* Add comments as a branch description in coverage .info files (#7843). [Eryk Szpotanski] +* Optimize random initialization. [Geza Lore, Testorrent USA, Inc.] +* Fix DFG misoptimizing bound checks (#7755). [Jakub Michalski] +* Fix unique0 case side effects (#7787). [Pawel Klopotek] +* Fix cleaning purity cache after assertions. [Geza Lore, Testorrent USA, Inc.] +* Fix clang++ ambiguous overload of '==' operator (#7863). [Pawel Kojma, Antmicro Ltd.] +* Fix heap-use-after-free in `VlRNG::VlRNG()` (#7865). [Dragon-Git] + Verilator 5.050 2026-07-01 ========================== diff --git a/docs/spelling.txt b/docs/spelling.txt index a9d91f52b..f99491317 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -477,6 +477,7 @@ Syms Synopsys SystemC SystemVerilog +Szpotanski Takatsukasa Tambe Tarik @@ -537,6 +538,7 @@ Verilog Vighnesh Viktor Vilp +VlRNG VlWide Vlip Vm @@ -947,6 +949,7 @@ misconnected misconversion misdetecting misoptimized +misoptimizing missized mk mno diff --git a/test_regress/t/t_clang_overload.py b/test_regress/t/t_class_cpp_overload.py similarity index 100% rename from test_regress/t/t_clang_overload.py rename to test_regress/t/t_class_cpp_overload.py diff --git a/test_regress/t/t_clang_overload.v b/test_regress/t/t_class_cpp_overload.v similarity index 100% rename from test_regress/t/t_clang_overload.v rename to test_regress/t/t_class_cpp_overload.v diff --git a/test_regress/t/t_unique0_case.v b/test_regress/t/t_unique0_case.v index 54cedb901..ed3f92535 100644 --- a/test_regress/t/t_unique0_case.v +++ b/test_regress/t/t_unique0_case.v @@ -7,7 +7,6 @@ // verilog_format: off `define stop $stop `define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); -`define checkp(gotv,expv_s) do begin string gotv_s; gotv_s = $sformatf("%p", gotv); if ((gotv_s) != (expv_s)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv_s), (expv_s)); `stop; end end while(0); // verilog_format: on module t; @@ -18,7 +17,7 @@ module t; y = 0; x = 0; z = 0; - unique0 case(increment_x()) + unique0 case (increment_x()) 1: begin y = 1; end