diff --git a/src/V3Ast.h b/src/V3Ast.h index 1cf11dcf1..0e86c79a4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1941,8 +1941,6 @@ public: inline void iterateChildrenBackwardsConst(AstNode* nodep); /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list inline void iterateAndNextConstNull(AstNode* nodep); - /// Call visit()s on const nodep (maybe nullptr) and nodep's nextp() list, in reverse order - inline void iterateAndNextConstNullBackwards(AstNode* nodep); virtual void visit(AstNode* nodep) = 0; virtual ~VNVisitorConst() {} @@ -3128,9 +3126,6 @@ void VNVisitorConst::iterateChildrenConst(AstNode* nodep) { nodep->iterateChildr void VNVisitorConst::iterateChildrenBackwardsConst(AstNode* nodep) { nodep->iterateChildrenBackwardsConst(*this); } -void VNVisitorConst::iterateAndNextConstNullBackwards(AstNode* nodep) { - if (VL_LIKELY(nodep)) nodep->iterateListBackwardsConst(*this); -} void VNVisitorConst::iterateAndNextConstNull(AstNode* nodep) { if (VL_LIKELY(nodep)) nodep->iterateAndNextConst(*this); } diff --git a/src/V3DfgPeephole.cpp b/src/V3DfgPeephole.cpp index 5a1df937f..96c857911 100644 --- a/src/V3DfgPeephole.cpp +++ b/src/V3DfgPeephole.cpp @@ -1216,6 +1216,7 @@ class V3DfgPeephole final : public DfgVisitor { DfgVarArray* const varp = vtxp->fromp()->cast(); if (!varp) return; if (varp->varp()->isForced()) return; + if (varp->varp()->isSigUserRWPublic()) return; DfgVertex* const srcp = varp->srcp(); if (!srcp) return; diff --git a/src/V3DfgVertices.h b/src/V3DfgVertices.h index 1b0370725..9e45add00 100644 --- a/src/V3DfgVertices.h +++ b/src/V3DfgVertices.h @@ -70,7 +70,8 @@ public: void tmpForp(AstNode* nodep) { m_tmpForp = nodep; } bool isDrivenFullyByDfg() const { - return srcp() && !srcp()->is() && !varp()->isForced(); + return srcp() && !srcp()->is() && !varp()->isForced() + && !varp()->isSigUserRWPublic(); } // Variable referenced via an AstVarXRef (hierarchical reference) diff --git a/src/V3Inline.cpp b/src/V3Inline.cpp index e1bd41da2..7b9c6c3f7 100644 --- a/src/V3Inline.cpp +++ b/src/V3Inline.cpp @@ -250,9 +250,9 @@ class InlineRelinkVisitor final : public VNVisitor { // STATE std::unordered_set m_renamedInterfaces; // Name of renamed interface variables - AstNodeModule* const m_modp; // Current module - const AstCell* const m_cellp; // Cell being cloned - bool m_initialStatic = false; // Inside InitialStatic + AstNodeModule* const m_modp; // The module we are inlining into + const AstCell* const m_cellp; // The cell being inlined + size_t m_nPlaceholders = 0; // Unique identifier sequence number for placeholder variables // VISITORS void visit(AstCellInline* nodep) override { @@ -279,66 +279,6 @@ class InlineRelinkVisitor final : public VNVisitor { iterateChildren(nodep); } void visit(AstVar* nodep) override { - if (nodep->user2p()) { - // Make an assignment, so we'll trace it properly - // user2p is either a const or a var. - FileLine* const flp = nodep->fileline(); - AstConst* const exprconstp = VN_CAST(nodep->user2p(), Const); - AstVarRef* exprvarrefp = VN_CAST(nodep->user2p(), VarRef); - UINFO(8, "connectto: " << nodep->user2p()); - UASSERT_OBJ(exprconstp || exprvarrefp, nodep, - "Unknown interconnect type; pinReconnectSimple should have cleared up"); - if (exprconstp) { - m_modp->addStmtsp(new AstAssignW{flp, new AstVarRef{flp, nodep, VAccess::WRITE}, - exprconstp->cloneTree(false)}); - nodep->user4(true); // Making assignment to it - } else if (nodep->user3()) { - // Public variable at the lower module end - we need to make sure we propagate - // the logic changes up and down; if we aliased, we might - // remove the change detection on the output variable. - UINFO(9, "public pin assign: " << exprvarrefp); - UASSERT_OBJ(!nodep->isNonOutput(), nodep, "Outputs only - inputs use AssignAlias"); - m_modp->addStmtsp(new AstAssignW{flp, exprvarrefp->cloneTree(false), - new AstVarRef{flp, nodep, VAccess::READ}}); - } else if (nodep->isSigPublic() && VN_IS(nodep->dtypep(), UnpackArrayDType)) { - // Public variable at this end and it is an unpacked array. We need to assign - // instead of aliased, because otherwise it will pass V3Slice and invalid - // code will be emitted. - UINFO(9, "assign to public and unpacked: " << nodep); - exprvarrefp = exprvarrefp->cloneTree(false); - exprvarrefp->access(VAccess::READ); - nodep->user4(true); // Making assignment to it - m_modp->addStmtsp( - new AstAssignW{flp, new AstVarRef{flp, nodep, VAccess::WRITE}, exprvarrefp}); - } else if (nodep->isIfaceRef()) { - exprvarrefp = exprvarrefp->cloneTree(false); - exprvarrefp->access(VAccess::READ); - m_modp->addStmtsp(new AstAssignVarScope{ - flp, new AstVarRef{flp, nodep, VAccess::WRITE}, exprvarrefp}); - FileLine* const flbp = exprvarrefp->varp()->fileline(); - flp->modifyStateInherit(flbp); - flbp->modifyStateInherit(flp); - } else { - // Do to inlining child's variable now within the same - // module, so a AstVarRef not AstVarXRef below - exprvarrefp = exprvarrefp->cloneTree(false); - exprvarrefp->access(VAccess::READ); - AstVarRef* const nodeVarRefp = new AstVarRef{flp, nodep, VAccess::WRITE}; - if (nodep->isForced() && nodep->direction() == VDirection::INPUT) { - nodep->user4(true); // Making assignment to it - m_modp->addStmtsp(new AstAssignW{flp, nodeVarRefp, exprvarrefp}); - } else if (nodep->isForced() && nodep->direction() == VDirection::OUTPUT) { - exprvarrefp->access(VAccess::WRITE); - nodeVarRefp->access(VAccess::READ); - m_modp->addStmtsp(new AstAssignW{flp, exprvarrefp, nodeVarRefp}); - } else { - m_modp->addStmtsp(new AstAssignAlias{flp, nodeVarRefp, exprvarrefp}); - } - FileLine* const flbp = exprvarrefp->varp()->fileline(); - flp->modifyStateInherit(flbp); - flbp->modifyStateInherit(flp); - } - } // Iterate won't hit AstIfaceRefDType directly as it is no longer underneath the module if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) { m_renamedInterfaces.insert(nodep->name()); @@ -350,7 +290,7 @@ class InlineRelinkVisitor final : public VNVisitor { ifacerefp->addNextHere(newdp); // Relink to point to newly cloned cell if (newdp->cellp()) { - if (AstCell* const newcellp = VN_CAST(newdp->cellp()->user4p(), Cell)) { + if (AstCell* const newcellp = VN_CAST(newdp->cellp()->user3p(), Cell)) { newdp->cellp(newcellp); newdp->cellName(newcellp->name()); // Tag the old ifacerefp to ensure it leaves no stale @@ -377,41 +317,47 @@ class InlineRelinkVisitor final : public VNVisitor { nodep->name(m_cellp->name() + "__DOT__" + nodep->name()); iterateChildren(nodep); } - void visit(AstInitialStatic* nodep) override { - VL_RESTORER(m_initialStatic); - m_initialStatic = true; - iterateChildren(nodep); - } - void visit(AstNodeAssign* nodep) override { - if (const AstVarRef* const varrefp = VN_CAST(nodep->lhsp(), VarRef)) { - if (m_initialStatic && varrefp->varp()->user2() && varrefp->varp()->user4()) { - // Initial assignment to i/o we are overriding, can remove - UINFO(9, "Remove InitialStatic " << nodep); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return; - } - } - iterateChildren(nodep); + void visit(AstAssignAlias* nodep) override { + // Don't replace port variable in the alias } void visit(AstVarRef* nodep) override { - if (nodep->varp()->user2p() // It's being converted to an alias. - && !nodep->varp()->user3() - // Don't constant propagate aliases (we just made) - && !VN_IS(nodep->backp(), AssignAlias) - // Forced signals do not use aliases - && !nodep->varp()->isForced()) { - const AstVar* const varp = nodep->varp(); - if (AstConst* const constp = VN_CAST(varp->user2p(), Const)) { - nodep->replaceWith(constp->cloneTree(false)); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - return; - } else if (const AstVarRef* const vrefp = VN_CAST(varp->user2p(), VarRef)) { - nodep->varp(vrefp->varp()); - nodep->classOrPackagep(vrefp->classOrPackagep()); + // If the target port is being inlined, replace reference with the + // connected expression (always a Const of a VarRef). + AstNode* const pinExpr = nodep->varp()->user2p(); + if (!pinExpr) return; + + // If it's a constant, inline it + if (AstConst* const constp = VN_CAST(pinExpr, Const)) { + // You might think we would not try to substitute a constant for + // a written variable, but we might need to do this if for example + // there is an assignment to an input port, and that input port + // is tied to a constant on the cell we are inlining. This does + // generate an ASSIGNIN warning, but that can be downgraded to + // a warning. (Also assigning to an input can has valid uses if + // e.g. done via a hierarchical reference from outside to an input + // unconnected on the instance, so we don't want ASSIGNIN fatal.) + // Same applies when there is a static initialzier for an input. + // To avoid having to special case malformed assignment, or worse + // yet emiting code like 0 = 0, we instead substitute a placeholder + // variable that will later be pruned (it will otherwise be unreferenced). + if (!nodep->access().isReadOnly()) { + AstVar* const varp = nodep->varp(); + const std::string name = "__vInlPlaceholder_" + std::to_string(++m_nPlaceholders); + AstVar* const holdep = new AstVar{varp->fileline(), VVarType::VAR, name, varp}; + m_modp->addStmtsp(holdep); + AstVarRef* const newp = new AstVarRef{nodep->fileline(), holdep, nodep->access()}; + nodep->replaceWith(newp); } else { - nodep->v3fatalSrc("Null connection?"); + nodep->replaceWith(constp->cloneTree(false)); } + VL_DO_DANGLING(nodep->deleteTree(), nodep); + return; } + + // Otherwise it must be a variable reference, retarget this ref + const AstVarRef* const vrefp = VN_AS(pinExpr, VarRef); + nodep->varp(vrefp->varp()); + nodep->classOrPackagep(vrefp->classOrPackagep()); } void visit(AstVarXRef* nodep) override { // Track what scope it was originally under so V3LinkDot can resolve it @@ -476,175 +422,226 @@ public: }; //###################################################################### -// Inline state, as a visitor of each AstNode +// Module inliner -class InlineVisitor final : public VNVisitor { - // NODE STATE - // Cleared entire netlist - // AstIfaceRefDType::user1() // Whether the cell pointed to by this - // // AstIfaceRefDType has been inlined - // Input: - // AstNodeModule::user1p() // ModuleState instance (via m_moduleState) - // Cleared each cell - // AstVar::user2p() // AstVarRef*/AstConst* Points to signal this - // // is a direct connect to - // AstVar::user3() // bool Don't alias the user2, keep it as signal - // AstVar::user4() // bool Was input, remove InitialStatic Assign - // AstCell::user4 // AstCell* of the created clone - const VNUser4InUse m_inuser4; +namespace ModuleInliner { - ModuleStateUser1Allocator& m_moduleState; +// A port variable in an inlined module can be connected 2 ways. +// Either add a continuous assignment between the pin expression from +// the instance and the port variable, or simply inline the pin expression +// in place of the port variable. We will prefer to do the later whenever +// possible (and sometimes required). When inlining, we need to create an +// alias for the inined variable, in order to resovle hierarchical refrences +// against it later in V3Scope (and also for tracing, which is inserted +//later). Returns ture iff the given port variable should be inlined, +// and false if a continuous assignment should be used. +bool inlinePort(AstVar* nodep) { + // Interface references are always inlined + if (nodep->isIfaceRef()) return true; + // Ref ports must be always inlined + if (nodep->direction() == VDirection::REF) return true; + // Forced signals must not be inlined. The port signal can be + // forced separately from the connected signals. + if (nodep->isForced()) return false; - // STATE - AstNodeModule* m_modp = nullptr; // Current module - VDouble0 m_statCells; // Statistic tracking + // Note: For singls marked 'public' (and not 'public_flat') inlining + // of their contaning modules is disabled so they wont reach here. - // METHODS - void inlineCell(AstCell* nodep) { - UINFO(5, " Inline CELL " << nodep); + // TODO: For now, writable public signals inside the cell cannot be + // eliminated as they are entered into the VerilatedScope, and + // changes would not propagate to it when assigned. (The alias created + // for them ensures they would be read correctly, but would not + // propagate any changes.) This can be removed when the VerialtedScope + // construction in V3EmitCSyms understands aliases. + if (nodep->isSigUserRWPublic()) return false; - const VNUser2InUse user2InUse; - const VNUser3InUse user3InUse; + // Otherwise we can repalce the variable + return true; +} - ++m_statCells; +// Connect the given port 'nodep' (being inlined into 'modp') to the given +// expression (from the Cell Pin) +void connectPort(AstNodeModule* modp, AstVar* nodep, AstNodeExpr* pinExprp) { + UINFO(6, "Connecting " << pinExprp); + UINFO(6, " to " << nodep); - // Before cloning simplify pin assignments. Better off before, as if the module has - // multiple instantiations we will save work, and we can't call pinReconnectSimple in this - // loop as it clone()s itself. - for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { - V3Inst::pinReconnectSimple(pinp, nodep, false); - } + // Decide whether to inline the port variable or use continuous assignments + const bool inlineIt = inlinePort(nodep); - // Is this the last cell referencing this module? - const bool lastCell = --m_moduleState(nodep->modp()).m_cellRefs == 0; + // If we deccided to inline it, record the expression to substitute this variable with + if (inlineIt) nodep->user2p(pinExprp); - // Important: If this is the last cell, then don't clone the instantiated module but - // inline the original directly. While this requires some special casing, doing so - // saves us having to temporarily clone the module for the last cell, which - // significantly reduces Verilator memory usage. This is especially true as often the - // top few levels of the hierarchy are singleton wrapper modules, which we always - // inline. In this case this special casing saves us from having to clone essentially - // the entire netlist, which would in effect double Verilator memory consumption, or - // worse if we put off deleting the inlined modules until the end. Not having to clone - // large trees also improves speed. - AstNodeModule* newmodp = nullptr; - if (!lastCell) { - // Clone original module - newmodp = nodep->modp()->cloneTree(false); + FileLine* const flp = nodep->fileline(); + + // Helper to creates an AstVarRef reference to the port variable + const auto portRef = [&](VAccess access) { return new AstVarRef{flp, nodep, access}; }; + + // If the connected expression is a constant, add an assignment to set + // the port variable. The constant can still be inlined, in which case + // this is needed for tracing the inlined port variable. + if (AstConst* const pinp = VN_CAST(pinExprp, Const)) { + modp->addStmtsp(new AstAssignW{flp, portRef(VAccess::WRITE), pinp->cloneTree(false)}); + return; + } + + // Otherwise it must be a variable reference due to having called pinReconnectSimple + const AstVarRef* const pinRefp = VN_AS(pinExprp, VarRef); + + // Helper to create an AstVarRef reference to the pin variable + const auto pinRef = [&](VAccess access) { + AstVarRef* const p = new AstVarRef{pinRefp->fileline(), pinRefp->varp(), access}; + p->classOrPackagep(pinRefp->classOrPackagep()); + return p; + }; + + // If it is being inlined, create the alias for it + if (inlineIt) { + UINFO(6, "Inlning port variable: " << nodep); + if (nodep->isIfaceRef()) { + modp->addStmtsp( + new AstAssignVarScope{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)}); } else { - // For the last cell, reuse the original module - nodep->modp()->unlinkFrBack(); - newmodp = nodep->modp(); + modp->addStmtsp( + new AstAssignAlias{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)}); } - // Find cell cross-references - nodep->modp()->foreach([](AstCell* cellp) { - // clonep is nullptr when inlining the last instance, if so the use original node - cellp->user4p(cellp->clonep() ? cellp->clonep() : cellp); - }); - // Create data for dotted variable resolution - AstCellInline* const inlinep - = new AstCellInline{nodep->fileline(), nodep->name(), nodep->modp()->origName(), - nodep->modp()->timeunit()}; - m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells - // Create assignments to the pins - for (AstPin* pinp = nodep->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { - if (!pinp->exprp()) continue; - UINFO(6, " Pin change from " << pinp->modVarp()); - - AstNode* const connectRefp = pinp->exprp(); - UASSERT_OBJ(VN_IS(connectRefp, Const) || VN_IS(connectRefp, VarRef), pinp, - "Unknown interconnect type; pinReconnectSimple should have cleared up"); - V3Inst::checkOutputShort(pinp); - - // Make new signal; even though we'll optimize the interconnect, we - // need an alias to trace correctly. If tracing is disabled, we'll - // delete it in later optimizations. - AstVar* const pinOldVarp = pinp->modVarp(); - AstVar* const pinNewVarp = lastCell ? pinOldVarp : pinOldVarp->clonep(); - UASSERT_OBJ(pinNewVarp, pinOldVarp, "Cloning failed"); - // Propagate any attributes across the interconnect - pinNewVarp->propagateAttrFrom(pinOldVarp); - if (const AstVarRef* const vrefp = VN_CAST(connectRefp, VarRef)) { - vrefp->varp()->propagateAttrFrom(pinOldVarp); - } - - // One to one interconnect won't make a temporary variable. - // This prevents creating a lot of extra wires for clock signals. - // It will become a tracing alias. - UINFO(6, "One-to-one " << connectRefp); - UINFO(6, " -to " << pinNewVarp); - pinNewVarp->user2p(connectRefp); - // Public output inside the cell must go via an assign rather - // than alias. Else the public logic will set the alias, losing - // the value to be propagated up (InOnly isn't a problem as the - // AssignAlias will create the assignment for us) - pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() - && pinNewVarp->direction() == VDirection::OUTPUT); - } - // Cleanup var names, etc, to not conflict - { InlineRelinkVisitor{newmodp, m_modp, nodep}; } - // Move statements under the module we are inlining into - if (AstNode* const stmtsp = newmodp->stmtsp()) { - stmtsp->unlinkFrBackWithNext(); - m_modp->addStmtsp(stmtsp); - } - // Clear any leftover ports, etc - VL_DO_DANGLING(newmodp->deleteTree(), newmodp); - // Remove the cell we just inlined - nodep->unlinkFrBack(); - VL_DO_DANGLING(pushDeletep(nodep), nodep); + // They will become the same variable, so propagate file-line and variable attributes + pinRefp->varp()->fileline()->modifyStateInherit(flp); + flp->modifyStateInherit(pinRefp->varp()->fileline()); + pinRefp->varp()->propagateAttrFrom(nodep); + nodep->propagateAttrFrom(pinRefp->varp()); + return; } - // VISITORS - void visit(AstNetlist* nodep) override { - // Iterate modules backwards, in bottom-up order. Required! - iterateAndNextConstNullBackwards(nodep->modulesp()); - // Clean up AstIfaceRefDType references - iterateChildren(nodep->typeTablep()); + // Otherwise create the continuous assignment between the port var and the pin expression + UINFO(6, "Not inlning port variable: " << nodep); + if (nodep->direction() == VDirection::INPUT) { + modp->addStmtsp(new AstAssignW{flp, portRef(VAccess::WRITE), pinRef(VAccess::READ)}); + } else if (nodep->direction() == VDirection::OUTPUT) { + modp->addStmtsp(new AstAssignW{flp, pinRef(VAccess::WRITE), portRef(VAccess::READ)}); + } else { + pinExprp->v3fatalSrc("V3Tristate left INOUT port"); // LCOV_EXCL_LINE } - void visit(AstNodeModule* nodep) override { - UASSERT_OBJ(!m_modp, nodep, "Unsupported: Nested modules"); - VL_RESTORER(m_modp); - m_modp = nodep; - // Iterate the stored cells directly to reduce traversal - for (AstCell* const cellp : m_moduleState(nodep).m_childCells) { - if (m_moduleState(cellp->modp()).m_inlined) inlineCell(cellp); - } - m_moduleState(nodep).m_childCells.clear(); +} + +// Inline 'cellp' into 'modp'. 'last' indicatest this is tha last instance of the inlined module +void inlineCell(AstNodeModule* modp, AstCell* cellp, bool last) { + UINFO(5, " Inline Cell " << cellp); + UINFO(5, " into Module " << modp); + + const VNUser2InUse user2InUse; + + // Important: If this is the last cell, then don't clone the instantiated module but + // inline the original directly. While this requires some special casing, doing so + // saves us having to temporarily clone the module for the last cell, which + // significantly reduces Verilator memory usage. This is especially true as often the + // top few levels of the hierarchy are singleton wrapper modules, which we always + // inline. In this case this special casing saves us from having to clone essentially + // the entire netlist, which would in effect double Verilator memory consumption, or + // worse if we put off deleting the inlined modules until the end. Not having to clone + // large trees also improves speed. + + // The module we will yank the contents out of and put into 'modp' + AstNodeModule* const inlinedp = last ? cellp->modp()->unlinkFrBack() // + : cellp->modp()->cloneTree(false); + + // Compute map from original port variables and cells to their clones + std::unordered_map modVar2Clone; + for (AstNode *ap = cellp->modp()->stmtsp(), *bp = inlinedp->stmtsp(); ap || bp; + ap = ap->nextp(), bp = bp->nextp()) { + UASSERT_OBJ(ap && bp, ap ? ap : bp, "Clone has different number of children"); + // We only care about AstVar and AstCell, but faster to just set them all + ap->user3p(bp); } - void visit(AstIfaceRefDType* nodep) override { - if (nodep->user1()) { - // The cell has been removed so let's make sure we don't leave a reference to it - // This dtype may still be in use by the AstAssignVarScope created earlier - // but that'll get cleared up later - nodep->cellp(nullptr); + + // Create data for resolving hierarchical references later. + modp->addInlinesp(new AstCellInline{cellp->fileline(), cellp->name(), + cellp->modp()->origName(), cellp->modp()->timeunit()}); + + // Connect the pins on the instance + for (AstPin* pinp = cellp->pinsp(); pinp; pinp = VN_AS(pinp->nextp(), Pin)) { + if (!pinp->exprp()) continue; + UINFO(6, "Conecting port " << pinp->modVarp()); + UINFO(6, " of instance " << cellp); + + // Make sure the conneccted pin expression is always a VarRef or a Const + V3Inst::pinReconnectSimple(pinp, cellp, false); + + // Warn + V3Inst::checkOutputShort(pinp); + + // Pick up the old and new port variables signal (new is the same on last instance) + const AstVar* const oldModVarp = pinp->modVarp(); + AstVar* const newModVarp = VN_AS(oldModVarp->user3p(), Var); + // Pick up the connected expression (a VarRef or Const due to pinReconnectSimple) + AstNodeExpr* const pinExprp = VN_AS(pinp->exprp(), NodeExpr); + + // Connect up the port + connectPort(modp, newModVarp, pinExprp); + } + + // Cleanup var names, etc, to not conflict, relink replaced variables + { InlineRelinkVisitor{inlinedp, modp, cellp}; } + // Move statements from the inlined module into the module we are inlining into + if (AstNode* const stmtsp = inlinedp->stmtsp()) { + modp->addStmtsp(stmtsp->unlinkFrBackWithNext()); + } + // Delete the empty shell of the inlined module + VL_DO_DANGLING(inlinedp->deleteTree(), inlinedp); + // Remove the cell we just inlined + VL_DO_DANGLING(cellp->unlinkFrBack(), cellp); +} + +// Apply all inlining decisions +void process(AstNetlist* netlistp, ModuleStateUser1Allocator& moduleStates) { + // NODE STATE + // Input: + // AstNodeModule::user1p() // ModuleState instance (via moduleState) + // + // Cleared entire netlist + // AstIfaceRefDType::user1() // Whether the cell pointed to by this + // // AstIfaceRefDType has been inlined + // AstCell::user3p() // AstCell*, the clone + // AstVar::user3p() // AstVar*, the clone clone + // Cleared each cell + // AstVar::user2p() // AstVarRef*/AstConst* This port is connected to (AstPin::expr()) + const VNUser3InUse m_user3InUse; + + // Number of inlined instances, for statistics + VDouble0 m_nInlined; + + // We want to inline bottom up. The modules under the netlist are in + // dependency order (top first, leaves last), so find the end of the list. + AstNode* nodep = netlistp->modulesp(); + while (nodep->nextp()) nodep = nodep->nextp(); + + // Iterate module list backwards (stop when we get back to the Netlist) + while (AstNodeModule* const modp = VN_CAST(nodep, NodeModule)) { + nodep = nodep->backp(); + + // Consider each cell inside the current module for inling + for (AstCell* const cellp : moduleStates(modp).m_childCells) { + ModuleState& childState = moduleStates(cellp->modp()); + if (!childState.m_inlined) continue; + ++m_nInlined; + inlineCell(modp, cellp, --childState.m_cellRefs == 0); } } - //-------------------- - void visit(AstCell* nodep) override { // LCOV_EXCL_START - nodep->v3fatalSrc("Traversal should have been short circuited"); - } - void visit(AstNodeStmt* nodep) override { - nodep->v3fatalSrc("Traversal should have been short circuited"); - } // LCOV_EXCL_STOP - void visit(AstNodeFile*) override {} // Accelerate - void visit(AstNodeDType*) override {} // Accelerate - void visit(AstNode* nodep) override { iterateChildren(nodep); } + V3Stats::addStat("Optimizations, Inlined instances", m_nInlined); -public: - // CONSTRUCTORS - explicit InlineVisitor(AstNode* nodep, ModuleStateUser1Allocator& moduleState) - : m_moduleState{moduleState} { - iterate(nodep); - } - ~InlineVisitor() override { - V3Stats::addStat("Optimizations, Inlined instances", m_statCells); - } -}; + // Clean up AstIfaceRefDType references + // If the cell has been removed let's make sure we don't leave a + // reference to it. This dtype may still be in use by the + // AstAssignVarScope created earlier but that'll get cleared up later + netlistp->typeTablep()->foreach([](AstIfaceRefDType* nodep) { + if (nodep->user1()) nodep->cellp(nullptr); + }); +} + +} //namespace ModuleInliner //###################################################################### -// Inline class functions +// V3Inline class functions void V3Inline::inlineAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ":"); @@ -656,7 +653,8 @@ void V3Inline::inlineAll(AstNetlist* nodep) { // Scoped to clean up temp userN's { InlineMarkVisitor{nodep, moduleState}; } - { InlineVisitor{nodep, moduleState}; } + // Inline the modles we decided to inline + ModuleInliner::process(nodep, moduleState); // Check inlined modules have been removed during traversal. Otherwise we might have blown // up Verilator memory consumption. diff --git a/src/V3Inst.cpp b/src/V3Inst.cpp index aaa3d87dd..fccaa5709 100644 --- a/src/V3Inst.cpp +++ b/src/V3Inst.cpp @@ -694,7 +694,7 @@ AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTri return InstStatic::pinReconnectSimple(pinp, cellp, forTristate, alwaysCvt); } -void V3Inst::checkOutputShort(AstPin* nodep) { +void V3Inst::checkOutputShort(const AstPin* nodep) { if (nodep->modVarp()->direction() == VDirection::OUTPUT) { if (VN_IS(nodep->exprp(), Const) || VN_IS(nodep->exprp(), Extend) || (VN_IS(nodep->exprp(), Concat) diff --git a/src/V3Inst.h b/src/V3Inst.h index 98a82d78d..1b5ffaf67 100644 --- a/src/V3Inst.h +++ b/src/V3Inst.h @@ -33,7 +33,7 @@ public: static void dearrayAll(AstNetlist* nodep) VL_MT_DISABLED; static AstAssignW* pinReconnectSimple(AstPin* pinp, AstCell* cellp, bool forTristate, bool alwaysCvt = false) VL_MT_DISABLED; - static void checkOutputShort(AstPin* nodep) VL_MT_DISABLED; + static void checkOutputShort(const AstPin* nodep) VL_MT_DISABLED; }; #endif // Guard diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 9b58281f0..a94d18b69 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -743,7 +743,7 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp, ss << "@("; V3EmitV::verilogForTree(senItemp, ss); ss << ")"; - addDebug(triggerNumber, ss.str()); + addDebug(triggerNumber, VString::quoteBackslash(ss.str())); // ++triggerNumber; diff --git a/test_regress/t/t_dist_cppstyle.py b/test_regress/t/t_dist_cppstyle.py index 596bd8dd3..a522fe10c 100755 --- a/test_regress/t/t_dist_cppstyle.py +++ b/test_regress/t/t_dist_cppstyle.py @@ -93,7 +93,7 @@ for filename in sorted(files.keys()): "Use brace instead of parenthesis-style constructors e.g. ': m_...{...}'") if re.search(r'\.(c|cpp)', filename): - check_pattern(filename, contents, r'(\w+\s+)*(inline)[^\n]*', None, + check_pattern(filename, contents, r'(\w+\s+)*(\binline\b)[^\n]*', None, "'inline' keyword is on functions defined in .cpp files") test.passes() diff --git a/test_regress/t/t_dpi_var.cpp b/test_regress/t/t_dpi_var.cpp index 6da1a12e6..77cf84450 100644 --- a/test_regress/t/t_dpi_var.cpp +++ b/test_regress/t/t_dpi_var.cpp @@ -19,25 +19,27 @@ struct MyMon { uint32_t* sigsp[2]; + uint32_t addend = 0; MyMon() { sigsp[0] = NULL; sigsp[1] = NULL; } }; -MyMon mons[2]; +MyMon mons[4]; -void mon_register_a(const char* namep, void* sigp, bool isOut) { +void mon_register_a(const char* namep, void* sigp, bool isOut, int n, int addend) { // Callback from initial block in monitor #ifdef TEST_VERBOSE - VL_PRINTF("- mon_register_a(\"%s\", %p, %d);\n", namep, sigp, isOut); + VL_PRINTF("- mon_register_a(\"%s\", %p, %d, %d, %d);\n", namep, sigp, isOut, n, addend); #endif - mons[0].sigsp[isOut] = (uint32_t*)sigp; + mons[n].sigsp[isOut] = (uint32_t*)sigp; + mons[n].addend = addend; } void mon_do(MyMon* monp) { if (!monp->sigsp[0]) vl_fatal(__FILE__, __LINE__, "", "never registered"); if (!monp->sigsp[1]) vl_fatal(__FILE__, __LINE__, "", "never registered"); - *monp->sigsp[1] = (*(monp->sigsp[0])) + 1; + *monp->sigsp[1] = (*(monp->sigsp[0])) + monp->addend; #ifdef TEST_VERBOSE VL_PRINTF("- mon_do(%08x(&%p) -> %08x(&%p));\n", *(monp->sigsp[0]), monp->sigsp[0], @@ -66,11 +68,10 @@ void mon_scope_name(const char* namep) { vl_fatal(__FILE__, __LINE__, "", ("Unexp dpiscope name "s + modp).c_str()); } -extern "C" void mon_register_b(const char* namep, int isOut); -void mon_register_b(const char* namep, int isOut) { +extern "C" void mon_register_b(const char* namep, int isOut, int n, int addend) { const char* modp = svGetNameFromScope(svGetScope()); #ifdef TEST_VERBOSE - VL_PRINTF("- mon_register_b('%s', \"%s\", %d);\n", modp, namep, isOut); + VL_PRINTF("- mon_register_b('%s', \"%s\", %d, %d %d);\n", modp, namep, isOut, n, addend); #endif // Use scope to get pointer and size of signal const VerilatedScope* scopep = Verilated::dpiScope(); @@ -82,7 +83,8 @@ void mon_register_b(const char* namep, int isOut) { } else { uint32_t* datap = (uint32_t*)(varp->datap()); VL_PRINTF("- mon_register_b('%s', \"%s\", %p, %d);\n", modp, namep, datap, isOut); - mons[1].sigsp[isOut] = (uint32_t*)(varp->datap()); + mons[n].sigsp[isOut] = (uint32_t*)(varp->datap()); + mons[n].addend = addend; } } @@ -106,6 +108,8 @@ void mon_eval() { // Callback from always@ negedge mon_do(&mons[0]); mon_do(&mons[1]); + mon_do(&mons[2]); + mon_do(&mons[3]); } //====================================================================== diff --git a/test_regress/t/t_dpi_var.py b/test_regress/t/t_dpi_var.py index 0cf3bcd6e..b54cadf07 100755 --- a/test_regress/t/t_dpi_var.py +++ b/test_regress/t/t_dpi_var.py @@ -21,19 +21,27 @@ test.compile( if test.vlt_all: test.file_grep( out_filename, - r'{"type":"VAR","name":"formatted",.*"loc":"\w,56:[^"]*",.*"origName":"formatted",.*"direction":"INPUT",.*"dtypeName":"string",.*"attrSFormat":true' + r'{"type":"VAR","name":"formatted",.*"loc":"\w,69:[^"]*",.*"origName":"formatted",.*"direction":"INPUT",.*"dtypeName":"string",.*"attrSFormat":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.in",.*"loc":"\w,77:[^"]*",.*"origName":"in",.*"dtypeName":"int",.*"isSigUserRdPublic":true' + r'{"type":"VAR","name":"t.sub.in",.*"loc":"\w,91:[^"]*",.*"origName":"in",.*"dtypeName":"int",.*"isSigUserRdPublic":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.fr_a",.*"loc":"\w,78:[^"]*",.*"origName":"fr_a",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + r'{"type":"VAR","name":"t.sub.in_a",.*"loc":"\w,92:[^"]*",.*"origName":"in_a",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' ) test.file_grep( out_filename, - r'{"type":"VAR","name":"t.sub.fr_b",.*"loc":"\w,79:[^"]*",.*"origName":"fr_b",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + r'{"type":"VAR","name":"t.sub.in_b",.*"loc":"\w,93:[^"]*",.*"origName":"in_b",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + ) + test.file_grep( + out_filename, + r'{"type":"VAR","name":"t.sub.fr_a",.*"loc":"\w,94:[^"]*",.*"origName":"fr_a",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' + ) + test.file_grep( + out_filename, + r'{"type":"VAR","name":"t.sub.fr_b",.*"loc":"\w,95:[^"]*",.*"origName":"fr_b",.*"dtypeName":"int",.*"isSigUserRdPublic":true,.*"isSigUserRWPublic":true' ) test.execute() diff --git a/test_regress/t/t_dpi_var.v b/test_regress/t/t_dpi_var.v index e5829cb89..45e772c0a 100644 --- a/test_regress/t/t_dpi_var.v +++ b/test_regress/t/t_dpi_var.v @@ -17,27 +17,40 @@ module t (/*AUTOARG*/ wire monclk = ~clk; int in; + int in_a; + int in_b; int fr_a; int fr_b; + int fr_a2; + int fr_b2; int fr_chk; sub sub (.*); // Test loop always @ (posedge clk) begin `ifdef TEST_VERBOSE - $write("[%0t] cyc==%0d in=%x fr_a=%x b=%x fr_chk=%x\n", $time, cyc, in, fr_a, fr_b, fr_chk); + $write("[%0t] cyc==%0d in=%x sub.in_a=%x sub.in_b=%x fr_a=%x fr_b=%x fr_a2=%x fr_b2=%x fr_chk=%x\n", + $time, cyc, in, sub.in_a, sub.in_b, fr_a, fr_b, fr_a2, fr_b2, fr_chk); `endif cyc <= cyc + 1; in <= {in[30:0], in[31]^in[2]^in[0]}; + // The inputs to sub will be updated externally on the neg-edge so these + // don't matter for the result + in_a <= in_a + 1; + in_b <= in_b + 1; if (cyc==0) begin // Setup in <= 32'hd70a4497; + in_a <= 0; + in_b <= 0; end else if (cyc<3) begin end else if (cyc<10) begin if (fr_chk != fr_a) $stop; if (fr_chk != fr_b) $stop; + if (fr_chk != fr_a2) $stop; + if (fr_chk != fr_b2) $stop; end else if (cyc==10) begin $write("*-* All Finished *-*\n"); @@ -57,33 +70,43 @@ import "DPI-C" context function void mon_scope_name (input string formatted /*ve `else import "DPI-C" context function void mon_scope_name (input string formatted); `endif -import "DPI-C" context function void mon_register_b(string name, int isOut); +import "DPI-C" context function void mon_register_b(string name, int isOut, int n, int addend); import "DPI-C" context function void mon_register_done(); import "DPI-C" context function void mon_eval(); module sub (/*AUTOARG*/ // Outputs - fr_a, fr_b, fr_chk, + fr_a, fr_b, fr_a2, fr_b2, fr_chk, // Inputs - in + in, in_a, in_b ); `systemc_imp_header void mon_class_name(const char* namep); - void mon_register_a(const char* namep, void* sigp, bool isOut); + void mon_register_a(const char* namep, void* sigp, bool isOut, int n, int addend); `verilog + /* verilator lint_off ASSIGNIN */ `ifdef ATTRIBUTES input int in /*verilator public_flat_rd*/; + input int in_a /*verilator public_flat_rw @(posedge t.monclk)*/; + input int in_b /*verilator public_flat_rw @(posedge t.monclk)*/; output int fr_a /*verilator public_flat_rw @(posedge t.monclk)*/; output int fr_b /*verilator public_flat_rw @(posedge t.monclk)*/; `else input int in; + input int in_a; + input int in_b; output int fr_a; output int fr_b; `endif + output int fr_a2; + output int fr_b2; output int fr_chk; + /* verilator lint_on ASSIGNIN */ + always @* fr_a2 = in_a + 1; + always @* fr_b2 = in_b + 1; always @* fr_chk = in + 1; initial begin @@ -91,11 +114,15 @@ module sub (/*AUTOARG*/ $c("mon_class_name(this->name());"); mon_scope_name("%m"); // Scheme A - pass pointer directly - $c("mon_register_a(\"in\", &", in, ", false);"); - $c("mon_register_a(\"fr_a\", &", fr_a, ", true);"); + $c("mon_register_a(\"in\", &", in, ", false, 0, 1);"); + $c("mon_register_a(\"fr_a\", &", fr_a, ", true, 0, 1);"); + $c("mon_register_a(\"in\", &", in, ", false, 1, 0);"); + $c("mon_register_a(\"in_a\", &", in_a, ", true, 1, 0);"); // Scheme B - use VPIish callbacks to see what signals exist - mon_register_b("in", 0); - mon_register_b("fr_b", 1); + mon_register_b("in", 0, 2, 1); + mon_register_b("fr_b", 1, 2, 1); + mon_register_b("in", 0, 3, 0); + mon_register_b("in_b", 1, 3, 0); mon_register_done(); end diff --git a/test_regress/t/t_dpi_var.vlt b/test_regress/t/t_dpi_var.vlt index 70216609d..e5fb1d038 100644 --- a/test_regress/t/t_dpi_var.vlt +++ b/test_regress/t/t_dpi_var.vlt @@ -8,5 +8,7 @@ sformat -task "mon_scope_name" -var "formatted" public_flat_rd -module "sub" -var "in" +public_flat_rw -module "sub" -var "in_a" @(posedge t.monclk) +public_flat_rw -module "sub" -var "in_b" @(posedge t.monclk) public_flat_rw -module "sub" -var "fr_a" @(posedge t.monclk) public_flat_rw -module "sub" -var "fr_b" @(posedge t.monclk) diff --git a/test_regress/t/t_force_port_alias.v b/test_regress/t/t_force_port_alias.v deleted file mode 100644 index d4bff0986..000000000 --- a/test_regress/t/t_force_port_alias.v +++ /dev/null @@ -1,61 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2024 by Wilson Snyder. -// 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 sub( - // Outputs - out, - // Inputs - clk - ); - // verilator inline_module - - output [3:0] out /* <-- this variable has to be marked as having external refs */; - input clk; - - reg [3:0] r; - - always @ (posedge clk) - r <= 4'h1; - assign out = r; -endmodule - -module t(/*AUTOARG*/ - // Inputs - clk - ); - - input clk; - reg [3:0] unused; - - sub sub1(unused, clk); - - integer cyc = 0; - - always @ (posedge clk) begin - cyc <= cyc + 1; - if (cyc == 1) begin - `checkh(sub1.r, 4'h1); - `checkh(sub1.out, 4'h1); - end - else if (cyc == 2) begin - force sub1.r = 4'h2; - force sub1.out = 4'h3; - end - else if (cyc == 3) begin - `checkh(sub1.r, 4'h2); - `checkh(sub1.out, 4'h3); - end - // - else if (cyc == 99) begin - $write("*-* All Finished *-*\n"); - $finish; - end - end - -endmodule diff --git a/test_regress/t/t_force_port_alias.py b/test_regress/t/t_force_port_inline.py similarity index 91% rename from test_regress/t/t_force_port_alias.py rename to test_regress/t/t_force_port_inline.py index cacc314ab..587a5d18f 100755 --- a/test_regress/t/t_force_port_alias.py +++ b/test_regress/t/t_force_port_inline.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('vlt_all') -test.compile() +test.compile(verilator_flags2=['--binary']) test.execute() diff --git a/test_regress/t/t_force_port_inline.v b/test_regress/t/t_force_port_inline.v new file mode 100644 index 000000000..062ddb88c --- /dev/null +++ b/test_regress/t/t_force_port_inline.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// 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 sub( + input wire [7:0] i, + output wire [7:0] o +); + // Must inline this module + // verilator inline_module + + wire [7:0] m; + assign m = i; + assign o = m; +endmodule + +module top; + // Variable input + reg [7:0] i = 8'h01; + reg [7:0] o_v; + sub sub_v(i, o_v); + + // Constant input + reg [7:0] o_c; + sub sub_c(8'h10, o_c); + + logic clk = 1'b0; + always #1 clk = ~clk; + int cyc = 0; + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc == 1) begin + `checkh(i, 8'h01); + `checkh(sub_v.i, 8'h01); + `checkh(sub_v.m, 8'h01); + `checkh(sub_v.o, 8'h01); + `checkh(o_v, 8'h01); + `checkh(sub_c.i, 8'h10); + `checkh(sub_c.m, 8'h10); + `checkh(sub_c.o, 8'h10); + `checkh(o_c, 8'h10); + end + else if (cyc == 2) begin + force sub_v.i = 8'h02; + force sub_v.m = 8'h03; + force sub_v.o = 8'h04; + force sub_c.i = 8'h20; + force sub_c.m = 8'h30; + force sub_c.o = 8'h40; + end + else if (cyc == 3) begin + `checkh(i, 8'h01); + `checkh(sub_v.i, 8'h02); + `checkh(sub_v.m, 8'h03); + `checkh(sub_v.o, 8'h04); + `checkh(o_v, 8'h04); + `checkh(sub_c.i, 8'h20); + `checkh(sub_c.m, 8'h30); + `checkh(sub_c.o, 8'h40); + `checkh(o_c, 8'h40); + end + // + else if (cyc == 99) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule diff --git a/test_regress/t/t_inst_tree_inl0_pub1.py b/test_regress/t/t_inst_tree_inl0_pub1.py index 5aca960d1..9d89a7285 100755 --- a/test_regress/t/t_inst_tree_inl0_pub1.py +++ b/test_regress/t/t_inst_tree_inl0_pub1.py @@ -42,7 +42,7 @@ if test.vlt_all: # We expect to combine sequent functions across multiple instances of # l2, l3, l4, l5. If this number drops, please confirm this has not broken. test.file_grep(test.stats, r'Optimizations, Combined CFuncs\s+(\d+)', - (107 if test.vltmt else 114)) + (99 if test.vltmt else 82)) # Everything should use relative references check_relative_refs("t", True) diff --git a/test_regress/t/t_json_only_flat.out b/test_regress/t/t_json_only_flat.out index 6ce3beadd..7f48ce13a 100644 --- a/test_regress/t/t_json_only_flat.out +++ b/test_regress/t/t_json_only_flat.out @@ -65,26 +65,26 @@ "lhsp": [ {"type":"VARREF","name":"t.d","addr":"(YB)","loc":"d,14:16,14:17","dtypep":"(H)","access":"WR","varp":"(N)","varScopep":"(GB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(ZB)","loc":"d,34:24,34:27","dtypep":"(J)", + {"type":"ASSIGNALIAS","name":"","addr":"(ZB)","loc":"d,36:30,36:31","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.clk","addr":"(AC)","loc":"d,21:42,21:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.between","addr":"(AC)","loc":"d,20:14,20:21","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.clk","addr":"(BC)","loc":"d,34:24,34:27","dtypep":"(J)","access":"WR","varp":"(S)","varScopep":"(JB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.q","addr":"(BC)","loc":"d,36:30,36:31","dtypep":"(H)","access":"WR","varp":"(U)","varScopep":"(LB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(CC)","loc":"d,35:30,35:31","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(CC)","loc":"d,34:24,34:27","dtypep":"(J)", "rhsp": [ - {"type":"VARREF","name":"t.d","addr":"(DC)","loc":"d,22:42,22:43","dtypep":"(H)","access":"RD","varp":"(N)","varScopep":"(GB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.clk","addr":"(DC)","loc":"d,21:42,21:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.d","addr":"(EC)","loc":"d,35:30,35:31","dtypep":"(H)","access":"WR","varp":"(T)","varScopep":"(KB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.clk","addr":"(EC)","loc":"d,34:24,34:27","dtypep":"(J)","access":"WR","varp":"(S)","varScopep":"(JB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(FC)","loc":"d,36:30,36:31","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(FC)","loc":"d,35:30,35:31","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.between","addr":"(GC)","loc":"d,20:14,20:21","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.d","addr":"(GC)","loc":"d,22:42,22:43","dtypep":"(H)","access":"RD","varp":"(N)","varScopep":"(GB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell1.q","addr":"(HC)","loc":"d,36:30,36:31","dtypep":"(H)","access":"WR","varp":"(U)","varScopep":"(LB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell1.d","addr":"(HC)","loc":"d,35:30,35:31","dtypep":"(H)","access":"WR","varp":"(T)","varScopep":"(KB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, {"type":"ALWAYS","name":"","addr":"(IC)","loc":"d,41:4,41:10","keyword":"always","isSuspendable":false,"needProcess":false, "sentreep": [ @@ -105,26 +105,26 @@ {"type":"VARREF","name":"t.between","addr":"(OC)","loc":"d,42:6,42:7","dtypep":"(H)","access":"WR","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []} ]}, - {"type":"ASSIGNALIAS","name":"","addr":"(PC)","loc":"d,48:10,48:13","dtypep":"(J)", + {"type":"ASSIGNALIAS","name":"","addr":"(PC)","loc":"d,49:16,49:17","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.clk","addr":"(QC)","loc":"d,27:42,27:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.between","addr":"(QC)","loc":"d,25:16,25:23","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.clk","addr":"(RC)","loc":"d,48:10,48:13","dtypep":"(J)","access":"WR","varp":"(X)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.d","addr":"(RC)","loc":"d,49:16,49:17","dtypep":"(H)","access":"WR","varp":"(Y)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(SC)","loc":"d,49:16,49:17","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(SC)","loc":"d,50:22,50:23","dtypep":"(H)", "rhsp": [ - {"type":"VARREF","name":"t.between","addr":"(TC)","loc":"d,25:16,25:23","dtypep":"(H)","access":"RD","varp":"(O)","varScopep":"(HB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.q","addr":"(TC)","loc":"d,26:42,26:43","dtypep":"(H)","access":"RD","varp":"(L)","varScopep":"(EB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.d","addr":"(UC)","loc":"d,49:16,49:17","dtypep":"(H)","access":"WR","varp":"(Y)","varScopep":"(OB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.q","addr":"(UC)","loc":"d,50:22,50:23","dtypep":"(H)","access":"WR","varp":"(Z)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, - {"type":"ASSIGNALIAS","name":"","addr":"(VC)","loc":"d,50:22,50:23","dtypep":"(H)", + {"type":"ASSIGNALIAS","name":"","addr":"(VC)","loc":"d,48:10,48:13","dtypep":"(J)", "rhsp": [ - {"type":"VARREF","name":"t.q","addr":"(WC)","loc":"d,26:42,26:43","dtypep":"(H)","access":"RD","varp":"(L)","varScopep":"(EB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.clk","addr":"(WC)","loc":"d,27:42,27:45","dtypep":"(J)","access":"RD","varp":"(M)","varScopep":"(FB)","classOrPackagep":"UNLINKED"} ], "lhsp": [ - {"type":"VARREF","name":"t.cell2.q","addr":"(XC)","loc":"d,50:22,50:23","dtypep":"(H)","access":"WR","varp":"(Z)","varScopep":"(PB)","classOrPackagep":"UNLINKED"} + {"type":"VARREF","name":"t.cell2.clk","addr":"(XC)","loc":"d,48:10,48:13","dtypep":"(J)","access":"WR","varp":"(X)","varScopep":"(NB)","classOrPackagep":"UNLINKED"} ],"timingControlp": []}, {"type":"ASSIGNW","name":"","addr":"(YC)","loc":"d,53:13,53:14","dtypep":"(H)", "rhsp": [ diff --git a/test_regress/t/t_var_in_assign_pedantic.py b/test_regress/t/t_var_in_assign_pedantic.py index 7b97942aa..778ba42c8 100755 --- a/test_regress/t/t_var_in_assign_pedantic.py +++ b/test_regress/t/t_var_in_assign_pedantic.py @@ -12,6 +12,8 @@ import vltest_bootstrap test.scenarios('vlt') test.top_filename = "t/t_var_in_assign_bad.v" -test.lint(verilator_flags2=['-Wpedantic -Wno-fatal']) +# Although this is mostly a lint test, do 'compile' to make sure we do not +# generate thrash code in the presence of a warning that is not fatal +test.compile(verilator_flags2=['-Wpedantic -Wno-fatal --flatten -fno-gate']) test.passes() diff --git a/test_regress/t/t_xml_flat.out b/test_regress/t/t_xml_flat.out index df60ede82..45d3b91cd 100644 --- a/test_regress/t/t_xml_flat.out +++ b/test_regress/t/t_xml_flat.out @@ -63,6 +63,10 @@ + + + + @@ -71,10 +75,6 @@ - - - - @@ -86,10 +86,6 @@ - - - - @@ -98,6 +94,10 @@ + + + +