// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Add temporaries, such as for inst nodes // // Code available from: http://www.veripool.org/verilator // //************************************************************************* // // Copyright 2003-2017 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. // // Verilator is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // //************************************************************************* // V3Inst's Transformations: // // Each module: // Pins: // Create a wire assign to interconnect to submodule // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include #include #include #include #include "V3Global.h" #include "V3Inst.h" #include "V3Ast.h" #include "V3Changed.h" #include "V3Const.h" //###################################################################### // Inst state, as a visitor of each AstNode class InstVisitor : public AstNVisitor { private: // NODE STATE // Cleared each Cell: // AstPin::user1p() -> bool. True if created assignment already AstUser1InUse m_inuser1; // STATE AstNodeModule* m_modp; // Current module AstCell* m_cellp; // Current cell static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } //int m_debug; int debug() { return m_debug; } // VISITORS virtual void visit(AstNodeModule* nodep) { UINFO(4," MOD "<name() == "t_chg") m_debug = 9; else m_debug=0; m_modp = nodep; nodep->iterateChildren(*this); m_modp = NULL; } virtual void visit(AstCell* nodep) { UINFO(4," CELL "<iterateChildren(*this); m_cellp = NULL; } virtual void visit(AstPin* nodep) { // PIN(p,expr) -> ASSIGNW(VARXREF(p),expr) (if sub's input) // or ASSIGNW(expr,VARXREF(p)) (if sub's output) UINFO(4," PIN "<exprp()) return; // No-connect if (debug()>=9) nodep->dumpTree(cout," Pin_oldb: "); if (nodep->modVarp()->isOutOnly() && nodep->exprp()->castConst()) nodep->v3error("Output port is connected to a constant pin, electrical short"); // Use user1p on the PIN to indicate we created an assign for this pin if (!nodep->user1SetOnce()) { // Simplify it V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, false); // Make a ASSIGNW (expr, pin) AstNode* exprp = nodep->exprp()->cloneTree(false); if (exprp->width() != nodep->modVarp()->width()) nodep->v3fatalSrc("Width mismatch, should have been handled in pinReconnectSimple"); if (nodep->modVarp()->isInout()) { nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator"); } else if (nodep->modVarp()->isOutput()) { AstNode* rhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false); AstAssignW* assp = new AstAssignW (exprp->fileline(), exprp, rhsp); m_modp->addStmtp(assp); } else if (nodep->modVarp()->isInput()) { // Don't bother moving constants now, // we'll be pushing the const down to the cell soon enough. AstNode* assp = new AstAssignW (exprp->fileline(), new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), true), exprp); m_modp->addStmtp(assp); if (debug()>=9) assp->dumpTree(cout," _new: "); } else if (nodep->modVarp()->isIfaceRef() || (nodep->modVarp()->subDTypep()->castUnpackArrayDType() && nodep->modVarp()->subDTypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType())) { // Create an AstAssignVarScope for Vars to Cells so we can link with their scope later AstNode* lhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false); AstVarRef* refp = exprp->castVarRef(); AstVarXRef* xrefp = exprp->castVarXRef(); if (!refp && !xrefp) exprp->v3fatalSrc("Interfaces: Pin is not connected to a VarRef or VarXRef"); AstAssignVarScope* assp = new AstAssignVarScope(exprp->fileline(), lhsp, exprp); m_modp->addStmtp(assp); } else { nodep->v3error("Assigned pin is neither input nor output"); } } // We're done with the pin nodep->unlinkFrBack()->deleteTree(); VL_DANGLING(nodep); } virtual void visit(AstUdpTable* nodep) { if (!v3Global.opt.bboxUnsup()) { // If we support primitives, update V3Undriven to remove special case nodep->v3error("Unsupported: Verilog 1995 UDP Tables. Use --bbox-unsup to ignore tables."); } } // Save some time virtual void visit(AstNodeAssign*) {} virtual void visit(AstAlways*) {} //-------------------- // Default: Just iterate virtual void visit(AstNode* nodep) { nodep->iterateChildren(*this); } public: // CONSTUCTORS explicit InstVisitor(AstNetlist* nodep) { m_modp=NULL; m_cellp=NULL; // nodep->accept(*this); } virtual ~InstVisitor() {} }; //###################################################################### class InstDeModVarVisitor : public AstNVisitor { // Expand all module variables, and save names for later reference private: // STATE typedef map VarNameMap; VarNameMap m_modVarNameMap; // Per module, name of cloned variables static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } // VISITORS virtual void visit(AstVar* nodep) { if (nodep->dtypep()->castIfaceRefDType()) { UINFO(8," dm-1-VAR "<iterateChildren(*this); } // Save some time virtual void visit(AstNodeMath*) {} // Default: Just iterate virtual void visit(AstNode* nodep) { nodep->iterateChildren(*this); } public: // METHODS void insert(AstVar* nodep) { UINFO(8," dmINSERT "<name(), nodep)); } AstVar* find(const string& name) { VarNameMap::iterator it = m_modVarNameMap.find(name); if (it != m_modVarNameMap.end()) { return it->second; } else { return NULL; } } void dump() { for (VarNameMap::iterator it=m_modVarNameMap.begin(); it!=m_modVarNameMap.end(); ++it) { cout<<"-namemap: "<first<<" -> "<second<accept(*this); } virtual ~InstDeModVarVisitor() {} }; //###################################################################### class InstDeVisitor : public AstNVisitor { // Find all cells with arrays, and convert to non-arrayed private: // STATE AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations int m_instSelNum; // Current instantiation count 0..N-1 InstDeModVarVisitor m_deModVars; // State of variables for current cell module typedef map VarNameMap; static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } // VISITORS virtual void visit(AstVar* nodep) { if (nodep->dtypep()->castUnpackArrayDType() && nodep->dtypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType()) { UINFO(8," dv-vec-VAR "<dtypep()->castUnpackArrayDType(); AstNode* prevp = NULL; for (int i = arrdtype->lsb(); i <= arrdtype->msb(); ++i) { string varNewName = nodep->name() + "__BRA__" + cvtToStr(i) + "__KET__"; UINFO(8,"VAR name insert "<subDTypep()->castIfaceRefDType()->cloneTree(false); arrdtype->addNextHere(ifaceRefp); ifaceRefp->cellp(NULL); AstVar* varNewp = nodep->cloneTree(false); varNewp->name(varNewName); varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); varNewp->dtypep(ifaceRefp); m_deModVars.insert(varNewp); if (!prevp) { prevp = varNewp; } else { prevp->addNextHere(varNewp); } } } if (prevp) nodep->addNextHere(prevp); if (prevp && debug()==9) { prevp->dumpTree(cout, "newintf: "); cout << endl; } } nodep->iterateChildren(*this); } virtual void visit(AstCell* nodep) { UINFO(4," CELL "<modp()) nodep->v3fatalSrc("Unlinked"); m_deModVars.main(nodep->modp()); // if (nodep->rangep()) { m_cellRangep = nodep->rangep(); AstVar* ifaceVarp = nodep->nextp()->castVar(); bool isIface = ifaceVarp && ifaceVarp->dtypep()->castUnpackArrayDType() && ifaceVarp->dtypep()->castUnpackArrayDType()->subDTypep()->castIfaceRefDType(); // Make all of the required clones for (int i = 0; i < m_cellRangep->elementsConst(); i++) { m_instSelNum = m_cellRangep->littleEndian() ? (m_cellRangep->elementsConst() - 1 - i) : i; int instNum = m_cellRangep->lsbConst() + i; AstCell* newp = nodep->cloneTree(false); nodep->addNextHere(newp); // Remove ranging and fix name newp->rangep()->unlinkFrBack()->deleteTree(); // Somewhat illogically, we need to rename the orignal name of the cell too. // as that is the name users expect for dotting // The spec says we add [x], but that won't work in C... newp->name(newp->name()+"__BRA__"+cvtToStr(instNum)+"__KET__"); newp->origName(newp->origName()+"__BRA__"+cvtToStr(instNum)+"__KET__"); UINFO(8," CELL loop "<dtypep()->castUnpackArrayDType(); AstIfaceRefDType* origIfaceRefp = arrdtype->subDTypep()->castIfaceRefDType(); origIfaceRefp->cellp(NULL); AstVar* varNewp = ifaceVarp->cloneTree(false); AstIfaceRefDType* ifaceRefp = arrdtype->subDTypep()->castIfaceRefDType()->cloneTree(false); arrdtype->addNextHere(ifaceRefp); ifaceRefp->cellp(newp); ifaceRefp->cellName(newp->name()); varNewp->name(varNewp->name() + "__BRA__" + cvtToStr(instNum) + "__KET__"); varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(instNum) + "__KET__"); varNewp->dtypep(ifaceRefp); newp->addNextHere(varNewp); if (debug()==9) { varNewp->dumpTree(cout, "newintf: "); cout << endl; } } // Fixup pins newp->pinsp()->iterateAndNext(*this); if (debug()==9) { newp->dumpTree(cout,"newcell: "); cout<unlinkFrBack(); pushDeletep(ifaceVarp); VL_DANGLING(ifaceVarp); } nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep); } else { m_cellRangep = NULL; nodep->iterateChildren(*this); } } virtual void visit(AstPin* nodep) { // Any non-direct pins need reconnection with a part-select if (!nodep->exprp()) return; // No-connect if (m_cellRangep) { UINFO(4," PIN "<modVarp()->width(); int expwidth = nodep->exprp()->width(); pair pinDim = nodep->modVarp()->dtypep()->dimensions(false); pair expDim = nodep->exprp()->dtypep()->dimensions(false); UINFO(4," PINVAR "<modVarp()<exprp()<exprp()->unlinkFrBack(); exprp = new AstArraySel (exprp->fileline(), exprp, m_instSelNum); nodep->exprp(exprp); } else if (expwidth == pinwidth) { // NOP: Arrayed instants: widths match so connect to each instance } else if (expwidth == pinwidth*m_cellRangep->elementsConst()) { // Arrayed instants: one bit for each of the instants (each assign is 1 pinwidth wide) if (m_cellRangep->littleEndian()) { nodep->v3warn(LITENDIAN,"Little endian cell range connecting to vector: MSB < LSB of cell range: " <lsbConst()<<":"<msbConst()); } AstNode* exprp = nodep->exprp()->unlinkFrBack(); bool inputPin = nodep->modVarp()->isInput(); if (!inputPin && !exprp->castVarRef() && !exprp->castConcat() // V3Const will collapse the SEL with the one we're about to make && !exprp->castSel()) { // V3Const will collapse the SEL with the one we're about to make nodep->v3error("Unsupported: Per-bit array instantiations with output connections to non-wires."); // Note spec allows more complicated matches such as slices and such } exprp = new AstSel (exprp->fileline(), exprp, pinwidth*m_instSelNum, pinwidth); nodep->exprp(exprp); } else { nodep->v3fatalSrc("Width mismatch; V3Width should have errored out."); } } else if (AstArraySel* arrselp = nodep->exprp()->castArraySel()) { if (AstUnpackArrayDType* arrp = arrselp->lhsp()->dtypep()->castUnpackArrayDType()) { if (!arrp->subDTypep()->castIfaceRefDType()) return; V3Const::constifyParamsEdit(arrselp->rhsp()); AstConst* constp = arrselp->rhsp()->castConst(); if (!constp) { nodep->v3error("Unsupported: Non-constant index when passing interface to module"); return; } string index = AstNode::encodeNumber(constp->toSInt()); AstVarRef* varrefp = arrselp->lhsp()->castVarRef(); AstVarXRef* newp = new AstVarXRef(nodep->fileline(), varrefp->name()+"__BRA__"+index+"__KET__", "", true); newp->dtypep(nodep->modVarp()->dtypep()); newp->packagep(varrefp->packagep()); arrselp->addNextHere(newp); arrselp->unlinkFrBack()->deleteTree(); } } else { AstVar* pinVarp = nodep->modVarp(); AstUnpackArrayDType* pinArrp = pinVarp->dtypep()->castUnpackArrayDType(); if (!pinArrp || !pinArrp->subDTypep()->castIfaceRefDType()) return; AstNode* prevp = NULL; AstNode* prevPinp = NULL; // Clone the var referenced by the pin, and clone each var referenced by the varref // Clone pin varp: for (int i = pinArrp->lsb(); i <= pinArrp->msb(); ++i) { string varNewName = pinVarp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; AstVar* varNewp = NULL; // Only clone the var once for all usages of a given child module if (!pinVarp->backp()) { varNewp = m_deModVars.find(varNewName); } else { AstIfaceRefDType* ifaceRefp = pinArrp->subDTypep()->castIfaceRefDType(); ifaceRefp->cellp(NULL); varNewp = pinVarp->cloneTree(false); varNewp->name(varNewName); varNewp->origName(varNewp->origName() + "__BRA__" + cvtToStr(i) + "__KET__"); varNewp->dtypep(ifaceRefp); m_deModVars.insert(varNewp); if (!prevp) { prevp = varNewp; } else { prevp->addNextHere(varNewp); } } if (!varNewp) { if (debug()>=9) m_deModVars.dump(); nodep->v3fatalSrc("Module dearray failed for "<cloneTree(false); newp->modVarp(varNewp); newp->name(newp->name() + "__BRA__" + cvtToStr(i) + "__KET__"); // And replace exprp with a new varxref AstVarRef* varrefp = newp->exprp()->castVarRef(); string newname = varrefp->name() + "__BRA__" + cvtToStr(i) + "__KET__"; AstVarXRef* newVarXRefp = new AstVarXRef (nodep->fileline(), newname, "", true); newVarXRefp->varp(newp->modVarp()); newVarXRefp->dtypep(newp->modVarp()->dtypep()); newp->exprp()->unlinkFrBack()->deleteTree(); newp->exprp(newVarXRefp); if (!prevPinp) { prevPinp = newp; } else { prevPinp->addNextHere(newp); } } if (prevp) { pinVarp->replaceWith(prevp); pushDeletep(pinVarp); } // else pinVarp already unlinked when another instance did this step nodep->replaceWith(prevPinp); pushDeletep(nodep); } } // Save some time virtual void visit(AstNodeMath*) {} //-------------------- // Default: Just iterate virtual void visit(AstNode* nodep) { nodep->iterateChildren(*this); } public: // CONSTUCTORS explicit InstDeVisitor(AstNetlist* nodep) { m_cellRangep=NULL; m_instSelNum=0; // nodep->accept(*this); } virtual ~InstDeVisitor() {} }; //###################################################################### // Inst static function class InstStatic { private: static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); return level; } InstStatic() {} // Static class static AstNode* extendOrSel(FileLine* fl, AstNode* rhsp, AstNode* cmpWidthp) { if (cmpWidthp->width() > rhsp->width()) { rhsp = (rhsp->isSigned() ? static_cast(new AstExtendS(fl, rhsp)) : static_cast(new AstExtend (fl, rhsp))); rhsp->dtypeFrom(cmpWidthp); // Need proper widthMin, which may differ from AstSel created above } else if (cmpWidthp->width() < rhsp->width()) { rhsp = new AstSel (fl, rhsp, 0, cmpWidthp->width()); rhsp->dtypeFrom(cmpWidthp); // Need proper widthMin, which may differ from AstSel created above } // else don't change dtype, as might be e.g. array of something return rhsp; } public: static AstAssignW* pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModule*, bool forTristate, bool alwaysCvt) { // If a pin connection is "simple" leave it as-is // Else create a intermediate wire to perform the interconnect // Return the new assignment, if one was made // Note this module calles cloneTree() via new AstVar AstVar* pinVarp = pinp->modVarp(); AstVarRef* connectRefp = pinp->exprp()->castVarRef(); AstVarXRef* connectXRefp = pinp->exprp()->castVarXRef(); AstBasicDType* pinBasicp = pinVarp->dtypep()->castBasicDType(); // Maybe NULL AstBasicDType* connBasicp = NULL; AstAssignW* assignp = NULL; if (connectRefp) connBasicp = connectRefp->varp()->dtypep()->castBasicDType(); // if (!alwaysCvt && connectRefp && connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep()) && !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types // Done. Same data type } else if (!alwaysCvt && connectRefp && connectRefp->varp()->isIfaceRef()) { // Done. Interface } else if (!alwaysCvt && connectXRefp && connectXRefp->varp() && connectXRefp->varp()->isIfaceRef()) { } else if (!alwaysCvt && connBasicp && pinBasicp && connBasicp->width() == pinBasicp->width() && connBasicp->lsb() == pinBasicp->lsb() && !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types && connBasicp->width() == pinVarp->width()) { // Done. One to one interconnect won't need a temporary variable. } else if (!alwaysCvt && !forTristate && pinp->exprp()->castConst()) { // Done. Constant. } else { // Make a new temp wire //if (1||debug()>=9) { pinp->dumpTree(cout,"-in_pin:"); } AstNode* pinexprp = pinp->exprp()->unlinkFrBack(); string newvarname = ((string)(pinVarp->isOutput() ? "__Vcellout" : "__Vcellinp") +(forTristate?"t":"") // Prevent name conflict if both tri & non-tri add signals +"__"+cellp->name()+"__"+pinp->name()); AstVar* newvarp = new AstVar (pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp); // Important to add statement next to cell, in case there is a generate with same named cell cellp->addNextHere(newvarp); if (pinVarp->isInout()) { pinVarp->v3fatalSrc("Unsupported: Inout connections to pins must be direct one-to-one connection (without any expression)"); } else if (pinVarp->isOutput()) { // See also V3Inst AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, false); UINFO(5,"pinRecon width "<width()<<" >? "<width()<<" >? "<width()<fileline(), rhsp, pinVarp); pinp->exprp(new AstVarRef (newvarp->fileline(), newvarp, true)); AstNode* rhsSelp = extendOrSel (pinp->fileline(), rhsp, pinexprp); assignp = new AstAssignW (pinp->fileline(), pinexprp, rhsSelp); } else { // V3 width should have range/extended to make the widths correct assignp = new AstAssignW (pinp->fileline(), new AstVarRef(pinp->fileline(), newvarp, true), pinexprp); pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false)); } if (assignp) cellp->addNextHere(assignp); //if (debug()) { pinp->dumpTree(cout,"- out:"); } //if (debug()) { assignp->dumpTree(cout,"- aout:"); } } return assignp; } }; //###################################################################### // Inst class functions AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModule* modp, bool forTristate, bool alwaysCvt) { return InstStatic::pinReconnectSimple(pinp, cellp, modp, forTristate, alwaysCvt); } //###################################################################### // Inst class visitor void V3Inst::instAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<= 3); } void V3Inst::dearrayAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<= 6); }