diff --git a/src/V3Force.cpp b/src/V3Force.cpp index 9ca6db121..7f3a4f8b5 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -47,6 +47,7 @@ #include "V3Force.h" #include "V3AstUserAllocator.h" +#include "V3UniqueNames.h" VL_DEFINE_DEBUG_FUNCTIONS; @@ -78,10 +79,12 @@ public: AstVarScope* const m_rdVscp; // New variable to replace read references with AstVarScope* const m_valVscp; // Forced value AstVarScope* const m_enVscp; // Force enabled signal + V3UniqueNames m_iterNames; // Names for loop iteration variables explicit ForceComponentsVarScope(AstVarScope* vscp, ForceComponentsVar& fcv) : m_rdVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_rdVarp}} , m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}} - , m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}} { + , m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}} + , m_iterNames{"__VForceIter"} { m_rdVscp->addNext(m_enVscp); m_rdVscp->addNext(m_valVscp); vscp->addNextHere(m_rdVscp); @@ -94,63 +97,30 @@ public: activeInitp->senTreeStorep(activeInitp->sentreep()); vscp->scopep()->addBlocksp(activeInitp); - AstVarRef* const enRefp = new AstVarRef{flp, m_enVscp, VAccess::WRITE}; + // Create statements that update __Rd variable. + // These nodes will be copied and used also for __En initialization + AstVarRef* const rdRefp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE}; + std::vector assigns; + AstNodeStmt* const rdUpdateStmtsp + = getForcedUpdateStmtsRecursep(rdRefp, vscp, rdRefp, assigns); - AstNodeStmt* toInsertp = nullptr; - AstNodeStmt* outerStmtp = nullptr; - std::vector loopVarRefs; - if (VN_IS(enRefp->dtypep()->skipRefp(), UnpackArrayDType)) { - // Create a loop to set all elements of __VforceEn array to 0. - // That loop node is then copied and used for updating elements of __VforceRd array - if (AstUnpackArrayDType* const unpackedp - = VN_CAST(m_rdVscp->varp()->dtypep(), UnpackArrayDType)) { - std::vector dims = unpackedp->unpackDimensions(); - loopVarRefs.reserve(dims.size()); - for (size_t i = 0; i < dims.size(); i++) { - AstVar* const loopVarp = new AstVar{ - flp, VVarType::MODULETEMP, - m_rdVscp->varp()->name() + "__VwhileIter" + std::to_string(i), - VFlagBitPacked{}, 32}; - m_rdVscp->varp()->addNext(loopVarp); - AstVarScope* const loopVarScopep - = new AstVarScope{flp, m_rdVscp->scopep(), loopVarp}; - m_rdVscp->addNext(loopVarScopep); - AstVarRef* const readRefp - = new AstVarRef{flp, loopVarScopep, VAccess::READ}; - loopVarRefs.push_back(readRefp); - AstNodeStmt* const currInitp - = new AstAssign{flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE}, - new AstConst{flp, 0}}; - if (toInsertp) { - toInsertp->addNextHere(currInitp); - } else { - outerStmtp = currInitp; - } - AstLoop* const currWhilep = new AstLoop{flp}; - currInitp->addNextHere(currWhilep); - AstLoopTest* const loopTestp = new AstLoopTest{ - flp, currWhilep, - new AstNeq{flp, readRefp, - new AstConst{ - flp, static_cast(dims[i]->elementsConst())}}}; - currWhilep->addStmtsp(loopTestp); - toInsertp = loopTestp; - AstAssign* const currIncrp = new AstAssign{ - flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE}, - new AstAdd{flp, readRefp->cloneTree(false), new AstConst{flp, 1}}}; - currWhilep->addStmtsp(currIncrp); - } - } + // To use these statements for __En initialization, replace references to __Rd with + // ones to __En and replace assignments RHS with 0 + AstNodeStmt* const enInitStmtsp = rdUpdateStmtsp->cloneTree(true); + for (size_t i = 0; i < assigns.size(); i++) { + // Save copies, because clonep() works only after the last cloneTree + assigns[i] = assigns[i]->clonep(); } - V3Number zero{m_enVscp, m_enVscp->width()}; - AstNodeExpr* const enRhsp = new AstConst{flp, zero}; - AstNodeExpr* enLhsp = applySelects(enRefp, loopVarRefs); - AstNodeStmt* stmtp = new AstAssign{flp, enLhsp, enRhsp}; - if (toInsertp) { - toInsertp->addNextHere(stmtp); - stmtp = outerStmtp; + for (AstAssign* const assignp : assigns) { + AstVarRef* const lhsVarRefp + = VN_AS(AstNodeVarRef::varRefLValueRecurse(assignp->lhsp()), VarRef); + lhsVarRefp->replaceWith(new AstVarRef{flp, m_enVscp, VAccess::WRITE}); + lhsVarRefp->deleteTree(); + assignp->rhsp()->unlinkFrBack()->deleteTree(); + V3Number zero{m_enVscp, assignp->lhsp()->dtypep()->width()}; + assignp->rhsp(new AstConst{flp, zero}); } - activeInitp->addStmtsp(new AstInitial{flp, stmtp->cloneTree(true)}); + activeInitp->addStmtsp(new AstInitial{flp, enInitStmtsp}); { // Add the combinational override // Explicitly list dependencies for update. // Note: rdVscp is also needed to retrigger assignment for the first time. @@ -167,48 +137,105 @@ public: = new AstActive{flp, "force-update", new AstSenTree{flp, itemsp}}; activep->senTreeStorep(activep->sentreep()); - // Reuse the statements created for __VforceEn initialization - // and replace var ref on the LHS and the RHS - AstVarRef* const rdRefp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE}; - AstNodeExpr* const rdRhsp = forcedUpdate(vscp, loopVarRefs); - enRefp->replaceWith(rdRefp); - VL_DO_DANGLING(enRefp->deleteTree(), enRefp); - enRhsp->replaceWith(rdRhsp); - VL_DO_DANGLING(enRhsp->deleteTree(), enRhsp); - - activep->addStmtsp(new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, stmtp}); + activep->addStmtsp( + new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, rdUpdateStmtsp}); vscp->scopep()->addBlocksp(activep); } } - static AstNodeExpr* applySelects(AstNodeExpr* exprp, - const std::vector& selectExprs) { - for (AstNodeExpr* const sp : selectExprs) { - exprp = new AstArraySel{exprp->fileline(), exprp, sp->cloneTreePure(false)}; + AstNodeStmt* getForcedUpdateStmtsRecursep(AstNodeExpr* const lhsp, AstVarScope* const vscp, + AstVarRef* const lhsVarRefp, + std::vector& assigns) { + // Create stataments that update values of __Rd variable. + // lhsp is either a reference to that variable or ArraySel or MemberSel on it. + // lhsVarRefp is a reference to that variable in lhsp subtree. + // assigns is a vector to which all assignments to __Rd are added. + FileLine* const flp = lhsp->fileline(); + const AstNodeDType* const lhsDtypep = lhsp->dtypep()->skipRefp(); + if (lhsDtypep->isIntegralOrPacked() || VN_IS(lhsDtypep, BasicDType)) { + AstAssign* const assignp + = new AstAssign{flp, lhsp, forcedUpdate(vscp, lhsp, lhsVarRefp)}; + assigns.push_back(assignp); + return assignp; + } else if (const AstStructDType* const structDtypep + = VN_CAST(lhsDtypep, StructDType)) { + AstNodeStmt* stmtsp = nullptr; + bool firstIter = true; + for (AstMemberDType* mdtp = structDtypep->membersp(); mdtp; + mdtp = VN_AS(mdtp->nextp(), MemberDType)) { + AstNodeExpr* const lhsCopyp = firstIter ? lhsp : lhsp->cloneTreePure(false); + AstVarRef* const lhsVarRefCopyp + = firstIter ? lhsVarRefp : lhsVarRefp->clonep(); + AstStructSel* const structSelp = new AstStructSel{flp, lhsCopyp, mdtp->name()}; + structSelp->dtypep(mdtp); + AstNodeStmt* const memberStmtp + = getForcedUpdateStmtsRecursep(structSelp, vscp, lhsVarRefCopyp, assigns); + stmtsp = firstIter ? memberStmtp : stmtsp->addNext(memberStmtp); + firstIter = false; + } + return stmtsp; + } else if (const AstUnpackArrayDType* const arrayDtypep + = VN_CAST(lhsDtypep, UnpackArrayDType)) { + AstVar* const loopVarp + = new AstVar{flp, VVarType::MODULETEMP, + m_iterNames.get(m_rdVscp->varp()->name()), VFlagBitPacked{}, 32}; + m_rdVscp->varp()->addNext(loopVarp); + AstVarScope* const loopVarScopep + = new AstVarScope{flp, m_rdVscp->scopep(), loopVarp}; + m_rdVscp->addNext(loopVarScopep); + AstVarRef* const readRefp = new AstVarRef{flp, loopVarScopep, VAccess::READ}; + AstNodeStmt* const currInitp = new AstAssign{ + flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE}, new AstConst{flp, 0}}; + AstLoop* const currWhilep = new AstLoop{flp}; + currInitp->addNextHere(currWhilep); + AstLoopTest* const loopTestp = new AstLoopTest{ + flp, currWhilep, + new AstNeq{ + flp, readRefp, + new AstConst{flp, static_cast(arrayDtypep->elementsConst())}}}; + currWhilep->addStmtsp(loopTestp); + AstArraySel* const lhsSelp + = new AstArraySel{flp, lhsp, readRefp->cloneTree(false)}; + AstNodeStmt* const loopBodyp + = getForcedUpdateStmtsRecursep(lhsSelp, vscp, lhsVarRefp, assigns); + currWhilep->addStmtsp(loopBodyp); + AstAssign* const currIncrp = new AstAssign{ + flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE}, + new AstAdd{flp, readRefp->cloneTree(false), new AstConst{flp, 1}}}; + currWhilep->addStmtsp(currIncrp); + return currInitp; + } else { + lhsDtypep->v3fatalSrc("Unhandled type"); } - return exprp; } - AstNodeExpr* forcedUpdate(AstVarScope* const vscp, - const std::vector& selectExprs) const { + static AstNodeExpr* wrapIntoExprp(AstVarRef* const refp, AstNodeExpr* const exprp, + AstVarRef* const varRefToReplacep) { + // Return a copy of exprp in which varRefToReplacep is replaced with refp + if (exprp == varRefToReplacep) { + return refp; + } else { + AstNodeExpr* const copiedExprp = exprp->cloneTreePure(false); + AstNode* const oldRefp = varRefToReplacep->clonep(); + varRefToReplacep->clonep()->replaceWith(refp); + oldRefp->deleteTree(); + return copiedExprp; + } + } + AstNodeExpr* forcedUpdate(AstVarScope* const vscp, AstNodeExpr* exprp = nullptr, + AstVarRef* const varRefToReplacep = nullptr) const { FileLine* const flp = vscp->fileline(); AstVarRef* origRefp = new AstVarRef{flp, vscp, VAccess::READ}; ForceState::markNonReplaceable(origRefp); - AstNodeExpr* const origp = applySelects(origRefp, selectExprs); + AstNodeExpr* const origExprp = wrapIntoExprp(origRefp, exprp, varRefToReplacep); + AstNodeExpr* const enExprp = wrapIntoExprp(new AstVarRef{flp, m_enVscp, VAccess::READ}, + exprp, varRefToReplacep); + AstNodeExpr* const valExprp = wrapIntoExprp( + new AstVarRef{flp, m_valVscp, VAccess::READ}, exprp, varRefToReplacep); if (ForceState::isRangedDType(vscp)) { return new AstOr{ - flp, - new AstAnd{ - flp, - applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ}, selectExprs), - applySelects(new AstVarRef{flp, m_valVscp, VAccess::READ}, selectExprs)}, - new AstAnd{ - flp, - new AstNot{flp, applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ}, - selectExprs)}, - origp}}; + flp, new AstAnd{flp, enExprp, valExprp}, + new AstAnd{flp, new AstNot{flp, enExprp->cloneTreePure(false)}, origExprp}}; } - return new AstCond{ - flp, applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ}, selectExprs), - applySelects(new AstVarRef{flp, m_valVscp, VAccess::READ}, selectExprs), origp}; + return new AstCond{flp, enExprp, valExprp, origExprp}; } }; @@ -229,39 +256,52 @@ private: m_valVscps; // `valVscp` force components of a forced RHS - static AstNodeDType* getEnVarpDTypep(AstVar* const varp) { + static size_t checkIfDTypeSupportedRecurse(const AstNodeDType* const dtypep, + const AstVar* const varp) { + // Checks if force stmt is supported on all subtypes + // and returns number of unpacked elements + const AstNodeDType* const dtp = dtypep->skipRefp(); + if (const AstUnpackArrayDType* const udtp = VN_CAST(dtp, UnpackArrayDType)) { + const size_t elemsInSubDType = checkIfDTypeSupportedRecurse(udtp->subDTypep(), varp); + return udtp->elementsConst() * elemsInSubDType; + } else if (const AstNodeUOrStructDType* const sdtp = VN_CAST(dtp, NodeUOrStructDType)) { + size_t elemCount = 0; + for (const AstMemberDType* mdtp = sdtp->membersp(); mdtp; + mdtp = VN_AS(mdtp->nextp(), MemberDType)) { + elemCount += checkIfDTypeSupportedRecurse(mdtp->subDTypep(), varp); + } + return elemCount; + } else if (const AstBasicDType* const bdtp = VN_CAST(dtp, BasicDType)) { + if (bdtp->isString() || bdtp->isEvent() || bdtp->keyword() == VBasicDTypeKwd::CHANDLE + || bdtp->keyword() == VBasicDTypeKwd::TIME) { + varp->v3warn(E_UNSUPPORTED, "Forcing variable of unsupported type: " + << varp->dtypep()->prettyTypeName()); + } + return 1; + } else if (!dtp->isIntegralOrPacked()) { + varp->v3warn(E_UNSUPPORTED, "Forcing variable of unsupported type: " + << varp->dtypep()->prettyTypeName()); + return 1; + } else { + // All packed types are supported + return 1; + } + } + static AstNodeDType* getEnVarpDTypep(const AstVar* const varp) { AstNodeDType* const origDTypep = varp->dtypep()->skipRefp(); + const size_t unpackElemNum = checkIfDTypeSupportedRecurse(origDTypep, varp); + if (unpackElemNum > ELEMENTS_MAX) { + varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of variable with " + ">= " + << ELEMENTS_MAX << " unpacked elements"); + } if (VN_IS(origDTypep, UnpackArrayDType)) { - size_t elemNum = 1; - AstNodeDType* dtp = origDTypep; - while (AstUnpackArrayDType* const uDtp = VN_CAST(dtp, UnpackArrayDType)) { - dtp = uDtp->subDTypep()->skipRefp(); - elemNum *= uDtp->elementsConst(); - } - if (elemNum > ELEMENTS_MAX) { - varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of unpacked array variable with " - ">= " - << ELEMENTS_MAX << " elements"); - } - bool complexElem = true; - if (const AstBasicDType* const basicp = VN_CAST(dtp, BasicDType)) { - complexElem = basicp->isOpaque(); - } - if (complexElem) { - varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of unpacked array variable with " - "elements of complex data type"); - } return origDTypep; } else if (VN_IS(origDTypep, BasicDType)) { return isRangedDType(varp) ? origDTypep : varp->findBitDType(); } else if (VN_IS(origDTypep, PackArrayDType)) { return origDTypep; - } else if (const AstNodeUOrStructDType* const sdtp - = VN_CAST(origDTypep, NodeUOrStructDType)) { - if (!sdtp->packed()) { - varp->v3warn(E_UNSUPPORTED, - "Unsupported: Force of unpacked struct / union variable"); - } + } else if (VN_IS(origDTypep, NodeUOrStructDType)) { return origDTypep; } else { varp->v3fatalSrc("Unsupported: Force of variable of unhandled data type"); @@ -275,7 +315,7 @@ public: VL_UNCOPYABLE(ForceState); // STATIC METHODS - static bool isRangedDType(AstNode* nodep) { + static bool isRangedDType(const AstNode* const nodep) { // If ranged we need a multibit enable to support bit-by-bit part-select forces, // otherwise forcing a real or other opaque dtype and need a single bit enable. const AstBasicDType* const basicp = nodep->dtypep()->skipRefp()->basicp(); @@ -416,26 +456,14 @@ class ForceConvertVisitor final : public VNVisitor { } }); // Replace write refs on RHS - if (VN_IS(resetRdp->rhsp(), ArraySel)) { - std::vector selIndices; - AstNodeExpr* exprp = resetRdp->rhsp(); - while (AstArraySel* const selp = VN_CAST(exprp, ArraySel)) { - selIndices.push_back(selp->bitp()); - exprp = selp->fromp(); - } - if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) { - AstVarScope* const vscp = refp->varScopep(); - std::vector reversedIndices(selIndices.size()); - std::reverse_copy(selIndices.begin(), selIndices.end(), reversedIndices.begin()); - AstNodeExpr* const origRhsp = resetRdp->rhsp(); - origRhsp->replaceWith( - m_state.getForceComponents(vscp).forcedUpdate(vscp, reversedIndices)); - VL_DO_DANGLING(origRhsp->deleteTree(), origRhsp); - } else { - exprp->v3warn( - E_UNSUPPORTED, - "Unsupported: Release statement argument is too complex array select"); - } + if (VN_IS(resetRdp->rhsp(), ArraySel) || VN_IS(resetRdp->rhsp(), StructSel)) { + AstVarRef* const refp + = VN_AS(AstNodeVarRef::varRefLValueRecurse(resetRdp->rhsp()), VarRef); + AstVarScope* const vscp = refp->varScopep(); + AstNodeExpr* const origRhsp = resetRdp->rhsp(); + origRhsp->replaceWith( + m_state.getForceComponents(vscp).forcedUpdate(vscp, origRhsp, refp)); + VL_DO_DANGLING(origRhsp->deleteTree(), origRhsp); } else { resetRdp->rhsp()->foreach([this](AstVarRef* refp) { if (refp->access() != VAccess::WRITE) return; @@ -444,7 +472,7 @@ class ForceConvertVisitor final : public VNVisitor { refp->access(VAccess::READ); ForceState::markNonReplaceable(refp); } else { - refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp, {})); + refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp)); VL_DO_DANGLING(refp->deleteTree(), refp); } }); @@ -493,7 +521,6 @@ class ForceReplaceVisitor final : public VNVisitor { AstNodeStmt* m_stmtp = nullptr; bool m_inLogic = false; bool m_releaseRhs = false; // Inside RHS of assignment created for release statement - std::vector m_selIndices; // Indices of array select expressions above // METHODS void iterateLogic(AstNode* logicp) { @@ -529,12 +556,6 @@ class ForceReplaceVisitor final : public VNVisitor { iterateLogic(nodep); } void visit(AstSenItem* nodep) override { iterateLogic(nodep); } - void visit(AstArraySel* nodep) override { - m_selIndices.push_back(nodep->bitp()); - iterateChildren(nodep); - UASSERT_OBJ(m_selIndices.size(), nodep, "Underflow"); - m_selIndices.pop_back(); - } void visit(AstVarRef* nodep) override { if (ForceState::isNotReplaceable(nodep)) return; @@ -554,12 +575,26 @@ class ForceReplaceVisitor final : public VNVisitor { if (ForceState::ForceComponentsVarScope* const fcp = m_state.tryGetForceComponents(nodep)) { FileLine* const flp = nodep->fileline(); - std::vector reversedIndices(m_selIndices.size()); - std::reverse_copy(m_selIndices.begin(), m_selIndices.end(), - reversedIndices.begin()); - AstNodeExpr* const lhsp = ForceState::ForceComponentsVarScope::applySelects( - new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE}, reversedIndices); - AstNodeExpr* const rhsp = fcp->forcedUpdate(nodep->varScopep(), reversedIndices); + AstVarRef* const lhsRefp = new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE}; + AstNodeExpr* lhsp; + AstNodeExpr* rhsp; + if (nodep->dtypep()->skipRefp()->isIntegralOrPacked()) { + rhsp = fcp->forcedUpdate(nodep->varScopep()); + lhsp = lhsRefp; + } else { + AstNodeExpr* wholeExprp = nodep; + while (VN_IS(wholeExprp->backp(), NodeExpr)) { + wholeExprp = VN_AS(wholeExprp->backp(), NodeExpr); + // wholeExprp should never be ExprStmt, because: + // * if nodep is inside stmtsp() of one, we should sooner get NodeStmt node + // * nodep should never be in resultp(), because it is a WRITE reference + // and resultp() should be an rvalue + UASSERT_OBJ(!VN_IS(wholeExprp, ExprStmt), nodep, "Unexpected AstExprStmt"); + } + lhsp = ForceState::ForceComponentsVarScope::wrapIntoExprp(lhsRefp, wholeExprp, + nodep); + rhsp = fcp->forcedUpdate(nodep->varScopep(), wholeExprp, nodep); + } m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp}); } // Emit valVscp update after each write to any VarRef on forced RHS. diff --git a/test_regress/t/t_force_readwrite_unsup.out b/test_regress/t/t_force_readwrite_unsup.out index a7423e077..e8f793171 100644 --- a/test_regress/t/t_force_readwrite_unsup.out +++ b/test_regress/t/t_force_readwrite_unsup.out @@ -1,8 +1,8 @@ -%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:25:18: Unsupported: Signals used via read-write reference cannot be forced - 25 | cls.take_ref(a); +%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:19:18: Unsupported: Signals used via read-write reference cannot be forced + 19 | cls.take_ref(a); | ^ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:26:18: Unsupported: Signals used via read-write reference cannot be forced - 26 | cls.take_ref(b); +%Error-UNSUPPORTED: t/t_force_readwrite_unsup.v:20:18: Unsupported: Signals used via read-write reference cannot be forced + 20 | cls.take_ref(b); | ^ %Error: Exiting due to diff --git a/test_regress/t/t_force_readwrite_unsup.v b/test_regress/t/t_force_readwrite_unsup.v index 6672c2b71..799938d4a 100644 --- a/test_regress/t/t_force_readwrite_unsup.v +++ b/test_regress/t/t_force_readwrite_unsup.v @@ -4,12 +4,6 @@ // SPDX-FileCopyrightText: 2025 Antmicro // SPDX-License-Identifier: CC0-1.0 -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro -// SPDX-License-Identifier: CC0-1.0 - class Cls; task take_ref(ref logic s); endtask diff --git a/test_regress/t/t_force_unpacked_struct.py b/test_regress/t/t_force_unpacked_struct.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_force_unpacked_struct.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_force_unpacked_struct.v b/test_regress/t/t_force_unpacked_struct.v new file mode 100644 index 000000000..fb404a23f --- /dev/null +++ b/test_regress/t/t_force_unpacked_struct.v @@ -0,0 +1,83 @@ +// 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 checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + +module t ( + input clk +); + + integer cyc = 0; + + typedef struct { + int x; + logic y; + int arr[5]; + } struct_t; + + struct_t s_array[3]; + struct_t my_struct; + + // Test loop + always @(posedge clk) begin + cyc <= cyc + 1; + if (cyc == 0) begin + s_array[1].x = 1; + s_array[1].arr[2] = 1; + my_struct.x <= 1; + end + else if (cyc == 1) begin + `checkh(s_array[1].x, 1); + `checkh(s_array[1].arr[2], 1); + `checkh(my_struct.x, 1); + end + else if (cyc == 2) begin + force s_array[1].x = 0; + force s_array[1].arr[2] = 2; + force my_struct.x = 0; + end + else if (cyc == 3) begin + `checkh(s_array[1].x, 0); + s_array[1].x = 1; + `checkh(s_array[1].arr[2], 2); + s_array[1].arr[2] = 3; + `checkh(my_struct.x, 0); + my_struct.x <= 1; + end + else if (cyc == 4) begin + `checkh(s_array[1].x, 0); + `checkh(s_array[1].arr[2], 2); + `checkh(my_struct.x, 0); + end + else if (cyc == 5) begin + release s_array[1].x; + release s_array[1].arr[2]; + release my_struct.x; + end + else if (cyc == 6) begin + `checkh(s_array[1].x, 0); + s_array[1].x = 1; + `checkh(s_array[1].arr[2], 2); + s_array[1].arr[2] = 4; + `checkh(my_struct.x, 0); + my_struct.x <= 1; + end + else if (cyc == 7) begin + `checkh(s_array[1].x, 1); + `checkh(s_array[1].arr[2], 4); + `checkh(my_struct.x, 1); + end + else if (cyc == 8) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_force_unpacked_unsup.out b/test_regress/t/t_force_unpacked_unsup.out index a770c9520..89b68ef55 100644 --- a/test_regress/t/t_force_unpacked_unsup.out +++ b/test_regress/t/t_force_unpacked_unsup.out @@ -1,17 +1,8 @@ -%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:23:8: Unsupported: Force of unpacked array variable with elements of complex data type - 23 | real r_array[2]; - | ^~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:22:7: Unsupported: Force of unpacked array variable with >= 1000 elements +%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:22:7: Unsupported: Force of variable with >= 1000 unpacked elements 22 | bit big_array[40][40][40]; | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of unpacked array variable with >= 1000 elements + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of variable with >= 1000 unpacked elements 21 | struct_t s_array[3000]; | ^~~~~~~ -%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of unpacked array variable with elements of complex data type - 21 | struct_t s_array[3000]; - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:24:12: Unsupported: Force of unpacked struct / union variable - 24 | struct_t my_struct; - | ^~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_force_unpacked_unsup.v b/test_regress/t/t_force_unpacked_unsup.v index 63ef45b67..27562ed45 100644 --- a/test_regress/t/t_force_unpacked_unsup.v +++ b/test_regress/t/t_force_unpacked_unsup.v @@ -1,7 +1,7 @@ // DESCRIPTION: Verilator: Verilog Test module // // This file ONLY is placed under the Creative Commons Public Domain -// SPDX-FileCopyrightText: 2025 Antmicro +// SPDX-FileCopyrightText: 2026 Antmicro // SPDX-License-Identifier: CC0-1.0 // verilog_format: off @@ -21,7 +21,6 @@ module t ( struct_t s_array[3000]; bit big_array[40][40][40]; real r_array[2]; - struct_t my_struct; // Test loop always @(posedge clk) begin @@ -30,19 +29,16 @@ module t ( r_array[0] <= 1; big_array[1][2][3] <= 1; s_array[1].x <= 1; - my_struct.x <= 1; end else if (cyc == 1) begin `checkr(r_array[0], 1); `checkr(big_array[1][2][3], 1); `checkh(s_array[1].x, 1); - `checkh(my_struct.x, 1); end else if (cyc == 2) begin force r_array[0] = 0; force big_array[1][2][3] = 0; force s_array[1].x = 0; - force my_struct.x = 0; end else if (cyc == 3) begin `checkr(r_array[0], 0); @@ -51,20 +47,16 @@ module t ( big_array[1][2][3] <= 1; `checkh(s_array[1].x, 0); s_array[1].x <= 1; - `checkh(my_struct.x, 0); - my_struct.x <= 1; end else if (cyc == 4) begin `checkr(r_array[0], 0); `checkr(big_array[1][2][3], 0); `checkh(s_array[1].x, 0); - `checkh(my_struct.x, 0); end else if (cyc == 5) begin release r_array[0]; release big_array[1][2][3]; release s_array[1].x; - release my_struct.x; end else if (cyc == 6) begin `checkr(r_array[0], 0); @@ -73,14 +65,11 @@ module t ( big_array[1][2][3] <= 1; `checkh(s_array[1].x, 0); s_array[1].x <= 1; - `checkh(my_struct.x, 0); - my_struct.x <= 1; end else if (cyc == 7) begin `checkr(r_array[0], 1); `checkr(big_array[1][2][3], 1); `checkh(s_array[1].x, 1); - `checkh(my_struct.x, 1); end else if (cyc == 8) begin $write("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_forceable_string_bad.out b/test_regress/t/t_forceable_string_bad.out index 6fb21ca01..6735c1393 100644 --- a/test_regress/t/t_forceable_string_bad.out +++ b/test_regress/t/t_forceable_string_bad.out @@ -2,4 +2,8 @@ 8 | string str /*verilator forceable*/; | ^~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_forceable_string_bad.v:8:10: Forcing variable of unsupported type: BASICDTYPE 'string' + 8 | string str /*verilator forceable*/; + | ^~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest %Error: Exiting due to