diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 0ba178158..7a8f065b0 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -956,6 +956,13 @@ List Of Warnings port list, since these are references to interfaces/modports declared at a higher level and are already specialized. These types of accesses do not require waiving HIERPARAM. +.. option:: IEEEMAYDEPRECATE + + This feature is not yet deprecated, but may be in a future version of the IEEE standard. + This warning is to alert users that they may want to avoid using this feature, as it + may be removed in a future version of the IEEE standard, and thus may not be supported + in future versions of Verilator. + .. option:: IFDEPTH Warns that if/if else statements have exceeded the depth specified with diff --git a/src/V3AstNodeStmt.h b/src/V3AstNodeStmt.h index 89b46011e..37def443a 100644 --- a/src/V3AstNodeStmt.h +++ b/src/V3AstNodeStmt.h @@ -539,6 +539,16 @@ public: } // but isPure() true }; +class AstDeassign final : public AstNodeStmt { + // Procedural 'deassign' statement + // @astgen op1 := lhsp : AstNodeExpr +public: + AstDeassign(FileLine* fl, AstNodeExpr* lhsp) + : ASTGEN_SUPER_Deassign(fl) { + this->lhsp(lhsp); + } + ASTGEN_MEMBERS_AstDeassign; +}; class AstDelay final : public AstNodeStmt { // Delay statement // @astgen op1 := lhsp : AstNodeExpr // Delay value (or min for range) diff --git a/src/V3Error.h b/src/V3Error.h index 07feb51df..cd12fca42 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -113,6 +113,7 @@ public: GENUNNAMED, // Generate unnamed, without label HIERBLOCK, // Ignored hierarchical block setting HIERPARAM, // Parameter using hierarchical value + IEEEMAYDEPRECATE, // Feature may be deprecated in future IEEE standard IFDEPTH, // If statements too deep IGNOREDRETURN, // Ignoring return value (function as task) IMPERFECTSCH, // Imperfect schedule (disabled by default). Historical, never issued. @@ -225,20 +226,21 @@ public: "CDCRSTLOGIC", "CLKDATA", "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", "DECLFILENAME", "DEFOVERRIDE", "DEFPARAM", "DEPRECATED", "ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE", "FSMMULTI", - "FUNCTIMECTL", "FUTURE", "GENCLK", "GENUNNAMED", "HIERBLOCK", "HIERPARAM", "IFDEPTH", - "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", - "INCABSPATH", "INFINITELOOP", "INITIALDLY", "INSECURE", "INSIDETRUE", "LATCH", - "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", - "MULTITOP", "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", "NORETURN", "NULLPORT", - "PARAMNODEFAULT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", - "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", - "PROTOTYPEMIS", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", - "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", "SPLITVAR", "STATICVAR", "STMTDLY", - "SUPERNFIRST", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", - "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSATCONSTR", "UNSIGNED", "UNUSED", - "UNUSEDGENVAR", "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", - "USERINFO", "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", - "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; + "FUNCTIMECTL", "FUTURE", "GENCLK", "GENUNNAMED", "HIERBLOCK", "HIERPARAM", + "IEEEMAYDEPRECATE", "IFDEPTH", "IGNOREDRETURN", "IMPERFECTSCH", "IMPLICIT", + "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH", "INFINITELOOP", "INITIALDLY", + "INSECURE", "INSIDETRUE", "LATCH", "LITENDIAN", "MINTYPMAXDLY", "MISINDENT", "MODDUP", + "MODMISSING", "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOEFFECT", "NOLATCH", "NONSTD", + "NORETURN", "NULLPORT", "PARAMNODEFAULT", "PINCONNECTEMPTY", "PINMISSING", + "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", + "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "PROTOTYPEMIS", "RANDC", "REALCVT", + "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", + "SPLITVAR", "STATICVAR", "STMTDLY", "SUPERNFIRST", "SYMRSVDWORD", "SYNCASYNCNET", + "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", + "UNPACKED", "UNSATCONSTR", "UNSIGNED", "UNUSED", "UNUSEDGENVAR", "UNUSEDLOOP", + "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", "USERFATAL", "USERINFO", "USERWARN", + "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", + "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; return names[m_e]; } // Warnings that default to off diff --git a/src/V3Force.cpp b/src/V3Force.cpp index f6306947a..8e0e7a584 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -138,9 +138,14 @@ private: const VNUser2InUse m_user2InUse; std::unordered_map m_varInfo; + std::unordered_set m_clockedWrites; + std::unordered_map> m_rhsDepToForces; + bool m_doingAssign = false; // If true, we're processing procedural continuous assign + // statements instead of force statements public: - ForceState() = default; + ForceState(bool doingAssign) + : m_doingAssign{doingAssign} {} VL_UNCOPYABLE(ForceState); // STATIC METHODS @@ -197,6 +202,26 @@ public: static bool isNotReplaceable(const AstVarRef* const nodep) { return nodep->user1(); } static void markNonReplaceable(AstVarRef* const nodep) { nodep->user1SetOnce(); } + static std::vector forceInfosInIdOrder(VarForceInfo& info) { + std::vector forceps; + forceps.reserve(info.m_forces.size()); + for (auto& it : info.m_forces) forceps.push_back(&it.second); + std::sort(forceps.begin(), forceps.end(), [](const ForceInfo* ap, const ForceInfo* bp) { + return ap->m_forceId < bp->m_forceId; + }); + return forceps; + } + + static std::vector forceInfosInIdOrder(const VarForceInfo& info) { + std::vector forceps; + forceps.reserve(info.m_forces.size()); + for (const auto& it : info.m_forces) forceps.push_back(&it.second); + std::sort(forceps.begin(), forceps.end(), [](const ForceInfo* ap, const ForceInfo* bp) { + return ap->m_forceId < bp->m_forceId; + }); + return forceps; + } + static bool isOpaquePathSelector(const AstNode* nodep) { return VN_IS(nodep, Sel) || VN_IS(nodep, NodeSel) || VN_IS(nodep, StructSel); } @@ -270,6 +295,30 @@ public: return info; } + AstNodeExpr* addRhsValueReads(const VarForceInfo& varInfo, AstNodeExpr* exprp) const { + if (!doingAssign()) return exprp; + + const std::vector forceps = forceInfosInIdOrder(varInfo); + if (forceps.empty()) return exprp; + + // VlForceVec stores pointers to RHS shadows, so expose those reads to scheduling. + AstCExpr* const cexprp = new AstCExpr{exprp->fileline(), AstCExpr::Pure{}}; + cexprp->dtypeFrom(exprp); + cexprp->add("("); + for (const ForceInfo* const finfop : forceps) { + UASSERT_OBJ(finfop->m_rhsVarVscp, exprp, "No RHS var for forced variable"); + AstVarRef* const refp + = new AstVarRef{exprp->fileline(), finfop->m_rhsVarVscp, VAccess::READ}; + markNonReplaceable(refp); + cexprp->add("(void)("); + cexprp->add(refp); + cexprp->add("), "); + } + cexprp->add(exprp); + cexprp->add(")"); + return cexprp; + } + AstNodeExpr* createForceReadCall(const VarForceInfo& varInfo, FileLine* flp, VCMethod method, AstNodeExpr* originalExprp, AstNode* dtypeFromp, AstNodeExpr* indexExprp) const { @@ -277,7 +326,8 @@ public: originalExprp->foreach( [](AstVarRef* const refp) { ForceState::markNonReplaceable(refp); }); - AstNodeExpr* const origValp = castToNodeDType(originalExprp, dtypeFromp); + AstNodeExpr* const origValp + = addRhsValueReads(varInfo, castToNodeDType(originalExprp, dtypeFromp)); AstCMethodHard* const callp = new AstCMethodHard{ flp, new AstVarRef{flp, varInfo.m_forceVecVscp, VAccess::READ}, method, origValp}; @@ -309,6 +359,11 @@ public: VarForceInfo& getOrCreateVarInfo(AstVar* varp) { return m_varInfo[varp]; } + void markClockedWrite(AstVar* varp) { m_clockedWrites.insert(varp); } + bool hasClockedWrite(AstVar* varp) const { return m_clockedWrites.count(varp); } + + bool doingAssign() const { return m_doingAssign; } + const VarForceInfo* getVarInfo(AstVar* varp) const { const auto it = m_varInfo.find(varp); return it != m_varInfo.end() ? &it->second : nullptr; @@ -331,8 +386,9 @@ public: AstCDType* const forceVecDtypep = new AstCDType{flp, "VlForceVec"}; v3Global.rootp()->typeTablep()->addTypesp(forceVecDtypep); - AstVar* const forceVecVarp - = new AstVar{flp, VVarType::MEMBER, varp->name() + "__VforceVec", forceVecDtypep}; + AstVar* const forceVecVarp = new AstVar{ + flp, VVarType::MEMBER, + varp->name() + (m_doingAssign ? "_VassignVec" : "__VforceVec"), forceVecDtypep}; forceVecVarp->funcLocal(false); forceVecVarp->isInternal(true); varp->addNextHere(forceVecVarp); @@ -340,8 +396,22 @@ public: scopep->addVarsp(info.m_forceVecVscp); } - info.m_forces.emplace(forceStmtp, ForceInfo{rangeLsb, rangeMsb, padLsb, padMsb, forceId, + auto pair = info.m_forces.emplace(forceStmtp, + ForceInfo{rangeLsb, rangeMsb, padLsb, padMsb, forceId, hasArraySel, nullptr, rhsExprp}); + ForceInfo& finfo = pair.first->second; + if (doingAssign()) { + std::vector depVarps; + finfo.m_rhsExprp->foreach([&](AstVarRef* const refp) { + if (!refp->access().isReadOnly()) return; + AstVar* const depVarp = refp->varp(); + if (depVarp + && std::find(depVarps.begin(), depVarps.end(), depVarp) == depVarps.end()) { + depVarps.push_back(depVarp); + } + }); + for (AstVar* const depVarp : depVarps) m_rhsDepToForces[depVarp].push_back(&finfo); + } UINFO(3, "Added force ID " << forceId << " for " << varp->name() << " [" << rangeMsb << ":" << rangeLsb << "]\n"); @@ -413,14 +483,7 @@ public: UASSERT_OBJ(scopep, varp, "Missing scope for force RHS vars"); FileLine* const flp = varp->fileline(); - // Process force entries in stable force-id order. - std::vector forceps; - forceps.reserve(info.m_forces.size()); - for (auto& fit : info.m_forces) forceps.push_back(&fit.second); - std::sort(forceps.begin(), forceps.end(), - [](const ForceInfo* ap, const ForceInfo* bp) { - return ap->m_forceId < bp->m_forceId; - }); + const std::vector forceps = forceInfosInIdOrder(info); for (ForceInfo* const finfop : forceps) { ForceInfo& finfo = *finfop; @@ -429,7 +492,8 @@ public: // Create per-force temporary storage for the captured RHS value. AstVar* const rhsVarp = new AstVar{flp, VVarType::VAR, - varp->name() + "__VforceRHS" + std::to_string(finfo.m_forceId), + varp->name() + (doingAssign() ? "_VassignRHS" : "__VforceRHS") + + std::to_string(finfo.m_forceId), finfo.m_rhsExprp->dtypep()}; rhsVarp->noSubst(true); rhsVarp->sigPublic(true); @@ -514,6 +578,30 @@ public: } } + AstNode* createRhsUpdatesForWrite(FileLine* flp, AstVar* writtenVarp) const { + if (!doingAssign()) return nullptr; + + const auto it = m_rhsDepToForces.find(writtenVarp); + if (it == m_rhsDepToForces.end()) return nullptr; + + AstNode* headp = nullptr; + AstNode* tailp = nullptr; + for (const ForceInfo* const finfop : it->second) { + UASSERT_OBJ(finfop->m_rhsVarVscp, writtenVarp, "No RHS var for forced variable"); + UASSERT_OBJ(finfop->m_rhsExprp, writtenVarp, "Missing RHS expression"); + AstAssign* const updatep + = new AstAssign{flp, new AstVarRef{flp, finfop->m_rhsVarVscp, VAccess::WRITE}, + finfop->m_rhsExprp->cloneTreePure(false)}; + if (tailp) { + tailp->addNextHere(updatep); + } else { + headp = updatep; + } + tailp = updatep; + } + return headp; + } + const ForceInfo& getForceInfo(AstAssignForce* forceStmtp) const { AstVar* varp = getOneVarRef(forceStmtp->lhsp())->varp(); auto it = m_varInfo.find(varp); @@ -551,11 +639,30 @@ public: } }; +// Split deassign concat LHS before converting to release internals. +static void splitDeassign(AstDeassign* nodep) { + AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat); + if (!concatp) return; + + FileLine* const flp = nodep->fileline(); + AstDeassign* const newLp = new AstDeassign{flp, concatp->lhsp()->unlinkFrBack()}; + AstDeassign* const newRp = new AstDeassign{flp, concatp->rhsp()->unlinkFrBack()}; + AstNodeExpr* const conp = concatp->unlinkFrBack(); + nodep->replaceWith(newLp); + newLp->addNextHere(newRp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + VL_DO_DANGLING(conp->deleteTree(), conp); + + splitDeassign(newLp); + splitDeassign(newRp); +} + //###################################################################### // ForceDiscoveryVisitor - Discover force statements class ForceDiscoveryVisitor final : public VNVisitorConst { ForceState& m_state; + bool m_inClockedActive = false; void visit(AstAssignForce* nodep) override { if (nodep->user2()) return; // External force statements are pre-registered. @@ -599,6 +706,21 @@ class ForceDiscoveryVisitor final : public VNVisitorConst { rangeInfo.m_padMsb, rangeInfo.m_hasArraySel); } + void visit(AstAssign* nodep) override { + if (m_state.doingAssign() && m_inClockedActive) { + if (AstVarRef* const lhsp = VN_CAST(nodep->lhsp(), VarRef)) { + m_state.markClockedWrite(lhsp->varp()); + } + } + iterateChildrenConst(nodep); + } + + void visit(AstActive* nodep) override { + VL_RESTORER(m_inClockedActive); + m_inClockedActive = nodep->hasClocked(); + iterateChildrenConst(nodep); + } + void visit(AstVarScope* nodep) override { if (nodep->varp()->isForceable()) { if (VN_IS(nodep->varp()->dtypeSkipRefp(), UnpackArrayDType)) { @@ -870,7 +992,12 @@ class ForceConvertVisitor final : public VNVisitor { // IEEE 1800-2023 10.6.2: When released, if the variable is not continuously driven, // it maintains its current value until the next procedural assignment. - if (!releasedVarp->isContinuously()) { + const bool fullBitwiseRelease + = ForceState::isBitwiseDType(releasedVarp) && !rangeInfo.m_hasArraySel && !selp + && rangeInfo.m_rangeLsb == 0 && rangeInfo.m_rangeMsb == releasedVarp->width() - 1; + if (!releasedVarp->isContinuously() + && !(m_state.doingAssign() && m_state.hasClockedWrite(releasedVarp) + && fullBitwiseRelease)) { // Member/struct paths on non-bitwise types do not lower to a plain VarRef/bit range, // so their current forced value is recovered via the same synthetic path index. // if (!continuously_driven) lhs = force_read_current(lhs_path); @@ -936,7 +1063,20 @@ class ForceReplaceVisitor final : public VNVisitor { m_stmtp = nodep; iterate(nodep->lhsp()); iterate(nodep->rhsp()); + if (AstVarRef* const lhsp = VN_CAST(AstArraySel::baseFromp(nodep->lhsp(), true), VarRef)) { + if (AstNode* const updatep + = m_state.createRhsUpdatesForWrite(nodep->fileline(), lhsp->varp())) { + nodep->addNextHere(updatep); + } + } } + void visit(AstAssignCont* nodep) override { + VL_RESTORER(m_stmtp); + m_stmtp = nodep; + iterateAndNextNull(nodep->timingControlp()); + iterate(nodep->rhsp()); + } + void visit(AstDeassign*) override {} void visit(AstCFunc* nodep) override { iterateLogic(nodep); } void visit(AstCoverToggle* nodep) override { iterateLogic(nodep); } void visit(AstNodeProcedure* nodep) override { iterateLogic(nodep); } @@ -1074,10 +1214,47 @@ public: void V3Force::forceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":\n"); if (!v3Global.hasForceableSignals()) return; - ForceState state; + ForceState state{false}; { ForceDiscoveryVisitor{nodep, state}; } state.finalizeRhsVars(); { ForceConvertVisitor{nodep, state}; } { ForceReplaceVisitor{nodep, state}; } V3Global::dumpCheckGlobalTree("force", 0, dumpTreeEitherLevel() >= 3); } + +void V3Force::assignAll(AstNetlist* nodep) { + UINFO(2, __FUNCTION__ << ":\n"); + if (!v3Global.hasAssignDeassign()) return; + + std::vector deassignps; + nodep->foreach([&](AstDeassign* deassignp) { deassignps.push_back(deassignp); }); + for (AstDeassign* const deassignp : deassignps) splitDeassign(deassignp); + + std::vector assignContps; + deassignps.clear(); + nodep->foreach([&](AstNodeStmt* nodep) { + if (AstAssignCont* const assignContp = VN_CAST(nodep, AssignCont)) { + assignContps.push_back(assignContp); + } else if (AstDeassign* const deassignp = VN_CAST(nodep, Deassign)) { + deassignps.push_back(deassignp); + } + }); + + for (AstAssignCont* const assignp : assignContps) { + assignp->replaceWith(new AstAssignForce{assignp->fileline(), + assignp->lhsp()->unlinkFrBack(), + assignp->rhsp()->unlinkFrBack()}); + assignp->deleteTree(); + } + for (AstDeassign* const deassignp : deassignps) { + deassignp->replaceWith( + new AstRelease{deassignp->fileline(), deassignp->lhsp()->cloneTreePure(true)}); + deassignp->deleteTree(); + } + ForceState state{true}; + { ForceDiscoveryVisitor{nodep, state}; } + state.finalizeRhsVars(); + { ForceConvertVisitor{nodep, state}; } + { ForceReplaceVisitor{nodep, state}; } + V3Global::dumpCheckGlobalTree("assign-deassign", 0, dumpTreeEitherLevel() >= 3); +} diff --git a/src/V3Force.h b/src/V3Force.h index 26b50d9c9..01f84f6a0 100644 --- a/src/V3Force.h +++ b/src/V3Force.h @@ -28,6 +28,7 @@ class AstNetlist; class V3Force final { public: static void forceAll(AstNetlist* nodep) VL_MT_DISABLED; + static void assignAll(AstNetlist* nodep) VL_MT_DISABLED; }; #endif // Guard diff --git a/src/V3Global.h b/src/V3Global.h index 44930c2aa..52f4af3f6 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -128,6 +128,7 @@ class V3Global final { bool m_usesForce = false; // Design uses force/release statements bool m_usesZeroDelay = false; // Design uses #0 delay (or non-constant delay) bool m_hasForceableSignals = false; // Need to apply V3Force pass + bool m_hasAssignDeassign = false; // Need to apply V3Force pass for assign/deassign statements bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted bool m_useParallelBuild = false; // Use parallel build for model bool m_useRandSequence = false; // Has `randsequence` @@ -206,6 +207,8 @@ public: void setUsesZeroDelay() { m_usesZeroDelay = true; } bool hasForceableSignals() const { return m_hasForceableSignals; } void setHasForceableSignals() { m_hasForceableSignals = true; } + bool hasAssignDeassign() const { return m_hasAssignDeassign; } + void setHasAssignDeassign() { m_hasAssignDeassign = true; } bool usesForce() const { return m_usesForce; } void setUsesForce() { m_usesForce = true; } bool hasSystemCSections() const VL_MT_SAFE { return m_hasSystemCSections; } diff --git a/src/V3LinkJump.cpp b/src/V3LinkJump.cpp index 28d97d941..63f0187cd 100644 --- a/src/V3LinkJump.cpp +++ b/src/V3LinkJump.cpp @@ -241,6 +241,7 @@ class LinkJumpVisitor final : public VNVisitor { new AstClassRefDType{fl, v3Global.rootp()->stdPackageClassp(), nullptr}, nullptr}}; processQueuep->lifetime(VLifetime::STATIC_EXPLICIT); processQueuep->processQueue(true); + processQueuep->setIgnoreSchedWrite(); topPkgp->addStmtsp(processQueuep); return processQueuep; } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 5a0613383..825665e52 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -169,6 +169,13 @@ class LinkLValueVisitor final : public VNVisitor { m_setForcedByCode = true; iterateAndNextNull(nodep->lhsp()); } + void visit(AstDeassign* nodep) override { + VL_RESTORER(m_setRefLvalue); + VL_RESTORER(m_setContinuously); + m_setRefLvalue = VAccess::WRITE; + m_setContinuously = false; + iterateAndNextNull(nodep->lhsp()); + } void visit(AstFireEvent* nodep) override { VL_RESTORER(m_setRefLvalue); m_setRefLvalue = VAccess::WRITE; diff --git a/src/V3OrderGraphBuilder.cpp b/src/V3OrderGraphBuilder.cpp index c16ef8941..48a65d7a7 100644 --- a/src/V3OrderGraphBuilder.cpp +++ b/src/V3OrderGraphBuilder.cpp @@ -105,6 +105,7 @@ class OrderGraphBuilder final : public VNVisitor { bool m_inPre = false; // Underneath AlwaysPre bool m_inPost = false; // Underneath AstAlwaysPost std::function m_readTriggersCombLogic; + V3Sched::util::VarScopeSet m_forceReadEdgeIgnores; // METHODS @@ -112,12 +113,16 @@ class OrderGraphBuilder final : public VNVisitor { UASSERT_OBJ(!m_logicVxp, nodep, "Should not nest"); // Reset VarUsage AstNode::user2ClearTree(); + m_forceReadEdgeIgnores.clear(); + if (!m_inClocked) + V3Sched::util::collectForceReadEdgeIgnores(nodep, m_forceReadEdgeIgnores); // Create LogicVertex for this logic node m_logicVxp = new OrderLogicVertex{m_graphp, m_scopep, m_domainp, m_hybridp, nodep}; // Gather variable dependencies based on usage iterateChildren(nodep); // Finished with this logic m_logicVxp = nullptr; + m_forceReadEdgeIgnores.clear(); } OrderVarVertex* getVarVertex(AstVarScope* varscp, VarVertexType type) { @@ -207,6 +212,7 @@ class OrderGraphBuilder final : public VNVisitor { // latch?). con = false; } + if (!m_inClocked && m_forceReadEdgeIgnores.count(varscp)) con = false; } // Note: See V3OrderGraph.h about the roles of the various vertex types diff --git a/src/V3Sched.h b/src/V3Sched.h index 0c5907886..26697813a 100644 --- a/src/V3Sched.h +++ b/src/V3Sched.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -33,6 +34,49 @@ class SenExprBuilder; namespace V3Sched { +namespace util { +using VarScopeSet = std::unordered_set; + +inline bool isVlForceVec(const AstVarScope* vscp) { + const AstCDType* const dtypep = VN_CAST(vscp->dtypep()->skipRefp(), CDType); + return dtypep && dtypep->name() == "VlForceVec"; +} + +inline bool isForceReadMethod(VCMethod method) { + return method == VCMethod::FORCE_READ || method == VCMethod::FORCE_READ_INDEX; +} + +inline void collectForceReadEdgeIgnores(AstNode* nodep, VarScopeSet& out) { + VarScopeSet writtenForceVecs; + VarScopeSet writtenVars; + + nodep->foreach([&](const AstVarRef* refp) { + AstVarScope* const vscp = refp->varScopep(); + if (!refp->access().isWriteOrRW()) return; + if (!refp->varp()->ignoreSchedWrite()) writtenVars.emplace(vscp); + if (!isVlForceVec(vscp)) return; + writtenForceVecs.emplace(vscp); + if (!refp->varp()->ignoreSchedWrite()) out.emplace(vscp); + }); + + if (writtenForceVecs.empty() || writtenVars.empty()) return; + + nodep->foreach([&](const AstCMethodHard* callp) { + if (!isForceReadMethod(callp->method())) return; + + const AstVarRef* const fromRefp = VN_CAST(callp->fromp(), VarRef); + if (!fromRefp || !writtenForceVecs.count(fromRefp->varScopep())) return; + + AstNodeExpr* const origp = callp->pinsp(); + if (!origp) return; + origp->foreach([&](const AstVarRef* refp) { + AstVarScope* const vscp = refp->varScopep(); + if (refp->access().isReadOrRW() && writtenVars.count(vscp)) out.emplace(vscp); + }); + }); +} +} // namespace util + //============================================================================ // Throughout scheduling, we need to keep hold of AstActive nodes, together with the AstScope that // they are under. LogicByScope is simply a vector of such pairs, with some additional convenience diff --git a/src/V3SchedAcyclic.cpp b/src/V3SchedAcyclic.cpp index 313364d3e..36a9cd096 100644 --- a/src/V3SchedAcyclic.cpp +++ b/src/V3SchedAcyclic.cpp @@ -152,6 +152,9 @@ std::unique_ptr buildGraph(const LogicByScope& lbs) { const VNUser2InUse user2InUse; const VNUser3InUse user3InUse; + V3Sched::util::VarScopeSet forceReadEdgeIgnores; + V3Sched::util::collectForceReadEdgeIgnores(nodep, forceReadEdgeIgnores); + nodep->foreach([&](AstVarRef* refp) { AstVarScope* const vscp = refp->varScopep(); SchedAcyclicVarVertex* const vvtxp = getVarVertex(vscp); @@ -164,7 +167,8 @@ std::unique_ptr buildGraph(const LogicByScope& lbs) { // If read, add var -> logic edge // Note: Use same heuristic as ordering does to ignore written variables // TODO: Use live variable analysis. - if (refp->access().isReadOrRW() && !vscp->user3SetOnce() && !vscp->user2()) + if (refp->access().isReadOrRW() && !vscp->user3SetOnce() && !vscp->user2() + && !forceReadEdgeIgnores.count(vscp)) addEdge(vvtxp, lvtxp, weight, true); }); } diff --git a/src/V3SchedPartition.cpp b/src/V3SchedPartition.cpp index 194a030ac..183c067a4 100644 --- a/src/V3SchedPartition.cpp +++ b/src/V3SchedPartition.cpp @@ -197,9 +197,12 @@ class SchedGraphBuilder final : public VNVisitor { } // Add edges based on references - nodep->foreach([this, logicVtxp](const AstVarRef* vrefp) { + V3Sched::util::VarScopeSet forceReadEdgeIgnores; + V3Sched::util::collectForceReadEdgeIgnores(nodep, forceReadEdgeIgnores); + nodep->foreach([this, logicVtxp, &forceReadEdgeIgnores](const AstVarRef* vrefp) { AstVarScope* const vscp = vrefp->varScopep(); - if (vrefp->access().isReadOrRW() && m_readTriggersThisLogic(vscp)) { + if (vrefp->access().isReadOrRW() && m_readTriggersThisLogic(vscp) + && !forceReadEdgeIgnores.count(vscp)) { new V3GraphEdge{m_graphp, getVarVertex(vscp), logicVtxp, 10}; } if (vrefp->access().isWriteOrRW() && !vrefp->varp()->ignoreSchedWrite()) { diff --git a/src/V3SchedReplicate.cpp b/src/V3SchedReplicate.cpp index 79596533e..35718b98a 100644 --- a/src/V3SchedReplicate.cpp +++ b/src/V3SchedReplicate.cpp @@ -217,15 +217,15 @@ std::unique_ptr buildGraph(const LogicRegions& logicRegions) { const VNUser2InUse user2InUse; const VNUser3InUse user3InUse; + V3Sched::util::VarScopeSet forceReadEdgeIgnores; + V3Sched::util::collectForceReadEdgeIgnores(nodep, forceReadEdgeIgnores); + nodep->foreach([&](AstVarRef* refp) { AstVarScope* const vscp = refp->varScopep(); SchedReplicateVarVertex* const vvtxp = getVarVertex(vscp); - - // If read, add var -> logic edge - // Note: Use same heuristic as ordering does to ignore written variables - // TODO: Use live variable analysis. if (refp->access().isReadOrRW() && !vscp->user3SetOnce() - && readTriggersThisLogic(vscp) && !vscp->user2()) { // + && readTriggersThisLogic(vscp) && !vscp->user2() + && !forceReadEdgeIgnores.count(vscp)) { // addEdge(vvtxp, lvtxp); } // If written, add logic -> var edge diff --git a/src/V3Width.cpp b/src/V3Width.cpp index dfc8d239b..d82ca6717 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6306,6 +6306,12 @@ class WidthVisitor final : public VNVisitor { UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?"); checkForceReleaseLhs(nodep, nodep->lhsp()); } + void visit(AstDeassign* nodep) override { + userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p()); + UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "L-value is untyped"); + UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "L-value width is unsized"); + checkForceReleaseLhs(nodep, nodep->lhsp()); + } static bool isFormatNonNumericArg(const AstNodeDType* dtypep) { dtypep = dtypep->skipRefp(); diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 3e1a94c37..651bff963 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -427,11 +427,6 @@ private: iterateAndNextNull(nodep->rhsp()); } editDType(nodep); - AstNode* const controlp - = nodep->timingControlp() ? nodep->timingControlp()->unlinkFrBack() : nullptr; - nodep->replaceWith(new AstAssign{nodep->fileline(), nodep->lhsp()->unlinkFrBack(), - nodep->rhsp()->unlinkFrBack(), controlp}); - VL_DO_DANGLING(pushDeletep(nodep), nodep); } void visit(AstAssignDly* nodep) override { iterateAndNextNull(nodep->timingControlp()); diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 683f71ad7..f67e830b2 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -425,6 +425,10 @@ static void process() { // forcing. V3Force::forceAll(v3Global.rootp()); + // Convert assign/deassign statements to forces on generated variables, so they can be + // handled by the same logic as regular force/release statements. + V3Force::assignAll(v3Global.rootp()); + // DFG optimization if (v3Global.opt.fDfg()) V3DfgOptimizer::optimize(v3Global.rootp()); diff --git a/src/verilog.y b/src/verilog.y index a8904701b..f8013b962 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3595,10 +3595,12 @@ statement_item: // IEEE: statement_item | fexprLvalue yP_LTE cycle_delay expr ';' { $$ = new AstAssignDly{$2, $1, $4, $3}; } //UNSUP cycle_delay fexprLvalue yP_LTE ';' { UNSUP } - | yASSIGN idClassSel '=' delay_or_event_controlE expr ';' - { $$ = new AstAssignCont{$1, $2, $5, $4}; } + | yASSIGN variable_lvalue '=' delay_or_event_controlE expr ';' + { $$ = new AstAssignCont{$1, $2, $5, $4}; + $1->v3warn(IEEEMAYDEPRECATE, "Feature may be deprecated in future IEEE standard"); v3Global.setHasAssignDeassign(); } | yDEASSIGN variable_lvalue ';' - { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); DEL($2); } + { $$ = new AstDeassign{$1, $2}; + $1->v3warn(IEEEMAYDEPRECATE, "Feature may be deprecated in future IEEE standard"); v3Global.setHasAssignDeassign(); } | yFORCE variable_lvalue '=' expr ';' { $$ = new AstAssignForce{$1, $2, $4}; v3Global.setHasForceableSignals(); } | yRELEASE variable_lvalue ';' diff --git a/test_regress/t/t_assign_cont_automatic_bad.out b/test_regress/t/t_assign_cont_automatic_bad.out index 0d32e5fca..d5a0ea5d6 100644 --- a/test_regress/t/t_assign_cont_automatic_bad.out +++ b/test_regress/t/t_assign_cont_automatic_bad.out @@ -1,3 +1,8 @@ +%Warning-IEEEMAYDEPRECATE: t/t_assign_cont_automatic_bad.v:14:7: Feature may be deprecated in future IEEE standard + 14 | assign g = signed'(l); + | ^~~~~~ + ... For warning description see https://verilator.org/warn/IEEEMAYDEPRECATE?v=latest + ... Use "/* verilator lint_off IEEEMAYDEPRECATE */" and lint_on around source to disable this message. %Error: t/t_assign_cont_automatic_bad.v:14:26: Automatic lifetime variable not allowed in continuous assignment (IEEE 1800-2023 6.21): 'l' : ... note: In instance 't' 14 | assign g = signed'(l); diff --git a/test_regress/t/t_lint_unsup_deassign_bad.py b/test_regress/t/t_assign_deassign_concat.py similarity index 68% rename from test_regress/t/t_lint_unsup_deassign_bad.py rename to test_regress/t/t_assign_deassign_concat.py index 971993c3a..b7d7b425d 100755 --- a/test_regress/t/t_lint_unsup_deassign_bad.py +++ b/test_regress/t/t_assign_deassign_concat.py @@ -4,14 +4,15 @@ # 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('linter') -test.top_filename = "t/t_lint_unsup_deassign.v" +test.scenarios('simulator') -test.lint(fails=True, expect_filename=test.golden_filename) +test.compile(verilator_flags2=["--binary --timing", "-Wno-IEEEMAYDEPRECATE"]) + +test.execute() test.passes() diff --git a/test_regress/t/t_assign_deassign_concat.v b/test_regress/t/t_assign_deassign_concat.v new file mode 100644 index 000000000..7557d093a --- /dev/null +++ b/test_regress/t/t_assign_deassign_concat.v @@ -0,0 +1,47 @@ +// 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); +// verilog_format: on + +module t; + reg a; + reg b; + reg c; + reg d; + reg control; + reg clock = 0; + + always @(posedge clock) {a, b, c, d} = 4'h3; + + always @(control) + if (control) + assign {a, b, c, d} = 4'h2; + else + deassign {a, b, c, d}; + + always begin + #2; + clock = ~clock; + end + + initial begin + #3; + `checkh({a, b, c, d}, 4'h3) + #2; + control = 1; + #1; + `checkh({a, b, c, d}, 4'h2) + #3; + control = 0; + #2; + `checkh({a, b, c, d}, 4'h3) + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_lint_unsup_deassign.py b/test_regress/t/t_assign_dff.py similarity index 72% rename from test_regress/t/t_lint_unsup_deassign.py rename to test_regress/t/t_assign_dff.py index 3afe39002..10f1e0290 100755 --- a/test_regress/t/t_lint_unsup_deassign.py +++ b/test_regress/t/t_assign_dff.py @@ -4,13 +4,15 @@ # 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.scenarios('simulator') -test.lint(verilator_flags2=["--bbox-unsup"]) +test.compile(verilator_flags2=["-Wno-IEEEMAYDEPRECATE"]) + +test.execute() test.passes() diff --git a/test_regress/t/t_assign_dff.v b/test_regress/t/t_assign_dff.v new file mode 100644 index 000000000..a7a04268d --- /dev/null +++ b/test_regress/t/t_assign_dff.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 + +`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); + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + integer cyc; initial cyc = 0; + + wire q; + logic d=0, clear, preset; + + dff flipflop(q, d, clear, preset, clk); + + //clear and preset signals are in inverted logic + always @ (posedge clk) begin + cyc <= cyc + 1; + if(cyc==0) begin + clear=1; + preset=0; + d=0; + end + else if(cyc==1) begin + `checkh(q, 1); + preset=1; + d=1; + end + else if(cyc==2) begin + `checkh(q, 1); + clear=0; + end + else if(cyc==3) begin + `checkh(q, 0); + preset=0; + end + else if(cyc==4) begin + `checkh(q, 0); + clear=1; + preset=1; + end + else if(cyc==5) begin + `checkh(q, 1); + d=0; + end + else if(cyc==6) begin + `checkh(q, 0); + d=1; + end + else if(cyc==7) begin + `checkh(q, 1); + end + else if (cyc==8) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +// From IEEE 1800-2023 10.6.1 + +module dff (q, d, clear, preset, clock); + output q; + input d, clear, preset, clock; + logic q; + + always @(clear or preset) + if (!clear) + assign q = 0; + else if (!preset) + assign q = 1; + else + deassign q; + always @(posedge clock) + q = d; +endmodule diff --git a/test_regress/t/t_force_assign.py b/test_regress/t/t_force_assign.py index 84b274f68..fbf657b89 100755 --- a/test_regress/t/t_force_assign.py +++ b/test_regress/t/t_force_assign.py @@ -4,14 +4,14 @@ # 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-FileCopyrightText: 2026 Wilson Snyder # SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 import vltest_bootstrap test.scenarios('simulator') -test.compile() +test.compile(timing_loop=True, verilator_flags2=["--timing", "-Wno-IEEEMAYDEPRECATE"]) test.execute() diff --git a/test_regress/t/t_force_assign.v b/test_regress/t/t_force_assign.v index a689c9008..f64273a28 100644 --- a/test_regress/t/t_force_assign.v +++ b/test_regress/t/t_force_assign.v @@ -1,24 +1,45 @@ // 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 +`define stop $stop +`define checkb(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='b%x exp='b%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +// verilog_format: on + module t; - reg [2:0] a = 0; - + reg [1:0] a=0, b=1; + reg [1:0] r; initial begin - a = 1; - if (a != 1) $stop; - - force a = 2; - if (a != 2) $stop; - - a = 3; - if (a != 2) $stop; - - $write("*-* All Finished *-*\n"); + r = 2'b00; + assign r = 2'b01; + `checkb(r, 2'b01) + r = 2'b00; // ignored + #1; `checkb(r, 2'b01) + deassign r; + `checkb(r, 2'b01) + r = 2'b00; + `checkb(r, 2'b00) + assign r = a; + `checkb(r, 2'b00) + a = 2'b01; + `checkb(r, 2'b01) + a = 2'b00; + `checkb(r, 2'b00) + force r = a + b; + a = 2'b00; b = 2'b00; + #1; `checkb(r, 2'b00) + a = 2'b01; b = 2'b01; + #1; `checkb(r, 2'b10) + assign r = b; // covered + r = 2'b11; // ignored + `checkb(r, 2'b10) + release r; + `checkb(r, 2'b01) + b = 2'b00; + `checkb(r, 2'b00) $finish; end - endmodule diff --git a/test_regress/t/t_force_assign_comb.out b/test_regress/t/t_force_assign_comb.out new file mode 100644 index 000000000..b1c626e8a --- /dev/null +++ b/test_regress/t/t_force_assign_comb.out @@ -0,0 +1,9 @@ +For: 0 +array size: 2 +array size: 0 +user task +do/while +while +repeat +repeat +forever diff --git a/test_regress/t/t_force_assign_comb.py b/test_regress/t/t_force_assign_comb.py new file mode 100755 index 000000000..64141e405 --- /dev/null +++ b/test_regress/t/t_force_assign_comb.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=["--timing", "-Wno-IEEEMAYDEPRECATE"]) + +test.execute(expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_force_assign_comb.v b/test_regress/t/t_force_assign_comb.v new file mode 100644 index 000000000..ccfc169e4 --- /dev/null +++ b/test_regress/t/t_force_assign_comb.v @@ -0,0 +1,103 @@ +// DESCRIPTION: Verilator: force/release and assign/deassign in combinational logic +// +// This file ONLY is placed under the Creative Commons Public Domain +// SPDX-FileCopyrightText: 2026 Antmicro +// SPDX-License-Identifier: CC0-1.0 + +// verilator lint_off ALWCOMBORDER +// verilator lint_off LATCH +// verilator lint_off MULTIDRIVEN +// verilator lint_off UNDRIVEN +// verilator lint_off UNUSEDSIGNAL +// verilator lint_off COMBDLY +// verilator lint_off WIDTHEXPAND +// verilator lint_off WIDTHTRUNC + +module t ( + input logic src, + output logic assign_out, + output logic comb_out, + output logic latch_out +); + logic assign_sig; + logic comb_sig; + logic latch_sig; + + reg a; + reg q, d; + event foo; + real rl; + int ar []; + int start = 0; + int stop = 1; + int step = 1; + int done = 0; + + task a_task; + real trl; + event tevt; + reg tvr; + $display("user task"); + endtask + + + always_comb begin : comb_force + comb_out = comb_sig; + force comb_sig = src; + release comb_sig; + end + + always_latch begin : latch_force + if (src) latch_out = latch_sig; + force latch_sig = src; + release latch_sig; + end + + always_comb begin : comb_assign + assign_out = assign_sig; + assign assign_sig = src; + deassign assign_sig; + end + + always_comb begin: blk_name + event int1, int2; + real intrl; + q <= d; + -> foo; + rl = 0.0; + rl <= 1.0; + ar = new [2]; + for (int idx = start; idx < stop; idx += step) $display("For: %0d", idx); + for (int idx = 0; done; idx = done + 1) $stop; + for (int idx = 0; idx; done = done + 1) $stop; + for (int idx = 0; idx; {done, idx} = done + 1) $stop; + for (int idx = 0; idx; idx <<= 1) $stop; + for (int idx = 0; idx; idx = idx << 1) $stop; + $display("array size: %0d", ar.size()); + ar.delete(); + $display("array size: %0d", ar.size()); + a_task; + assign a = 1'b0; + deassign a; + do $display("do/while"); + while (a); + force a = 1'b1; + release a; + while(a) begin + $display("while"); + a = 1'b0; + end + repeat(2) $display("repeat"); + disable out_name; + forever begin + $display("forever"); + disable blk_name; // This one should not generate a warning + end + end + + initial begin: out_name + #2 $stop; + end + + initial #10 $finish; +endmodule diff --git a/test_regress/t/t_force_input_assign_bad.out b/test_regress/t/t_force_input_assign_bad.out index bcdcd234c..62fdd44d0 100644 --- a/test_regress/t/t_force_input_assign_bad.out +++ b/test_regress/t/t_force_input_assign_bad.out @@ -1,3 +1,8 @@ +%Warning-IEEEMAYDEPRECATE: t/t_force_input_assign_bad.v:27:5: Feature may be deprecated in future IEEE standard + 27 | assign s3.i = 2; + | ^~~~~~ + ... For warning description see https://verilator.org/warn/IEEEMAYDEPRECATE?v=latest + ... Use "/* verilator lint_off IEEEMAYDEPRECATE */" and lint_on around source to disable this message. %Error-ASSIGNIN: t/t_force_input_assign_bad.v:20:8: Assigning to input/const variable: 'i' : ... note: In instance 't' 20 | s1.i = 2; diff --git a/test_regress/t/t_force_release.out b/test_regress/t/t_force_release.out deleted file mode 100644 index 11b13fbac..000000000 --- a/test_regress/t/t_force_release.out +++ /dev/null @@ -1,4 +0,0 @@ - 0 d=0,e=0 - 10 d=1,e=1 - 20 d=1,e=0 -%Error: t/t_force_release.v:39: got='h1 exp='h00000000 diff --git a/test_regress/t/t_force_release.py b/test_regress/t/t_force_release.py index e9f4a44c9..31b1f0e53 100755 --- a/test_regress/t/t_force_release.py +++ b/test_regress/t/t_force_release.py @@ -9,10 +9,10 @@ import vltest_bootstrap -test.scenarios('vlt') +test.scenarios('simulator') test.compile(verilator_flags2=["--binary"]) -test.execute(expect_filename=test.golden_filename) +test.execute() test.passes() diff --git a/test_regress/t/t_force_release.v b/test_regress/t/t_force_release.v index 6fcefb6af..fe78ccfcd 100644 --- a/test_regress/t/t_force_release.v +++ b/test_regress/t/t_force_release.v @@ -18,7 +18,7 @@ module t; initial begin $monitor("%d d=%b,e=%b", $stime, d, e); - assign d = a & b & c; + d = a & b & c; a = 1; b = 0; c = 1; @@ -30,11 +30,6 @@ module t; #10; release d; release e; - // TODO support procedural continuous assignments. - // - // As per IEEE 1800-2023 10.6.2, value of `d` should be updated - // after release. However, Verilator treats `assign` inside an initial block - // as procedural assign thus value update is not properly restored. #10; `checkh(d, 0); `checkh(e, 0); diff --git a/test_regress/t/t_func_redef.v b/test_regress/t/t_func_redef.v index d80248cc2..ad992902a 100644 --- a/test_regress/t/t_func_redef.v +++ b/test_regress/t/t_func_redef.v @@ -23,6 +23,6 @@ module t #( input [B-1:0] b; output logic [min(A,B)-1:0] c; - always_comb for (int i = 0; i < min(A, B); i++) assign c[i] = a[i] | b[i]; + always_comb for (int i = 0; i < min(A, B); i++) c[i] = a[i] | b[i]; endmodule diff --git a/test_regress/t/t_initial.v b/test_regress/t/t_initial.v index 196129ea1..97148bdef 100644 --- a/test_regress/t/t_initial.v +++ b/test_regress/t/t_initial.v @@ -13,7 +13,7 @@ module t ( `include "t_initial_inc.vh" // surefire lint_off STMINI - initial assign user_loaded_value = 1; + initial user_loaded_value = 1; initial _ranit = 0; diff --git a/test_regress/t/t_lint_unsup_deassign.v b/test_regress/t/t_lint_unsup_deassign.v deleted file mode 100644 index 5ac943cfd..000000000 --- a/test_regress/t/t_lint_unsup_deassign.v +++ /dev/null @@ -1,19 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain. -// SPDX-FileCopyrightText: 2016 Wilson Snyder -// SPDX-License-Identifier: CC0-1.0 - -module t ( - input wire rst -); - - integer q; - - // verilator lint_off LATCH - always @(*) - if (rst) assign q = 0; - else deassign q; - // verilator lint_on LATCH - -endmodule diff --git a/test_regress/t/t_lint_unsup_deassign_bad.out b/test_regress/t/t_lint_unsup_deassign_bad.out deleted file mode 100644 index 508551cda..000000000 --- a/test_regress/t/t_lint_unsup_deassign_bad.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_lint_unsup_deassign.v:16:10: Unsupported: Verilog 1995 deassign - 16 | else deassign q; - | ^~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to