diff --git a/docs/guide/languages.rst b/docs/guide/languages.rst index 1956c7c56..108e58212 100644 --- a/docs/guide/languages.rst +++ b/docs/guide/languages.rst @@ -336,6 +336,24 @@ Verilator does not support SEREs yet. All assertion and coverage statements must be simple expressions that complete in one cycle. +Force statement +--------------- + +Verilator supports the procedural `force` (and corresponding `release`) +statement. The behaviour of the `force` statement however does not entirely +comply with the IEEE 1800 SystemVerilog standard. According to the standard, +when a procedural statement of the form `force a = b;` is executed, the +simulation should behave as if from that point onwards, a continuous +assignment `assign a = b;` have been added to override the drivers of `a`. +More specifically: the value of `a` should be updated, whenever the value of +`b` changes, all the way until a `release a;` statement is executed. +Verilator instead evaluates the current value of `b` at the time the `force` +statement is executed, and forces `a` to that value, without updating it +until a new `force` or `release` statement is encountered that applies to +`a`. This non-standard behaviour is nevertheless consistent with some other +simulators. + + Encrypted Verilog ----------------- diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index aa2c33526..a711e9a27 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -71,29 +71,6 @@ AstNode::AstNode(VNType t, FileLine* fl) editCountInc(); } -AstNode* AstNode::usernp(int n) const { - switch (n) { - case 1: return user1p(); - case 2: return user2p(); - case 3: return user3p(); - case 4: return user4p(); - case 5: return user5p(); - } - v3fatalSrc("Bad Case"); - return nullptr; // LCOV_EXCL_LINE -} -void AstNode::usernp(int n, void* userp) { - switch (n) { - case 1: user1p(userp); return; - case 2: user2p(userp); return; - case 3: user3p(userp); return; - case 4: user4p(userp); return; - case 5: user5p(userp); return; - } - v3fatalSrc("Bad Case"); - VL_UNREACHABLE -} - AstNode* AstNode::abovep() const { // m_headtailp only valid at beginning or end of list // Avoid supporting at other locations as would require walking diff --git a/src/V3Ast.h b/src/V3Ast.h index 2d404953b..885c88039 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1511,14 +1511,6 @@ public: uint8_t brokenState() const { return m_brokenState; } void brokenState(uint8_t value) { m_brokenState = value; } - void prefetch() const { - ASTNODE_PREFETCH(m_op1p); - ASTNODE_PREFETCH(m_op2p); - ASTNODE_PREFETCH(m_op3p); - ASTNODE_PREFETCH(m_op4p); - ASTNODE_PREFETCH(m_nextp); - } - // Used by AstNode::broken() bool brokeExists() const { return V3Broken::isLinkable(this); } bool brokeExistsAbove() const { return brokeExists() && (m_brokenState >> 7); } @@ -1673,9 +1665,6 @@ public: static void user5ClearTree() { VNUser5InUse::clear(); } // Clear userp()'s across the entire tree // clang-format on - AstNode* usernp(int n) const; // Return user1..userN based on provided n - void usernp(int n, void* userp); // Set user1..userN based on provided n - vluint64_t editCount() const { return m_editCount; } void editCountInc() { m_editCount = ++s_editCntGbl; // Preincrement, so can "watch AstNode::s_editCntGbl=##" diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 3ccddb659..4529b7118 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -3559,11 +3559,10 @@ public: }; class AstAssignForce final : public AstNodeAssign { + // Procedural 'force' statement public: AstAssignForce(FileLine* fl, AstNode* lhsp, AstNode* rhsp) - : ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) { - v3Global.useForce(true); - } + : ASTGEN_SUPER_AssignForce(fl, lhsp, rhsp) {} ASTNODE_NODE_FUNCS(AssignForce) virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override { return new AstAssignForce{this->fileline(), lhsp, rhsp}; @@ -3571,22 +3570,15 @@ public: virtual bool brokeLhsMustBeLvalue() const override { return true; } }; -class AstAssignRelease final : public AstNodeAssign { - // Release is treated similar to an assign to `z +class AstRelease final : public AstNodeStmt { + // Procedural 'release' statement public: - // Only for use in parser, as V3Width needs to resolve the '0 width. - AstAssignRelease(FileLine* fl, VFlagChildDType, AstNode* lhsp) - : ASTGEN_SUPER_AssignRelease(fl, lhsp, new AstConst{fl, AstConst::StringToParse{}, "'0"}) { - v3Global.useForce(true); + AstRelease(FileLine* fl, AstNode* lhsp) + : ASTGEN_SUPER_Release(fl) { + setOp1p(lhsp); } - AstAssignRelease(FileLine* fl, AstNode* lhsp, AstNode* rhsp) - : ASTGEN_SUPER_AssignRelease(fl, lhsp, rhsp) {} - ASTNODE_NODE_FUNCS(AssignRelease) - virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override { - return new AstAssignRelease{this->fileline(), lhsp, rhsp}; - } - virtual bool brokeLhsMustBeLvalue() const override { return true; } - AstNode* lhsp() const { return op2p(); } + ASTNODE_NODE_FUNCS(Release); + AstNode* lhsp() const { return op1p(); } }; class AstAssignPre final : public AstNodeAssign { diff --git a/src/V3AstUserAllocator.h b/src/V3AstUserAllocator.h index d757ba680..3be1cf481 100644 --- a/src/V3AstUserAllocator.h +++ b/src/V3AstUserAllocator.h @@ -24,6 +24,7 @@ #include "V3Ast.h" #include +#include #include template class AstUserAllocatorBase VL_NOT_FINAL { @@ -92,11 +93,12 @@ protected: VL_UNCOPYABLE(AstUserAllocatorBase); public: - // Get a reference to the user data - T_Data& operator()(T_Node* nodep) { + // Get a reference to the user data. If does not exist, construct it with given arguments. + template // + T_Data& operator()(T_Node* nodep, Args&&... args) { T_Data* userp = getUserp(nodep); if (!userp) { - userp = new T_Data; + userp = new T_Data{std::forward(args)...}; m_allocated.push_back(userp); setUserp(nodep, userp); } @@ -109,6 +111,9 @@ public: UASSERT_OBJ(userp, nodep, "Missing User data on const AstNode"); return *userp; } + + // Get a pointer to the user data if exists, otherwise nullptr + T_Data* tryGet(const T_Node* nodep) { return getUserp(nodep); } }; // User pointer allocator classes. T_Node is the type of node the allocator should be applied to diff --git a/src/V3Broken.cpp b/src/V3Broken.cpp index bb3123dcb..42f57e4e8 100644 --- a/src/V3Broken.cpp +++ b/src/V3Broken.cpp @@ -220,6 +220,12 @@ private: && !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()), nodep, "Assignment LHS is not an lvalue"); } + virtual void visit(AstRelease* nodep) override { + processAndIterate(nodep); + UASSERT_OBJ(!(v3Global.assertDTypesResolved() && VN_IS(nodep->lhsp(), NodeVarRef) + && !VN_AS(nodep->lhsp(), NodeVarRef)->access().isWriteOrRW()), + nodep, "Release LHS is not an lvalue"); + } virtual void visit(AstScope* nodep) override { VL_RESTORER(m_inScope); { diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 93622e8fc..6a782accd 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2768,6 +2768,18 @@ private: varrefp->varp()->valuep(initvaluep); } } + virtual void visit(AstRelease* nodep) override { + if (AstConcat* const concatp = VN_CAST(nodep->lhsp(), Concat)) { + FileLine* const flp = nodep->fileline(); + AstRelease* const newLp = new AstRelease{flp, concatp->lhsp()->unlinkFrBack()}; + AstRelease* const newRp = new AstRelease{flp, concatp->rhsp()->unlinkFrBack()}; + nodep->replaceWith(newLp); + newLp->addNextHere(newRp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + visit(newLp); + visit(newRp); + } + } virtual void visit(AstNodeIf* nodep) override { iterateChildren(nodep); diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index f8d37bd8a..f16223128 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -103,6 +103,8 @@ private: VL_DEBUG_FUNC; // Declare debug() void markVarUsage(AstNodeVarRef* nodep, bool blocking) { + // Ignore if warning is disabled on this reference (used by V3Force). + if (nodep->fileline()->warnIsOff(V3ErrorCode::BLKANDNBLK)) return; if (blocking) nodep->user5(true); AstVarScope* const vscp = nodep->varScopep(); // UINFO(4, " MVU " << blocking << " " << nodep << endl); diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 14596c79b..ecec3802e 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -124,15 +124,10 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { putqs(nodep, "*/\n"); } virtual void visit(AstNodeAssign* nodep) override { - if (VN_IS(nodep, AssignRelease)) { - puts("release "); - iterateAndNextNull(nodep->lhsp()); - } else { - if (VN_IS(nodep, AssignForce)) puts("force "); - iterateAndNextNull(nodep->lhsp()); - putfs(nodep, " " + nodep->verilogKwd() + " "); - iterateAndNextNull(nodep->rhsp()); - } + if (VN_IS(nodep, AssignForce)) puts("force "); + iterateAndNextNull(nodep->lhsp()); + putfs(nodep, " " + nodep->verilogKwd() + " "); + iterateAndNextNull(nodep->rhsp()); if (!m_suppressSemi) puts(";\n"); } virtual void visit(AstAssignDly* nodep) override { @@ -155,6 +150,11 @@ class EmitVBaseVisitor VL_NOT_FINAL : public EmitCBaseVisitor { iterateAndNextNull(nodep->rhsp()); if (!m_suppressSemi) puts(";\n"); } + virtual void visit(AstRelease* nodep) override { + puts("release "); + iterateAndNextNull(nodep->lhsp()); + if (!m_suppressSemi) puts(";\n"); + } virtual void visit(AstBreak*) override { putbs("break"); if (!m_suppressSemi) puts(";\n"); diff --git a/src/V3Force.cpp b/src/V3Force.cpp index cb2f8bfe9..d86363bbd 100644 --- a/src/V3Force.cpp +++ b/src/V3Force.cpp @@ -1,6 +1,6 @@ // -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* -// DESCRIPTION: Verilator: Make lookup forces +// DESCRIPTION: Verilator: Covert forceable signals, process force/release // // Code available from: https://verilator.org // @@ -13,350 +13,288 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* -// FORCE TRANSFORMATIONS: -// Step 1 (ForceVisitor): -// For forced nets, make: -// __forceon - enable bitmask of what bits are forced -// __forcein - value being forced -// __preforce - value before force is applied +// V3Force's Transformations: // -// Force sets the appropriate __forceon bits indicating a force is in -// effect using the value in __forcein. Release clears the -// appropriate __forceon bits. +// For each forceable net with name "": +// add 3 extra signals: +// - __VforceRd: a net with same type as signal +// - __VforceEn: a var with same type as signal, which is the bitwise force enable +// - __VforceVl: a var with same type as signal, which is the forced value +// add an initial statement: +// initial __VforceEn = 0; +// add a continuous assignment: +// assign __VforceRd = __VforceEn ? __VforceVl : ; +// replace all READ references to with a read reference to _VforceRd // -// IEEE says that procedural assignments "hold" the forced value even -// after a release, so add an assignment to the original __preforce too. +// Replace each AstAssignForce with 3 assignments: +// - __VforceEn = 1 +// - __VforceVl = +// - __VforceRd = // -// Tristates can't be forced, that would need a __forceen and makes a -// large mess, so just error out. -// -// Step 2 (ForceReplaceVisitor): (If any forces made) -// -// Replace any VarRef's to a forced signal to instead go to the -// reconsiled signal. +// Replace each AstRelease with 1 or 2 assignments: +// - __VforceEn = 0 +// - __VforceRd = // iff lhs is a net // //************************************************************************* #include "config_build.h" #include "verilatedos.h" +#include "V3Error.h" #include "V3Global.h" #include "V3Force.h" -#include "V3Simulate.h" -#include "V3Stats.h" -#include "V3Ast.h" -#include -#include +#include "V3AstUserAllocator.h" //###################################################################### -// Force shared state +// Convert force/release statements and signals marked 'forceable' -class ForceBaseVisitor VL_NOT_FINAL : public VNVisitor { +class ForceConvertVisitor final : public VNVisitor { // TYPES -public: - // Enum value must correspond to which user#p is used - enum class FVar : uint8_t { FORCEON = 2, FORCEIN = 3, PREFORCE = 4 }; + struct ForceComponentsVar { + AstVar* const m_rdVarp; // New variable to replace read references with + AstVar* const m_enVarp; // Force enabled signal + AstVar* const m_valVarp; // Forced value + AstVar* const m_phVarp; // Placeholder variable for release (never read) + explicit ForceComponentsVar(AstVar* varp) + : m_rdVarp{new AstVar{varp->fileline(), VVarType::WIRE, varp->name() + "__VforceRd", + varp->dtypep()}} + , m_enVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn", + varp->dtypep()}} + , m_valVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceVal", + varp->dtypep()}} + , m_phVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforcePh", + varp->dtypep()}} { + m_rdVarp->addNext(m_enVarp); + m_rdVarp->addNext(m_valVarp); + m_rdVarp->addNext(m_phVarp); + varp->addNextHere(m_rdVarp); + + if (varp->isPrimaryIO()) { + varp->v3warn( + E_UNSUPPORTED, + "Unsupported: Force/Release on primary input/output net " + << varp->prettyNameQ() << "\n" + << varp->warnMore() + << "... Suggest assign it to/from a temporary net and force/release that"); + } + } + }; + + struct ForceComponentsVarScope { + AstVarScope* const m_rdVscp; // New variable to replace read references with + AstVarScope* const m_enVscp; // Force enabled signal + AstVarScope* const m_valVscp; // Forced value + AstVarScope* const m_phVscp; // Placeholder variable for release (never read) + explicit ForceComponentsVarScope(AstVarScope* vscp, ForceComponentsVar& fcv) + : m_rdVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_rdVarp}} + , m_enVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_enVarp}} + , m_valVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_valVarp}} + , m_phVscp{new AstVarScope{vscp->fileline(), vscp->scopep(), fcv.m_phVarp}} { + m_rdVscp->addNext(m_enVscp); + m_rdVscp->addNext(m_valVscp); + m_rdVscp->addNext(m_phVscp); + vscp->addNextHere(m_rdVscp); + + FileLine* const flp = vscp->fileline(); + + { // Add initialization of the enable signal + AstVarRef* const lhsp = new AstVarRef{flp, m_enVscp, VAccess::WRITE}; + V3Number zero{m_enVscp, m_enVscp->width()}; + zero.setAllBits0(); + AstNodeMath* const rhsp = new AstConst{flp, zero}; + AstAssign* const assignp = new AstAssign{flp, lhsp, rhsp}; + AstActive* const activep = new AstActive{ + flp, "force-init", + new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Initial{}}}}; + activep->sensesStorep(activep->sensesp()); + activep->addStmtsp(new AstInitial{flp, assignp}); + vscp->scopep()->addActivep(activep); + } + + { // Add the combinational override + AstVarRef* const lhsp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE}; + AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ}; + origp->user2(1); // Don't replace this read ref with the read signal + AstOr* const rhsp = new AstOr{ + flp, + new AstAnd{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}, + new AstVarRef{flp, m_valVscp, VAccess::READ}}, + new AstAnd{flp, new AstNot{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}}, + origp}}; + AstActive* const activep + = new AstActive{flp, "force-comb", + new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Combo{}}}}; + activep->sensesStorep(activep->sensesp()); + activep->addStmtsp(new AstAssignW{flp, lhsp, rhsp}); + vscp->scopep()->addActivep(activep); + } + } + }; -private: // NODE STATE - // Ast*::user1 -> bool - processed - // AstVar::user1 -> bool - created here - // AstVarScope::user1 -> bool - created here - // AstVar::user2p -> AstVar* pointer to __forceon - // AstVarScope::user2p -> AstVarScope* pointer to __forceon - // AstVar::user3p -> AstVar* pointer to __forcein - // AstVarScope::user3p -> AstVarScope* pointer to __forcein - // AstVar::user4p -> AstVar* pointer to __preforce - // AstVarScope::user4p -> AstVarScope* pointer to __preforce - // Uses are in ForceVisitor + // AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar) + // AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope) + // AstVarRef::user2 -> Flag indicating not to replace reference + const VNUser1InUse m_user1InUse; + const VNUser2InUse m_user2InUse; + AstUser1Allocator m_forceComponentsVar; + AstUser1Allocator m_forceComponentsVarScope; -public: - VL_DEBUG_FUNC; // Declare debug() + // METHODS + const ForceComponentsVarScope& getForceComponents(AstVarScope* vscp) { + AstVar* const varp = vscp->varp(); + return m_forceComponentsVarScope(vscp, vscp, m_forceComponentsVar(varp, varp)); + } - static string fvarName(FVar fvar) { - switch (fvar) { - case FVar::FORCEON: return "__forceon"; - case FVar::FORCEIN: return "__forcein"; - case FVar::PREFORCE: return "__preforce"; - } - v3fatalSrc("bad case"); - return ""; + // Replace each AstNodeVarRef in the given 'nodep' that writes a variable by transforming the + // referenced AstVarScope with the given function. + void transformWritenVarScopes(AstNode* nodep, std::function f) { + UASSERT_OBJ(nodep->backp(), nodep, "Must have backp, otherwise will be lost if replaced"); + nodep->foreach([this, &f](AstNodeVarRef* refp) { + if (refp->access() != VAccess::WRITE) return; + // TODO: this is not strictly speaking safe for some complicated lvalues, eg.: + // 'force foo[a(cnt)] = 1;', where 'cnt' is an out parameter, but it will + // do for now... + refp->replaceWith( + new AstVarRef{refp->fileline(), f(refp->varScopep()), VAccess::WRITE}); + pushDeletep(refp); + }); } - static AstVar* getForceVarNull(const AstVar* const nodep, FVar fvar) { - // E.g. trying to make a __perforce__FOO would be bad - UASSERT_OBJ(!nodep->user1(), nodep, "lookup on var that Force made itself"); - return VN_AS(nodep->usernp(static_cast(fvar)), Var); + + // VISIT methods + void visit(AstNode* nodep) override { iterateChildren(nodep); } + + void visit(AstAssignForce* nodep) override { + // The AstAssignForce node will be removed for sure + VNRelinker relinker; + nodep->unlinkFrBack(&relinker); + pushDeletep(nodep); + + FileLine* const flp = nodep->fileline(); + AstNode* const lhsp = nodep->lhsp(); // The LValue we are forcing + AstNode* const rhsp = nodep->rhsp(); // The value we are forcing it to + + // Set corresponding enable signals to ones + V3Number ones{lhsp, lhsp->width()}; + ones.setAllBits1(); + AstAssign* const setEnp + = new AstAssign{flp, lhsp->cloneTree(false), new AstConst{rhsp->fileline(), ones}}; + transformWritenVarScopes(setEnp->lhsp(), [this](AstVarScope* vscp) { + return getForceComponents(vscp).m_enVscp; + }); + // Set corresponding value signals to the forced value + AstAssign* const setValp + = new AstAssign{flp, lhsp->cloneTree(false), rhsp->cloneTree(false)}; + transformWritenVarScopes(setValp->lhsp(), [this](AstVarScope* vscp) { + return getForceComponents(vscp).m_valVscp; + }); + // Set corresponding read signal directly as well, in case something in the same process + // reads it later + AstAssign* const setRdp = new AstAssign{flp, lhsp->unlinkFrBack(), rhsp->unlinkFrBack()}; + transformWritenVarScopes(setRdp->lhsp(), [this](AstVarScope* vscp) { + return getForceComponents(vscp).m_rdVscp; + }); + + setEnp->addNext(setValp); + setEnp->addNext(setRdp); + relinker.relink(setEnp); } - static AstVar* getForceVar(AstVar* const nodep, FVar fvar) { - AstVar* const foundp = getForceVarNull(nodep, fvar); - if (foundp) return foundp; - if (nodep->isPrimaryIO()) { - nodep->v3warn( - E_UNSUPPORTED, - "Unsupported: Force/Release on primary input/output net " - << nodep->prettyNameQ() << "\n" - << nodep->warnMore() - << "... Suggest assign it to/from a temporary net and force/release that"); - } - auto* const newp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, - nodep->name() + fvarName(fvar), nodep}; - newp->user1(true); - UINFO(9, "getForceVar for " << nodep << endl); - UINFO(9, "getForceVar new " << newp << endl); - nodep->addNextHere(newp); - nodep->usernp(static_cast(fvar), newp); - return newp; - } - static AstVarScope* getForceVscNull(const AstVarScope* const nodep, FVar fvar) { - // E.g. trying to make a __perforce__FOO would be bad - UASSERT_OBJ(!nodep->user1(), nodep, "lookup on varscope that Force made itself"); - return VN_AS(nodep->usernp(static_cast(fvar)), VarScope); - } - static AstVarScope* getForceVsc(AstVarScope* const nodep, FVar fvar) { - AstVarScope* const foundp = getForceVscNull(nodep, fvar); - if (foundp) return foundp; - FileLine* const fl_nowarn = new FileLine{nodep->fileline()}; + + void visit(AstRelease* nodep) override { + // The AstRelease node will be removed for sure + VNRelinker relinker; + nodep->unlinkFrBack(&relinker); + pushDeletep(nodep); + + FileLine* const flp = nodep->fileline(); + AstNode* const lhsp = nodep->lhsp(); // The LValue we are releasing + + // Set corresponding enable signals to zero + V3Number zero{lhsp, lhsp->width()}; + zero.setAllBits0(); + AstAssign* const resetEnp + = new AstAssign{flp, lhsp->cloneTree(false), new AstConst{lhsp->fileline(), zero}}; + transformWritenVarScopes(resetEnp->lhsp(), [this](AstVarScope* vscp) { + return getForceComponents(vscp).m_enVscp; + }); + // IEEE 1800-2017 10.6.2: If this is a net, and not a variable, then reset the read + // signal directly as well, in case something in the same process reads it later. Also, if + // it is a variable, and not a net, set the original signal to the forced value, as it + // needs to retain the forced value until the next procedural update, which might happen on + // a later eval. Luckily we can do all this in a single assignment. + FileLine* const fl_nowarn = new FileLine{flp}; fl_nowarn->warnOff(V3ErrorCode::BLKANDNBLK, true); - auto* const newp - = new AstVarScope{fl_nowarn, nodep->scopep(), getForceVar(nodep->varp(), fvar)}; - newp->user1(true); - UINFO(9, "getForceVsc for " << nodep << endl); - UINFO(9, "getForceVsc new " << newp << endl); - nodep->addNextHere(newp); - nodep->usernp(static_cast(fvar), newp); - return newp; - } - static AstVarRef* makeVarRef(AstNodeVarRef* nodep, FVar fvar, VAccess access) { - return new AstVarRef{nodep->fileline(), getForceVsc(nodep->varScopep(), fvar), access}; - } - static AstNode* makeForcingEquation(AstNodeVarRef* nodep) { - // Forcing: out = ((__forceon & __forcein) | (~__forceon & __preforce)) - UINFO(9, "makeForcingEquation for " << nodep << endl); - FileLine* const fl = nodep->fileline(); - AstNode* const orp = new AstOr{ - fl, - new AstAnd{fl, makeVarRef(nodep, FVar::FORCEON, VAccess::READ), - makeVarRef(nodep, FVar::FORCEIN, VAccess::READ)}, - new AstAnd{fl, new AstNot{fl, makeVarRef(nodep, FVar::FORCEON, VAccess::READ)}, - makeVarRef(nodep, FVar::PREFORCE, VAccess::READ)}}; - return orp; - } -}; + AstAssign* const resetRdp + = new AstAssign{fl_nowarn, lhsp->cloneTree(false), lhsp->unlinkFrBack()}; + // Replace write refs on the LHS + resetRdp->lhsp()->foreach([this](AstNodeVarRef* refp) { + if (refp->access() != VAccess::WRITE) return; + AstVarScope* const vscp = refp->varScopep(); + AstVarScope* const newVscp + = vscp->varp()->isContinuously() ? getForceComponents(vscp).m_rdVscp : vscp; + // Disable BLKANDNBLK for this reference + FileLine* const flp = new FileLine{refp->fileline()}; + flp->warnOff(V3ErrorCode::BLKANDNBLK, true); + AstVarRef* const newpRefp = new AstVarRef{flp, newVscp, VAccess::WRITE}; + refp->replaceWith(newpRefp); + pushDeletep(refp); + }); + // Replace write refs on RHS + resetRdp->rhsp()->foreach([this](AstNodeVarRef* refp) { + if (refp->access() != VAccess::WRITE) return; + AstVarScope* const vscp = refp->varScopep(); + AstVarScope* const newVscp + = vscp->varp()->isContinuously() ? vscp : getForceComponents(vscp).m_valVscp; + AstVarRef* const newpRefp = new AstVarRef{refp->fileline(), newVscp, VAccess::READ}; + newpRefp->user2(1); // Don't replace this read ref with the read signal + refp->replaceWith(newpRefp); + pushDeletep(refp); + }); -//###################################################################### -// Recurse left-hand-side variables to do replaces underneath a force or release + resetEnp->addNext(resetRdp); + relinker.relink(resetEnp); + } -class ForceLhsVisitor final : public ForceBaseVisitor { -private: - // STATE - FVar const m_fvar; // Which variable to replace with - AstNodeVarRef* m_releaseVarRefp = nullptr; // Left hand side variable under release + // CONSTRUCTOR + explicit ForceConvertVisitor(AstNetlist* nodep) { + // Transform all force and release statements + iterateAndNextNull(nodep->modulesp()); - virtual void visit(AstNodeVarRef* nodep) override { - if (nodep->user1()) return; - if (nodep->access().isWriteOrRW()) { - if (m_releaseVarRefp) { - nodep->v3error("Multiple variables forced in single statement: " - << m_releaseVarRefp->prettyNameQ() << ", " - << nodep->varScopep()->prettyNameQ()); - return; + // Replace references to forced signals + nodep->modulesp()->foreachAndNext([this](AstVarRef* nodep) { + if (ForceComponentsVarScope* const fcp + = m_forceComponentsVarScope.tryGet(nodep->varScopep())) { + switch (nodep->access()) { + case VAccess::READ: + // Read references replaced to read the new, possibly forced signal + if (!nodep->user2()) { + nodep->varp(fcp->m_rdVscp->varp()); + nodep->varScopep(fcp->m_rdVscp); + } + break; + case VAccess::WRITE: + // Write references use the original signal + break; + default: + nodep->v3error( + "Unsupported: Signals used via read-write reference cannot be forced"); + break; + } } - m_releaseVarRefp = nodep; - AstNode* const newp = makeVarRef(nodep, m_fvar, VAccess::WRITE); - newp->user1(true); - nodep->replaceWith(newp); - pushDeletep(nodep); - } + }); } - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } public: - // CONSTRUCTORS - explicit ForceLhsVisitor(AstNode* nodep, FVar fvar) - : m_fvar(fvar) { - iterate(nodep); - } - virtual ~ForceLhsVisitor() override = default; - // METHODS - AstNodeVarRef* releaseVarRefp() const { return m_releaseVarRefp; } + static void apply(AstNetlist* nodep) { ForceConvertVisitor{nodep}; } }; //###################################################################### -// Force class functions - -class ForceVisitor final : public ForceBaseVisitor { -private: - // NODE STATE - // See ForceBaseVisitor - const VNUser1InUse m_inuser1; - const VNUser2InUse m_inuser2; - const VNUser3InUse m_inuser3; - const VNUser4InUse m_inuser4; - - // STATE - bool m_anyForce = false; // Any force, need reconciliation step - VDouble0 m_statForces; // stat tracking - - std::deque m_forces; // Pointer to found forces - std::deque m_releases; // Pointer to found releases - - virtual void visit(AstAssignForce* nodep) override { m_forces.push_back(nodep); } - void visitEarlierForce(AstAssignForce* nodep) { - if (nodep->user1SetOnce()) return; - if (debug() >= 9) nodep->dumpTree(cout, "-force-i- "); - ++m_statForces; - m_anyForce = true; - // For force/release, duplicate assignment to make - // AssignForce __forceon = '1 - // AssignForce __forcein = {value} - // and clone LHS's node tree to handle appropriate extractions - { // __forceon = '1 - AstNodeAssign* const newp = nodep->cloneTree(false); - pushDeletep(newp->rhsp()->unlinkFrBack()); - V3Number num{nodep, nodep->width()}; - num.setAllBits1(); - newp->rhsp(new AstConst{nodep->fileline(), num}); - { ForceLhsVisitor{newp->lhsp(), FVar::FORCEON}; } - newp->user1(true); // Don't process it again - nodep->addNextHere(newp); - if (debug() >= 9) newp->dumpTree(cout, "-force-fo- "); - } - { // Edit to create assignment to have VarRef that refers to __forceon - { ForceLhsVisitor{nodep->lhsp(), FVar::FORCEIN}; } - nodep->user1(true); // Don't process it again - if (debug() >= 9) nodep->dumpTree(cout, "-force-fi- "); - } - } - virtual void visit(AstAssignRelease* nodep) override { m_releases.push_back(nodep); } - void visitEarlierRelease(AstAssignRelease* nodep) { - if (nodep->user1SetOnce()) return; - if (debug() >= 9) nodep->dumpTree(cout, "-release-i- "); - // RHS is not relevant, so no iterate - // For release, edit assignment to make - // AssignRelease __forceon = `0 - // we already have 0's on RHS, were made when AstNode created - // Create assignment to have VarRef that refers to __forceon - ForceLhsVisitor fvisitor{nodep->lhsp(), FVar::FORCEON}; - // releaseVarRefp might be deleted when ForceLhsVisitor destructs, so - // keep ForceLhsVisitor in scope for now - AstNodeVarRef* const releaseVarRefp = fvisitor.releaseVarRefp(); - UASSERT_OBJ(releaseVarRefp, nodep, "No LHS variable found under release"); - if (!getForceVscNull(releaseVarRefp->varScopep(), FVar::FORCEIN)) { - UINFO(9, "Deleting release of variable that's never forced: " << nodep << endl); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return; - } - if (!releaseVarRefp->varp()->isContinuously()) { - // Create assignment __out == _forceen so when release happens value sticks - // See IEEE - a strange historical language artifact - UINFO(9, "force var is procedural " << releaseVarRefp->varScopep() << endl); - FileLine* const fl_nowarn = new FileLine{nodep->fileline()}; - fl_nowarn->warnOff(V3ErrorCode::BLKANDNBLK, true); - AstNodeAssign* const newp = new AstAssignRelease{ - fl_nowarn, makeVarRef(releaseVarRefp, FVar::PREFORCE, VAccess::WRITE), - makeForcingEquation(releaseVarRefp)}; - newp->user1(true); // Don't process it again - nodep->addHereThisAsNext(newp); // Must go before change forceon - if (debug() >= 9) newp->dumpTree(cout, "-release-rp- "); - } - if (debug() >= 9) nodep->dumpTree(cout, "-release-ro- "); - } - - virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit ForceVisitor(AstNetlist* nodep) { - iterate(nodep); - // Now that we know procedural markers in user5, do forces - for (auto* const nodep : m_forces) visitEarlierForce(nodep); - m_forces.clear(); // As dangling pointers now - // Do releases after all forces are processed, so we can just - // ignore any release with no corresponding force - for (auto* const nodep : m_releases) visitEarlierRelease(nodep); - m_releases.clear(); // As dangling pointers now - } - virtual ~ForceVisitor() override { // - V3Stats::addStat("Tristate, Forces", m_statForces); - } - // METHODS - bool anyForce() const { return m_anyForce; } -}; - -//###################################################################### -// Force class functions - -class ForceReplace final : public ForceBaseVisitor { - // This extra complete-netlist visit could be avoided by recording all - // AstVarRefs to every AstVar, but that's a lot of data structure - // building, faster to read-only iterate. - // As we only care about VarRef's we use direct recusion rather than a visitor - -private: - void visitVarRef(AstNodeVarRef* nodep) { - if (nodep->varScopep()->user2p()) { - if (nodep->access().isRW()) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: forced variable used in read-modify-write context"); - } else if (nodep->access().isWriteOrRW()) { - UINFO(9, " changeRecurse-WR-replace " << nodep << endl); - AstNode* const newp = makeVarRef(nodep, FVar::PREFORCE, VAccess::WRITE); - newp->user1(true); - nodep->replaceWith(newp); - pushDeletep(nodep); - return; - } else if (nodep->access().isReadOrRW()) { - // We build forcing equation on each usage rather than making - // a variable otherwise we wouldn't know where between a statement - // that sets a preforce and uses a forced to insert the proposed - // assignment - UINFO(9, " changeRecurse-RD-replace " << nodep << endl); - AstNode* const newp = makeForcingEquation(nodep); - newp->user1(true); - nodep->replaceWith(newp); - pushDeletep(nodep); - return; - } - } - } - - void changeRecurse(AstNode* nodep) { - // Recurse and replace any VarRef WRITEs to refer to the force equation - if (VL_LIKELY(!nodep->user1())) { // Else processed already - if (auto* const varrefp = VN_CAST(nodep, NodeVarRef)) { - visitVarRef(varrefp); - return; // Might have been edited -- and has no children so ok to exit - } - } - nodep->prefetch(); - if (nodep->op1p()) changeRecurse(nodep->op1p()); - if (nodep->op2p()) changeRecurse(nodep->op2p()); - if (nodep->op3p()) changeRecurse(nodep->op3p()); - if (nodep->op4p()) changeRecurse(nodep->op4p()); - if (nodep->nextp()) changeRecurse(nodep->nextp()); - } - - virtual void visit(AstNode* nodep) override { v3error("Unused"); } // LCOV_EXCL_LINE - -public: - // CONSTRUCTORS - explicit ForceReplace(AstNetlist* nodep) { changeRecurse(nodep); } - virtual ~ForceReplace() override = default; -}; - -//###################################################################### -// Force class functions +// void V3Force::forceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { // Destruct before final check - ForceVisitor visitor{nodep}; - if (visitor.anyForce()) { - V3Global::dumpCheckGlobalTree("force-mid", 0, - v3Global.opt.dumpTreeLevel(__FILE__) >= 3); - ForceReplace{nodep}; - } - } + if (!v3Global.hasForceableSignals()) return; + ForceConvertVisitor::apply(nodep); V3Global::dumpCheckGlobalTree("force", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); } diff --git a/src/V3Global.h b/src/V3Global.h index edf10a7ed..40d102aee 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -103,7 +103,7 @@ class V3Global final { // Experimenting with always requiring heavy, see (#2701) bool m_needTraceDumper = false; // Need __Vm_dumperp in symbols bool m_dpi = false; // Need __Dpi include files - bool m_useForce = false; // Need force/release processing + bool m_hasForceableSignals = false; // Need to apply V3Force pass bool m_hasSCTextSections = false; // Has `systemc_* sections that need to be emitted bool m_useParallelBuild = false; // Use parallel build for model bool m_useRandomizeMethods = false; // Need to define randomize() class methods @@ -147,6 +147,8 @@ public: void needTraceDumper(bool flag) { m_needTraceDumper = flag; } bool dpi() const { return m_dpi; } void dpi(bool flag) { m_dpi = flag; } + bool hasForceableSignals() const { return m_hasForceableSignals; } + void setHasForceableSignals() { m_hasForceableSignals = true; } bool hasSCTextSections() const { return m_hasSCTextSections; } void setHasSCTextSections() { m_hasSCTextSections = true; } V3HierBlockPlan* hierPlanp() const { return m_hierPlanp; } @@ -154,8 +156,6 @@ public: UASSERT(!m_hierPlanp, "call once"); m_hierPlanp = plan; } - void useForce(bool flag) { m_useForce = flag; } - bool useForce() const { return m_useForce; } void useParallelBuild(bool flag) { m_useParallelBuild = flag; } bool useParallelBuild() const { return m_useParallelBuild; } void useRandomizeMethods(bool flag) { m_useRandomizeMethods = flag; } diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 8fd76cee0..6ab7eb8e7 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -83,6 +83,15 @@ private: iterateAndNextNull(nodep->rhsp()); } } + virtual void visit(AstRelease* nodep) override { + VL_RESTORER(m_setRefLvalue); + VL_RESTORER(m_setContinuously); + { + m_setRefLvalue = VAccess::WRITE; + m_setContinuously = false; + iterateAndNextNull(nodep->lhsp()); + } + } virtual void visit(AstCastDynamic* nodep) override { VL_RESTORER(m_setRefLvalue); { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 4b934f2e3..1fdc45c57 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4025,6 +4025,12 @@ private: } } + virtual void visit(AstRelease* nodep) override { + userIterateAndNext(nodep->lhsp(), WidthVP(SELF, BOTH).p()); + UASSERT_OBJ(nodep->lhsp()->dtypep(), nodep, "How can LValue be untyped?"); + UASSERT_OBJ(nodep->lhsp()->dtypep()->widthSized(), nodep, "How can LValue be unsized?"); + } + virtual void visit(AstSFormatF* nodep) override { // Excludes NodeDisplay, see below if (m_vup && !m_vup->prelim()) return; // Can be called as statement or function diff --git a/src/Verilator.cpp b/src/Verilator.cpp index c68fd11ef..7ba0c113a 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -301,10 +301,6 @@ static void process() { // After V3Task so task internal variables will get renamed V3Name::nameAll(v3Global.rootp()); - // Process force/releases if there are any - // After flattening, before Life optimizations - if (v3Global.useForce()) V3Force::forceAll(v3Global.rootp()); - // Loop unrolling & convert FORs to WHILEs V3Unroll::unrollAll(v3Global.rootp()); @@ -338,6 +334,10 @@ static void process() { // Create tracing sample points, before we start eliminating signals if (v3Global.opt.trace()) V3TraceDecl::traceDeclAll(v3Global.rootp()); + // Convert forceable signals, process force/release statements. + // After V3TraceDecl so we don't trace additional signals inserted to implement forcing. + V3Force::forceAll(v3Global.rootp()); + // Gate-based logic elimination; eliminate signals and push constant across cell boundaries // Instant propagation makes lots-o-constant reduction possibilities. if (v3Global.opt.oGate()) { diff --git a/src/verilog.y b/src/verilog.y index 29678bcf2..7ec48eda5 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3088,10 +3088,10 @@ statement_item: // IEEE: statement_item //UNSUP: delay_or_event_controlE above | yDEASSIGN variable_lvalue ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: Verilog 1995 deassign"); } - | yFORCE expr '=' expr ';' - { $$ = new AstAssignForce{$1, $2, $4}; } + | yFORCE variable_lvalue '=' expr ';' + { $$ = new AstAssignForce{$1, $2, $4}; v3Global.setHasForceableSignals(); } | yRELEASE variable_lvalue ';' - { $$ = new AstAssignRelease{$1, VFlagChildDType{}, $2}; } + { $$ = new AstRelease{$1, $2}; v3Global.setHasForceableSignals(); } // // // IEEE: case_statement | unique_priorityE caseStart caseAttrE case_itemListE yENDCASE { $$ = $2; if ($4) $2->addItemsp($4); diff --git a/test_regress/t/t_force_release_net.pl b/test_regress/t/t_force_release_net.pl new file mode 100755 index 000000000..7b483d32e --- /dev/null +++ b/test_regress/t/t_force_release_net.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile(); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_force_release_net.v b/test_regress/t/t_force_release_net.v new file mode 100644 index 000000000..0edff5b13 --- /dev/null +++ b/test_regress/t/t_force_release_net.v @@ -0,0 +1,130 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Geza Lore. +// 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; + + int cyc = 0; + always @(posedge clk) cyc <= cyc + 1; + + wire net_1; + wire [7:0] net_8; + assign net_1 = ~cyc[0]; + assign net_8 = ~cyc[1 +: 8]; + + always @ (posedge clk) begin + $display("%d pre : %x %x", cyc, net_8, net_1); + + case (cyc) + 4: begin + `checkh (net_1, 0); + `checkh (net_8, ~cyc[1 +: 8]); + end + 5: begin + `checkh (net_1, 0); + `checkh (net_8, 8'h5f); + end + 6: begin + `checkh (net_1, 1); + `checkh (net_8, 8'h5f); + end + 7, 8: begin + `checkh (net_1, 1); + `checkh (net_8, 8'hf5); + end + 9: begin + `checkh (net_1, ~cyc[0]); + `checkh (net_8, 8'hf5); + end + 11, 12: begin + `checkh (net_1, 1); + `checkh (net_8, 8'h5a); + end + 13, 14: begin + `checkh (net_1, 0); + `checkh (net_8, 8'ha5); + end + default: begin + `checkh ({net_8, net_1}, ~cyc[0 +: 9]); + end + endcase + +`ifndef REVERSE + if (cyc == 3) force net_1 = 0; + if (cyc == 5) force net_1 = 1; + if (cyc == 8) release net_1; + + if (cyc == 4) force net_8 = 8'h5f; + if (cyc == 6) force net_8 = 8'hf5; + if (cyc == 9) release net_8; + + if (cyc == 10) force {net_1, net_8} = 9'b1_0101_1010; + if (cyc == 12) force {net_8, net_1} = 9'b1010_0101_0; + if (cyc == 14) release {net_1, net_8}; +`else + if (cyc == 8) release net_1; + if (cyc == 5) force net_1 = 1; + if (cyc == 3) force net_1 = 0; + + if (cyc == 9) release net_8; + if (cyc == 6) force net_8 = 8'hf5; + if (cyc == 4) force net_8 = 8'h5f; + + if (cyc == 14) release {net_1, net_8}; + if (cyc == 12) force {net_8, net_1} = 9'b1010_0101_0; + if (cyc == 10) force {net_1, net_8} = 9'b1_0101_1010; +`endif + + $display("%d post: %x %x", cyc, net_8, net_1); + + case (cyc) + 3: begin + `checkh (net_1, 0); + `checkh (net_8, ~cyc[1 +: 8]); + end + 4: begin + `checkh (net_1, 0); + `checkh (net_8, 8'h5f); + end + 5: begin + `checkh (net_1, 1); + `checkh (net_8, 8'h5f); + end + 6, 7: begin + `checkh (net_1, 1); + `checkh (net_8, 8'hf5); + end + 8: begin + `checkh (net_1, ~cyc[0]); + `checkh (net_8, 8'hf5); + end + 10, 11: begin + `checkh (net_1, 1); + `checkh (net_8, 8'h5a); + end + 12, 13: begin + `checkh (net_1, 0); + `checkh (net_8, 8'ha5); + end + default: begin + `checkh ({net_8, net_1}, ~cyc[0 +: 9]); + end + endcase + + if (cyc == 30) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_force_release_net_reverse.pl b/test_regress/t/t_force_release_net_reverse.pl new file mode 100755 index 000000000..709491b91 --- /dev/null +++ b/test_regress/t/t_force_release_net_reverse.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +top_filename("t/t_force_release_net.v"); + +compile( + verilator_flags2 => ['+define+REVERSE'] + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_force_release_net_trace.out b/test_regress/t/t_force_release_net_trace.out new file mode 100755 index 000000000..8e4f066fc --- /dev/null +++ b/test_regress/t/t_force_release_net_trace.out @@ -0,0 +1,213 @@ +$version Generated by VerilatedVcd $end +$date Sun Dec 19 14:44:51 2021 $end +$timescale 1ps $end + + $scope module top $end + $var wire 1 # clk $end + $scope module t $end + $var wire 1 # clk $end + $var wire 32 $ cyc [31:0] $end + $var wire 1 % net_1 $end + $var wire 8 & net_8 [7:0] $end + $upscope $end + $upscope $end +$enddefinitions $end + + +#0 +0# +b00000000000000000000000000000000 $ +1% +b11111111 & +#10 +1# +b00000000000000000000000000000001 $ +0% +#15 +0# +#20 +1# +b00000000000000000000000000000010 $ +1% +b11111110 & +#25 +0# +#30 +1# +b00000000000000000000000000000011 $ +0% +#35 +0# +#40 +1# +b00000000000000000000000000000100 $ +b11111101 & +#45 +0# +#50 +1# +b00000000000000000000000000000101 $ +b01011111 & +#55 +0# +#60 +1# +b00000000000000000000000000000110 $ +1% +#65 +0# +#70 +1# +b00000000000000000000000000000111 $ +b11110101 & +#75 +0# +#80 +1# +b00000000000000000000000000001000 $ +#85 +0# +#90 +1# +b00000000000000000000000000001001 $ +0% +#95 +0# +#100 +1# +b00000000000000000000000000001010 $ +1% +b11111010 & +#105 +0# +#110 +1# +b00000000000000000000000000001011 $ +b01011010 & +#115 +0# +#120 +1# +b00000000000000000000000000001100 $ +#125 +0# +#130 +1# +b00000000000000000000000000001101 $ +0% +b10100101 & +#135 +0# +#140 +1# +b00000000000000000000000000001110 $ +#145 +0# +#150 +1# +b00000000000000000000000000001111 $ +b11111000 & +#155 +0# +#160 +1# +b00000000000000000000000000010000 $ +1% +b11110111 & +#165 +0# +#170 +1# +b00000000000000000000000000010001 $ +0% +#175 +0# +#180 +1# +b00000000000000000000000000010010 $ +1% +b11110110 & +#185 +0# +#190 +1# +b00000000000000000000000000010011 $ +0% +#195 +0# +#200 +1# +b00000000000000000000000000010100 $ +1% +b11110101 & +#205 +0# +#210 +1# +b00000000000000000000000000010101 $ +0% +#215 +0# +#220 +1# +b00000000000000000000000000010110 $ +1% +b11110100 & +#225 +0# +#230 +1# +b00000000000000000000000000010111 $ +0% +#235 +0# +#240 +1# +b00000000000000000000000000011000 $ +1% +b11110011 & +#245 +0# +#250 +1# +b00000000000000000000000000011001 $ +0% +#255 +0# +#260 +1# +b00000000000000000000000000011010 $ +1% +b11110010 & +#265 +0# +#270 +1# +b00000000000000000000000000011011 $ +0% +#275 +0# +#280 +1# +b00000000000000000000000000011100 $ +1% +b11110001 & +#285 +0# +#290 +1# +b00000000000000000000000000011101 $ +0% +#295 +0# +#300 +1# +b00000000000000000000000000011110 $ +1% +b11110000 & +#305 +0# +#310 +1# +b00000000000000000000000000011111 $ +0% diff --git a/test_regress/t/t_force_release_net_trace.pl b/test_regress/t/t_force_release_net_trace.pl new file mode 100755 index 000000000..6a0f17b04 --- /dev/null +++ b/test_regress/t/t_force_release_net_trace.pl @@ -0,0 +1,26 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +top_filename("t/t_force_release_net.v"); + +compile( + verilator_flags2 => ['--trace'] + ); + +execute( + check_finished => 1, + ); + +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_force_release_var.pl b/test_regress/t/t_force_release_var.pl new file mode 100755 index 000000000..7b483d32e --- /dev/null +++ b/test_regress/t/t_force_release_var.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +compile(); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_force_release_var.v b/test_regress/t/t_force_release_var.v new file mode 100644 index 000000000..6f2791e63 --- /dev/null +++ b/test_regress/t/t_force_release_var.v @@ -0,0 +1,138 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2021 by Geza Lore. +// 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; + + int cyc = 0; + always @(posedge clk) cyc <= cyc + 1; + + reg var_1 = 0; + reg [7:0] var_8 = 0; + always @(posedge clk) begin + var_1 <= cyc[0]; + var_8 <= cyc[1 +: 8]; + end + + always @ (posedge clk) begin + $display("%d pre : %x %x", cyc, var_8, var_1); + + case (cyc) + 0: begin + // Uninitialized + end + 14: begin + `checkh (var_1, 1); + `checkh ({1'b0, var_8}, (cyc[0 +: 9] - 1) >> 1); + end + 15: begin + `checkh (var_1, 1); + `checkh (var_8, 8'hf5); + end + 16: begin + `checkh (var_1, 0); + `checkh (var_8, 8'hf5); + end + 17, 18: begin + `checkh (var_1, 0); + `checkh (var_8, 8'h5f); + end + 19: begin + `checkh (var_1, ~cyc[0]); + `checkh (var_8, 8'h5f); + end + 21, 22: begin + `checkh (var_1, 1); + `checkh (var_8, 8'h5a); + end + 23, 24: begin + `checkh (var_1, 0); + `checkh (var_8, 8'ha5); + end + default: begin + `checkh ({var_8, var_1}, cyc[0 +: 9] - 1); + end + endcase + +`ifndef REVERSE + if (cyc == 13) force var_1 = 1; + if (cyc == 15) force var_1 = 0; + if (cyc == 18) release var_1; + + if (cyc == 14) force var_8 = 8'hf5; + if (cyc == 16) force var_8 = 8'h5f; + if (cyc == 19) release var_8; + + if (cyc == 20) force {var_1, var_8} = 9'b1_0101_1010; + if (cyc == 22) force {var_8, var_1} = 9'b1010_0101_0; + if (cyc == 24) release {var_1, var_8}; +`else + if (cyc == 18) release var_1; + if (cyc == 15) force var_1 = 0; + if (cyc == 13) force var_1 = 1; + + if (cyc == 19) release var_8; + if (cyc == 16) force var_8 = 8'h5f; + if (cyc == 14) force var_8 = 8'hf5; + + if (cyc == 24) release {var_1, var_8}; + if (cyc == 22) force {var_8, var_1} = 9'b1010_0101_0; + if (cyc == 20) force {var_1, var_8} = 9'b1_0101_1010; +`endif + + $display("%d post: %x %x", cyc, var_8, var_1); + + case (cyc) + 0: begin + // Uninitialized + end + 13: begin + `checkh (var_1, 1); + `checkh ({1'b0, var_8}, (cyc[0 +: 9] - 1) >> 1); + end + 14: begin + `checkh (var_1, 1); + `checkh (var_8, 8'hf5); + end + 15: begin + `checkh (var_1, 0); + `checkh (var_8, 8'hf5); + end + 16, 17, 18: begin + `checkh (var_1, 0); + `checkh (var_8, 8'h5f); + end + 19: begin + `checkh (var_1, ~cyc[0]); + `checkh (var_8, 8'h5f); + end + 20, 21: begin + `checkh (var_1, 1); + `checkh (var_8, 8'h5a); + end + 22, 23, 24: begin + `checkh (var_1, 0); + `checkh (var_8, 8'ha5); + end + default: begin + `checkh ({var_8, var_1}, cyc[0 +: 9] - 1); + end + endcase + + if (cyc == 30) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_force_release_var_reverse.pl b/test_regress/t/t_force_release_var_reverse.pl new file mode 100755 index 000000000..7b31629df --- /dev/null +++ b/test_regress/t/t_force_release_var_reverse.pl @@ -0,0 +1,24 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +top_filename("t/t_force_release_var.v"); + +compile( + verilator_flags2 => ['+define+REVERSE'] + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_force_release_var_trace.out b/test_regress/t/t_force_release_var_trace.out new file mode 100755 index 000000000..42d114f66 --- /dev/null +++ b/test_regress/t/t_force_release_var_trace.out @@ -0,0 +1,211 @@ +$version Generated by VerilatedVcd $end +$date Sun Dec 19 14:45:16 2021 $end +$timescale 1ps $end + + $scope module top $end + $var wire 1 # clk $end + $scope module t $end + $var wire 1 # clk $end + $var wire 32 $ cyc [31:0] $end + $var wire 1 % var_1 $end + $var wire 8 & var_8 [7:0] $end + $upscope $end + $upscope $end +$enddefinitions $end + + +#0 +0# +b00000000000000000000000000000000 $ +0% +b00000000 & +#10 +1# +b00000000000000000000000000000001 $ +#15 +0# +#20 +1# +b00000000000000000000000000000010 $ +1% +#25 +0# +#30 +1# +b00000000000000000000000000000011 $ +0% +b00000001 & +#35 +0# +#40 +1# +b00000000000000000000000000000100 $ +1% +#45 +0# +#50 +1# +b00000000000000000000000000000101 $ +0% +b00000010 & +#55 +0# +#60 +1# +b00000000000000000000000000000110 $ +1% +#65 +0# +#70 +1# +b00000000000000000000000000000111 $ +0% +b00000011 & +#75 +0# +#80 +1# +b00000000000000000000000000001000 $ +1% +#85 +0# +#90 +1# +b00000000000000000000000000001001 $ +0% +b00000100 & +#95 +0# +#100 +1# +b00000000000000000000000000001010 $ +1% +#105 +0# +#110 +1# +b00000000000000000000000000001011 $ +0% +b00000101 & +#115 +0# +#120 +1# +b00000000000000000000000000001100 $ +1% +#125 +0# +#130 +1# +b00000000000000000000000000001101 $ +0% +b00000110 & +#135 +0# +#140 +1# +b00000000000000000000000000001110 $ +1% +#145 +0# +#150 +1# +b00000000000000000000000000001111 $ +b11110101 & +#155 +0# +#160 +1# +b00000000000000000000000000010000 $ +0% +#165 +0# +#170 +1# +b00000000000000000000000000010001 $ +b01011111 & +#175 +0# +#180 +1# +b00000000000000000000000000010010 $ +#185 +0# +#190 +1# +b00000000000000000000000000010011 $ +#195 +0# +#200 +1# +b00000000000000000000000000010100 $ +1% +b00001001 & +#205 +0# +#210 +1# +b00000000000000000000000000010101 $ +b01011010 & +#215 +0# +#220 +1# +b00000000000000000000000000010110 $ +#225 +0# +#230 +1# +b00000000000000000000000000010111 $ +0% +b10100101 & +#235 +0# +#240 +1# +b00000000000000000000000000011000 $ +#245 +0# +#250 +1# +b00000000000000000000000000011001 $ +b00001100 & +#255 +0# +#260 +1# +b00000000000000000000000000011010 $ +1% +#265 +0# +#270 +1# +b00000000000000000000000000011011 $ +0% +b00001101 & +#275 +0# +#280 +1# +b00000000000000000000000000011100 $ +1% +#285 +0# +#290 +1# +b00000000000000000000000000011101 $ +0% +b00001110 & +#295 +0# +#300 +1# +b00000000000000000000000000011110 $ +1% +#305 +0# +#310 +1# +b00000000000000000000000000011111 $ +0% +b00001111 & diff --git a/test_regress/t/t_force_release_var_trace.pl b/test_regress/t/t_force_release_var_trace.pl new file mode 100755 index 000000000..2e7da6c27 --- /dev/null +++ b/test_regress/t/t_force_release_var_trace.pl @@ -0,0 +1,26 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +top_filename("t/t_force_release_var.v"); + +compile( + verilator_flags2 => ['--trace'] + ); + +execute( + check_finished => 1, + ); + +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); + +ok(1); +1;