// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Parse module/signal name references // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2003-2020 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 // //************************************************************************* // Slice TRANSFORMATIONS: // Top-down traversal (SliceVisitor): // NODEASSIGN // ARRAYSEL // Compare the dimensions to the Var to check for implicit slices. // Using ->length() calculate the number of clones needed. // VARREF // Check the dimensions of the Var for an implicit slice. // Replace with ArraySel nodes if needed. // SEL, EXTEND // We might be assigning a 1-D packed array to a 2-D packed array, // this is unsupported. // SliceCloneVisitor (called if this node is a slice): // NODEASSIGN // Clone and iterate the clone: // ARRAYSEL // Modify bitp() for the new value and set ->length(1) // // TODO: This code was written before SLICESEL was a type it might be // simplified to look primarily for SLICESELs. //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include "V3Global.h" #include "V3Slice.h" #include "V3Ast.h" //************************************************************************* class SliceVisitor : public AstNVisitor { // NODE STATE // Cleared on netlist // AstNodeAssign::user1() -> bool. True if find is complete // AstNodeUniop::user1() -> bool. True if find is complete // AstArraySel::user1p() -> AstVarRef. The VarRef that the final ArraySel points to AstUser1InUse m_inuser1; // STATE AstNode* m_assignp; // Assignment we are under bool m_assignError; // True if the current assign already has an error // METHODS VL_DEBUG_FUNC; // Declare debug() AstNode* cloneAndSel(AstNode* nodep, int elements, int offset) { // Insert an ArraySel, except for a few special cases AstUnpackArrayDType* arrayp = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); if (!arrayp) { // V3Width should have complained, but... if (!m_assignError) { nodep->v3error( nodep->prettyTypeName() << " is not an unpacked array, but is in an unpacked array context"); } m_assignError = true; return nodep->cloneTree(false); // Likely will cause downstream errors } if (arrayp->rangep()->elementsConst() != elements) { if (!m_assignError) { nodep->v3error( "Slices of arrays in assignments have different unpacked dimensions, " << elements << " versus " << arrayp->rangep()->elementsConst()); } m_assignError = true; elements = 1; offset = 0; } AstNode* newp; if (AstInitArray* initp = VN_CAST(nodep, InitArray)) { UINFO(9, " cloneInitArray(" << elements << "," << offset << ") " << nodep << endl); int leOffset = !arrayp->rangep()->littleEndian() ? arrayp->rangep()->elementsConst() - 1 - offset : offset; AstNode* itemp = initp->getIndexDefaultedValuep(leOffset); if (!itemp) { nodep->v3error("Array initialization has too few elements, need element " << offset); itemp = initp->initsp(); } newp = itemp->cloneTree(false); } else if (AstNodeCond* snodep = VN_CAST(nodep, NodeCond)) { UINFO(9, " cloneCond(" << elements << "," << offset << ") " << nodep << endl); return snodep->cloneType(snodep->condp()->cloneTree(false), cloneAndSel(snodep->expr1p(), elements, offset), cloneAndSel(snodep->expr2p(), elements, offset)); } else if (AstSliceSel* snodep = VN_CAST(nodep, SliceSel)) { UINFO(9, " cloneSliceSel(" << elements << "," << offset << ") " << nodep << endl); int leOffset = (snodep->declRange().lo() + (!snodep->declRange().littleEndian() ? snodep->declRange().elements() - 1 - offset : offset)); newp = new AstArraySel(nodep->fileline(), snodep->fromp()->cloneTree(false), leOffset); } else if (VN_IS(nodep, ArraySel) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, NodeSel)) { UINFO(9, " cloneSel(" << elements << "," << offset << ") " << nodep << endl); int leOffset = !arrayp->rangep()->littleEndian() ? arrayp->rangep()->elementsConst() - 1 - offset : offset; newp = new AstArraySel(nodep->fileline(), nodep->cloneTree(false), leOffset); } else { if (!m_assignError) { nodep->v3error(nodep->prettyTypeName() << " unexpected in assignment to unpacked array"); } m_assignError = true; newp = nodep->cloneTree(false); // Likely will cause downstream errors } return newp; } virtual void visit(AstNodeAssign* nodep) VL_OVERRIDE { // Called recursively on newly created assignments if (!nodep->user1() && !VN_IS(nodep, AssignAlias)) { nodep->user1(true); m_assignError = false; if (debug() >= 9) nodep->dumpTree(cout, " Deslice-In: "); AstNodeDType* dtp = nodep->lhsp()->dtypep()->skipRefp(); if (AstUnpackArrayDType* arrayp = VN_CAST(dtp, UnpackArrayDType)) { // Left and right could have different msb/lsbs/endianness, but #elements is common // and all variables are realigned to start at zero // Assign of a little endian'ed slice to a big endian one must reverse the elements AstNode* newlistp = NULL; int elements = arrayp->rangep()->elementsConst(); for (int offset = 0; offset < elements; ++offset) { AstNode* newp = nodep->cloneType // AstNodeAssign (cloneAndSel(nodep->lhsp(), elements, offset), cloneAndSel(nodep->rhsp(), elements, offset)); if (debug() >= 9) { newp->dumpTree(cout, "-new "); } newlistp = AstNode::addNextNull(newlistp, newp); } if (debug() >= 9) nodep->dumpTree(cout, " Deslice-Dn: "); nodep->replaceWith(newlistp); VL_DO_DANGLING(nodep->deleteTree(), nodep); // Normal edit iterator will now iterate on all of the expansion assignments // This will potentially call this function again to resolve next level of slicing return; } m_assignp = nodep; iterateChildren(nodep); m_assignp = NULL; } } virtual void visit(AstInitArray* nodep) VL_OVERRIDE { UASSERT_OBJ(!m_assignp, nodep, "Array initialization should have been removed earlier"); } void expandBiOp(AstNodeBiop* nodep) { if (!nodep->user1()) { nodep->user1(true); // If it's an unpacked array, blow it up into comparing each element AstNodeDType* fromDtp = nodep->lhsp()->dtypep()->skipRefp(); UINFO(9, " Bi-Eq/Neq expansion " << nodep << endl); if (AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { AstNodeBiop* logp = NULL; if (!VN_IS(nodep->lhsp()->dtypep()->skipRefp(), NodeArrayDType)) { nodep->lhsp()->v3error( "Slice operator " << nodep->lhsp()->prettyTypeName() << " on non-slicable (e.g. non-vector) left-hand-side operand"); } else if (!VN_IS(nodep->rhsp()->dtypep()->skipRefp(), NodeArrayDType)) { nodep->rhsp()->v3error( "Slice operator " << nodep->rhsp()->prettyTypeName() << " on non-slicable (e.g. non-vector) right-hand-side operand"); } else { for (int index = 0; index < adtypep->rangep()->elementsConst(); ++index) { // EQ(a,b) -> LOGAND(EQ(ARRAYSEL(a,0), ARRAYSEL(b,0)), ...[1]) AstNodeBiop* clonep = VN_CAST(nodep->cloneType( new AstArraySel(nodep->fileline(), nodep->lhsp()->cloneTree(false), index), new AstArraySel(nodep->fileline(), nodep->rhsp()->cloneTree(false), index)), NodeBiop); if (!logp) logp = clonep; else { switch (nodep->type()) { case AstType::atEq: // FALLTHRU case AstType::atEqCase: logp = new AstLogAnd(nodep->fileline(), logp, clonep); break; case AstType::atNeq: // FALLTHRU case AstType::atNeqCase: logp = new AstLogOr(nodep->fileline(), logp, clonep); break; default: nodep->v3fatalSrc("Unknown node type processing array slice"); break; } } } UASSERT_OBJ(logp, nodep, "Unpacked array with empty indices range"); nodep->replaceWith(logp); VL_DO_DANGLING(pushDeletep(nodep), nodep); nodep = logp; } } iterateChildren(nodep); } } virtual void visit(AstEq* nodep) VL_OVERRIDE { expandBiOp(nodep); } virtual void visit(AstNeq* nodep) VL_OVERRIDE { expandBiOp(nodep); } virtual void visit(AstEqCase* nodep) VL_OVERRIDE { expandBiOp(nodep); } virtual void visit(AstNeqCase* nodep) VL_OVERRIDE { expandBiOp(nodep); } virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); } public: // CONSTRUCTORS explicit SliceVisitor(AstNetlist* nodep) { m_assignp = NULL; m_assignError = false; iterate(nodep); } virtual ~SliceVisitor() {} }; //###################################################################### // Link class functions void V3Slice::sliceAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { SliceVisitor visitor(nodep); } // Destruct before checking V3Global::dumpCheckGlobalTree("slice", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3); }