diff --git a/Changes b/Changes index e912ff79f..558f81a11 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ The contributors that suggested a given feature are shown in []. Thanks! * Verilator 4.029 devel +** Add split_var metacomment to assist UNOPTFLAT fixes, #2066. [Yutetsu TAKATSUKASA] + *** Add +verilator+noassert flag to disable assertion checking. [Tobias Wölfel] *** Add check for assertOn for asserts, #2162. [Tobias Wölfel] diff --git a/bin/verilator b/bin/verilator index bb3dbb69b..a50add371 100755 --- a/bin/verilator +++ b/bin/verilator @@ -3420,6 +3420,33 @@ behavior. See the test_regress/t/t_dpi_display.v file for an example. Same as C in configuration files, see L for more information. +=item /*verilator split_var*/ + +Attached to a variable or a net declaration to break the variable into +multiple pieces typically to resolve UNOPTFLAT performance issues. +Typically the variables to attach this to are recommeded by Verilator +itself, see UNOPTFLAT below. + +For example, Verilator will internally convert a variable with the +metacomment such as: + + logic [7:0] x [0:1] /*verilator split_var*/; + +To: + + logic [7:0] x__BRA__0__KET__ /*verilator split_var*/; + logic [7:0] x__BRA__1__KET__ /*verilator split_var*/; + +Note that the generated packed variables retain the split_var metacomment +because they may be split into further smaller pieces accorting to the +access patterns. + +This only supports unpacked arrays, packed arrays, and packed structs of +integer types (reg, logic, bit, byte, int...); otherwise if a split was +requested but cannot occur a SPLITVAR warning is issued. Splitting large +arrays may slow donw the Verilation speed, so use this only on variables +that require it. + =item /*verilator tag */ Attached after a variable or structure member to indicate opaque (to @@ -4383,6 +4410,29 @@ Ignoring this warning may make Verilator simulations differ from other simulators, if the increased precision of real affects your model or DPI calls. +=item SPLITVAR + +Warns that a variable with a C metacomment was not split. +Some possible reasons for this are: + +* The datatype of the variable is not supported for splitting. (e.g. is a +real). + +* The access pattern of the variable can not be determined +statically. (e.g. is accessed as a memory). + +* The index of the array exceeds the array size. + +* The variable is accessed from outside using dotted reference. +(e.g. top.instance0.variable0 = 1). + +* The variable is not declared in a module, but in a package or an +interface. + +* The variable is a parameter, localparam, genvar, or queue. + +* The variable is tirstate or bidirectional. (e.g. inout or ref). + =item STMTDLY Warns that you have a statement with a delayed time in front of it, for @@ -4514,6 +4564,11 @@ being generated from an always statement that consumed high bits of the same bus processed by another series of always blocks. The fix is the same; split it into two separate signals generated from each block. +Another way to resolve this warning is to add a C metacomment +described above. This will cause the variable to be split internally, +potentially resolving the conflict. If you run with --report-unoptflat +Verilator will suggest possible candidates for C. + The UNOPTFLAT warning may also be due to clock enables, identified from the reported path going through a clock gating cell. To fix these, use the clock_enable meta comment described above. diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index f847288bb..4f677b61c 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -233,6 +233,7 @@ RAW_OBJS = \ V3Slice.o \ V3Split.o \ V3SplitAs.o \ + V3SplitVar.o \ V3Stats.o \ V3StatsReport.o \ V3String.o \ diff --git a/src/V3Ast.h b/src/V3Ast.h index 2f8c2857b..356df43d3 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -326,7 +326,8 @@ public: VAR_SC_BV, // V3LinkParse moves to AstVar::attrScBv VAR_SFORMAT, // V3LinkParse moves to AstVar::attrSFormat VAR_CLOCKER, // V3LinkParse moves to AstVar::attrClocker - VAR_NO_CLOCKER // V3LinkParse moves to AstVar::attrClocker + VAR_NO_CLOCKER, // V3LinkParse moves to AstVar::attrClocker + VAR_SPLIT_VAR // V3LinkParse moves to AstVar::attrSplitVar }; enum en m_e; const char* ascii() const { @@ -342,7 +343,7 @@ public: "VAR_BASE", "VAR_CLOCK", "VAR_CLOCK_ENABLE", "VAR_PUBLIC", "VAR_PUBLIC_FLAT", "VAR_PUBLIC_FLAT_RD", "VAR_PUBLIC_FLAT_RW", "VAR_ISOLATE_ASSIGNMENTS", "VAR_SC_BV", "VAR_SFORMAT", "VAR_CLOCKER", - "VAR_NO_CLOCKER" + "VAR_NO_CLOCKER", "VAR_SPLIT_VAR" }; return names[m_e]; } diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 02d2a316c..448589c9b 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -1456,6 +1456,7 @@ private: bool m_attrScBv:1; // User force bit vector attribute bool m_attrIsolateAssign:1;// User isolate_assignments attribute bool m_attrSFormat:1;// User sformat attribute + bool m_attrSplitVar:1; // declared with split_var metacomment bool m_fileDescr:1; // File descriptor bool m_isConst:1; // Table contains constant data bool m_isStatic:1; // Static variable @@ -1478,7 +1479,7 @@ private: m_sigUserRdPublic = false; m_sigUserRWPublic = false; m_funcLocal = false; m_funcReturn = false; m_attrClockEn = false; m_attrScBv = false; - m_attrIsolateAssign = false; m_attrSFormat = false; + m_attrIsolateAssign = false; m_attrSFormat = false; m_attrSplitVar = false; m_fileDescr = false; m_isConst = false; m_isStatic = false; m_isPulldown = false; m_isPullup = false; m_isIfaceParent = false; m_isDpiOpenArray = false; @@ -1582,6 +1583,7 @@ public: void attrScBv(bool flag) { m_attrScBv = flag; } void attrIsolateAssign(bool flag) { m_attrIsolateAssign = flag; } void attrSFormat(bool flag) { m_attrSFormat = flag; } + void attrSplitVar(bool flag) { m_attrSplitVar = flag; } void usedClock(bool flag) { m_usedClock = flag; } void usedParam(bool flag) { m_usedParam = flag; } void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; } @@ -1658,6 +1660,7 @@ public: bool attrFileDescr() const { return m_fileDescr; } bool attrScClocked() const { return m_scClocked; } bool attrSFormat() const { return m_attrSFormat; } + bool attrSplitVar() const { return m_attrSplitVar; } bool attrIsolateAssign() const { return m_attrIsolateAssign; } VVarAttrClocker attrClocker() const { return m_attrClocker; } virtual string verilogKwd() const; diff --git a/src/V3Error.h b/src/V3Error.h index b6adb3681..79e8c49ae 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -102,6 +102,7 @@ public: REDEFMACRO, // Redefining existing define macro SELRANGE, // Selection index out of range SHORTREAL, // Shortreal not supported + SPLITVAR, // Cannot split the variable STMTDLY, // Delayed statement SYMRSVDWORD, // Symbol is Reserved Word SYNCASYNCNET, // Mixed sync + async reset @@ -153,7 +154,7 @@ public: "MULTIDRIVEN", "MULTITOP", "PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY", "PROCASSWIRE", "REALCVT", "REDEFMACRO", - "SELRANGE", "SHORTREAL", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", + "SELRANGE", "SHORTREAL", "SPLITVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", "UNSIGNED", "UNUSED", diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 557e2769c..ccc9aa8d3 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2057,6 +2057,12 @@ private: AstVarXRef* refp = new AstVarXRef(nodep->fileline(), nodep->name(), m_ds.m_dotText, false); // lvalue'ness computed later refp->varp(varp); + if (varp->attrSplitVar()) { + refp->v3warn(SPLITVAR, varp->prettyNameQ() + << " has split_var metacomment but will not be split because" + << " it is accessed from another module via a dot."); + varp->attrSplitVar(false); + } m_ds.m_dotText = ""; if (m_ds.m_unresolved && m_ds.m_unlinkedScope) { string dotted = refp->dotted(); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index cb3d2d26f..55c79ddd0 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -305,6 +305,16 @@ private: m_varp->attrSFormat(true); VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); } + else if (nodep->attrType() == AstAttrType::VAR_SPLIT_VAR) { + UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable"); + if (!VN_IS(m_modp, Module)) { + m_varp->v3warn(SPLITVAR, m_varp->prettyNameQ() << " has split_var metacomment, " + "but will not be split because it is not declared in a module."); + } else { + m_varp->attrSplitVar(true); + } + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + } else if (nodep->attrType() == AstAttrType::VAR_SC_BV) { UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable"); m_varp->attrScBv(true); diff --git a/src/V3Order.cpp b/src/V3Order.cpp index 703b93b51..1644f14c8 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -91,6 +91,7 @@ #include "V3Partition.h" #include "V3PartitionGraph.h" #include "V3SenTree.h" +#include "V3SplitVar.h" #include "V3Stats.h" #include "V3Order.h" @@ -892,32 +893,48 @@ private: m_graph.userClearVertices(); // May be very large vector, so only report the "most important" // elements. Up to 10 of the widest - std::cerr< canSplitList; int lim = m_unoptflatVars.size() < 10 ? m_unoptflatVars.size() : 10; for (int i = 0; i < lim; i++) { OrderVarStdVertex* vsvertexp = m_unoptflatVars[i]; AstVar* varp = vsvertexp->varScp()->varp(); - std::cerr<fileline()<<" "<prettyName()<width()<<", fanout " - <fanout()<fileline() << " " + << varp->prettyName() << std::dec << ", width " << varp->width() + << ", fanout " << vsvertexp->fanout(); + if (canSplit) { + std::cerr <<", can split_var"; + canSplitList.insert(varp); + } + std::cerr << std::endl; } // Up to 10 of the most fanned out - std::cerr<varScp()->varp(); - std::cerr<fileline()<<" "<prettyName() - <<", width "<width() - <<", fanout "<fanout()<fileline() << " " + << varp->prettyName() << ", width " << std::dec << varp->width() + << ", fanout " << vsvertexp->fanout(); + if (canSplit) { + std::cerr << ", can split_var"; + canSplitList.insert(varp); + } + std::cerr< // sort +#include +#include +#include +#include VL_INCLUDE_UNORDERED_MAP +#include VL_INCLUDE_UNORDERED_SET + +struct SplitVarImpl { + static AstNodeAssign* newAssign(FileLine* fileline, AstNode* lhsp, AstNode* rhsp, + const AstVar* varp) { + if (varp->isFuncLocal() || varp->isFuncReturn()) { + return new AstAssign(fileline, lhsp, rhsp); + } else { + return new AstAssignW(fileline, lhsp, rhsp); + } + } + + static const char* const notSplitMsg; + + // These check functions return valid pointer to the reason text if a variable cannot be split. + + // Check if a var type can be split + static const char* cannotSplitVarTypeReason(AstVarType type) { + // Only SplitUnpackedVarVisitor can split WREAL. SplitPackedVarVisitor cannot. + const bool ok + = type == type.VAR || type == type.WIRE || type == type.PORT || type == type.WREAL; + if (ok) return NULL; + return "it is not one of variable, net, port, nor wreal"; + } + + static const char* cannotSplitVarDirectionReason(VDirection dir) { + if (dir == VDirection::REF) return "it is a ref argument"; + if (dir == VDirection::INOUT) return "it is an inout port"; + return NULL; + } + + static const char* cannotSplitConnectedPortReason(AstPin* pinp) { + AstVar* varp = pinp->modVarp(); + if (!varp) return "it is not connected"; + if (const char* reason = cannotSplitVarDirectionReason(varp->direction())) return reason; + return NULL; + } + + static const char* cannotSplitTaskReason(const AstNodeFTask* taskp) { + if (taskp->prototype()) return "the task is prototype declaration"; + if (taskp->dpiImport()) return "the task is imported from DPI-C"; + if (taskp->dpiOpenChild()) return "the task takes DPI-C open array"; + return NULL; + } + + static const char* cannotSplitVarCommonReason(const AstVar* varp) { + if (AstNodeFTask* taskp = VN_CAST(varp->backp(), NodeFTask)) { + if (const char* reason = cannotSplitTaskReason(taskp)) return reason; + } + if (const char* reason = cannotSplitVarTypeReason(varp->varType())) return reason; + if (const char* reason = cannotSplitVarDirectionReason(varp->direction())) return reason; + if (varp->isSigPublic()) return "it is public"; + if (varp->isInoutish()) return "it is bidirectional"; + if (varp->isUsedLoopIdx()) return "it is used as a loop variable"; + if (varp->isGenVar()) return "it is genvar"; + if (varp->isParam()) return "it is parameter"; + return NULL; + } + + static const char* cannotSplitPackedVarReason(const AstVar* varp); + + template + static void insertBeginCore(T_ALWAYSLIKE* ap, AstNodeStmt* stmtp, AstNodeModule* modp) { + if (ap->isJustOneBodyStmt() && ap->bodysp() == stmtp) { + stmtp->unlinkFrBack(); + // Insert begin-end because temp value may be inserted to this block later. + const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); + ap->addStmtp(new AstBegin(ap->fileline(), name, stmtp)); + } + } + + static void insertBeginCore(AstInitial* initp, AstNodeStmt* stmtp, AstNodeModule* modp) { + if (initp->isJustOneBodyStmt() && initp->bodysp() == stmtp) { + stmtp->unlinkFrBack(); + // Insert begin-end because temp value may be inserted to this block later. + const std::string name = "__VsplitVarBlk" + cvtToStr(modp->varNumGetInc()); + initp->replaceWith( + new AstInitial(initp->fileline(), new AstBegin(initp->fileline(), name, stmtp))); + VL_DO_DANGLING(initp->deleteTree(), initp); + } + } + + static void insertBeginIfNecessary(AstNodeStmt* stmtp, AstNodeModule* modp) { + AstNode* backp = stmtp->backp(); + if (AstAlways* ap = VN_CAST(backp, Always)) { + insertBeginCore(ap, stmtp, modp); + } else if (AstAlwaysPublic* ap = VN_CAST(backp, AlwaysPublic)) { + insertBeginCore(ap, stmtp, modp); + } else if (AstInitial* ap = VN_CAST(backp, Initial)) { + insertBeginCore(ap, stmtp, modp); + } + } + +}; // SplitVarImpl + +const char* const SplitVarImpl::notSplitMsg + = " has split_var metacomment but will not be split because "; + +//###################################################################### +// Split Unpacked Variables +// Replacement policy: +// AstArraySel -> Just replace with the AstVarRef for the split variable +// AstVarRef -> Create a temporary variable and refer the variable +// AstSliceSel -> Create a temporary variable and refer the variable + +class UnpackRef { + // m_nodep is called in this context (AstNodeStmt, AstCell, AstNodeFTask, or AstAlways) + AstNode* m_contextp; + AstNode* m_nodep; // ArraySel, SliceSel, ArrayVarRef (entire value) + int m_index; // for ArraySel + int m_msb; // for SliceSel + int m_lsb; // for SliceSel + bool m_lvalue; + bool m_ftask; // true if the reference is in function/task. false if in module. +public: + UnpackRef(AstNode* stmtp, AstVarRef* nodep, bool ftask) + : m_contextp(stmtp) + , m_nodep(nodep) + , m_index(-1) + , m_msb(0) + , m_lsb(1) + , m_lvalue(nodep->lvalue()) + , m_ftask(ftask) {} + UnpackRef(AstNode* stmtp, AstArraySel* nodep, int idx, bool lvalue, bool ftask) + : m_contextp(stmtp) + , m_nodep(nodep) + , m_index(idx) + , m_msb(0) + , m_lsb(1) + , m_lvalue(lvalue) + , m_ftask(ftask) {} + UnpackRef(AstNode* stmtp, AstSliceSel* nodep, int msb, int lsb, bool lvalue, bool ftask) + : m_contextp(stmtp) + , m_nodep(nodep) + , m_index(msb == lsb ? msb : -1) // Equivalent to ArraySel + , m_msb(msb) + , m_lsb(lsb) + , m_lvalue(lvalue) + , m_ftask(ftask) {} + AstNode* nodep() const { return m_nodep; } + bool isSingleRef() const { + return VN_IS(m_nodep, ArraySel) || (m_msb == m_lsb && m_lsb == m_index); + } + int index() const { + UASSERT_OBJ(isSingleRef(), m_nodep, "not array sel"); + return m_index; + } + AstNode* context() const { return m_contextp; } + std::pair range() const { + UASSERT_OBJ(VN_IS(m_nodep, SliceSel), m_nodep, "not slice sel"); + return std::make_pair(m_msb, m_lsb); + } + bool lvalue() const { return m_lvalue; } + bool ftask() const { return m_ftask; } +}; + +class UnpackRefMap { +public: + struct Hash { + size_t operator()(const UnpackRef& r) const { return reinterpret_cast(r.nodep()); } + }; + struct Compare { + bool operator()(const UnpackRef& a, const UnpackRef& b) const { + return a.nodep() == b.nodep(); + } + }; + typedef std::map > MapType; + typedef MapType::iterator MapIt; + typedef MapType::value_type::second_type::iterator SetIt; + +private: + MapType m_map; + bool addCore(AstVarRef* refp, const UnpackRef& ref) { + AstVar* varp = refp->varp(); + UASSERT_OBJ(varp->attrSplitVar(), varp, " no split_var metacomment"); + MapIt it = m_map.find(varp); + if (it == m_map.end()) return false; // Not registered + const bool ok = m_map[varp].insert(ref).second; + return ok; + } + +public: + // Register a variable to split + void registerVar(AstVar* varp) { + const bool inserted + = m_map.insert(std::make_pair(varp, MapType::value_type::second_type())).second; + UASSERT_OBJ(inserted, varp, "already registered"); + } + // Register the location where a variable is used. + bool tryAdd(AstNode* context, AstVarRef* refp, AstArraySel* selp, int idx, bool ftask) { + return addCore(refp, UnpackRef(context, selp, idx, refp->lvalue(), ftask)); + } + bool tryAdd(AstNode* context, AstVarRef* refp, AstSliceSel* selp, int msb, int lsb, + bool ftask) { + return addCore(refp, UnpackRef(context, selp, msb, lsb, refp->lvalue(), ftask)); + } + bool tryAdd(AstNode* context, AstVarRef* refp, bool ftask) { + return addCore(refp, UnpackRef(context, refp, ftask)); + } + + // Remove a variable from the list to split + void remove(AstVar* varp) { + UASSERT_OBJ(varp->attrSplitVar(), varp, " no split_var metacomment"); + m_map.erase(varp); + varp->attrSplitVar(!SplitVarImpl::cannotSplitPackedVarReason(varp)); + } + bool empty() const { return m_map.empty(); } + void swap(UnpackRefMap& other) { other.m_map.swap(m_map); } + + MapIt begin() { return m_map.begin(); } + MapIt end() { return m_map.end(); } +}; + +// Compare AstNode* to get deterministic ordering when showing messages. +struct AstNodeComparator { + bool operator()(const AstNode* ap, const AstNode* bp) const { + const FileLine* afp = ap->fileline(); + const FileLine* bfp = bp->fileline(); + if (afp->firstLineno() != bfp->firstLineno()) + return afp->firstLineno() < bfp->firstLineno(); + if (afp->firstColumn() != bfp->firstColumn()) + return afp->firstColumn() < bfp->firstColumn(); + // Don't comapre its filename because it is expensive. + // Line number and column is practically enough. + // The comparison of raw pointer here is just in case. + // The comparison of this pointer may differ on different run, + // but this is just warning message order. (mostly for CI) + // Verilated result is same anyway. + return ap < bp; + } +}; + +// Found nodes for SplitPackedVarVisitor +struct RefsInModule { + std::set m_vars; + std::set m_refs; + std::set m_sels; + +public: + void add(AstVar* nodep) { m_vars.insert(nodep); } + void add(AstVarRef* nodep) { m_refs.insert(nodep); } + void add(AstSel* nodep) { m_sels.insert(nodep); } + void remove(AstNode* nodep) { + struct Visitor : public AstNVisitor { + RefsInModule& m_parent; + virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); } + virtual void visit(AstVar* nodep) VL_OVERRIDE { m_parent.m_vars.erase(nodep); } + virtual void visit(AstVarRef* nodep) VL_OVERRIDE { m_parent.m_refs.erase(nodep); } + virtual void visit(AstSel* nodep) VL_OVERRIDE { + m_parent.m_sels.erase(nodep); + iterateChildren(nodep); + } + explicit Visitor(RefsInModule& p) + : m_parent(p) {} + } v(*this); + v.iterate(nodep); + } + void visit(AstNVisitor* visitor) { + for (std::set::iterator it = m_vars.begin(), it_end = m_vars.end(); it != it_end; + ++it) { + visitor->iterate(*it); + } + for (std::set::iterator it = m_sels.begin(), it_end = m_sels.end(); it != it_end; + ++it) { + // If m_refs includes VarRef from ArraySel, remove it + // because the VarRef would not be visited in SplitPackedVarVisitor::visit(AstSel*). + if (AstVarRef* refp = VN_CAST((*it)->fromp(), VarRef)) { + m_refs.erase(refp); + } else if (AstVarRef* refp = VN_CAST((*it)->lsbp(), VarRef)) { + m_refs.erase(refp); + } else if (AstVarRef* refp = VN_CAST((*it)->widthp(), VarRef)) { + m_refs.erase(refp); + } + UASSERT_OBJ(reinterpret_cast((*it)->op1p()) != 1, *it, "stale"); + visitor->iterate(*it); + } + for (std::set::iterator it = m_refs.begin(), it_end = m_refs.end(); + it != it_end; ++it) { + UASSERT_OBJ(reinterpret_cast((*it)->op1p()) != 1, *it, "stale"); + visitor->iterate(*it); + } + } +}; + +typedef std::map SplitVarRefsMap; + +class SplitUnpackedVarVisitor : public AstNVisitor, public SplitVarImpl { + typedef std::set VarSet; + VarSet m_foundTargetVar; + UnpackRefMap m_refs; + AstNodeModule* m_modp; + // AstNodeStmt, AstCell, AstNodeFTaskRef, or AstAlways(Public) for sensitivity + AstNode* m_contextp; + AstNodeFTask* m_inFTask; + size_t m_numSplit; + // List for SplitPackedVarVisitor + SplitVarRefsMap m_refsForPackedSplit; + + static AstVarRef* isTargetVref(AstNode* nodep) { + if (AstVarRef* refp = VN_CAST(nodep, VarRef)) { + if (refp->varp()->attrSplitVar()) return refp; + } + return NULL; + } + static int outerMostSizeOfUnpackedArray(AstVar* nodep) { + AstUnpackArrayDType* dtypep = VN_CAST(nodep->dtypep()->skipRefp(), UnpackArrayDType); + UASSERT_OBJ(dtypep, nodep, "Must be unapcked array"); + return dtypep->msb() - dtypep->lsb() + 1; + } + + void setContextAndIterateChildren(AstNode* nodep) { + AstNode* origContextp = m_contextp; + { + m_contextp = nodep; + iterateChildren(nodep); + } + m_contextp = origContextp; + } + void setContextAndIterate(AstNode* contextp, AstNode* nodep) { + AstNode* origContextp = m_contextp; + { + m_contextp = contextp; + iterate(nodep); + } + m_contextp = origContextp; + } + void pushDeletep(AstNode* nodep) { // overriding AstNVisitor::pusDeletep() + UASSERT_OBJ(m_modp, nodep, "Must not NULL"); + m_refsForPackedSplit[m_modp].remove(nodep); + AstNVisitor::pushDeletep(nodep); + } + AstVar* newVar(FileLine* fl, AstVarType type, const std::string& name, AstNodeDType* dtp) { + AstVar* varp = new AstVar(fl, type, name, dtp); + UASSERT_OBJ(m_modp, varp, "Must not NULL"); + m_refsForPackedSplit[m_modp].add(varp); + return varp; + } + AstVarRef* newVarRef(FileLine* fl, AstVar* varp, bool lvalue) { + AstVarRef* refp = new AstVarRef(fl, varp, lvalue); + UASSERT_OBJ(m_modp, refp, "Must not NULL"); + m_refsForPackedSplit[m_modp].add(refp); + return refp; + } + + virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); } + virtual void visit(AstNodeModule* nodep) VL_OVERRIDE { + UINFO(4, "Start checking " << nodep->prettyNameQ() << "\n"); + if (!VN_IS(nodep, Module)) { + UINFO(4, "Skip " << nodep->prettyNameQ() << "\n"); + return; + } + UASSERT_OBJ(!m_modp, m_modp, "Nested module declration"); + UASSERT_OBJ(m_refs.empty(), nodep, "The last module didn't finish split()"); + m_modp = nodep; + iterateChildren(nodep); + split(); + m_modp = NULL; + } + virtual void visit(AstNodeStmt* nodep) VL_OVERRIDE { setContextAndIterateChildren(nodep); } + virtual void visit(AstCell* nodep) VL_OVERRIDE { setContextAndIterateChildren(nodep); } + virtual void visit(AstAlways* nodep) VL_OVERRIDE { + if (nodep->sensesp()) { // When visiting sensitivity list, always is the context + setContextAndIterate(nodep, nodep->sensesp()); + } + if (AstNode* bodysp = nodep->bodysp()) iterate(bodysp); + }; + virtual void visit(AstAlwaysPublic* nodep) VL_OVERRIDE { + if (nodep->sensesp()) { // When visiting sensitivity list, always is the context + setContextAndIterate(nodep, nodep->sensesp()); + } + if (AstNode* bodysp = nodep->bodysp()) iterate(bodysp); + } + virtual void visit(AstNodeFTaskRef* nodep) VL_OVERRIDE { + AstNode* origContextp = m_contextp; + { + m_contextp = nodep; + AstNodeFTask* ftaskp = nodep->taskp(); + // Iterate arguments of a function/task. + for (AstNode *argp = nodep->pinsp(), *paramp = ftaskp->stmtsp(); argp; + argp = argp->nextp(), paramp = paramp ? paramp->nextp() : NULL) { + const char* reason = NULL; + AstVar* vparamp = NULL; + while (paramp) { + vparamp = VN_CAST(paramp, Var); + if (vparamp && vparamp->isIO()) { + reason = cannotSplitVarDirectionReason(vparamp->direction()); + break; + } + paramp = paramp->nextp(); + vparamp = NULL; + } + if (!reason && !vparamp) { + reason = "the number of argument to the task/function mismatches"; + } + m_foundTargetVar.clear(); + iterate(argp); + if (reason) { + for (VarSet::iterator it = m_foundTargetVar.begin(), + it_end = m_foundTargetVar.end(); + it != it_end; ++it) { + argp->v3warn(SPLITVAR, (*it)->prettyNameQ() + << notSplitMsg << reason << ".\n"); + m_refs.remove(*it); + } + } + m_foundTargetVar.clear(); + } + } + m_contextp = origContextp; + } + virtual void visit(AstPin* nodep) VL_OVERRIDE { + UINFO(5, nodep->modVarp()->prettyNameQ() << " pin \n"); + AstNode* exprp = nodep->exprp(); + if (!exprp) return; // Not connected pin + m_foundTargetVar.clear(); + iterate(exprp); + if (const char* reason = cannotSplitConnectedPortReason(nodep)) { + for (VarSet::iterator it = m_foundTargetVar.begin(), it_end = m_foundTargetVar.end(); + it != it_end; ++it) { + nodep->v3warn(SPLITVAR, (*it)->prettyNameQ() << notSplitMsg << reason << ".\n"); + m_refs.remove(*it); + } + m_foundTargetVar.clear(); + } + } + virtual void visit(AstNodeFTask* nodep) VL_OVERRIDE { + UASSERT_OBJ(!m_inFTask, nodep, "Nested func/task"); + if (!cannotSplitTaskReason(nodep)) { + m_inFTask = nodep; + iterateChildren(nodep); + m_inFTask = NULL; + } + } + virtual void visit(AstVar* nodep) VL_OVERRIDE { + if (!nodep->attrSplitVar()) return; // Nothing to do + if (!cannotSplitReason(nodep)) { + m_refs.registerVar(nodep); + UINFO(4, nodep->name() << " is added to candidate list.\n"); + } + m_refsForPackedSplit[m_modp].add(nodep); + } + virtual void visit(AstVarRef* nodep) VL_OVERRIDE { + if (!nodep->varp()->attrSplitVar()) return; // Nothing to do + if (m_refs.tryAdd(m_contextp, nodep, m_inFTask)) { + m_foundTargetVar.insert(nodep->varp()); + } + m_refsForPackedSplit[m_modp].add(nodep); + } + virtual void visit(AstSel* nodep) VL_OVERRIDE { + if (VN_IS(nodep->fromp(), VarRef)) m_refsForPackedSplit[m_modp].add(nodep); + iterateChildren(nodep); + } + virtual void visit(AstArraySel* nodep) VL_OVERRIDE { + if (AstVarRef* refp = isTargetVref(nodep->fromp())) { + AstConst* indexp = VN_CAST(nodep->bitp(), Const); + if (indexp) { // OK + UINFO(4, "add " << nodep << " for " << refp->varp()->prettyName() << "\n"); + if (indexp->toSInt() < outerMostSizeOfUnpackedArray(refp->varp())) { + m_refs.tryAdd(m_contextp, refp, nodep, indexp->toSInt(), m_inFTask); + } else { + nodep->bitp()->v3warn(SPLITVAR, refp->prettyNameQ() + << notSplitMsg + << "index is out of range.\n"); + m_refs.remove(refp->varp()); + } + } else { + nodep->bitp()->v3warn(SPLITVAR, refp->prettyNameQ() + << notSplitMsg + << "index cannot be determined statically.\n"); + m_refs.remove(refp->varp()); + iterate(nodep->bitp()); + } + } else { + iterateChildren(nodep); + } + } + virtual void visit(AstSliceSel* nodep) VL_OVERRIDE { + if (AstVarRef* refp = isTargetVref(nodep->fromp())) { + AstUnpackArrayDType* dtypep + = VN_CAST(refp->varp()->dtypep()->skipRefp(), UnpackArrayDType); + if (dtypep->lsb() <= nodep->declRange().lo() + && nodep->declRange().hi() <= dtypep->msb()) { // Range is ok + UINFO(4, "add " << nodep << " for " << refp->varp()->prettyName() << "\n"); + m_refs.tryAdd(m_contextp, refp, nodep, nodep->declRange().hi(), + nodep->declRange().lo(), m_inFTask); + } else { + nodep->v3warn(SPLITVAR, refp->prettyNameQ() + << notSplitMsg << "index if out of range.\n"); + m_refs.remove(refp->varp()); + } + } else { + iterateChildren(nodep); + } + } + static AstNode* toInsertPoint(AstNode* insertp) { + if (AstNodeStmt* stmtp = VN_CAST(insertp, NodeStmt)) { + if (!stmtp->isStatement()) insertp = stmtp->backp(); + } + return insertp; + } + AstVarRef* createTempVar(AstNode* context, AstNode* nodep, AstUnpackArrayDType* dtypep, + const std::string& name_prefix, std::vector& vars, + int start_idx, bool lvalue, bool ftask) { + const std::string name + = "__VsplitVar" + cvtToStr(m_modp->varNumGetInc()) + "__" + name_prefix; + AstNodeAssign* assignp = VN_CAST(context, NodeAssign); + if (assignp) { + // "always_comb a = b;" to "always_comb begin a = b; end" so that local + // variable can be added. + insertBeginIfNecessary(assignp, m_modp); + } + AstVar* varp = newVar(nodep->fileline(), AstVarType::VAR, name, dtypep); + // Variable will be registered in the caller side. + UINFO(3, varp->prettyNameQ() + << " is created lsb:" << dtypep->lsb() << " msb:" << dtypep->msb() << "\n"); + // Use AstAssign if true, otherwise AstAssignW + const bool use_simple_assign + = (context && VN_IS(context, NodeFTaskRef)) || (assignp && VN_IS(assignp, Assign)); + + for (int i = 0; i <= dtypep->msb() - dtypep->lsb(); ++i) { + AstNode* lhsp = newVarRef(nodep->fileline(), vars.at(start_idx + i), lvalue); + AstNode* rhsp = new AstArraySel(nodep->fileline(), + newVarRef(nodep->fileline(), varp, !lvalue), i); + AstNode* refp = lhsp; + UINFO(9, "Creating assign idx:" << i << " + " << start_idx << "\n"); + if (!lvalue) std::swap(lhsp, rhsp); + AstNode* newassignp; + if (use_simple_assign) { + AstNode* insertp = toInsertPoint(context); + newassignp = new AstAssign(nodep->fileline(), lhsp, rhsp); + if (lvalue) { + // If varp is LHS, this assignment must appear after the original + // assignment(context). + insertp->addNextHere(newassignp); + } else { + // If varp is RHS, this assignment comes just before the original assignment + insertp->addHereThisAsNext(newassignp); + } + } else { + newassignp = new AstAssignW(nodep->fileline(), lhsp, rhsp); + // Continuous assignment must be in module context. + varp->addNextHere(newassignp); + } + UASSERT_OBJ(!m_contextp, m_contextp, "must be null"); + setContextAndIterate(newassignp, refp); + } + return newVarRef(nodep->fileline(), varp, lvalue); + } + void connectPort(AstVar* varp, std::vector& vars, AstNode* insertp) { + UASSERT_OBJ(varp->isIO(), varp, "must be port"); + insertp = insertp ? toInsertPoint(insertp) : NULL; + const bool lvalue = varp->direction().isWritable(); + for (size_t i = 0; i < vars.size(); ++i) { + AstNode* nodes[] + = {new AstArraySel(varp->fileline(), newVarRef(varp->fileline(), varp, lvalue), i), + newVarRef(varp->fileline(), vars.at(i), !lvalue)}; + AstNode* lhsp = nodes[lvalue ? 0 : 1]; + AstNode* rhsp = nodes[lvalue ? 1 : 0]; + AstNodeAssign* assignp = newAssign(varp->fileline(), lhsp, rhsp, varp); + if (insertp) { + if (lvalue) { // Just after writing to the temporary variable + insertp->addNextHere(assignp); + } else { // Just before reading the temporary variable + insertp->addHereThisAsNext(assignp); + } + } else { + UASSERT_OBJ(VN_IS(assignp, AssignW), varp, "must be AssginW"); + vars.at(i)->addNextHere(assignp); + } + setContextAndIterate(assignp, nodes[1]); + } + } + size_t collapse(UnpackRefMap& refs) { + size_t numSplit = 0; + for (UnpackRefMap::MapIt it = refs.begin(), it_end = refs.end(); it != it_end; ++it) { + UINFO(3, "In module " << m_modp->name() << " var " << it->first->prettyNameQ() + << " which has " << it->second.size() + << " refs will be split.\n"); + AstVar* varp = it->first; + AstNode* insertp = varp; + AstUnpackArrayDType* dtypep = VN_CAST(varp->dtypep()->skipRefp(), UnpackArrayDType); + AstNodeDType* subTypep = dtypep->subDTypep(); + const bool needNext = VN_IS(subTypep, UnpackArrayDType); // Still unpacked array. + std::vector vars; + // Add the split variables + for (vlsint32_t i = 0; i <= dtypep->msb() - dtypep->lsb(); ++i) { + // Unpacked array is traced as var(idx), not var[idx]. + const std::string name + = varp->name() + AstNode::encodeName('(' + cvtToStr(i + dtypep->lsb()) + ')'); + AstVar* newp = newVar(varp->fileline(), AstVarType::VAR, name, subTypep); + newp->propagateAttrFrom(varp); + // If varp is an IO, varp will remain and will be traced. + newp->trace(!varp->isIO() && varp->isTrace()); + newp->funcLocal(varp->isFuncLocal() || varp->isFuncReturn()); + insertp->addNextHere(newp); + insertp = newp; + newp->attrSplitVar(needNext || !cannotSplitPackedVarReason(newp)); + vars.push_back(newp); + setContextAndIterate(NULL, newp); + } + for (UnpackRefMap::SetIt sit = it->second.begin(), sit_end = it->second.end(); + sit != sit_end; ++sit) { + AstNode* newp = NULL; + if (sit->isSingleRef()) { + newp = newVarRef(sit->nodep()->fileline(), vars.at(sit->index()), + sit->lvalue()); + } else { + AstVarRef* refp = VN_CAST(sit->nodep(), VarRef); + AstUnpackArrayDType* dtypep; + int lsb = 0; + if (refp) { + dtypep = VN_CAST(refp->dtypep()->skipRefp(), UnpackArrayDType); + } else { + AstSliceSel* selp = VN_CAST(sit->nodep(), SliceSel); + UASSERT_OBJ(selp, sit->nodep(), "Unexpected op is registered"); + refp = VN_CAST(selp->fromp(), VarRef); + UASSERT_OBJ(refp, selp, "Unexpected op is registered"); + dtypep = VN_CAST(selp->dtypep()->skipRefp(), UnpackArrayDType); + lsb = dtypep->lsb(); + } + AstVarRef* newrefp = createTempVar(sit->context(), refp, dtypep, varp->name(), + vars, lsb, refp->lvalue(), sit->ftask()); + newp = newrefp; + refp->varp()->addNextHere(newrefp->varp()); + UINFO(3, + "Create " << newrefp->varp()->prettyNameQ() << " for " << refp << "\n"); + } + sit->nodep()->replaceWith(newp); + pushDeletep(sit->nodep()); + setContextAndIterate(sit->context(), newp->backp()); + // AstAssign is used. So assignment is necessary for each reference. + if (varp->isIO() && (varp->isFuncLocal() || varp->isFuncReturn())) + connectPort(varp, vars, sit->context()); + } + if (varp->isIO()) { + // AssignW will be created, so just once + if (!varp->isFuncLocal() && !varp->isFuncReturn()) { + connectPort(varp, vars, NULL); + } + varp->attrSplitVar(!cannotSplitPackedVarReason(varp)); + m_refsForPackedSplit[m_modp].add(varp); + } else { + pushDeletep(varp->unlinkFrBack()); + } + ++numSplit; + } + return numSplit; + } + void split() { + for (int trial = 0; !m_refs.empty(); ++trial) { + UnpackRefMap next; + m_refs.swap(next); + const size_t n = collapse(next); + UINFO(2, n << " Variables are split " << trial << " th trial in " + << m_modp->prettyNameQ() << '\n'); + if (trial == 0) m_numSplit += n; + } + doDeletes(); + } + +public: + explicit SplitUnpackedVarVisitor(AstNetlist* nodep) + : m_refs() + , m_modp(NULL) + , m_contextp(NULL) + , m_inFTask(NULL) + , m_numSplit(0) { + iterate(nodep); + } + ~SplitUnpackedVarVisitor() { + UASSERT(m_refs.empty(), "Don't forget to call split()"); + V3Stats::addStat("SplitVar, Split unpacked arrays", m_numSplit); + } + const SplitVarRefsMap& getPackedVarRefs() const { return m_refsForPackedSplit; } + VL_DEBUG_FUNC; // Declare debug() + + // Check if the passed variable can be split. + // Even if this function returns true, the variable may not be split + // because the access to the variable cannot be determined statically. + static const char* cannotSplitReason(const AstVar* nodep) { + const std::pair dim = nodep->dtypep()->dimensions(false); + UINFO(7, nodep->prettyNameQ() + << " pub:" << nodep->isSigPublic() << " pri:" << nodep->isPrimaryIO() + << " io:" << nodep->isInoutish() << " typ:" << nodep->varType() << "\n"); + const char* reason = NULL; + // Public variable cannot be split. + // at least one unpacked dimension must exist + if (dim.second < 1 || !VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType)) + reason = "it is not an unpacked array"; + if (!reason) reason = cannotSplitVarCommonReason(nodep); + if (reason) UINFO(5, "Check " << nodep->prettyNameQ() + << " cannot split because" << reason << ".\n"); + return reason; + } +}; + +//###################################################################### +// Split packed variables + +// Split variable +class SplitNewVar { + int m_lsb; // LSB in the original bitvector + int m_bitwidth; + AstVar* m_varp; // The LSB of this variable is always 0, not m_lsb +public: + SplitNewVar(int lsb, int bitwidth, AstVar* varp = NULL) + : m_lsb(lsb) + , m_bitwidth(bitwidth) + , m_varp(varp) {} + int lsb() const { return m_lsb; } + int msb() const { return m_lsb + m_bitwidth - 1; } + int bitwidth() const { return m_bitwidth; } + void varp(AstVar* vp) { + UASSERT_OBJ(!m_varp, m_varp, "must be NULL"); + m_varp = vp; + } + AstVar* varp() const { return m_varp; } + + struct Match { + bool operator()(int bit, const SplitNewVar& a) const { + return bit < a.m_lsb + a.m_bitwidth; + } + }; +}; + +// One Entry instance for an AstVarRef instance +class PackedVarRefEntry { + AstNode* m_nodep; // Either AstSel or AstVarRef is expected. + int m_lsb; + int m_bitwidth; + +public: + PackedVarRefEntry(AstSel* selp, int lsb, int bitwidth) + : m_nodep(selp) + , m_lsb(lsb) + , m_bitwidth(bitwidth) {} + PackedVarRefEntry(AstVarRef* refp, int lsb, int bitwidth) + : m_nodep(refp) + , m_lsb(lsb) + , m_bitwidth(bitwidth) {} + AstNode* nodep() const { return m_nodep; } + int lsb() const { return m_lsb; } + int msb() const { return m_lsb + m_bitwidth - 1; } + int bitwidth() const { return m_bitwidth; } + void replaceNodeWith(AstNode* nodep) { + m_nodep->replaceWith(nodep); + VL_DO_DANGLING(m_nodep->deleteTree(), m_nodep); + } + // If this is AstVarRef and referred in the sensitivity list of always@, + // return the sensitivity item + AstSenItem* backSenItemp() const { + if (AstVarRef* refp = VN_CAST(m_nodep, VarRef)) { return VN_CAST(refp->backp(), SenItem); } + return NULL; + } +}; + +// How a variable is used +class PackedVarRef { + struct SortByFirst { + bool operator()(const std::pair& a, const std::pair& b) const { + if (a.first == b.first) return a.second < b.second; + return a.first < b.first; + } + }; + std::vector m_lhs, m_rhs; + AstBasicDType* m_basicp; // Cache the ptr since varp->dtypep()->basicp() is expensive + bool m_dedupDone; + static void dedupRefs(std::vector& refs) { + vl_unordered_map nodes; + for (size_t i = 0; i < refs.size(); ++i) { + nodes.insert(std::make_pair(refs[i].nodep(), i)); + } + std::vector vect; + vect.reserve(nodes.size()); + for (vl_unordered_map::const_iterator it = nodes.begin(), + it_end = nodes.end(); + it != it_end; ++it) { + vect.push_back(refs[it->second]); + } + refs.swap(vect); + } + +public: + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + std::vector& lhs() { + UASSERT(m_dedupDone, "cannot read before dedup()"); + return m_lhs; + } + std::vector& rhs() { + UASSERT(m_dedupDone, "cannot read before dedup()"); + return m_rhs; + } + explicit PackedVarRef(AstVar* varp) + : m_basicp(varp->dtypep()->basicp()) + , m_dedupDone(false) {} + void append(const PackedVarRefEntry& e, bool lvalue) { + UASSERT(!m_dedupDone, "cannot add after dedup()"); + if (lvalue) + m_lhs.push_back(e); + else + m_rhs.push_back(e); + } + void dedup() { + UASSERT(!m_dedupDone, "dedup() called twice"); + dedupRefs(m_lhs); + dedupRefs(m_rhs); + m_dedupDone = true; + } + const AstBasicDType* basicp() const { return m_basicp; } + // Make a plan for variables after split + // when skipUnused==true, split variable for unread bits will not be created. + std::vector splitPlan(bool skipUnused) const { + UASSERT(m_dedupDone, "dedup() must be called before"); + std::vector plan; + std::vector > points; // + points.reserve(m_lhs.size() * 2 + 2); // 2 points will be added per one PackedVarRefEntry + for (const_iterator it = m_lhs.begin(), itend = m_lhs.end(); it != itend; ++it) { + points.push_back(std::make_pair(it->lsb(), false)); // Start of a region + points.push_back(std::make_pair(it->msb() + 1, true)); // End of a region + } + if (skipUnused && !m_rhs.empty()) { // Range to be read must be kept, so add points here + int lsb = m_basicp->msb() + 1, msb = m_basicp->lsb() - 1; + for (size_t i = 0; i < m_rhs.size(); ++i) { + lsb = std::min(lsb, m_rhs[i].lsb()); + msb = std::max(msb, m_rhs[i].msb()); + } + UASSERT_OBJ(lsb <= msb, m_basicp, "lsb:" << lsb << " msb:" << msb << " are wrong"); + points.push_back(std::make_pair(lsb, false)); + points.push_back(std::make_pair(msb + 1, true)); + } + if (!skipUnused) { // All bits are necessary + points.push_back(std::make_pair(m_basicp->lsb(), false)); + points.push_back(std::make_pair(m_basicp->msb() + 1, true)); + } + std::sort(points.begin(), points.end(), SortByFirst()); + + // Scan the sorted points and sub bitfields + int refcount = 0; + for (size_t i = 0; i + 1 < points.size(); ++i) { + const int bitwidth = points[i + 1].first - points[i].first; + if (points[i].second) { + --refcount; // End of a region + } else { + ++refcount; // Start of a region + } + UASSERT(refcount >= 0, "refcounut must not be negative"); + if (bitwidth == 0 || refcount == 0) continue; // Vacant region + plan.push_back(SplitNewVar(points[i].first, bitwidth)); + } + + return plan; + } +}; + +class SplitPackedVarVisitor : public AstNVisitor, public SplitVarImpl { + AstNetlist* m_netp; + AstNodeModule* m_modp; // Current module (just for log) + int m_numSplit; // Total number of split variables + // key:variable to be split. value:location where the variable is referenced. + vl_unordered_map m_refs; + virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); } + virtual void visit(AstNodeModule* nodep) VL_OVERRIDE { + UASSERT_OBJ(m_modp == NULL, m_modp, "Nested module declration"); + if (!VN_IS(nodep, Module)) { + UINFO(5, "Skip " << nodep->prettyNameQ() << "\n"); + return; + } + UASSERT_OBJ(m_refs.empty(), nodep, "The last module didn't finish split()"); + m_modp = nodep; + UINFO(3, "Start analyzing module " << nodep->prettyName() << '\n'); + iterateChildren(nodep); + split(); + m_modp = NULL; + } + virtual void visit(AstNodeFTask* nodep) VL_OVERRIDE { + if (!cannotSplitTaskReason(nodep)) iterateChildren(nodep); + } + virtual void visit(AstVar* nodep) VL_OVERRIDE { + if (!nodep->attrSplitVar()) return; // Nothing to do + if (const char* reason = cannotSplitReason(nodep, true)) { + nodep->v3warn(SPLITVAR, nodep->prettyNameQ() << notSplitMsg << reason); + nodep->attrSplitVar(false); + } else { // Finally find a good candidate + const bool inserted = m_refs.insert(std::make_pair(nodep, PackedVarRef(nodep))).second; + if (inserted) { UINFO(3, nodep->prettyNameQ() << " is added to candidate list.\n"); } + } + } + virtual void visit(AstVarRef* nodep) VL_OVERRIDE { + AstVar* varp = nodep->varp(); + visit(varp); + vl_unordered_map::iterator refit = m_refs.find(varp); + if (refit == m_refs.end()) return; // variable without split_var metacomment + UASSERT_OBJ(varp->attrSplitVar(), varp, "split_var attribute must be attached"); + UASSERT_OBJ(!nodep->packagep(), nodep, + "variable in package must have been dropped beforehand."); + const AstBasicDType* basicp = refit->second.basicp(); + refit->second.append(PackedVarRefEntry(nodep, basicp->lsb(), varp->width()), + nodep->lvalue()); + UINFO(5, varp->prettyName() + << " Entire bit of [" << basicp->lsb() << ":+" << varp->width() << "] \n"); + } + virtual void visit(AstSel* nodep) VL_OVERRIDE { + AstVarRef* vrefp = VN_CAST(nodep->fromp(), VarRef); + if (!vrefp) { + iterateChildren(nodep); + return; + } + + AstVar* varp = vrefp->varp(); + vl_unordered_map::iterator refit = m_refs.find(varp); + if (refit == m_refs.end()) { + iterateChildren(nodep); + return; // Variable without split_var metacomment + } + UASSERT_OBJ(varp->attrSplitVar(), varp, "split_var attribute must be attached"); + + AstConst* consts[2] = {VN_CAST(nodep->lsbp(), Const), VN_CAST(nodep->widthp(), Const)}; + if (consts[0] && consts[1]) { // OK + refit->second.append( + PackedVarRefEntry(nodep, consts[0]->toSInt() + refit->second.basicp()->lsb(), + consts[1]->toUInt()), + vrefp->lvalue()); + UINFO(5, varp->prettyName() << " [" << consts[0]->toSInt() << ":+" + << consts[1]->toSInt() << "] lsb:" << refit->second.basicp()->lsb() << "\n"); + } else { + nodep->v3warn(SPLITVAR, vrefp->prettyNameQ() << notSplitMsg + << "its bit range cannot be determined statically."); + if (!consts[0]) { + UINFO(4, "LSB " << nodep->lsbp() << " is expected to be constant, but not\n"); + } + if (!consts[1]) { + UINFO(4, "WIDTH " << nodep->widthp() << " is expected to be constant, but not\n"); + } + m_refs.erase(varp); + varp->attrSplitVar(false); + iterateChildren(nodep); + } + } + // Extract necessary bit range from a newly created variable to meet ref + static AstNode* extractBits(const PackedVarRefEntry& ref, const SplitNewVar& var, + bool lvalue) { + AstVarRef* refp = new AstVarRef(ref.nodep()->fileline(), var.varp(), lvalue); + if (ref.lsb() <= var.lsb() && var.msb() <= ref.msb()) { // Use the entire bits + return refp; + } else { // Use slice + const int lsb = std::max(ref.lsb(), var.lsb()); + const int msb = std::min(ref.msb(), var.msb()); + const int bitwidth = msb + 1 - lsb; + UINFO(4, var.varp()->prettyNameQ() << "[" << msb << ":" << lsb << "] used for " + << ref.nodep()->prettyNameQ() << '\n'); + // LSB of varp is always 0. "lsb - var.lsb()" means this. see also SplitNewVar + AstSel* selp = new AstSel(ref.nodep()->fileline(), refp, lsb - var.lsb(), bitwidth); + return selp; + } + } + static void connectPortAndVar(const std::vector& vars, AstVar* portp, + AstNode* insertp) { + for (; insertp; insertp = insertp->backp()) { + if (AstNodeStmt* stmtp = VN_CAST(insertp, NodeStmt)) { + if (stmtp->isStatement()) break; + } + } + const bool in = portp->isReadOnly(); + for (size_t i = 0; i < vars.size(); ++i) { + AstNode* rhsp = new AstSel(portp->fileline(), + new AstVarRef(portp->fileline(), portp, !in), + vars[i].lsb(), vars[i].bitwidth()); + AstNode* lhsp = new AstVarRef(portp->fileline(), vars[i].varp(), in); + if (!in) std::swap(lhsp, rhsp); + AstNodeAssign* assignp = newAssign(portp->fileline(), lhsp, rhsp, portp); + if (insertp) { + if (in) { + insertp->addHereThisAsNext(assignp); + } else { + insertp->addNextHere(assignp); + } + } else { + vars[i].varp()->addNextHere(assignp); + } + } + } + void createVars(AstVar* varp, const AstBasicDType* basicp, std::vector& vars) { + for (size_t i = 0; i < vars.size(); ++i) { + SplitNewVar* newvarp = &vars[i]; + int left = newvarp->msb(), right = newvarp->lsb(); + if (basicp->littleEndian()) std::swap(left, right); + const std::string name + = (left == right) + ? varp->name() + "__BRA__" + AstNode::encodeNumber(left) + "__KET__" + : varp->name() + "__BRA__" + AstNode::encodeNumber(left) + + AstNode::encodeName(":") + AstNode::encodeNumber(right) + + "__KET__"; + + AstBasicDType* dtypep; + switch (basicp->keyword()) { + case AstBasicDTypeKwd::BIT: + dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagBitPacked(), + newvarp->bitwidth()); + break; + case AstBasicDTypeKwd::LOGIC: + dtypep = new AstBasicDType(varp->subDTypep()->fileline(), VFlagLogicPacked(), + newvarp->bitwidth()); + break; + default: UASSERT_OBJ(false, basicp, "Only bit and logic are allowed"); + } + dtypep->rangep(new AstRange(varp->fileline(), newvarp->msb(), newvarp->lsb())); + dtypep->rangep()->littleEndian(basicp->littleEndian()); + newvarp->varp(new AstVar(varp->fileline(), AstVarType::VAR, name, dtypep)); + newvarp->varp()->propagateAttrFrom(varp); + newvarp->varp()->funcLocal(varp->isFuncLocal() || varp->isFuncReturn()); + // Enable this line to trace split variable directly: + // newvarp->varp()->trace(varp->isTrace()); + m_netp->typeTablep()->addTypesp(dtypep); + varp->addNextHere(newvarp->varp()); + UINFO(4, newvarp->varp()->prettyNameQ() + << " is added for " << varp->prettyNameQ() << '\n'); + } + } + static void updateReferences(AstVar* varp, PackedVarRef& ref, + const std::vector& vars) { + typedef std::vector NewVars; // Sorted by its lsb + for (int lvalue = 0; lvalue <= 1; ++lvalue) { // Refer the new split variables + std::vector& refs = lvalue ? ref.lhs() : ref.rhs(); + for (PackedVarRef::iterator refit = refs.begin(), refitend = refs.end(); + refit != refitend; ++refit) { + NewVars::const_iterator varit = std::upper_bound( + vars.begin(), vars.end(), refit->lsb(), SplitNewVar::Match()); + UASSERT_OBJ(varit != vars.end(), refit->nodep(), "Not found"); + UASSERT(!(varit->msb() < refit->lsb() || refit->msb() < varit->lsb()), + "wrong search result"); + AstNode* prevp; + bool inSentitivityList = false; + if (AstSenItem* senitemp = refit->backSenItemp()) { + AstNode* oldsenrefp = senitemp->sensp(); + oldsenrefp->replaceWith( + new AstVarRef(senitemp->fileline(), varit->varp(), false)); + VL_DO_DANGLING(oldsenrefp->deleteTree(), oldsenrefp); + prevp = senitemp; + inSentitivityList = true; + } else { + prevp = extractBits(*refit, *varit, lvalue); + } + for (int residue = refit->msb() - varit->msb(); residue > 0; + residue -= varit->bitwidth()) { + ++varit; + UASSERT_OBJ(varit != vars.end(), refit->nodep(), "not enough split variables"); + if (AstSenItem* senitemp = VN_CAST(prevp, SenItem)) { + prevp = new AstSenItem( + senitemp->fileline(), senitemp->edgeType(), + new AstVarRef(senitemp->fileline(), varit->varp(), false)); + senitemp->addNextHere(prevp); + } else { + AstNode* bitsp = extractBits(*refit, *varit, lvalue); + prevp = new AstConcat(refit->nodep()->fileline(), bitsp, prevp); + } + } + // If varp is an argument of task/func, need to update temporary var + // everytime the var is updated. See also another call of connectPortAndVar() in + // split() + if (varp->isIO() && (varp->isFuncLocal() || varp->isFuncReturn())) + connectPortAndVar(vars, varp, refit->nodep()); + if (!inSentitivityList) refit->replaceNodeWith(prevp); + UASSERT_OBJ(varit->msb() >= refit->msb(), varit->varp(), "Out of range"); + } + } + } + // Do the actual splitting operation + void split() { + for (vl_unordered_map::iterator it = m_refs.begin(), + it_end = m_refs.end(); + it != it_end; ++it) { + it->second.dedup(); + AstVar* varp = it->first; + UINFO(3, "In module " << m_modp->name() << " var " << varp->prettyNameQ() + << " which has " << it->second.lhs().size() << " lhs refs and " + << it->second.rhs().size() << " rhs refs will be split.\n"); + std::vector vars + = it->second.splitPlan(!varp->isTrace()); // If traced, all bit must be kept + if (vars.empty()) continue; + if (vars.size() == 1 && vars.front().bitwidth() == varp->width()) + continue; // No split + + createVars(varp, it->second.basicp(), vars); // Add the split variables + + updateReferences(varp, it->second, vars); + + if (varp->isIO()) { // port cannot be deleted + // If varp is a port of a module, single AssignW is sufficient + if (!(varp->isFuncLocal() || varp->isFuncReturn())) + connectPortAndVar(vars, varp, NULL); + } else if (varp->isTrace()) { + // Let's reuse the original variable for tracing + AstNode* rhsp + = new AstVarRef(vars.front().varp()->fileline(), vars.front().varp(), false); + for (size_t i = 1; i < vars.size(); ++i) { + rhsp = new AstConcat(varp->fileline(), + new AstVarRef(varp->fileline(), vars[i].varp(), false), + rhsp); + } + varp->addNextHere(newAssign(varp->fileline(), + new AstVarRef(varp->fileline(), varp, true), + rhsp, varp)); + } else { // the original variable is not used anymore. + VL_DO_DANGLING(varp->unlinkFrBack()->deleteTree(), varp); + } + ++m_numSplit; + } + m_refs.clear(); // Done + } + +public: + // When reusing the information from SplitUnpackedVarVisitor + SplitPackedVarVisitor(AstNetlist* nodep, SplitVarRefsMap& refs) + : m_netp(nodep) + , m_modp(NULL) + , m_numSplit(0) { + // If you want ignore refs and walk the tne entire AST, + // just call iterate(nodep) and disable the following for-loop. + for (SplitVarRefsMap::iterator it = refs.begin(), it_end = refs.end(); it != it_end; + ++it) { + m_modp = it->first; + it->second.visit(this); + split(); + m_modp = NULL; + } + } + ~SplitPackedVarVisitor() { + UASSERT(m_refs.empty(), "Forgot to call split()"); + V3Stats::addStat("SplitVar, Split packed variables", m_numSplit); + } + + // Check if the passed variable can be split. + // Even if this function returns true, the variable may not be split + // when the access to the variable cannot be determined statically. + static const char* cannotSplitReason(const AstVar* nodep, bool checkUnpacked) { + const char* reason = NULL; + if (AstBasicDType* const basicp = nodep->dtypep()->basicp()) { + const std::pair dim = nodep->dtypep()->dimensions(false); + // Unpacked array will be split in SplitUnpackedVarVisitor() beforehand + if (!((!checkUnpacked || dim.second == 0) && nodep->dtypep()->widthMin() > 1)) + reason = "its bitwidth is 1"; + if (!reason && !basicp->isBitLogic()) // Floating point and string are not supported + reason = "it is not an aggregate type of bit nor logic"; + if (!reason) reason = cannotSplitVarCommonReason(nodep); + } else { + reason = "its type is unknown"; + } + if (reason) UINFO(5, "Check " << nodep->prettyNameQ() + << " cannot split because" << reason << endl); + return reason; + } + VL_DEBUG_FUNC; // Declare debug() +}; + +const char* SplitVarImpl::cannotSplitPackedVarReason(const AstVar* varp) { + return SplitPackedVarVisitor::cannotSplitReason(varp, true); +} + +//###################################################################### +// Split class functions + +void V3SplitVar::splitVariable(AstNetlist* nodep) { + UINFO(2, __FUNCTION__ << ": " << endl); + SplitVarRefsMap refs; + { + SplitUnpackedVarVisitor visitor(nodep); + refs = visitor.getPackedVarRefs(); + } + V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9); + { SplitPackedVarVisitor visitor(nodep, refs); } + V3Global::dumpCheckGlobalTree("split_var", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 9); +} + +bool V3SplitVar::canSplitVar(const AstVar* varp) { + // If either SplitUnpackedVarVisitor or SplitPackedVarVisitor can handle, + // then accept varp. + return !SplitUnpackedVarVisitor::cannotSplitReason(varp) + || !SplitPackedVarVisitor::cannotSplitReason(varp, false); +} diff --git a/src/V3SplitVar.h b/src/V3SplitVar.h new file mode 100644 index 000000000..61a9e8db2 --- /dev/null +++ b/src/V3SplitVar.h @@ -0,0 +1,39 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: Break variables into separate words to avoid UNOPTFLAT +// +// 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. +// +// 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. +// +//************************************************************************* + +#ifndef _V3SPLITVAR_H_ +#define _V3SPLITVAR_H_ 1 + +//============================================================================ + +class AstNetlist; +class AstVar; + +class V3SplitVar { +public: + // Split variables marked with split_var metacomment. + static void splitVariable(AstNetlist* nodep); + + // Return true if the variable can be split. + // This check is not perfect. + static bool canSplitVar(const AstVar* varp); +}; + +#endif // Guard diff --git a/src/Verilator.cpp b/src/Verilator.cpp index dae14ea6c..bf74ef44d 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -82,6 +82,7 @@ #include "V3Slice.h" #include "V3Split.h" #include "V3SplitAs.h" +#include "V3SplitVar.h" #include "V3Stats.h" #include "V3String.h" #include "V3Subst.h" @@ -175,12 +176,14 @@ static void process() { V3Const::constifyAllLint(v3Global.rootp()); if (!v3Global.opt.xmlOnly()) { + // Split packed variables into multiple pieces to resolve UNOPTFLAT. + // should be after constifyAllLint() which flattens to 1D bit vector + V3SplitVar::splitVariable(v3Global.rootp()); + // Remove cell arrays (must be between V3Width and scoping) V3Inst::dearrayAll(v3Global.rootp()); V3LinkDot::linkDotArrayed(v3Global.rootp()); - } - if (!v3Global.opt.xmlOnly()) { // Task inlining & pushing BEGINs names to variables/cells // Begin processing must be after Param, before module inlining V3Begin::debeginAll(v3Global.rootp()); // Flatten cell names, before inliner diff --git a/src/verilog.l b/src/verilog.l index d6797e228..23fad9c63 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -704,6 +704,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "/*verilator public_flat_rd*/" { FL; return yVL_PUBLIC_FLAT_RD; } "/*verilator public_flat_rw*/" { FL; return yVL_PUBLIC_FLAT_RW; } // The @(edge) is converted by the preproc "/*verilator public_module*/" { FL; return yVL_PUBLIC_MODULE; } + "/*verilator split_var*/" { FL; return yVL_SPLIT_VAR; } "/*verilator sc_clock*/" { FL; return yVL_CLOCK; } "/*verilator clocker*/" { FL; return yVL_CLOCKER; } "/*verilator no_clocker*/" { FL; return yVL_NO_CLOCKER; } diff --git a/src/verilog.y b/src/verilog.y index f7b9320e7..e23427536 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -639,6 +639,7 @@ class AstSenTree; %token yVL_PUBLIC_FLAT_RD "/*verilator public_flat_rd*/" %token yVL_PUBLIC_FLAT_RW "/*verilator public_flat_rw*/" %token yVL_PUBLIC_MODULE "/*verilator public_module*/" +%token yVL_SPLIT_VAR "/*verilator split_var*/" %token yP_TICK "'" %token yP_TICKBRA "'{" @@ -2282,6 +2283,7 @@ sigAttr: | yVL_ISOLATE_ASSIGNMENTS { $$ = new AstAttrOf($1,AstAttrType::VAR_ISOLATE_ASSIGNMENTS); } | yVL_SC_BV { $$ = new AstAttrOf($1,AstAttrType::VAR_SC_BV); } | yVL_SFORMAT { $$ = new AstAttrOf($1,AstAttrType::VAR_SFORMAT); } + | yVL_SPLIT_VAR { $$ = new AstAttrOf($1,AstAttrType::VAR_SPLIT_VAR); } ; rangeListE: // IEEE: [{packed_dimension}] diff --git a/test_regress/t/t_split_var_0.pl b/test_regress/t/t_split_var_0.pl new file mode 100755 index 000000000..b74a78821 --- /dev/null +++ b/test_regress/t/t_split_var_0.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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. + +scenarios(simulator => 1); + +# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning. +# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. +# So use 6 threads here though it's not optimal in performace wise, but ok. +compile( + verilator_flags2 => ['--stats' . ($Self->{vltmt} ? ' --threads 6' : '')], + ); + +execute( + check_finished => 1, + ); + +file_grep($Self->{stats}, qr/SplitVar,\s+Split packed variables\s+(\d+)/i, 13); +file_grep($Self->{stats}, qr/SplitVar,\s+Split unpacked arrays\s+(\d+)/i, 23); +ok(1); +1; diff --git a/test_regress/t/t_split_var_0.v b/test_regress/t/t_split_var_0.v new file mode 100644 index 000000000..d7111f948 --- /dev/null +++ b/test_regress/t/t_split_var_0.v @@ -0,0 +1,431 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2020 by Yutetsu TAKATSUKASA. + +// If split_var pragma is removed, UNOPTFLAT appears. + +module barshift_1d_unpacked #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out /*verilator split_var*/); + + localparam OFFSET = -3; + logic [WIDTH-1:0] tmp[DEPTH+OFFSET:OFFSET] /*verilator split_var*/; + generate + for(genvar i = 0; i < DEPTH; ++i) begin + always_comb + if (shift[i]) begin + /*verilator lint_off ALWCOMBORDER*/ + tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]}; + /*verilator lint_on ALWCOMBORDER*/ + end + else begin + tmp[i+1+OFFSET] = tmp[i+OFFSET]; + end + end + endgenerate + assign tmp[0+OFFSET] = in; + assign out[WIDTH-1-:WIDTH-1] = tmp[DEPTH+OFFSET][WIDTH-1:1]; + assign out[0] = tmp[DEPTH+OFFSET][0+:1]; +endmodule + + +module barshift_1d_unpacked_le #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + localparam OFFSET = -3; + // almost same as above module, but tmp[smaller:bigger] here. + logic [WIDTH-1:0] tmp[OFFSET:DEPTH+OFFSET] /*verilator split_var*/; + generate + for(genvar i = 0; i < DEPTH; ++i) begin + always_comb + if (shift[i]) begin + /*verilator lint_off ALWCOMBORDER*/ + tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]}; + /*verilator lint_on ALWCOMBORDER*/ + end + else begin + tmp[i+1+OFFSET] = tmp[i+OFFSET]; + end + end + endgenerate + assign tmp[0+OFFSET] = in; + assign out = tmp[DEPTH+OFFSET]; +endmodule + + +module barshift_1d_unpacked_struct0 #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + localparam OFFSET = 1; + typedef struct packed { logic [WIDTH-1:0] data; } data_type; + data_type tmp[DEPTH+OFFSET:OFFSET] /*verilator split_var*/; + generate + for(genvar i = 0; i < DEPTH; ++i) begin + always_comb + if (shift[i]) begin + /*verilator lint_off ALWCOMBORDER*/ + tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]}; + /*verilator lint_on ALWCOMBORDER*/ + end + else begin + tmp[i+1+OFFSET] = tmp[i+OFFSET]; + end + end + endgenerate + assign tmp[0+OFFSET] = in; + assign out = tmp[DEPTH+OFFSET]; +endmodule + + +module barshift_2d_unpacked #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + localparam OFFSET = 1; + localparam N = 3; + reg [WIDTH-1:0] tmp0[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp1[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp2[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1]; + reg [WIDTH-1:0] tmp3[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp4[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp5[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1]; + reg [WIDTH-1:0] tmp6[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + + reg [WIDTH-1:0] tmp7[DEPTH+OFFSET+1:OFFSET+1][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp8[DEPTH+OFFSET+3:OFFSET-1][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp9[DEPTH+OFFSET+3:OFFSET+3][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp10[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + // because tmp11 is not split for testing mixture usage of split_var and no-spliv_ar, + // UNOPTFLAT appears, but it's fine. + /*verilator lint_off UNOPTFLAT*/ + reg [WIDTH-1:0] tmp11[-1:1][DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1]; + /*verilator lint_on UNOPTFLAT*/ + reg [WIDTH-1:0] tmp12[-1:0][DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + reg [WIDTH-1:0] tmp13[DEPTH+OFFSET:OFFSET][OFFSET:OFFSET+N-1] /*verilator split_var*/; + + generate + for(genvar i = 0; i < DEPTH; ++i) begin + for(genvar j = OFFSET; j < N + OFFSET; ++j) begin + always_comb + if (shift[i]) begin + /*verilator lint_off ALWCOMBORDER*/ + tmp0[i+1+OFFSET][j] = {tmp0[i+OFFSET][j][(1 << i)-1:0], tmp0[i+OFFSET][j][WIDTH-1:(2**i)]}; + /*verilator lint_on ALWCOMBORDER*/ + end + else begin + tmp0[i+1+OFFSET][j] = tmp0[i+OFFSET][j]; + end + end + end + for(genvar j = OFFSET; j < N + OFFSET; ++j) begin + assign tmp0[0 + OFFSET][j] = in; + end + endgenerate + assign tmp1 = tmp0; // split both side + assign tmp2 = tmp1; // split only rhs + assign tmp3 = tmp2; // split only lhs + always_comb tmp4 = tmp3; // split both side + always_comb tmp5 = tmp4; // split only rhs + always_comb tmp6 = tmp5; // split only lhs + + assign tmp7 = tmp6; + assign tmp8[DEPTH+OFFSET+1:OFFSET+1] = tmp7; + assign tmp9 = tmp8[DEPTH+OFFSET+1:OFFSET+1]; + assign tmp10[DEPTH+OFFSET:OFFSET] = tmp9[DEPTH+OFFSET+3:OFFSET+3]; + assign tmp11[1] = tmp10; + assign tmp11[-1] = tmp11[1]; + assign tmp11[0] = tmp11[-1]; + assign tmp12 = tmp11[0:1]; + assign out = tmp12[1][DEPTH+OFFSET][OFFSET]; +endmodule + + +module barshift_1d_unpacked_struct1 #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + localparam OFFSET = 2; + typedef struct packed { int data; } data_type; + data_type tmp[DEPTH+OFFSET:OFFSET] /*verilator split_var*/; + + localparam [32-WIDTH-1:0] pad = 0; + generate + for(genvar i = 0; i < DEPTH; ++i) begin + always_comb + if (shift[i]) begin + /*verilator lint_off ALWCOMBORDER*/ + tmp[i+1+OFFSET] = {pad, tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]}; + /*verilator lint_on ALWCOMBORDER*/ + end + else begin + tmp[i+1+OFFSET] = tmp[i+OFFSET]; + end + end + endgenerate + assign tmp[0+OFFSET] = {pad, in}; + assign out = tmp[DEPTH+OFFSET][WIDTH-1:0]; +endmodule + + +module barshift_2d_packed_array #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + localparam OFFSET = -2; + /*verilator lint_off LITENDIAN*/ + reg [OFFSET:DEPTH+OFFSET][WIDTH-1:0] tmp /*verilator split_var*/; + /*verilator lint_on LITENDIAN*/ + + generate + for(genvar i = 0; i < DEPTH; ++i) begin + always_comb + /*verilator lint_off ALWCOMBORDER*/ + if (shift[i]) begin + tmp[i+1+OFFSET] = {tmp[i+OFFSET][(1 << i)-1:0], tmp[i+OFFSET][WIDTH-1:(2**i)]}; + end + else begin + tmp[i+1+OFFSET][1:0] = tmp[i+OFFSET][1:0]; + tmp[i+1+OFFSET][WIDTH-1:2] = tmp[i+OFFSET][WIDTH-1:2]; + end + /*verilator lint_on ALWCOMBORDER*/ + end + endgenerate + assign tmp[0+OFFSET] = in; + assign out = tmp[DEPTH+OFFSET]; +endmodule + +module barshift_2d_packed_array_le #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + localparam OFFSET = -2; + /*verilator lint_off LITENDIAN*/ + reg [OFFSET:DEPTH+OFFSET][OFFSET:WIDTH-1+OFFSET] tmp /*verilator split_var*/; + /*verilator lint_on LITENDIAN*/ + + generate + for(genvar i = 0; i < DEPTH; ++i) begin + always_comb + /*verilator lint_off ALWCOMBORDER*/ + if (shift[i]) begin + tmp[i+1+OFFSET] = {tmp[i+OFFSET][WIDTH-(2**i)+OFFSET:WIDTH-1+OFFSET], tmp[i+OFFSET][OFFSET:WIDTH-(2**i)-1+OFFSET]}; + end + else begin // actulally just tmp[i+1+OFFSET] = tmp[i+OFFSET] + tmp[i+1+OFFSET][0+OFFSET:2+OFFSET] = tmp[i+OFFSET][0+OFFSET:2+OFFSET]; + tmp[i+1+OFFSET][3+OFFSET] = tmp[i+OFFSET][3+OFFSET]; + {tmp[i+1+OFFSET][4+OFFSET],tmp[i+1+OFFSET][5+OFFSET]} = {tmp[i+OFFSET][4+OFFSET], tmp[i+OFFSET][5+OFFSET]}; + {tmp[i+1+OFFSET][7+OFFSET],tmp[i+1+OFFSET][6+OFFSET]} = {tmp[i+OFFSET][7+OFFSET], tmp[i+OFFSET][6+OFFSET]}; + end + /*verilator lint_on ALWCOMBORDER*/ + end + endgenerate + assign tmp[0+OFFSET] = in; + assign out = tmp[DEPTH+OFFSET]; +endmodule + + +module barshift_1d_packed_struct #(localparam DEPTH = 3, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + typedef struct packed { + logic [WIDTH-1:0] v0, v1, v2, v3; + } data_type; + wire data_type tmp /*verilator split_var*/; + + assign tmp.v0 = in; + assign tmp.v1 = shift[0] == 1'b1 ? {tmp.v0[(1 << 0)-1:0], tmp.v0[WIDTH-1:2**0]} : tmp.v0; + assign tmp.v2 = shift[1] == 1'b1 ? {tmp.v1[(1 << 1)-1:0], tmp.v1[WIDTH-1:2**1]} : tmp.v1; + assign tmp.v3 = shift[2] == 1'b1 ? {tmp.v2[(1 << 2)-1:0], tmp.v2[WIDTH-1:2**2]} : tmp.v2; + assign out = tmp.v3; +endmodule + + +module barshift_bitslice #(parameter DEPTH = 2, localparam WIDTH = 2**DEPTH) + (input [WIDTH-1:0] in, input [DEPTH-1:0] shift, output [WIDTH-1:0] out); + + /*verilator lint_off LITENDIAN*/ + wire [0:WIDTH*(DEPTH+1) - 1] tmp /*verilator split_var*/; + /*verilator lint_on LITENDIAN*/ + + generate + for(genvar i = 0; i < DEPTH; ++i) begin + always_comb + if (shift[i]) begin + tmp[WIDTH*(i+1):WIDTH*(i+1+1)-1] = {tmp[WIDTH*(i+1)-(1< 1); + +compile( + fails => 1, + verilator_flags2 => ['--stats'], + expect_filename => $Self->{golden_filename}, +); + +ok(1); +1; diff --git a/test_regress/t/t_split_var_1_bad.v b/test_regress/t/t_split_var_1_bad.v new file mode 100644 index 000000000..d3e5b9e04 --- /dev/null +++ b/test_regress/t/t_split_var_1_bad.v @@ -0,0 +1,84 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2020 by Yutetsu TAKATSUKASA. + +logic [7:0] should_show_warning_global0 /* verilator split_var */; +logic [7:0] should_show_warning_global1 [1:0] /* verilator split_var */; + +interface ifs; + logic [7:0] should_show_warning_ifs0 /* verilator split_var */; + logic [7:0] should_show_warning_ifs1 [1:0] /* verilator split_var */; +endinterface + +module t(); + // The following variables can not be splitted. will see warnings. + real should_show_warning0 /*verilator split_var*/; + string should_show_warning1 /*verilator split_var*/; + wire should_show_warning2 /*verilator split_var*/; + + logic [3:0] addr; + logic [7:0] rd_data0, rd_data1, rd_data2; + + sub0 i_sub0(.addr(addr), .rd_data(rd_data0)); + sub1 i_sub1(.addr(addr), .rd_data(rd_data2)); + sub2 i_sub2; + sub3 i_sub3; + ifs i_ifs(); + + function int bad_func(inout logic [3:0] inout_port /*verilator split_var*/, + ref logic [7:0] ref_port /*verilator split_var*/); + return 0; + endfunction + + initial begin + addr = 0; + addr = 1; + i_sub0.cannot_split1[0] = 0; + i_sub0.cannot_split1[1] = bad_func(addr, rd_data0); + $finish; + end + +endmodule + + +module sub0(input [3:0]addr, output logic [7:0] rd_data); + + logic [7:0] cannot_split0[0:15] /*verilator split_var*/; + logic [7:0] cannot_split1[0:15] /*verilator split_var*/; + always_comb + rd_data = cannot_split0[addr]; + +endmodule + + +module sub1(input [3:0]addr, output logic [7:0] rd_data); + genvar cannot_split_genvar /*verilator split_var*/; + logic [15:0] [7:0] cannot_split /*verilator split_var*/; + always_comb + rd_data = cannot_split[addr]; + +endmodule + + +module sub2; // from t_bitsel_wire_array_bad.v + + // a and b are arrays of length 1. + wire a[0:0] /* verilator split_var */ ; // Array of nets + wire b[0:0] /* verilator split_var */ ; + + assign a = 1'b0; // Only net assignment allowed + assign b = a[0]; // Only net assignment allowed + +endmodule + +module sub3; // from t_select_bad_range3.v + + logic [7:0] inwires [12:10] /* verilator split_var */; + wire [7:0] outwires [12:10] /* verilator split_var */; + + assign outwires[10] = inwires[11]; + assign outwires[11] = inwires[12]; + assign outwires[12] = inwires[13]; // must be an error here + +endmodule diff --git a/test_regress/t/t_split_var_2_trace.out b/test_regress/t/t_split_var_2_trace.out new file mode 100644 index 000000000..b922cd557 --- /dev/null +++ b/test_regress/t/t_split_var_2_trace.out @@ -0,0 +1,1930 @@ +$version Generated by VerilatedVcd $end +$date Tue Feb 11 16:07:02 2020 + $end +$timescale 1ns $end + + $scope module top $end + $var wire 1 o2 clk $end + $scope module t $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 )3 NUMSUB [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 1 o2 clk $end + $var wire 64 93 expc [63:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 M- out(0) [7:0] $end + $var wire 8 N- out(1) [7:0] $end + $var wire 8 O- out(2) [7:0] $end + $var wire 8 P- out(3) [7:0] $end + $var wire 8 Q- out(4) [7:0] $end + $var wire 8 R- out(5) [7:0] $end + $var wire 8 S- out(6) [7:0] $end + $var wire 8 T- out(7) [7:0] $end + $var wire 8 U- out(8) [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 8 ?. through_tmp [7:0] $end + $scope module always_block $end + $var wire 1 O2 failed $end + $scope module unnamedblk1 $end + $var wire 32 W2 i [31:0] $end + $upscope $end + $upscope $end + $scope module i_t_array_rev $end + $var wire 1 C arrd(0) $end + $var wire 1 K arrd(1) $end + $var wire 1 o2 clk $end + $var wire 32 }5 cyc [31:0] $end + $var wire 1 _2 localbkw(0) $end + $var wire 1 g2 localbkw(1) $end + $var wire 1 S y0 $end + $var wire 1 [ y1 $end + $scope module arr_rev_u $end + $var wire 1 c arrbkw(0) $end + $var wire 1 d arrbkw(1) $end + $var wire 1 S y0 $end + $var wire 1 [ y1 $end + $upscope $end + $upscope $end + $scope module i_var_decl_with_init $end + $var wire 32 # var0 [-1:30] $end + $var wire 32 3 var1 [30:-1] $end + $var wire 32 + var2 [-1:30] $end + $var wire 32 ; var3 [30:-1] $end + $upscope $end + $scope module shifter0 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 I3 OFFSET [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 { out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 8 -! tmp(-1) [7:0] $end + $var wire 8 %! tmp(-2) [7:0] $end + $var wire 8 13 tmp(-3) [7:0] $end + $var wire 8 5! tmp(0) [7:0] $end + $upscope $end + $scope module shifter1 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 I3 OFFSET [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 =! out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 8 E! tmp(-1) [7:0] $end + $var wire 8 %! tmp(-2) [7:0] $end + $var wire 8 13 tmp(-3) [7:0] $end + $var wire 8 =! tmp(0) [7:0] $end + $upscope $end + $scope module shifter2 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 Q3 OFFSET [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 M! out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 8 13 tmp(1) [7:0] $end + $var wire 8 U! tmp(2) [7:0] $end + $var wire 8 ]! tmp(3) [7:0] $end + $var wire 8 M! tmp(4) [7:0] $end + $upscope $end + $scope module shifter3 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 w2 N [31:0] $end + $var wire 32 Q3 OFFSET [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 G. out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 8 13 tmp0(1)(1) [7:0] $end + $var wire 8 13 tmp0(1)(2) [7:0] $end + $var wire 8 13 tmp0(1)(3) [7:0] $end + $var wire 8 %! tmp0(2)(1) [7:0] $end + $var wire 8 %! tmp0(2)(2) [7:0] $end + $var wire 8 %! tmp0(2)(3) [7:0] $end + $var wire 8 e! tmp0(3)(1) [7:0] $end + $var wire 8 m! tmp0(3)(2) [7:0] $end + $var wire 8 u! tmp0(3)(3) [7:0] $end + $var wire 8 }! tmp0(4)(1) [7:0] $end + $var wire 8 '" tmp0(4)(2) [7:0] $end + $var wire 8 /" tmp0(4)(3) [7:0] $end + $var wire 8 7" tmp1(1)(1) [7:0] $end + $var wire 8 ?" tmp1(1)(2) [7:0] $end + $var wire 8 G" tmp1(1)(3) [7:0] $end + $var wire 8 O" tmp1(2)(1) [7:0] $end + $var wire 8 W" tmp1(2)(2) [7:0] $end + $var wire 8 _" tmp1(2)(3) [7:0] $end + $var wire 8 g" tmp1(3)(1) [7:0] $end + $var wire 8 o" tmp1(3)(2) [7:0] $end + $var wire 8 w" tmp1(3)(3) [7:0] $end + $var wire 8 !# tmp1(4)(1) [7:0] $end + $var wire 8 )# tmp1(4)(2) [7:0] $end + $var wire 8 1# tmp1(4)(3) [7:0] $end + $var wire 8 I+ tmp10(1)(1) [7:0] $end + $var wire 8 Q+ tmp10(1)(2) [7:0] $end + $var wire 8 Y+ tmp10(1)(3) [7:0] $end + $var wire 8 a+ tmp10(2)(1) [7:0] $end + $var wire 8 i+ tmp10(2)(2) [7:0] $end + $var wire 8 q+ tmp10(2)(3) [7:0] $end + $var wire 8 y+ tmp10(3)(1) [7:0] $end + $var wire 8 #, tmp10(3)(2) [7:0] $end + $var wire 8 +, tmp10(3)(3) [7:0] $end + $var wire 8 3, tmp10(4)(1) [7:0] $end + $var wire 8 ;, tmp10(4)(2) [7:0] $end + $var wire 8 C, tmp10(4)(3) [7:0] $end + $var wire 8 O. tmp12(-1)(1)(1) [7:0] $end + $var wire 8 W. tmp12(-1)(1)(2) [7:0] $end + $var wire 8 _. tmp12(-1)(1)(3) [7:0] $end + $var wire 8 g. tmp12(-1)(2)(1) [7:0] $end + $var wire 8 o. tmp12(-1)(2)(2) [7:0] $end + $var wire 8 w. tmp12(-1)(2)(3) [7:0] $end + $var wire 8 !/ tmp12(-1)(3)(1) [7:0] $end + $var wire 8 )/ tmp12(-1)(3)(2) [7:0] $end + $var wire 8 1/ tmp12(-1)(3)(3) [7:0] $end + $var wire 8 G. tmp12(-1)(4)(1) [7:0] $end + $var wire 8 9/ tmp12(-1)(4)(2) [7:0] $end + $var wire 8 A/ tmp12(-1)(4)(3) [7:0] $end + $var wire 8 I/ tmp12(0)(1)(1) [7:0] $end + $var wire 8 Q/ tmp12(0)(1)(2) [7:0] $end + $var wire 8 Y/ tmp12(0)(1)(3) [7:0] $end + $var wire 8 a/ tmp12(0)(2)(1) [7:0] $end + $var wire 8 i/ tmp12(0)(2)(2) [7:0] $end + $var wire 8 q/ tmp12(0)(2)(3) [7:0] $end + $var wire 8 y/ tmp12(0)(3)(1) [7:0] $end + $var wire 8 #0 tmp12(0)(3)(2) [7:0] $end + $var wire 8 +0 tmp12(0)(3)(3) [7:0] $end + $var wire 8 30 tmp12(0)(4)(1) [7:0] $end + $var wire 8 ;0 tmp12(0)(4)(2) [7:0] $end + $var wire 8 C0 tmp12(0)(4)(3) [7:0] $end + $var wire 8 [4 tmp13(1)(1) [7:0] $end + $var wire 8 c4 tmp13(1)(2) [7:0] $end + $var wire 8 k4 tmp13(1)(3) [7:0] $end + $var wire 8 s4 tmp13(2)(1) [7:0] $end + $var wire 8 {4 tmp13(2)(2) [7:0] $end + $var wire 8 %5 tmp13(2)(3) [7:0] $end + $var wire 8 -5 tmp13(3)(1) [7:0] $end + $var wire 8 55 tmp13(3)(2) [7:0] $end + $var wire 8 =5 tmp13(3)(3) [7:0] $end + $var wire 8 E5 tmp13(4)(1) [7:0] $end + $var wire 8 M5 tmp13(4)(2) [7:0] $end + $var wire 8 U5 tmp13(4)(3) [7:0] $end + $var wire 8 9# tmp2(1)(1) [7:0] $end + $var wire 8 A# tmp2(1)(2) [7:0] $end + $var wire 8 I# tmp2(1)(3) [7:0] $end + $var wire 8 Q# tmp2(2)(1) [7:0] $end + $var wire 8 Y# tmp2(2)(2) [7:0] $end + $var wire 8 a# tmp2(2)(3) [7:0] $end + $var wire 8 i# tmp2(3)(1) [7:0] $end + $var wire 8 q# tmp2(3)(2) [7:0] $end + $var wire 8 y# tmp2(3)(3) [7:0] $end + $var wire 8 #$ tmp2(4)(1) [7:0] $end + $var wire 8 +$ tmp2(4)(2) [7:0] $end + $var wire 8 3$ tmp2(4)(3) [7:0] $end + $var wire 8 ;$ tmp3(1)(1) [7:0] $end + $var wire 8 C$ tmp3(1)(2) [7:0] $end + $var wire 8 K$ tmp3(1)(3) [7:0] $end + $var wire 8 S$ tmp3(2)(1) [7:0] $end + $var wire 8 [$ tmp3(2)(2) [7:0] $end + $var wire 8 c$ tmp3(2)(3) [7:0] $end + $var wire 8 k$ tmp3(3)(1) [7:0] $end + $var wire 8 s$ tmp3(3)(2) [7:0] $end + $var wire 8 {$ tmp3(3)(3) [7:0] $end + $var wire 8 %% tmp3(4)(1) [7:0] $end + $var wire 8 -% tmp3(4)(2) [7:0] $end + $var wire 8 5% tmp3(4)(3) [7:0] $end + $var wire 8 =% tmp4(1)(1) [7:0] $end + $var wire 8 E% tmp4(1)(2) [7:0] $end + $var wire 8 M% tmp4(1)(3) [7:0] $end + $var wire 8 U% tmp4(2)(1) [7:0] $end + $var wire 8 ]% tmp4(2)(2) [7:0] $end + $var wire 8 e% tmp4(2)(3) [7:0] $end + $var wire 8 m% tmp4(3)(1) [7:0] $end + $var wire 8 u% tmp4(3)(2) [7:0] $end + $var wire 8 }% tmp4(3)(3) [7:0] $end + $var wire 8 '& tmp4(4)(1) [7:0] $end + $var wire 8 /& tmp4(4)(2) [7:0] $end + $var wire 8 7& tmp4(4)(3) [7:0] $end + $var wire 8 ?& tmp5(1)(1) [7:0] $end + $var wire 8 G& tmp5(1)(2) [7:0] $end + $var wire 8 O& tmp5(1)(3) [7:0] $end + $var wire 8 W& tmp5(2)(1) [7:0] $end + $var wire 8 _& tmp5(2)(2) [7:0] $end + $var wire 8 g& tmp5(2)(3) [7:0] $end + $var wire 8 o& tmp5(3)(1) [7:0] $end + $var wire 8 w& tmp5(3)(2) [7:0] $end + $var wire 8 !' tmp5(3)(3) [7:0] $end + $var wire 8 )' tmp5(4)(1) [7:0] $end + $var wire 8 1' tmp5(4)(2) [7:0] $end + $var wire 8 9' tmp5(4)(3) [7:0] $end + $var wire 8 A' tmp6(1)(1) [7:0] $end + $var wire 8 I' tmp6(1)(2) [7:0] $end + $var wire 8 Q' tmp6(1)(3) [7:0] $end + $var wire 8 Y' tmp6(2)(1) [7:0] $end + $var wire 8 a' tmp6(2)(2) [7:0] $end + $var wire 8 i' tmp6(2)(3) [7:0] $end + $var wire 8 q' tmp6(3)(1) [7:0] $end + $var wire 8 y' tmp6(3)(2) [7:0] $end + $var wire 8 #( tmp6(3)(3) [7:0] $end + $var wire 8 +( tmp6(4)(1) [7:0] $end + $var wire 8 3( tmp6(4)(2) [7:0] $end + $var wire 8 ;( tmp6(4)(3) [7:0] $end + $var wire 8 C( tmp7(2)(1) [7:0] $end + $var wire 8 K( tmp7(2)(2) [7:0] $end + $var wire 8 S( tmp7(2)(3) [7:0] $end + $var wire 8 [( tmp7(3)(1) [7:0] $end + $var wire 8 c( tmp7(3)(2) [7:0] $end + $var wire 8 k( tmp7(3)(3) [7:0] $end + $var wire 8 s( tmp7(4)(1) [7:0] $end + $var wire 8 {( tmp7(4)(2) [7:0] $end + $var wire 8 %) tmp7(4)(3) [7:0] $end + $var wire 8 -) tmp7(5)(1) [7:0] $end + $var wire 8 5) tmp7(5)(2) [7:0] $end + $var wire 8 =) tmp7(5)(3) [7:0] $end + $var wire 8 Y3 tmp8(0)(1) [7:0] $end + $var wire 8 a3 tmp8(0)(2) [7:0] $end + $var wire 8 i3 tmp8(0)(3) [7:0] $end + $var wire 8 q3 tmp8(1)(1) [7:0] $end + $var wire 8 y3 tmp8(1)(2) [7:0] $end + $var wire 8 #4 tmp8(1)(3) [7:0] $end + $var wire 8 E) tmp8(2)(1) [7:0] $end + $var wire 8 M) tmp8(2)(2) [7:0] $end + $var wire 8 U) tmp8(2)(3) [7:0] $end + $var wire 8 ]) tmp8(3)(1) [7:0] $end + $var wire 8 e) tmp8(3)(2) [7:0] $end + $var wire 8 m) tmp8(3)(3) [7:0] $end + $var wire 8 u) tmp8(4)(1) [7:0] $end + $var wire 8 }) tmp8(4)(2) [7:0] $end + $var wire 8 '* tmp8(4)(3) [7:0] $end + $var wire 8 /* tmp8(5)(1) [7:0] $end + $var wire 8 7* tmp8(5)(2) [7:0] $end + $var wire 8 ?* tmp8(5)(3) [7:0] $end + $var wire 8 +4 tmp8(6)(1) [7:0] $end + $var wire 8 34 tmp8(6)(2) [7:0] $end + $var wire 8 ;4 tmp8(6)(3) [7:0] $end + $var wire 8 C4 tmp8(7)(1) [7:0] $end + $var wire 8 K4 tmp8(7)(2) [7:0] $end + $var wire 8 S4 tmp8(7)(3) [7:0] $end + $var wire 8 G* tmp9(4)(1) [7:0] $end + $var wire 8 O* tmp9(4)(2) [7:0] $end + $var wire 8 W* tmp9(4)(3) [7:0] $end + $var wire 8 _* tmp9(5)(1) [7:0] $end + $var wire 8 g* tmp9(5)(2) [7:0] $end + $var wire 8 o* tmp9(5)(3) [7:0] $end + $var wire 8 w* tmp9(6)(1) [7:0] $end + $var wire 8 !+ tmp9(6)(2) [7:0] $end + $var wire 8 )+ tmp9(6)(3) [7:0] $end + $var wire 8 1+ tmp9(7)(1) [7:0] $end + $var wire 8 9+ tmp9(7)(2) [7:0] $end + $var wire 8 A+ tmp9(7)(3) [7:0] $end + $upscope $end + $scope module shifter4 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 ]5 OFFSET [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 K, out [7:0] $end + $var wire 24 m5 pad [23:0] $end + $var wire 3 s shift [2:0] $end + $var wire 32 e5 tmp(2) [31:0] $end + $var wire 32 S, tmp(3) [31:0] $end + $var wire 32 [, tmp(4) [31:0] $end + $var wire 32 c, tmp(5) [31:0] $end + $upscope $end + $scope module shifter5 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 u5 OFFSET [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 k, out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 32 s, tmp [31:0] $end + $upscope $end + $scope module shifter6 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 u5 OFFSET [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 {, out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 32 %- tmp [31:0] $end + $upscope $end + $scope module shifter7 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 -- out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 32 5- tmp [31:0] $end + $upscope $end + $scope module shifter8 $end + $var wire 32 w2 DEPTH [31:0] $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 13 in [7:0] $end + $var wire 8 =- out [7:0] $end + $var wire 3 s shift [2:0] $end + $var wire 32 E- tmp [0:31] $end + $upscope $end + $scope module though0 $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 7. in [7:0] $end + $var wire 8 ?. out [7:0] $end + $var wire 1 K0 unpack_tmp(0) $end + $var wire 1 S0 unpack_tmp(1) $end + $var wire 1 [0 unpack_tmp(2) $end + $var wire 1 c0 unpack_tmp(3) $end + $var wire 1 k0 unpack_tmp(4) $end + $var wire 1 s0 unpack_tmp(5) $end + $var wire 1 {0 unpack_tmp(6) $end + $var wire 1 %1 unpack_tmp(7) $end + $scope module i_pack2unpack $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 8 7. in [7:0] $end + $var wire 1 -1 out(0) $end + $var wire 1 .1 out(1) $end + $var wire 1 /1 out(2) $end + $var wire 1 01 out(3) $end + $var wire 1 11 out(4) $end + $var wire 1 21 out(5) $end + $var wire 1 31 out(6) $end + $var wire 1 41 out(7) $end + $upscope $end + $scope module i_unpack2pack $end + $var wire 32 !3 WIDTH [31:0] $end + $var wire 1 m1 in(0) $end + $var wire 1 n1 in(1) $end + $var wire 1 o1 in(2) $end + $var wire 1 p1 in(3) $end + $var wire 1 q1 in(4) $end + $var wire 1 r1 in(5) $end + $var wire 1 s1 in(6) $end + $var wire 1 t1 in(7) $end + $var wire 8 ?. out [7:0] $end + $upscope $end + $upscope $end + $upscope $end + $upscope $end +$enddefinitions $end + + +#0 +b00000001001000110100010101100111 # +b00100000000000000000000000000000 + +b00000001001000110100000000100111 3 +b00000000000000000000000000000011 ; +1C +0K +0S +1[ +0c +1d +b000 s +b10001110 { +b10001110 %! +b10001110 -! +b10001110 5! +b10001110 =! +b10001110 E! +b10001110 M! +b10001110 U! +b10001110 ]! +b10001110 e! +b10001110 m! +b10001110 u! +b10001110 }! +b10001110 '" +b10001110 /" +b10001110 7" +b10001110 ?" +b10001110 G" +b10001110 O" +b10001110 W" +b10001110 _" +b10001110 g" +b10001110 o" +b10001110 w" +b10001110 !# +b10001110 )# +b10001110 1# +b10001110 9# +b10001110 A# +b10001110 I# +b10001110 Q# +b10001110 Y# +b10001110 a# +b10001110 i# +b10001110 q# +b10001110 y# +b10001110 #$ +b10001110 +$ +b10001110 3$ +b10001110 ;$ +b10001110 C$ +b10001110 K$ +b10001110 S$ +b10001110 [$ +b10001110 c$ +b10001110 k$ +b10001110 s$ +b10001110 {$ +b10001110 %% +b10001110 -% +b10001110 5% +b10001110 =% +b10001110 E% +b10001110 M% +b10001110 U% +b10001110 ]% +b10001110 e% +b10001110 m% +b10001110 u% +b10001110 }% +b10001110 '& +b10001110 /& +b10001110 7& +b10001110 ?& +b10001110 G& +b10001110 O& +b10001110 W& +b10001110 _& +b10001110 g& +b10001110 o& +b10001110 w& +b10001110 !' +b10001110 )' +b10001110 1' +b10001110 9' +b10001110 A' +b10001110 I' +b10001110 Q' +b10001110 Y' +b10001110 a' +b10001110 i' +b10001110 q' +b10001110 y' +b10001110 #( +b10001110 +( +b10001110 3( +b10001110 ;( +b10001110 C( +b10001110 K( +b10001110 S( +b10001110 [( +b10001110 c( +b10001110 k( +b10001110 s( +b10001110 {( +b10001110 %) +b10001110 -) +b10001110 5) +b10001110 =) +b10001110 E) +b10001110 M) +b10001110 U) +b10001110 ]) +b10001110 e) +b10001110 m) +b10001110 u) +b10001110 }) +b10001110 '* +b10001110 /* +b10001110 7* +b10001110 ?* +b10001110 G* +b10001110 O* +b10001110 W* +b10001110 _* +b10001110 g* +b10001110 o* +b10001110 w* +b10001110 !+ +b10001110 )+ +b10001110 1+ +b10001110 9+ +b10001110 A+ +b10001110 I+ +b10001110 Q+ +b10001110 Y+ +b10001110 a+ +b10001110 i+ +b10001110 q+ +b10001110 y+ +b10001110 #, +b10001110 +, +b10001110 3, +b10001110 ;, +b10001110 C, +b10001110 K, +b00000000000000000000000010001110 S, +b00000000000000000000000010001110 [, +b00000000000000000000000010001110 c, +b10001110 k, +b10001110100011101000111010001110 s, +b10001110 {, +b10001110100011101000111010001110 %- +b10001110 -- +b10001110100011101000111010001110 5- +b10001110 =- +b10001110100011101000111010001110 E- +b10001110 M- +b10001110 N- +b10001110 O- +b10001110 P- +b10001110 Q- +b10001110 R- +b10001110 S- +b10001110 T- +b10001110 U- +b10001110 7. +b10001110 ?. +b10001110 G. +b10001110 O. +b10001110 W. +b10001110 _. +b10001110 g. +b10001110 o. +b10001110 w. +b10001110 !/ +b10001110 )/ +b10001110 1/ +b10001110 9/ +b10001110 A/ +b10001110 I/ +b10001110 Q/ +b10001110 Y/ +b10001110 a/ +b10001110 i/ +b10001110 q/ +b10001110 y/ +b10001110 #0 +b10001110 +0 +b10001110 30 +b10001110 ;0 +b10001110 C0 +1K0 +0S0 +0[0 +0c0 +1k0 +1s0 +1{0 +0%1 +0-1 +1.1 +1/1 +101 +011 +021 +031 +141 +0m1 +1n1 +1o1 +1p1 +0q1 +0r1 +0s1 +1t1 +0O2 +b00000000000000000000000000000000 W2 +0_2 +0g2 +0o2 +b00000000000000000000000000000011 w2 +b00000000000000000000000000001000 !3 +b00000000000000000000000000001001 )3 +b10001110 13 +b1000111001000111101000111101000111101000011101000011101000011101 93 +b11111111111111111111111111111101 I3 +b00000000000000000000000000000001 Q3 +b00000000 Y3 +b00000000 a3 +b00000000 i3 +b00000000 q3 +b00000000 y3 +b00000000 #4 +b00000000 +4 +b00000000 34 +b00000000 ;4 +b00000000 C4 +b00000000 K4 +b00000000 S4 +b00000000 [4 +b00000000 c4 +b00000000 k4 +b00000000 s4 +b00000000 {4 +b00000000 %5 +b00000000 -5 +b00000000 55 +b00000000 =5 +b00000000 E5 +b00000000 M5 +b00000000 U5 +b00000000000000000000000000000010 ]5 +b00000000000000000000000010001110 e5 +b000000000000000000000000 m5 +b11111111111111111111111111111110 u5 +b00000000000000000000000000000000 }5 +#10 +b001 s +b01000111 { +b01000111 %! +b01000111 -! +b01000111 5! +b01000111 =! +b01000111 E! +b01000111 M! +b01000111 U! +b01000111 ]! +b01000111 e! +b01000111 m! +b01000111 u! +b01000111 }! +b01000111 '" +b01000111 /" +b01000111 O" +b01000111 W" +b01000111 _" +b01000111 g" +b01000111 o" +b01000111 w" +b01000111 !# +b01000111 )# +b01000111 1# +b01000111 Q# +b01000111 Y# +b01000111 a# +b01000111 i# +b01000111 q# +b01000111 y# +b01000111 #$ +b01000111 +$ +b01000111 3$ +b01000111 S$ +b01000111 [$ +b01000111 c$ +b01000111 k$ +b01000111 s$ +b01000111 {$ +b01000111 %% +b01000111 -% +b01000111 5% +b01000111 U% +b01000111 ]% +b01000111 e% +b01000111 m% +b01000111 u% +b01000111 }% +b01000111 '& +b01000111 /& +b01000111 7& +b01000111 W& +b01000111 _& +b01000111 g& +b01000111 o& +b01000111 w& +b01000111 !' +b01000111 )' +b01000111 1' +b01000111 9' +b01000111 Y' +b01000111 a' +b01000111 i' +b01000111 q' +b01000111 y' +b01000111 #( +b01000111 +( +b01000111 3( +b01000111 ;( +b01000111 [( +b01000111 c( +b01000111 k( +b01000111 s( +b01000111 {( +b01000111 %) +b01000111 -) +b01000111 5) +b01000111 =) +b01000111 ]) +b01000111 e) +b01000111 m) +b01000111 u) +b01000111 }) +b01000111 '* +b01000111 /* +b01000111 7* +b01000111 ?* +b01000111 _* +b01000111 g* +b01000111 o* +b01000111 w* +b01000111 !+ +b01000111 )+ +b01000111 1+ +b01000111 9+ +b01000111 A+ +b01000111 a+ +b01000111 i+ +b01000111 q+ +b01000111 y+ +b01000111 #, +b01000111 +, +b01000111 3, +b01000111 ;, +b01000111 C, +b01000111 K, +b00000000000000000000000001000111 S, +b00000000000000000000000001000111 [, +b00000000000000000000000001000111 c, +b01000111 k, +b10001110010001110100011101000111 s, +b01000111 {, +b10001110010001110100011101000111 %- +b01000111 -- +b10001110010001110100011101000111 5- +b01000111 =- +b10001110010001110100011101000111 E- +b01000111 M- +b01000111 N- +b01000111 O- +b01000111 P- +b01000111 Q- +b01000111 R- +b01000111 S- +b01000111 T- +b01000111 U- +b01000111 7. +b01000111 ?. +b01000111 G. +b01000111 g. +b01000111 o. +b01000111 w. +b01000111 !/ +b01000111 )/ +b01000111 1/ +b01000111 9/ +b01000111 A/ +b01000111 a/ +b01000111 i/ +b01000111 q/ +b01000111 y/ +b01000111 #0 +b01000111 +0 +b01000111 30 +b01000111 ;0 +b01000111 C0 +0K0 +1S0 +0k0 +1%1 +1-1 +001 +131 +041 +1m1 +0p1 +1s1 +0t1 +b00000000000000000000000000001001 W2 +1g2 +1o2 +#15 +0o2 +#20 +b010 s +b10100011 { +b10001110 %! +b10100011 -! +b10100011 5! +b10100011 =! +b10100011 E! +b10100011 M! +b10001110 U! +b10100011 ]! +b10100011 e! +b10100011 m! +b10100011 u! +b10100011 }! +b10100011 '" +b10100011 /" +b10001110 O" +b10001110 W" +b10001110 _" +b10100011 g" +b10100011 o" +b10100011 w" +b10100011 !# +b10100011 )# +b10100011 1# +b10001110 Q# +b10001110 Y# +b10001110 a# +b10100011 i# +b10100011 q# +b10100011 y# +b10100011 #$ +b10100011 +$ +b10100011 3$ +b10001110 S$ +b10001110 [$ +b10001110 c$ +b10100011 k$ +b10100011 s$ +b10100011 {$ +b10100011 %% +b10100011 -% +b10100011 5% +b10001110 U% +b10001110 ]% +b10001110 e% +b10100011 m% +b10100011 u% +b10100011 }% +b10100011 '& +b10100011 /& +b10100011 7& +b10001110 W& +b10001110 _& +b10001110 g& +b10100011 o& +b10100011 w& +b10100011 !' +b10100011 )' +b10100011 1' +b10100011 9' +b10001110 Y' +b10001110 a' +b10001110 i' +b10100011 q' +b10100011 y' +b10100011 #( +b10100011 +( +b10100011 3( +b10100011 ;( +b10001110 [( +b10001110 c( +b10001110 k( +b10100011 s( +b10100011 {( +b10100011 %) +b10100011 -) +b10100011 5) +b10100011 =) +b10001110 ]) +b10001110 e) +b10001110 m) +b10100011 u) +b10100011 }) +b10100011 '* +b10100011 /* +b10100011 7* +b10100011 ?* +b10001110 _* +b10001110 g* +b10001110 o* +b10100011 w* +b10100011 !+ +b10100011 )+ +b10100011 1+ +b10100011 9+ +b10100011 A+ +b10001110 a+ +b10001110 i+ +b10001110 q+ +b10100011 y+ +b10100011 #, +b10100011 +, +b10100011 3, +b10100011 ;, +b10100011 C, +b10100011 K, +b00000000000000000000000010001110 S, +b00000000000000000000000010100011 [, +b00000000000000000000000010100011 c, +b10100011 k, +b10001110100011101010001110100011 s, +b10100011 {, +b10001110100011101010001110100011 %- +b10100011 -- +b10001110100011101010001110100011 5- +b10100011 =- +b10001110100011101010001110100011 E- +b10100011 M- +b10100011 N- +b10100011 O- +b10100011 P- +b10100011 Q- +b10100011 R- +b10100011 S- +b10100011 T- +b10100011 U- +b10100011 7. +b10100011 ?. +b10100011 G. +b10001110 g. +b10001110 o. +b10001110 w. +b10100011 !/ +b10100011 )/ +b10100011 1/ +b10100011 9/ +b10100011 A/ +b10001110 a/ +b10001110 i/ +b10001110 q/ +b10100011 y/ +b10100011 #0 +b10100011 +0 +b10100011 30 +b10100011 ;0 +b10100011 C0 +1K0 +0S0 +1[0 +0s0 +0/1 +121 +031 +141 +0o1 +1r1 +0s1 +1t1 +1o2 +#25 +0o2 +#30 +b011 s +b11010001 { +b01000111 %! +b11010001 -! +b11010001 5! +b11010001 =! +b11010001 E! +b11010001 M! +b01000111 U! +b11010001 ]! +b11010001 e! +b11010001 m! +b11010001 u! +b11010001 }! +b11010001 '" +b11010001 /" +b01000111 O" +b01000111 W" +b01000111 _" +b11010001 g" +b11010001 o" +b11010001 w" +b11010001 !# +b11010001 )# +b11010001 1# +b01000111 Q# +b01000111 Y# +b01000111 a# +b11010001 i# +b11010001 q# +b11010001 y# +b11010001 #$ +b11010001 +$ +b11010001 3$ +b01000111 S$ +b01000111 [$ +b01000111 c$ +b11010001 k$ +b11010001 s$ +b11010001 {$ +b11010001 %% +b11010001 -% +b11010001 5% +b01000111 U% +b01000111 ]% +b01000111 e% +b11010001 m% +b11010001 u% +b11010001 }% +b11010001 '& +b11010001 /& +b11010001 7& +b01000111 W& +b01000111 _& +b01000111 g& +b11010001 o& +b11010001 w& +b11010001 !' +b11010001 )' +b11010001 1' +b11010001 9' +b01000111 Y' +b01000111 a' +b01000111 i' +b11010001 q' +b11010001 y' +b11010001 #( +b11010001 +( +b11010001 3( +b11010001 ;( +b01000111 [( +b01000111 c( +b01000111 k( +b11010001 s( +b11010001 {( +b11010001 %) +b11010001 -) +b11010001 5) +b11010001 =) +b01000111 ]) +b01000111 e) +b01000111 m) +b11010001 u) +b11010001 }) +b11010001 '* +b11010001 /* +b11010001 7* +b11010001 ?* +b01000111 _* +b01000111 g* +b01000111 o* +b11010001 w* +b11010001 !+ +b11010001 )+ +b11010001 1+ +b11010001 9+ +b11010001 A+ +b01000111 a+ +b01000111 i+ +b01000111 q+ +b11010001 y+ +b11010001 #, +b11010001 +, +b11010001 3, +b11010001 ;, +b11010001 C, +b11010001 K, +b00000000000000000000000001000111 S, +b00000000000000000000000011010001 [, +b00000000000000000000000011010001 c, +b11010001 k, +b10001110010001111101000111010001 s, +b11010001 {, +b10001110010001111101000111010001 %- +b11010001 -- +b10001110010001111101000111010001 5- +b11010001 =- +b10001110010001111101000111010001 E- +b11010001 M- +b11010001 N- +b11010001 O- +b11010001 P- +b11010001 Q- +b11010001 R- +b11010001 S- +b11010001 T- +b11010001 U- +b11010001 7. +b11010001 ?. +b11010001 G. +b01000111 g. +b01000111 o. +b01000111 w. +b11010001 !/ +b11010001 )/ +b11010001 1/ +b11010001 9/ +b11010001 A/ +b01000111 a/ +b01000111 i/ +b01000111 q/ +b11010001 y/ +b11010001 #0 +b11010001 +0 +b11010001 30 +b11010001 ;0 +b11010001 C0 +1S0 +0[0 +1c0 +0{0 +0.1 +111 +021 +131 +0n1 +1q1 +0r1 +1s1 +1o2 +#35 +0o2 +#40 +b100 s +b11101000 { +b10001110 %! +b10001110 -! +b11101000 5! +b11101000 =! +b10001110 E! +b11101000 M! +b10001110 U! +b10001110 ]! +b10001110 e! +b10001110 m! +b10001110 u! +b11101000 }! +b11101000 '" +b11101000 /" +b10001110 O" +b10001110 W" +b10001110 _" +b10001110 g" +b10001110 o" +b10001110 w" +b11101000 !# +b11101000 )# +b11101000 1# +b10001110 Q# +b10001110 Y# +b10001110 a# +b10001110 i# +b10001110 q# +b10001110 y# +b11101000 #$ +b11101000 +$ +b11101000 3$ +b10001110 S$ +b10001110 [$ +b10001110 c$ +b10001110 k$ +b10001110 s$ +b10001110 {$ +b11101000 %% +b11101000 -% +b11101000 5% +b10001110 U% +b10001110 ]% +b10001110 e% +b10001110 m% +b10001110 u% +b10001110 }% +b11101000 '& +b11101000 /& +b11101000 7& +b10001110 W& +b10001110 _& +b10001110 g& +b10001110 o& +b10001110 w& +b10001110 !' +b11101000 )' +b11101000 1' +b11101000 9' +b10001110 Y' +b10001110 a' +b10001110 i' +b10001110 q' +b10001110 y' +b10001110 #( +b11101000 +( +b11101000 3( +b11101000 ;( +b10001110 [( +b10001110 c( +b10001110 k( +b10001110 s( +b10001110 {( +b10001110 %) +b11101000 -) +b11101000 5) +b11101000 =) +b10001110 ]) +b10001110 e) +b10001110 m) +b10001110 u) +b10001110 }) +b10001110 '* +b11101000 /* +b11101000 7* +b11101000 ?* +b10001110 _* +b10001110 g* +b10001110 o* +b10001110 w* +b10001110 !+ +b10001110 )+ +b11101000 1+ +b11101000 9+ +b11101000 A+ +b10001110 a+ +b10001110 i+ +b10001110 q+ +b10001110 y+ +b10001110 #, +b10001110 +, +b11101000 3, +b11101000 ;, +b11101000 C, +b11101000 K, +b00000000000000000000000010001110 S, +b00000000000000000000000010001110 [, +b00000000000000000000000011101000 c, +b11101000 k, +b10001110100011101000111011101000 s, +b11101000 {, +b10001110100011101000111011101000 %- +b11101000 -- +b10001110100011101000111011101000 5- +b11101000 =- +b10001110100011101000111011101000 E- +b11101000 M- +b11101000 N- +b11101000 O- +b11101000 P- +b11101000 Q- +b11101000 R- +b11101000 S- +b11101000 T- +b11101000 U- +b11101000 7. +b11101000 ?. +b11101000 G. +b10001110 g. +b10001110 o. +b10001110 w. +b10001110 !/ +b10001110 )/ +b10001110 1/ +b11101000 9/ +b11101000 A/ +b10001110 a/ +b10001110 i/ +b10001110 q/ +b10001110 y/ +b10001110 #0 +b10001110 +0 +b11101000 30 +b11101000 ;0 +b11101000 C0 +1[0 +0c0 +1k0 +0%1 +0-1 +101 +011 +121 +0m1 +1p1 +0q1 +1r1 +1o2 +#45 +0o2 +#50 +b101 s +b01110100 { +b01000111 %! +b01000111 -! +b01110100 5! +b01110100 =! +b01000111 E! +b01110100 M! +b01000111 U! +b01000111 ]! +b01000111 e! +b01000111 m! +b01000111 u! +b01110100 }! +b01110100 '" +b01110100 /" +b01000111 O" +b01000111 W" +b01000111 _" +b01000111 g" +b01000111 o" +b01000111 w" +b01110100 !# +b01110100 )# +b01110100 1# +b01000111 Q# +b01000111 Y# +b01000111 a# +b01000111 i# +b01000111 q# +b01000111 y# +b01110100 #$ +b01110100 +$ +b01110100 3$ +b01000111 S$ +b01000111 [$ +b01000111 c$ +b01000111 k$ +b01000111 s$ +b01000111 {$ +b01110100 %% +b01110100 -% +b01110100 5% +b01000111 U% +b01000111 ]% +b01000111 e% +b01000111 m% +b01000111 u% +b01000111 }% +b01110100 '& +b01110100 /& +b01110100 7& +b01000111 W& +b01000111 _& +b01000111 g& +b01000111 o& +b01000111 w& +b01000111 !' +b01110100 )' +b01110100 1' +b01110100 9' +b01000111 Y' +b01000111 a' +b01000111 i' +b01000111 q' +b01000111 y' +b01000111 #( +b01110100 +( +b01110100 3( +b01110100 ;( +b01000111 [( +b01000111 c( +b01000111 k( +b01000111 s( +b01000111 {( +b01000111 %) +b01110100 -) +b01110100 5) +b01110100 =) +b01000111 ]) +b01000111 e) +b01000111 m) +b01000111 u) +b01000111 }) +b01000111 '* +b01110100 /* +b01110100 7* +b01110100 ?* +b01000111 _* +b01000111 g* +b01000111 o* +b01000111 w* +b01000111 !+ +b01000111 )+ +b01110100 1+ +b01110100 9+ +b01110100 A+ +b01000111 a+ +b01000111 i+ +b01000111 q+ +b01000111 y+ +b01000111 #, +b01000111 +, +b01110100 3, +b01110100 ;, +b01110100 C, +b01110100 K, +b00000000000000000000000001000111 S, +b00000000000000000000000001000111 [, +b00000000000000000000000001110100 c, +b01110100 k, +b10001110010001110100011101110100 s, +b01110100 {, +b10001110010001110100011101110100 %- +b01110100 -- +b10001110010001110100011101110100 5- +b01110100 =- +b10001110010001110100011101110100 E- +b01110100 M- +b01110100 N- +b01110100 O- +b01110100 P- +b01110100 Q- +b01110100 R- +b01110100 S- +b01110100 T- +b01110100 U- +b01110100 7. +b01110100 ?. +b01110100 G. +b01000111 g. +b01000111 o. +b01000111 w. +b01000111 !/ +b01000111 )/ +b01000111 1/ +b01110100 9/ +b01110100 A/ +b01000111 a/ +b01000111 i/ +b01000111 q/ +b01000111 y/ +b01000111 #0 +b01000111 +0 +b01110100 30 +b01110100 ;0 +b01110100 C0 +0K0 +1c0 +0k0 +1s0 +1/1 +001 +111 +041 +1o1 +0p1 +1q1 +0t1 +1o2 +#55 +0o2 +#60 +b110 s +b00111010 { +b10001110 %! +b10100011 -! +b00111010 5! +b00111010 =! +b10100011 E! +b00111010 M! +b10001110 U! +b10100011 ]! +b10100011 e! +b10100011 m! +b10100011 u! +b00111010 }! +b00111010 '" +b00111010 /" +b10001110 O" +b10001110 W" +b10001110 _" +b10100011 g" +b10100011 o" +b10100011 w" +b00111010 !# +b00111010 )# +b00111010 1# +b10001110 Q# +b10001110 Y# +b10001110 a# +b10100011 i# +b10100011 q# +b10100011 y# +b00111010 #$ +b00111010 +$ +b00111010 3$ +b10001110 S$ +b10001110 [$ +b10001110 c$ +b10100011 k$ +b10100011 s$ +b10100011 {$ +b00111010 %% +b00111010 -% +b00111010 5% +b10001110 U% +b10001110 ]% +b10001110 e% +b10100011 m% +b10100011 u% +b10100011 }% +b00111010 '& +b00111010 /& +b00111010 7& +b10001110 W& +b10001110 _& +b10001110 g& +b10100011 o& +b10100011 w& +b10100011 !' +b00111010 )' +b00111010 1' +b00111010 9' +b10001110 Y' +b10001110 a' +b10001110 i' +b10100011 q' +b10100011 y' +b10100011 #( +b00111010 +( +b00111010 3( +b00111010 ;( +b10001110 [( +b10001110 c( +b10001110 k( +b10100011 s( +b10100011 {( +b10100011 %) +b00111010 -) +b00111010 5) +b00111010 =) +b10001110 ]) +b10001110 e) +b10001110 m) +b10100011 u) +b10100011 }) +b10100011 '* +b00111010 /* +b00111010 7* +b00111010 ?* +b10001110 _* +b10001110 g* +b10001110 o* +b10100011 w* +b10100011 !+ +b10100011 )+ +b00111010 1+ +b00111010 9+ +b00111010 A+ +b10001110 a+ +b10001110 i+ +b10001110 q+ +b10100011 y+ +b10100011 #, +b10100011 +, +b00111010 3, +b00111010 ;, +b00111010 C, +b00111010 K, +b00000000000000000000000010001110 S, +b00000000000000000000000010100011 [, +b00000000000000000000000000111010 c, +b00111010 k, +b10001110100011101010001100111010 s, +b00111010 {, +b10001110100011101010001100111010 %- +b00111010 -- +b10001110100011101010001100111010 5- +b00111010 =- +b10001110100011101010001100111010 E- +b00111010 M- +b00111010 N- +b00111010 O- +b00111010 P- +b00111010 Q- +b00111010 R- +b00111010 S- +b00111010 T- +b00111010 U- +b00111010 7. +b00111010 ?. +b00111010 G. +b10001110 g. +b10001110 o. +b10001110 w. +b10100011 !/ +b10100011 )/ +b10100011 1/ +b00111010 9/ +b00111010 A/ +b10001110 a/ +b10001110 i/ +b10001110 q/ +b10100011 y/ +b10100011 #0 +b10100011 +0 +b00111010 30 +b00111010 ;0 +b00111010 C0 +0S0 +1k0 +0s0 +1{0 +1.1 +0/1 +101 +031 +1n1 +0o1 +1p1 +0s1 +1o2 +#65 +0o2 +#70 +b111 s +b00011101 { +b01000111 %! +b11010001 -! +b00011101 5! +b00011101 =! +b11010001 E! +b00011101 M! +b01000111 U! +b11010001 ]! +b11010001 e! +b11010001 m! +b11010001 u! +b00011101 }! +b00011101 '" +b00011101 /" +b01000111 O" +b01000111 W" +b01000111 _" +b11010001 g" +b11010001 o" +b11010001 w" +b00011101 !# +b00011101 )# +b00011101 1# +b01000111 Q# +b01000111 Y# +b01000111 a# +b11010001 i# +b11010001 q# +b11010001 y# +b00011101 #$ +b00011101 +$ +b00011101 3$ +b01000111 S$ +b01000111 [$ +b01000111 c$ +b11010001 k$ +b11010001 s$ +b11010001 {$ +b00011101 %% +b00011101 -% +b00011101 5% +b01000111 U% +b01000111 ]% +b01000111 e% +b11010001 m% +b11010001 u% +b11010001 }% +b00011101 '& +b00011101 /& +b00011101 7& +b01000111 W& +b01000111 _& +b01000111 g& +b11010001 o& +b11010001 w& +b11010001 !' +b00011101 )' +b00011101 1' +b00011101 9' +b01000111 Y' +b01000111 a' +b01000111 i' +b11010001 q' +b11010001 y' +b11010001 #( +b00011101 +( +b00011101 3( +b00011101 ;( +b01000111 [( +b01000111 c( +b01000111 k( +b11010001 s( +b11010001 {( +b11010001 %) +b00011101 -) +b00011101 5) +b00011101 =) +b01000111 ]) +b01000111 e) +b01000111 m) +b11010001 u) +b11010001 }) +b11010001 '* +b00011101 /* +b00011101 7* +b00011101 ?* +b01000111 _* +b01000111 g* +b01000111 o* +b11010001 w* +b11010001 !+ +b11010001 )+ +b00011101 1+ +b00011101 9+ +b00011101 A+ +b01000111 a+ +b01000111 i+ +b01000111 q+ +b11010001 y+ +b11010001 #, +b11010001 +, +b00011101 3, +b00011101 ;, +b00011101 C, +b00011101 K, +b00000000000000000000000001000111 S, +b00000000000000000000000011010001 [, +b00000000000000000000000000011101 c, +b00011101 k, +b10001110010001111101000100011101 s, +b00011101 {, +b10001110010001111101000100011101 %- +b00011101 -- +b10001110010001111101000100011101 5- +b00011101 =- +b10001110010001111101000100011101 E- +b00011101 M- +b00011101 N- +b00011101 O- +b00011101 P- +b00011101 Q- +b00011101 R- +b00011101 S- +b00011101 T- +b00011101 U- +b00011101 7. +b00011101 ?. +b00011101 G. +b01000111 g. +b01000111 o. +b01000111 w. +b11010001 !/ +b11010001 )/ +b11010001 1/ +b00011101 9/ +b00011101 A/ +b01000111 a/ +b01000111 i/ +b01000111 q/ +b11010001 y/ +b11010001 #0 +b11010001 +0 +b00011101 30 +b00011101 ;0 +b00011101 C0 +0[0 +1s0 +0{0 +1%1 +1-1 +0.1 +1/1 +021 +1m1 +0n1 +1o1 +0r1 +1o2 +#75 +0o2 +#80 +b000 s +b10001110 { +b10001110 %! +b10001110 -! +b10001110 5! +b10001110 =! +b10001110 E! +b10001110 M! +b10001110 U! +b10001110 ]! +b10001110 e! +b10001110 m! +b10001110 u! +b10001110 }! +b10001110 '" +b10001110 /" +b10001110 O" +b10001110 W" +b10001110 _" +b10001110 g" +b10001110 o" +b10001110 w" +b10001110 !# +b10001110 )# +b10001110 1# +b10001110 Q# +b10001110 Y# +b10001110 a# +b10001110 i# +b10001110 q# +b10001110 y# +b10001110 #$ +b10001110 +$ +b10001110 3$ +b10001110 S$ +b10001110 [$ +b10001110 c$ +b10001110 k$ +b10001110 s$ +b10001110 {$ +b10001110 %% +b10001110 -% +b10001110 5% +b10001110 U% +b10001110 ]% +b10001110 e% +b10001110 m% +b10001110 u% +b10001110 }% +b10001110 '& +b10001110 /& +b10001110 7& +b10001110 W& +b10001110 _& +b10001110 g& +b10001110 o& +b10001110 w& +b10001110 !' +b10001110 )' +b10001110 1' +b10001110 9' +b10001110 Y' +b10001110 a' +b10001110 i' +b10001110 q' +b10001110 y' +b10001110 #( +b10001110 +( +b10001110 3( +b10001110 ;( +b10001110 [( +b10001110 c( +b10001110 k( +b10001110 s( +b10001110 {( +b10001110 %) +b10001110 -) +b10001110 5) +b10001110 =) +b10001110 ]) +b10001110 e) +b10001110 m) +b10001110 u) +b10001110 }) +b10001110 '* +b10001110 /* +b10001110 7* +b10001110 ?* +b10001110 _* +b10001110 g* +b10001110 o* +b10001110 w* +b10001110 !+ +b10001110 )+ +b10001110 1+ +b10001110 9+ +b10001110 A+ +b10001110 a+ +b10001110 i+ +b10001110 q+ +b10001110 y+ +b10001110 #, +b10001110 +, +b10001110 3, +b10001110 ;, +b10001110 C, +b10001110 K, +b00000000000000000000000010001110 S, +b00000000000000000000000010001110 [, +b00000000000000000000000010001110 c, +b10001110 k, +b10001110100011101000111010001110 s, +b10001110 {, +b10001110100011101000111010001110 %- +b10001110 -- +b10001110100011101000111010001110 5- +b10001110 =- +b10001110100011101000111010001110 E- +b10001110 M- +b10001110 N- +b10001110 O- +b10001110 P- +b10001110 Q- +b10001110 R- +b10001110 S- +b10001110 T- +b10001110 U- +b10001110 7. +b10001110 ?. +b10001110 G. +b10001110 g. +b10001110 o. +b10001110 w. +b10001110 !/ +b10001110 )/ +b10001110 1/ +b10001110 9/ +b10001110 A/ +b10001110 a/ +b10001110 i/ +b10001110 q/ +b10001110 y/ +b10001110 #0 +b10001110 +0 +b10001110 30 +b10001110 ;0 +b10001110 C0 +1K0 +0c0 +1{0 +0%1 +0-1 +1.1 +011 +141 +0m1 +1n1 +0q1 +1t1 +1o2 diff --git a/test_regress/t/t_split_var_2_trace.pl b/test_regress/t/t_split_var_2_trace.pl new file mode 100755 index 000000000..64a77d139 --- /dev/null +++ b/test_regress/t/t_split_var_2_trace.pl @@ -0,0 +1,29 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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. + +scenarios(simulator => 1); +top_filename("t/t_split_var_0.v"); + +# Travis environment offers 2 VCPUs, 2 thread setting causes the following warning. +# %Warning-UNOPTTHREADS: Thread scheduler is unable to provide requested parallelism; consider asking for fewer threads. +# So use 6 threads here though it's not optimal in performace wise, but ok. +compile( + verilator_flags2 => ['--cc --trace --stats' . ($Self->{vltmt} ? ' --threads 6' : '')], + ); + +execute( + check_finished => 1, + ); + +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); +file_grep($Self->{stats}, qr/SplitVar,\s+Split packed variables\s+(\d+)/i, 12); +file_grep($Self->{stats}, qr/SplitVar,\s+Split unpacked arrays\s+(\d+)/i, 23); + +ok(1); +1; diff --git a/test_regress/t/t_unoptflat_simple_2_bad.out b/test_regress/t/t_unoptflat_simple_2_bad.out index f926a30e1..ff0be7f54 100644 --- a/test_regress/t/t_unoptflat_simple_2_bad.out +++ b/test_regress/t/t_unoptflat_simple_2_bad.out @@ -5,8 +5,9 @@ t/t_unoptflat_simple_2.v:14: Example path: t.x t/t_unoptflat_simple_2.v:16: Example path: ASSIGNW t/t_unoptflat_simple_2.v:14: Example path: t.x -%Warning-UNOPTFLAT: Widest candidate vars to split: -%Warning-UNOPTFLAT: t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10 -%Warning-UNOPTFLAT: Most fanned out candidate vars to split: -%Warning-UNOPTFLAT: t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10 + ... Widest candidate vars to split: + t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10, can split_var + ... Most fanned out candidate vars to split: + t/t_unoptflat_simple_2.v:14: t.x, width 3, fanout 10, can split_var + ... Suggest add /*verilator split_var*/ to appropriate variables above. %Error: Exiting due to