diff --git a/Changes b/Changes index 61b2a28a2..0036913df 100644 --- a/Changes +++ b/Changes @@ -5,8 +5,15 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.840 devel +** Rewrote tristate handling; supports tri0, tri1, tristate bit selects, + concatenates and pullup/pulldowns, bug395, bug56, bug54, bug51. + [Alex Solomatnikov, Lane Brooks, et al] + +*** Support tri0 and tri1, bug462. [Alex Solomatnikov] + *** Fix generate operators not short circuiting, bug413. [by Jeremy Bennett] + * Verilator 3.833 2012/04/15 *** Support += and -= in standard for loops, bug463. [Alex Solomatnikov] diff --git a/bin/verilator b/bin/verilator index 4c530f38c..9ac97cc2f 100755 --- a/bin/verilator +++ b/bin/verilator @@ -2212,11 +2212,11 @@ optimizations will be disabled around the latch. All delays (#) are ignored, as they are in synthesis. -=head2 Two State +=head2 Unknown states -Verilator is a two state simulator, not a four state simulator. However, it -has two features which uncover most initialization bugs (including many that -a four state simulator will miss.) +Verilator is mostly a two state simulator, not a four state simulator. +However, it has two features which uncover most initialization bugs +(including many that a four state simulator will miss.) Identity comparisons (=== or !==) are converted to standard ==/!== when neither side is a constant. This may make the expression result differ @@ -2238,19 +2238,21 @@ practice, just setting all variables to one at startup finds most problems. =head2 Tri/Inout -Verilator converts some simple tristate structures into two state. An assignment -of the form: +Verilator converts some simple tristate structures into two state. Pullup, +pulldown, bufif0, bufif1, notif0, notif1, tri0 and tri1 are also supported. +Simple comparisons with === 1'bz are also supported. + +An assignment of the form: inout driver; wire driver = (enable) ? output_value : 1'bz; Will be converted to - input driver__in; // Value being driven in from "external" drivers - output driver__en; // True if driven from this module - output driver__enout; // Value being driven from this module + input driver; // Value being driven in from "external" drivers + output driver__en; // True if driven from this module + output driver__out; // Value being driven from this module -Pullup, pulldown, bufif0, bufif1, notif0, notif1 are also supported. External logic will be needed to combine these signals with any external drivers. diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 3f310c060..7a6b49301 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -21,28 +21,19 @@ //************************************************************************* // V3Tristate's Transformations: // -// Tristate transformations are accomplished via three operations on the tree: -// -// 1. Find 'hZ constructs and change the logic to a two state output. -// This requires creating an __en signal for the driver resolution -// logic to use. This function is *not* generic right now in that -// it can transform any logic structure. It is currently highly -// specialize to only work on assign structures where the 'hZ -// assignment is in the first level. More work needs done to make -// this work with any logic structure. -// -// 2. While walking the tree looking for 'hZ constructs, also detect -// any nets that have multiple LHS (left-hand side) drivers. These -// are the locations where driver resolution will occur. -// -// 3. Finally, make a pass through the cell pin assignments to push -// the __en pins up the hierarchy until they are finally terminated -// at the driver resolution stage. -// -// Inouts are treated differently than tristates, though they are certainly -// related. Inouts are expanded prior to tristate expansion. The inout -// expansion takes each inout port and creates an new input with a name suffix -// of __in and transforms the original port to an output port. +// This module modifies the design to expand tristate logic into its +// corresponding two state reprasentation. At the lowest levels, +// expressions that have Z in them are converted into two state +// drivers and corresponding output enable signals are generated. +// These enable signals get transformed and regenerated through any +// logic that they may go through until they hit the module level. At +// the module level, all the output enable signals from what can be +// many tristate drivers are combined together to produce a single +// driver and output enable. If the signal propigates up into higher +// modules, then new ports are created with for the signal with +// suffixes __en and __out. The original port is turned from an inout +// to an input and the __out port carries the output driver signal and +// the __en port carried the output enable for that driver. // //************************************************************************* @@ -57,19 +48,16 @@ #include "V3Global.h" #include "V3Tristate.h" #include "V3Ast.h" +#include "V3Const.h" #include "V3Stats.h" #include "V3Inst.h" - -//###################################################################### - -typedef std::vector VarVec; -typedef std::vector RefVec; -typedef std::map VarMap; +#include "V3Stats.h" //###################################################################### class TristateBaseVisitor : public AstNVisitor { public: + // METHODS static int debug() { static int level = -1; if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__); @@ -78,664 +66,752 @@ public: }; //###################################################################### +// Given a node, flip any VarRef from LValue to RValue (i.e. make it an input) -class TristateExpander : public TristateBaseVisitor { - // Finds all tristate logic and creates an __en signal and removes Z's. - // Only supports limited assign statements initially. - // Also detects multiple lhs var drivers. - -private: - // NODE STATE - // Cleared on Netlist - // AstVarRef::user1p -> used to track any newly create __en signals - // AstVarRef::user2 -> already visited - // AstVarRef::user3p -> a parent SEL - // AstVar::user1p -> used to track any newly create __en signals - // AstVar::user2 -> used for pullup/pulldown direction - - // STATE - int m_unique; - AstNodeModule* m_modp; // Current module - VarMap* m_lhsmapp; // LHS driver map - AstSel* m_sel; - - virtual void visit(AstSel* nodep, AstNUser*) { - m_sel=nodep; - nodep->iterateChildren(*this); - m_sel=NULL; - } - - //******************************************************************* - // The following visitor functions deal with detecting Z's in the - // logic, stripping the Z's out and creating an __en signal and its - // logic. - //******************************************************************* - virtual void visit(AstPull* nodep, AstNUser*) { - // replace any pullup/pulldowns with assignw logic and an __en - // signal just like it is any other tristate signal. The only - // difference is that the user2() variable on the __en signal - // will be given a pull direction--i.e. pulldown=1, pullup=2. - // This will signal the driver exansion logic to put a default - // pullup or pulldown state on the tristate bus under the high-Z - // condition when no one is driving the bus. Given the complexity - // of merging tristate drivers at any level, the current limitation - // of this implementation is that a pullup/down gets applied - // to all bits of a bus and a bus cannot have drivers in opposite - // directions on indvidual pins. - AstNode* outp = nodep->lhsp()->unlinkFrBack();; - AstVarRef* outrefp = NULL; - int width=-1; - if (outp->castVarRef()) { - outrefp = outp->castVarRef(); - } else if (outp->castSel()) { - outrefp = outp->castSel()->fromp()->castVarRef(); - width = outp->castSel()->widthConst(); - } else { - nodep->v3error("Can't find LHS varref"); - } - outrefp->lvalue(true); - AstVar* varp = outrefp->varp(); - if (width==-1) width=varp->width(); - - V3Number num0 (nodep->fileline(), width); - num0.setAllBits0(); - V3Number num1 (nodep->fileline(), width); - num1.setAllBits1(); - - AstConst* enrhsp = new AstConst(nodep->fileline(), num0); - AstVar* enp = createEnableVar(outp, outrefp, enrhsp, width, "pull"); - enp->user2(nodep->direction()+1); // record the pull direction - - AstAssignW* newassp = new AstAssignW(nodep->fileline(), outp, - new AstConst(nodep->fileline(), nodep->direction() ? num1 : num0)); - nodep->replaceWith(newassp); - nodep->deleteTree(); nodep=NULL; - newassp->iterateChildren(*this); - } - - virtual void visit(AstAssignW* nodep, AstNUser*) { - // Note: this detects and expands tristates of the forms: - // assign x = (OE) ? y : 'hZ; - // assign x = (OE) ? 'hz : y; - - // see if this a COND and separate out the __en logic from the output logic if it is - if (AstCond* condp = nodep->rhsp()->castCond()) { - //if (debug()>=9) nodep->dumpTree(cout,"- cond-in: "); - AstNode* oep = condp->condp(); - AstNode* expr1p = condp->expr1p(); - AstNode* expr2p = condp->expr2p(); - AstNode* enrhsp; - AstNode* outrhsp; - - if (expr1p->castConst() && expr1p->castConst()->num().isAllZ()) { - enrhsp = new AstNot(oep->fileline(), oep->unlinkFrBack()); - outrhsp = expr2p->unlinkFrBack(); - } else if (expr2p->castConst() && expr2p->castConst()->num().isAllZ()){ - enrhsp = oep->unlinkFrBack(); - outrhsp = expr1p->unlinkFrBack(); - } else { - // not a tristate or not in a form we recgonize, so exit and move on. - return; - } - - AstNode* outp = nodep->lhsp()->unlinkFrBack();; - AstVarRef* outrefp = NULL; - if (outp->castVarRef()) { - outrefp = outp->castVarRef(); - } else if (outp->castSel()) { - outrefp = outp->castSel()->fromp()->castVarRef(); - } else { - nodep->v3error("Can't find LHS varref"); - } - - createEnableVar(outp, outrefp, enrhsp, outrhsp->width()); - - // replace the old assign logic with the new one - AstAssignW* newassp = new AstAssignW(nodep->fileline(), outp,outrhsp); - //if (debug()>=9) newassp->dumpTreeAndNext(cout,"- cond-out: "); - nodep->replaceWith(newassp); - nodep->deleteTree(); nodep=NULL; - newassp->iterateChildren(*this); - - } - // How about a tri gate? - else if (AstBufIf1* bufp = nodep->rhsp()->castBufIf1()) { - //if (debug()>=9) nodep->dumpTree(cout,"- tri-in : "); - AstNode* enrhsp = bufp->lhsp()->unlinkFrBack(); - AstNode* outrhsp = bufp->rhsp()->unlinkFrBack(); - - AstNode* outp = nodep->lhsp()->unlinkFrBack();; - AstVarRef* outrefp = NULL; - if (outp->castVarRef()) { - outrefp = outp->castVarRef(); - } else if (outp->castSel()) { - outrefp = outp->castSel()->fromp()->castVarRef(); - } else { - nodep->v3error("Can't find LHS varref"); - } - - createEnableVar(outp, outrefp, enrhsp, outrhsp->width()); - - // replace the old assign logic with the new one - AstAssignW* newassp = new AstAssignW(nodep->fileline(), outp,outrhsp); - //if (debug()>=9) newassp->dumpTreeAndNext(cout,"- tri-out: "); - nodep->replaceWith(newassp); - nodep->deleteTree(); nodep=NULL; - newassp->iterateChildren(*this); - } - else { - nodep->iterateChildren(*this); - } - } - - AstVar* createEnableVar(AstNode* outp, AstVarRef* outrefp, AstNode* enrhsp, int width, string suffix="") { - // this function creates an __en Var that corresponds to - // the outp and outrefp and creates an assignw to enrhsp - AstVar* enp = new AstVar (outrefp->varp()->fileline(), - AstVarType::MODULETEMP, - outrefp->name() + "__en" + suffix + cvtToStr(m_unique++), - VFlagLogicPacked(), width); - enp->varType2Out(); - - if (enp->width() != enrhsp->width()) { - if (enrhsp->width1()) { // it seems from my futzing that the linter guarantees this condition - enrhsp = new AstReplicate(enrhsp->fileline(), enrhsp, - new AstConst(enrhsp->fileline(), V3Number(enrhsp->fileline(), 32, enp->width()))); - enrhsp->width(enp->width(), enp->width()); //minwidth==width - } else { - enrhsp->v3error("Don't know how to deal with selection logic wider than 1 bit"); - } - } - - AstNode* newassp = new AstAssignW (enp->fileline(), - new AstVarRef (enp->fileline(), enp, true), - enrhsp); - if (debug()>=9) enp->dumpTreeAndNext(cout,"- cev-out: "); - if (debug()>=9) newassp->dumpTreeAndNext(cout,"- cev-out: "); - m_modp->addStmtp(enp); - m_modp->addStmtp(newassp); - - outrefp->user1p(enp); // put __en signal into varref for later usage - outrefp->varp()->user1p(enp); // put __en signal into var as well in the event this is a single lhs driver and this needs passed up one level - - return enp; - } - - //********************************************************************** - - //********************************************************************** - // These functions detect all var lhs drivers and add them to the - // VarMap so that after the walk through the module we can - // expand the driver to determine any that have multiple lhs drivers. - //********************************************************************** - +class TristateInPinVisitor : public TristateBaseVisitor { + // VISITORS virtual void visit(AstVarRef* nodep, AstNUser*) { - if (nodep->lvalue() && !nodep->user2()) { - nodep->user2(true); // mark this ref as visited - AstVar* key = nodep->varp(); - - VarMap::iterator it = m_lhsmapp->find(key); - if (it == m_lhsmapp->end()) { - // this key does not exist yet, so create it - RefVec* refs = new RefVec(); - refs->push_back(nodep); - m_lhsmapp->insert(pair(key, refs)); - } else { - (*it).second->push_back(nodep); - } - nodep->user3p(m_sel); // attach the sel to this varref + if (nodep->lvalue()) { + UINFO(9," Flip-to-RValue "<lvalue(false); } - nodep->iterateChildren(*this); } - - // Default - Iterate children to find all possible varrefs virtual void visit(AstNode* nodep, AstNUser*) { nodep->iterateChildren(*this); } - public: - TristateExpander(AstNodeModule* nodep, VarMap* lhsmapp) { - m_modp = nodep; - m_lhsmapp = lhsmapp; - m_unique = 0; - m_sel = NULL; - nodep->accept(*this); // visit eveyone + // CONSTUCTORS + TristateInPinVisitor(AstNode* nodep) { + nodep->accept(*this); } - virtual ~TristateExpander() { } + virtual ~TristateInPinVisitor() {} }; //###################################################################### class TristateVisitor : public TristateBaseVisitor { -private: // NODE STATE - // NOT Cleared on Netlist - // AstVarRef::user1p -> used to track any newly create __en signals - // AstVarRef::user3p -> a parent SEL - // AstVar::user1p -> used to track any newly create __en signals - // AstVar::user2 -> used for pullup/pulldown direction + // *::user1p -> pointer to output enable __en expressions + // AstVarRef::user2 -> bool - already visited + // AstVar::user2 -> bool - already visited + // AstPin::user2 -> bool - already visited + // AstVar::user3p -> AstPull* pullup/pulldown direction (input Var's user3p) + // AstVar::user4p -> AstVar* pointer to output __out var (input Var's user2p) - AstUser1InUse m_inuser1; - AstUser2InUse m_inuser2; - AstUser3InUse m_inuser3; + // TYPES + typedef std::vector VarVec; + typedef std::vector RefVec; + typedef std::map VarMap; - AstNodeModule* m_modp; // Current module - AstCell* m_cellp; // current cell - int m_unique; + // MEMBERS + AstNodeModule* m_modp; // Current module + AstCell* m_cellp; // current cell + VarMap m_lhsmap; // LHS driver map + VarVec m_varvec; // list of all vars for doing a final cleanup of inouts and undriven outputs that were not detected through finding Z logic in the module itself + int m_unique; + bool m_alhs; // On LHS of assignment - virtual void visit(AstNetlist* nodep, AstNUser*) { - AstNode::user1ClearTree(); - nodep->iterateChildrenBackwards(*this); + // STATS + V3Double0 m_statTriSigs; // stat tracking + + AstUser1InUse m_inuser1; + AstUser2InUse m_inuser2; + AstUser3InUse m_inuser3; + AstUser4InUse m_inuser4; + + // METHODS + AstNode* getEnp(AstNode* nodep) { + // checks if user1p() is null, and if so, adds a constant output + // enable driver of all 1's. Otherwise returns the user1p() data. + if (!nodep->user1p()) { + V3Number num(nodep->fileline(), nodep->width()); + num.setAllBits1(); + AstNode* enp = new AstConst(nodep->fileline(), num); + nodep->user1p(enp); + } + return nodep->user1p()->castNode(); + } + + AstVar* getCreateEnVarp(AstVar* invarp) { + // Return the master __en for the specified input variable + if (!invarp->user1p()) { + AstVar* newp = new AstVar(invarp->fileline(), + AstVarType::MODULETEMP, + invarp->name()+"__en", + invarp); + if (!m_modp) { invarp->v3error("Unsupported: Creating tristate signal not underneath a module: "<prettyName()); } + else m_modp->addStmtp(newp); + invarp->user1p(newp); // find envar given invarp + } + return invarp->user1p()->castNode()->castVar(); + } + + AstVar* getCreateOutVarp(AstVar* invarp) { + // Return the master __out for the specified input variable + if (!invarp->user4p()) { + AstVar* newp = new AstVar(invarp->fileline(), + AstVarType::MODULETEMP, + invarp->name()+"__out", + invarp); + if (!m_modp) { invarp->v3error("Unsupported: Creating tristate signal not underneath a module: "<prettyName()); } + else m_modp->addStmtp(newp); + invarp->user4p(newp); // find outvar given invarp + } + return invarp->user4p()->castNode()->castVar(); + } + + AstNode* newEnableDeposit(AstSel* selp, AstNode* enp) { + // Form a "deposit" instruction for given enable, using existing select as a template. + // Would be nicer if we made this a new AST type + AstNode* newp = new AstShiftL(selp->fileline(), + new AstExtend(selp->fileline(), + enp, + selp->fromp()->width()), + selp->lsbp()->cloneTree(false), + selp->fromp()->width()); + return newp; + } + + void checkPullDirection(AstPull* pullp1, AstPull* pullp2) { + if (pullp1 && pullp2 && pullp1->direction() != pullp2->direction()) { + pullp1->v3error("Unsupported: Conflicting pull directions."); + pullp2->v3error("... Location of conflicing pull."); + } + } + + void checkUnhandled(AstNode* nodep) { + // Check for unsupported tristate constructs. This is not a 100% check. + // The best way would be to visit the tree again and find any user1p() + // pointers that did not get picked up and expanded. + if (m_alhs && nodep->user1p()) + nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); + if ((nodep->op1p() && nodep->op1p()->user1p()) + || (nodep->op2p() && nodep->op2p()->user1p()) + || (nodep->op3p() && nodep->op3p()->user1p()) + || (nodep->op4p() && nodep->op4p()->user1p())) + nodep->v3error("Unsupported tristate construct: "<prettyTypeName()); + } + + // VISITORS + virtual void visit(AstConst* nodep, AstNUser*) { + UINFO(9,(m_alhs?"alhs":"")<<" "<num().hasZ()) { + FileLine* fl = nodep->fileline(); + V3Number numz (fl,nodep->width()); numz.opBitsZ(nodep->num()); //Z->1, else 0 + V3Number numz0(fl,nodep->width()); numz0.opNot(numz); // Z->0, else 1 + V3Number num1 (fl,nodep->width()); num1.opAnd(nodep->num(),numz0); // 01X->01X, Z->0 + AstConst* newconstp = new AstConst(fl, num1); + AstConst* enp = new AstConst(fl, numz0); + nodep->replaceWith(newconstp); + pushDeletep(nodep); nodep = NULL; + newconstp->user1p(enp); + } + } + + virtual void visit(AstCond* nodep, AstNUser*) { + if (m_alhs && nodep->user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } + nodep->iterateChildren(*this); + UINFO(9,(m_alhs?"alhs":"")<<" "<condp(); + if (condp->user1p()) { + condp->v3error("Unsupported: don't know how to deal with tristate logic in the conditional expression"); + } + AstNode* expr1p = nodep->expr1p(); + AstNode* expr2p = nodep->expr2p(); + if (!expr1p->user1p() && !expr2p->user1p()) { + return; // no tristates in either expression, so nothing to do + } + AstNode* en1p = getEnp(expr1p); + AstNode* en2p = getEnp(expr2p); + // The output enable of a cond is a cond of the output enable of the + // two expressions with the same conditional. + AstNode* enp = new AstCond(nodep->fileline(), condp->cloneTree(false), en1p, en2p); + nodep->user1p(enp); + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } + + virtual void visit(AstSel* nodep, AstNUser*) { + if (m_alhs) { + UINFO(9,"alhs "<user1p()) { + // Form a "deposit" instruction. Would be nicer if we made this a new AST type + AstNode* newp = newEnableDeposit(nodep, nodep->user1p()->castNode()); + nodep->fromp()->user1p(newp); // Push to varref (etc) + if (debug()>=9) newp->dumpTree(cout,"-assign-sel; "); + } + nodep->iterateChildren(*this); + } else { + nodep->iterateChildren(*this); + UINFO(9," "<fromp()->user1p() || nodep->lsbp()->user1p()) + nodep->v3error("Unsupported RHS tristate construct: "<prettyTypeName()); + } + } + + virtual void visit(AstConcat* nodep, AstNUser*) { + if (m_alhs) { + UINFO(9,(m_alhs?"alhs":"")<<" "<user1p()) { + // Each half of the concat gets a select of the enable expression + AstNode* enp = nodep->user1p()->castNode(); + nodep->user1p(NULL); + nodep->lhsp()->user1p(new AstSel(nodep->fileline(), + enp->cloneTree(true), + nodep->rhsp()->width(), + nodep->lhsp()->width())); + nodep->rhsp()->user1p(new AstSel(nodep->fileline(), + enp, + 0, + nodep->rhsp()->width())); + } + nodep->iterateChildren(*this); + } else { + nodep->iterateChildren(*this); + UINFO(9,(m_alhs?"alhs":"")<<" "<lhsp(); + AstNode* expr2p = nodep->rhsp(); + if (!expr1p->user1p() && !expr2p->user1p()) { + return; // no tristates in either expression, so nothing to do + } + AstNode* en1p = getEnp(expr1p); + AstNode* en2p = getEnp(expr2p); + AstNode* enp = new AstConcat(nodep->fileline(), en1p, en2p); + nodep->user1p(enp); + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } + } + + virtual void visit(AstBufIf1* nodep, AstNUser*) { + // For BufIf1, the enable is the LHS expression + nodep->iterateChildren(*this); + UINFO(9,(m_alhs?"alhs":"")<<" "<=9) nodep->backp()->dumpTree(cout,"-bufif: "); + if (m_alhs) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } + AstNode* expr1p = nodep->lhsp()->unlinkFrBack(); + AstNode* expr2p = nodep->rhsp()->unlinkFrBack(); + AstNode* enp; + if (AstNode* en2p = expr2p->user1p()->castNode()) { + enp = new AstAnd(nodep->fileline(), expr1p, en2p); + } else { + enp = expr1p; + } + expr1p->user1p(NULL); + expr2p->user1p(enp); // Becomes new node + // Don't need the BufIf any more, can just have the data direct + nodep->replaceWith(expr2p); + UINFO(9," bufif datap="<user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } + // ANDs and Z's have issues. Earlier optimizations convert + // expressions like "(COND) ? 1'bz : 1'b0" to "COND & 1'bz". So we + // have to define what is means to AND 1'bz with other + // expressions. I don't think this is spec, but here I take the + // approach that when one expression is 1, that the Z passes. This + // makes the COND's work. It is probably better to not perform the + // conditional optimization if the bits are Z. + AstNode* expr1p = nodep->lhsp(); + AstNode* expr2p = nodep->rhsp(); + if (!expr1p->user1p() && !expr2p->user1p()) { + return; // no tristates in either expression, so nothing to do + } + AstNode* en1p = getEnp(expr1p); + AstNode* en2p = getEnp(expr2p); + // calc new output enable. + AstNode* enp = new AstOr(nodep->fileline(), + new AstAnd(nodep->fileline(), en1p, en2p), + new AstOr(nodep->fileline(), + new AstAnd(nodep->fileline(), + en1p->cloneTree(false), + new AstNot(nodep->fileline(), expr1p->cloneTree(false))), + new AstAnd(nodep->fileline(), + en2p->cloneTree(false), + new AstNot(nodep->fileline(), expr2p->cloneTree(false))))); + nodep->user1p(enp); + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } + + virtual void visit(AstOr* nodep, AstNUser*) { + nodep->iterateChildren(*this); + UINFO(9,(m_alhs?"alhs":"")<<" "<user1p()) { nodep->v3error("Unsupported LHS tristate construct: "<prettyTypeName()); return; } + // ORs have the same issues as ANDs. Earlier optimizations convert + // expressions like "(COND) ? 1'bz : 1'b1" to "COND | 1'bz". So we + // have to define what is means to OR 1'bz with other + // expressions. Here I take the approach that when one expression + // is 0, that is passes the other. + AstNode* expr1p = nodep->lhsp(); + AstNode* expr2p = nodep->rhsp(); + if (!expr1p->user1p() && !expr2p->user1p()) { + return; // no tristates in either expression, so nothing to do + } + AstNode* en1p = getEnp(expr1p); + AstNode* en2p = getEnp(expr2p); + // calc new output enable + AstNode* enp = new AstOr(nodep->fileline(), + new AstAnd(nodep->fileline(), en1p, en2p), + new AstOr(nodep->fileline(), + new AstAnd(nodep->fileline(), + en1p->cloneTree(false), + expr1p->cloneTree(false)), + new AstAnd(nodep->fileline(), + en2p->cloneTree(false), + expr2p->cloneTree(false)))); + nodep->user1p(enp); + expr1p->user1p(NULL); + expr2p->user1p(NULL); + } + + void visitAssign(AstNodeAssign* nodep) { + nodep->rhsp()->iterateAndNext(*this); + UINFO(9," "<rhsp()->user1p()) { + nodep->lhsp()->user1p(nodep->rhsp()->user1p()); + nodep->rhsp()->user1p(NULL); + UINFO(9," enp<-rhs "<lhsp()->user1p()<lhsp()->iterateAndNext(*this); + m_alhs = false; + } + virtual void visit(AstAssignW* nodep, AstNUser*) { + visitAssign(nodep); + } + virtual void visit(AstAssign* nodep, AstNUser*) { + visitAssign(nodep); + } + + void visitCaseEq(AstNodeBiop* nodep, bool neq) { + checkUnhandled(nodep); + // Unsupported: A === 3'b000 should compare with the enables, but we don't do + // so at present, we only compare if there is a z in the equation. + // Otherwise we'd need to attach an enable to every signal, then optimize then + // away later when we determine the signal has no tristate + nodep->iterateChildren(*this); + UINFO(9," "<lhsp()->castConst(); // Constification always moves const to LHS + AstVarRef* varrefp = nodep->rhsp()->castVarRef(); // Input variable + if (constp && constp->user1p() && varrefp) { + // 3'b1z0 -> ((3'b101 == __en) && (3'b100 == __in)) + varrefp->unlinkFrBack(); + FileLine* fl = nodep->fileline(); + V3Number oneIfEn = constp->user1p()->castNode()->castConst()->num(); // visit(AstConst) already split into en/ones + V3Number oneIfEnOne = constp->num(); + AstVar* envarp = getCreateEnVarp(varrefp->varp()); + AstNode* newp = new AstLogAnd (fl, new AstEq (fl, new AstConst(fl, oneIfEn), + new AstVarRef(fl, envarp, false)), + // Keep the caseeq if there are X's present + new AstEqCase(fl, new AstConst(fl, oneIfEnOne), + varrefp)); + if (neq) newp = new AstLogNot(fl, newp); + if (debug()>=9) nodep->dumpTree(cout,"-caseeq-old: "); + if (debug()>=9) newp->dumpTree(cout,"-caseeq-new: "); + nodep->replaceWith(newp); + pushDeletep(nodep); nodep=NULL; + } else { + checkUnhandled(nodep); + } + } + virtual void visit(AstEqCase* nodep, AstNUser*) { + visitCaseEq(nodep,false); + } + virtual void visit(AstNeqCase* nodep, AstNUser*) { + visitCaseEq(nodep,true); + } + + virtual void visit(AstPull* nodep, AstNUser*) { + UINFO(9," "<lhsp()->castVarRef()) { + AstVarRef* lhsp = nodep->lhsp()->unlinkFrBack()->castVarRef(); + lhsp->lvalue(true); + AstVar* varp = lhsp->varp(); + AstPull* pullp = (AstPull*)varp->user3p(); + checkPullDirection(pullp, nodep); + V3Number zeros (nodep->fileline(), varp->width()); + zeros.setAllBits0(); + AstConst* constp = new AstConst(nodep->fileline(), zeros); + constp->user1p(new AstConst(nodep->fileline(), zeros));//set output enable to always be off on this assign statement. + AstAssignW* assp = new AstAssignW(nodep->fileline(), lhsp, constp); + nodep->replaceWith(assp); + assp->iterateChildren(*this); + if (!varp->user3p()) { + varp->user3p(nodep); //save off to indicate the pull direction + pushDeletep(nodep); nodep = NULL; + } + } else { + nodep->v3error("Unsupported pullup/down (weak driver) construct."); + } + } + + virtual void visit(AstPin* nodep, AstNUser*) { + // .tri(SEL(trisig,x)) becomes + // INPUT: -> (VARREF(trisig__pinin)), + // trisig__pinin = SEL(trisig,x) // via pinReconnectSimple + // OUTPUT: -> (VARREF(trisig__pinout)) + // ENABLE: -> (VARREF(trisig__pinen) + // SEL(trisig,x) = BUFIF1(enable__temp, trisig__pinen) + UINFO(9," "<modVarp()->user1p(); + if (!enModVarp) { // no __en signals on this pin + nodep->iterateChildren(*this); + return; + } + if (nodep->user2()) { // this pin is already expanded + return; + } + nodep->user2(true); // mark this pin already expanded + if (debug()>=9) nodep->dumpTree(cout,"-pin-pre: "); + + // pinReconnectSimple needs to presume input or output behavior; we need both + // Therefore, create the BUFIF1 on output and separate input pin, then pinReconnectSimple both + + // Create the output enable pin, connect to new signal + AstNode* enrefp; + { + AstVar* enVarp = new AstVar(nodep->fileline(), + AstVarType::MODULETEMP, + nodep->name() + "__en" + cvtToStr(m_unique), + VFlagLogicPacked(), enModVarp->width()); + AstPin* enpinp = new AstPin(nodep->fileline(), + nodep->pinNum(), + nodep->name() + "__en" + cvtToStr(m_unique++), + new AstVarRef(nodep->fileline(), enVarp, true)); + enpinp->widthSignedFrom(enModVarp); + enpinp->modVarp(enModVarp); + enpinp->user2(true); // mark this visited + m_cellp->addPinsp(enpinp); + m_modp->addStmtp(enVarp); + enrefp = new AstVarRef(nodep->fileline(), enVarp, false); + if (debug()>=9) enpinp->dumpTree(cout,"-pin-ena: "); + } + // Create new output pin + AstAssignW* outAssignp = NULL; // If reconnected, the related assignment + AstPin* outpinp; + { + AstVar* outModVarp = (AstVar*) nodep->modVarp()->user4p(); + AstNode* outexprp = nodep->exprp()->cloneTree(false); // Note has lvalue() set + outpinp = new AstPin(nodep->fileline(), + nodep->pinNum(), + nodep->name() + "__out"+cvtToStr(m_unique), + outexprp); + outpinp->widthSignedFrom(outModVarp); + outpinp->modVarp(outModVarp); + outpinp->user2(true); // mark this visited + m_cellp->addPinsp(outpinp); + // Simplify + outAssignp = V3Inst::pinReconnectSimple(outpinp, m_cellp, m_modp); // Note may change outpinp->exprp() + if (debug()>=9) outpinp->dumpTree(cout,"-pin-out: "); + if (debug()>=9 && outAssignp) outAssignp->dumpTree(cout,"-pin-out: "); + } + + // Existing pin becomes an input + TristateInPinVisitor visitor (nodep->exprp()); + V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp); // Note may change nodep->exprp() + if (debug()>=9) nodep->dumpTree(cout,"-pin-in: "); + + // Connect enable to output signal + AstVarRef* refp; + if (!outAssignp) { + refp = outpinp->exprp()->castVarRef(); + } else { + refp = outAssignp->rhsp()->castVarRef(); // This should be the same var as the output pin + } + if (!refp) { // deal with simple varref port + nodep->v3error("Unsupported tristate port expression: "<exprp()->prettyTypeName()); + } else { + refp->user1p(enrefp); // Mark as now tristated; iteration will pick it up from there + visit(refp, NULL); // visit this var ref to get it in the varmap + } + + // Propagate any pullups/pulldowns upwards if necessary + if (AstPull* pullp = (AstPull*) nodep->modVarp()->user3p()) { + if (refp && !refp->varp()->user3p()) { + refp->varp()->user3p(pullp); + } else { + //selp: Note we don't currently obey selects; all bits must be consistently pulled + checkPullDirection(pullp, (AstPull*) refp->varp()->user3p()); + } + } + // Don't need to visit the created assigns, as it was added at the end of the next links + // and normal iterateChild recursion will come back to them eventually. + } + + virtual void visit(AstVarRef* nodep, AstNUser*) { + UINFO(9,(m_alhs?"alhs":"")<<" "<lvalue() && !nodep->user2()) { + nodep->user2(true); // mark this ref as visited + AstVar* key = nodep->varp(); + VarMap::iterator it = m_lhsmap.find(key); + if (it == m_lhsmap.end()) { // Not found + RefVec* refs = new RefVec(); + refs->push_back(nodep); + m_lhsmap.insert(make_pair(key, refs)); + } else { + (*it).second->push_back(nodep); + } + } + if (m_alhs) {} // NOP; user1() already passed down from assignment + nodep->iterateChildren(*this); + } + + virtual void visit(AstVar* nodep, AstNUser*) { + if (nodep->user2Inc()) return; // Already processed + UINFO(9," "<iterateChildren(*this); + // If tri0/1 force a pullup + bool pulldown = nodep->varType()==AstVarType::TRI0; + bool pullup = nodep->varType()==AstVarType::TRI1; + if (pulldown || pullup) { + AstNode* newp = new AstPull(nodep->fileline(), + new AstVarRef(nodep->fileline(), nodep, true), + pullup); + m_modp->addStmtp(newp); + // We'll iterate on the new AstPull later + } } virtual void visit(AstNodeModule* nodep, AstNUser*) { - UINFO(9," MOD "<iterateChildren(*this); - m_modp = NULL; + // Go through all the vars and find any that are outputs without drivers + // or inouts without high-Z logic and put a 1'bz driver on them and add + // them to the lhs map so they get expanded correctly. + for (VarVec::iterator ii = m_varvec.begin(); ii != m_varvec.end(); ++ii) { + AstVar* varp = (*ii); + if (varp->isInout() + //|| varp->isOutput() + // Note unconnected output only changes behavior vs. previous versions and causes outputs + // that don't come from anywhere to possibly create connection errors. + // One example of problems is this: "output z; task t; z <= {something}; endtask" + ) { + VarMap::iterator it = m_lhsmap.find(varp); + if (it == m_lhsmap.end()) { + UINFO(8," Adding driver to var "<fileline(), varp->width()); + zeros.setAllBits0(); + AstConst* constp = new AstConst(varp->fileline(), zeros); + AstVarRef* varrefp = new AstVarRef(varp->fileline(), varp, true); + nodep->addStmtp(new AstAssignW(varp->fileline(), varrefp, constp)); + visit(varrefp, NULL); + varrefp->user1p(new AstConst(varp->fileline(),zeros));//set output enable to always be off on this assign statement so that this var is floating + } + } + } - // go through each multiple lhs driver & collapse it to a single driver - for (VarMap::iterator nextit, it=lhsmapp->begin(); it != lhsmapp->end(); it=nextit) { + // Now go through the lhs driver map and generate the output + // enable logic for any tristates. + for (VarMap::iterator nextit, it = m_lhsmap.begin(); it != m_lhsmap.end(); it = nextit) { nextit = it; ++nextit; - m_unique = 0; - AstVar* lhsp = (*it).first; + AstVar* invarp = (*it).first; RefVec* refs = (*it).second; - bool isOutput = (lhsp->varType() == AstVarType::OUTPUT) && (nodep->level() > 1); // force termination at top level - if (refs->size() < 2 && isOutput) { - // if only one driver and this is an output, then exit and - // let the driver propagate on its own. If the signals - // terminates at this level, then we need to let the - // undriven state get generated. - lhsmapp->erase(lhsp); - delete refs; - continue; - } - - - UINFO(9, " Checking " << refs->size() << " drivers for tristates signals on net " << lhsp << endl); - int pull = 0; // initially assume no pull direction - - // Now remove and multple lhs signals that do not have __en for - // all possible drivers. - bool complete = true; - int found_one = 0; - - for (RefVec::iterator ii=refs->begin(); ii != refs->end(); ++ii) { + // Figure out if this var needs tristate expanded. + int needs_expanded = 0; + // If need enable signal gets expanded + if (invarp->user1p()) { needs_expanded++; } + // all inouts get expanded + if (invarp->isInout()) { needs_expanded++; } + // loop through to find all vars that have __en logic. They get expanded. + for (RefVec::iterator ii = refs->begin(); ii != refs->end(); ++ii) { AstVarRef* refp = (*ii); - if (!refp->user1p()) { // if no __en signal, then delete the entry - complete = false; - } else { - found_one++; - } + if (refp->user1p()) { needs_expanded++; } } - if (!complete) { - if (found_one) { - UINFO(9, " Problem mixing tristate and low-Z on " << lhsp << endl); - UINFO(9, " Found " << found_one << " __en signals from of " << refs->size() << " possible drivers" << endl); - // not sure what I should do here other than error that they are mixing low-Z and tristate drivers. - // The other scenerio, and probably more likely, is that they are using a high-Z construct that - // is not supported. Improving the high-Z detection logic will reduce the occurance of this failure. - nodep->v3error("Mixing tristate and low-Z drivers. Perhaps you are using a high-Z construct not supported"); - } else { - UINFO(9, " No tristates found on " << lhsp <erase(lhsp); + + if (needs_expanded == 0) { + // This var has no tristate logic, so we leave it alone. + UINFO(8, " NO TRISTATE ON:" << invarp << endl); + m_lhsmap.erase(invarp); delete refs; continue; } - UINFO(9, " TRISTATE LHS DRIVER FOUND:" << lhsp << endl); + m_statTriSigs++; + UINFO(8, " TRISTATE EXPANDING("<isTop() && invarp->isIO()) { + // This var becomes an input + invarp->varType2In(); // convert existing port to type input + // Create an output port (__out) + AstVar* outvarp = getCreateOutVarp(invarp); + outvarp->varType2Out(); + lhsp = outvarp; // Must assign to __out, not to normal input signal + // Create an output enable port (__en) + envarp = getCreateEnVarp(invarp); // May already be created if have foo === 1'bz somewhere + envarp->varType2Out(); + // + outvarp->user1p(envarp); + outvarp->user3p(invarp->user3p()); + } else if (invarp->user1p()) { + envarp = invarp->user1p()->castNode()->castVar(); // From CASEEQ, foo === 1'bz + } + + AstNode* orp = NULL; + AstNode* andp = NULL; + AstNode* enp = NULL; + AstNode* undrivenp = NULL; // loop through the lhs drivers to build the driver resolution logic for (RefVec::iterator ii=refs->begin(); ii != refs->end(); ++ii) { AstVarRef* refp = (*ii); int w = lhsp->width(); - int wfill = 0; // width filler when necessary due to sels - AstSel* selp = NULL; - if (refp->user3p()) { // this varref has a sel - selp = (AstSel*) refp->user3p(); - w = selp->widthConst(); - wfill = lhsp->width() - w; - } - // create a new var for this assignment. - AstVar* enp = (AstVar*)refp->user1p(); + // create the new lhs driver for this var AstVar* newlhsp = new AstVar(lhsp->fileline(), AstVarType::MODULETEMP, - lhsp->name()+"__lhs"+cvtToStr(m_unique++), + lhsp->name()+"__out"+cvtToStr(m_unique), VFlagLogicPacked(), w); nodep->addStmtp(newlhsp); + refp->varp(newlhsp); // assign the new var to the varref + refp->name(newlhsp->name()); + + // create a new var for this drivers enable signal + AstVar* newenp = new AstVar(lhsp->fileline(), + AstVarType::MODULETEMP, + lhsp->name()+"__en"+cvtToStr(m_unique++), + VFlagLogicPacked(), w); + + nodep->addStmtp(newenp); + nodep->addStmtp(new AstAssignW(refp->fileline(), + new AstVarRef(refp->fileline(), newenp, true), + getEnp(refp))); // now append this driver to the driver logic. - AstNode* ref1 = new AstVarRef(nodep->fileline(), newlhsp,false); - AstNode* ref2 = new AstVarRef(nodep->fileline(), enp, false); - andp = new AstAnd(nodep->fileline(), ref1, ref2); - - - AstVar* bitselp = NULL; - if (selp) { // this varref has a sel - int ws = V3Number::log2b(lhsp->width())+1; - bitselp = new AstVar(lhsp->fileline(), - AstVarType::MODULETEMP, - lhsp->name()+"__sel"+cvtToStr(m_unique-1), - VFlagLogicPacked(), ws); - // - nodep->addStmtp(bitselp); - nodep->addStmtp(new AstAssignW(lhsp->fileline(), - new AstVarRef(lhsp->fileline(), bitselp, true), - selp->lsbp()->cloneTree(false))); - andp = new AstShiftL(lhsp->fileline(), - new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), andp), - new AstVarRef(lhsp->fileline(), bitselp, false), - lhsp->width() - ); - - selp->replaceWith(new AstVarRef(refp->fileline(), newlhsp, true)); - pushDeletep(selp); // Setting selp here or deleting immediately - // breaks the t_tri_select test, this probably indicates a problem - } else { - refp->varp(newlhsp); // assign the new var to the varref - refp->name(newlhsp->name()); - } + AstNode* ref1p = new AstVarRef(nodep->fileline(), newlhsp,false); + AstNode* ref2p = new AstVarRef(nodep->fileline(), newenp, false); + andp = new AstAnd(nodep->fileline(), ref1p, ref2p); // or this to the others orp = (!orp) ? andp : new AstOr(nodep->fileline(), orp, andp); - if (isOutput) { - AstNode *en1p = new AstVarRef(nodep->fileline(), enp, false); - if (selp) { - en1p = new AstShiftL(enp->fileline(), - new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), en1p), - new AstVarRef(lhsp->fileline(), bitselp, false), - lhsp->width() - ); - } - if (!newenlogicp) { - newenlogicp = en1p; - } else { - newenlogicp = new AstOr(nodep->fileline(), newenlogicp, en1p); - } - } else { - if (!undrivenp) { - undrivenp = new AstNot(nodep->fileline(), new AstVarRef(nodep->fileline(), enp, false)); - if (selp) - undrivenp = new AstShiftL(enp->fileline(), - new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), undrivenp), - new AstVarRef(lhsp->fileline(), bitselp, false), - lhsp->width()); - } else { - AstNode *tmp = new AstNot(nodep->fileline(), new AstVarRef(nodep->fileline(), enp, false)); - if (selp) { - tmp = new AstShiftL(enp->fileline(), - new AstConcat(lhsp->fileline(), new AstConst(lhsp->fileline(), V3Number(lhsp->fileline(), wfill, 0)), tmp), - new AstVarRef(lhsp->fileline(), bitselp, false), - lhsp->width()); - } - undrivenp = new AstAnd(nodep->fileline(), tmp, undrivenp); - } - } - - refp->user1p(NULL); // clear the user1p() as we done with it in the VarRef at this point - - if (enp->user2()) { // if this net is pulled up/down - int newpull = enp->user2(); - if (pull == 0) { - pull = newpull; - } else if (newpull != pull) { - pull = -1; // conflict over the pull direction - } + if (envarp) { + AstNode* ref3p = new AstVarRef(nodep->fileline(), newenp, false); + enp = (!enp) ? ref3p : new AstOr(ref3p->fileline(), enp, ref3p); } + AstNode* tmp = new AstNot(newenp->fileline(), new AstVarRef(newenp->fileline(), newenp, false)); + undrivenp = ((!undrivenp) ? tmp + : new AstAnd(nodep->fileline(), tmp, undrivenp)); } - if (isOutput) { - AstVar* newenp = new AstVar(lhsp->fileline(), - AstVarType::OUTPUT, - lhsp->name()+"__enout"+cvtToStr(m_unique++), - lhsp); - nodep->addStmtp(newenp); - nodep->addStmtp(new AstAssignW(lhsp->fileline(), - new AstVarRef(lhsp->fileline(), newenp, true), - newenlogicp)); - newenp->user2(pull); // put the pull direction in the next __en signal to pass it up - lhsp->user1p(newenp); // put the new __en signal in the var so it can be pushed up the hierarchy. - } else { // this is the level where the signal terminates, we do final conflict resolution here - UINFO(9, " Terminating tristate logic for " << lhsp->name() << endl); - UINFO(9, " Pull direction is " << pull << " where -1=X, 0=Z, 1=low, 2=high." << endl); - // figure out what to drive when no one is driving the bus - V3Number num(nodep->fileline(), lhsp->width()); - if (pull==0) { - num.setAllBitsZ(); - } else if (pull==1) { - num.setAllBits0(); - } else if (pull==2) { - num.setAllBits1(); + if (!undrivenp) { // No drivers on the bus + V3Number ones(nodep->fileline(), lhsp->width()); ones.setAllBits1(); + undrivenp = new AstConst(nodep->fileline(), ones); + } + + if (!outvarp) { + // This is the final resolution of the tristate, so we apply + // the pull direction to any undriven pins. + V3Number pull(nodep->fileline(), lhsp->width()); + AstPull* pullp = (AstPull*)lhsp->user3p(); + if (pullp && pullp->direction() == 1) { + pull.setAllBits1(); } else { - num.setAllBitsX(); + pull.setAllBits0(); // default pull direction is down. } undrivenp = new AstAnd(nodep->fileline(), undrivenp, - new AstConst(nodep->fileline(), num)); + new AstConst(nodep->fileline(), pull)); orp = new AstOr(nodep->fileline(), orp, undrivenp); } - nodep->addStmtp(new AstAssignW(lhsp->fileline(), - new AstVarRef(lhsp->fileline(), lhsp, true), orp)); + if (envarp) { + nodep->addStmtp(new AstAssignW(enp->fileline(), + new AstVarRef(envarp->fileline(), + envarp, true), enp)); + } - // delete the map and vector list now that we have collapsed it. - lhsmapp->erase(lhsp); + AstNode* assp = new AstAssignW(lhsp->fileline(), + new AstVarRef(lhsp->fileline(), + lhsp, + true), + orp); + if (debug()>=9) assp->dumpTree(cout,"-lhsp-eqn: "); + nodep->addStmtp(assp); + // Delete the map and vector list now that we have expanded it. + m_lhsmap.erase(invarp); delete refs; } - delete lhsmapp; // delete the map now that we are done - nodep->user1p(NULL); + m_modp = NULL; + } + + virtual void visit(AstNodeFTask* nodep, AstNUser*) { + // don't deal with functions + } + + virtual void visit(AstCaseItem* nodep, AstNUser*) { + // don't deal with casez compare '???? values + nodep->bodysp()->iterateAndNext(*this); } virtual void visit(AstCell* nodep, AstNUser*) { m_cellp = nodep; + m_alhs = false; nodep->iterateChildren(*this); m_cellp = NULL; } - AstVarRef* findVarRef(AstPin* nodep) { - if (nodep->exprp()->castVarRef()) { - return nodep->exprp()->castVarRef(); - } else if (nodep->exprp()->castSel() && nodep->exprp()->castSel()->fromp()->castVarRef()) { - return nodep->exprp()->castSel()->fromp()->castVarRef(); - } else { - return NULL; - } - } - - virtual void visit(AstPin* nodep, AstNUser*) { - // Check to see if any output pins have __en pins and create the __en pins to match - AstVarRef* refp = findVarRef(nodep); - - if (refp && refp->lvalue() && nodep->modVarp()->user1p()) { - AstVar* enchildp = (AstVar*)nodep->modVarp()->user1p(); - UINFO(9, " Pulling __en var" << enchildp << endl); - AstVar* enp = new AstVar(enchildp->fileline(), - AstVarType::OUTPUT, - enchildp->name()+cvtToStr(m_unique++), - enchildp); - enp->user2(enchildp->user2()); - m_modp->addStmtp(enp); - AstPin* pinp = new AstPin(nodep->fileline(), - nodep->pinNum(), - enp->name(), - new AstVarRef(nodep->fileline(), enp, true)); - AstVarRef *rp = findVarRef(pinp); - rp->replaceWith(new AstVarRef(nodep->fileline(), enp, true)); - rp->deleteTree(); rp=NULL; - pinp->width(enp->width(),enp->width()); // minwidth==width - pinp->modVarp(enchildp); - m_cellp->addPinsp(pinp); - refp->user1p(enp); - refp->varp()->user1p(enp); - } - // Simplify interconnect in preperation for V3Inst - // (This could be a separate visitor, but we're in the neighborhood) - V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp); + virtual void visit(AstNetlist* nodep, AstNUser*) { + nodep->iterateChildrenBackwards(*this); } // Default: Just iterate virtual void visit(AstNode* nodep, AstNUser*) { - return; // no need to iterate further b/c AstCell and AstPin grab everything this visitor needs to hook up newly created __en pins correctly + nodep->iterateChildren(*this); + UINFO(9," "<accept(*this); } - virtual ~TristateVisitor() { } -}; - -//###################################################################### - -class InoutVisitor : public TristateBaseVisitor { - // This visitor walks the tree and expands inouts into two ports. - // The original port is switched to an output port and an additional - // input port is created with a name suffix of __in. All LHS logic - // is left along connected to the original port. All RHS varrefs - // that point the original port are switched to point to the __in - // input port. The expansion is accomplished in a series of three - // stages. First entire hierarchy is visited and all inout vars are - // expanded in an output and input var. Then a second pass through - // the hierarchy switches all the rhs varrefs to point to the newly - // created __in vars. Finally a third pass looks at the ports and - // creates any needed __in ports. - -private: - // NODE STATE - // Cleared on Netlist - // AstVar::user1p -> a pointer to the created input port __in - - AstUser1InUse m_inuser1; - - enum States { - CONVERT_VARS, - CONVERT_VARREFS, - CONVERT_PINS - }; - - AstNodeModule* m_modp; // Current module - AstCell* m_cellp; // Current cell - AstNodeFTask* m_ftaskp; // Current function/task - States m_state; - - virtual void visit(AstNetlist* nodep, AstNUser*) { - m_state = CONVERT_VARS; - nodep->iterateChildren(*this); - m_state = CONVERT_VARREFS; - nodep->iterateChildren(*this); - m_state = CONVERT_PINS; - nodep->iterateChildren(*this); + virtual ~TristateVisitor() { + V3Stats::addStat("Tristate, Tristate resolved nets", m_statTriSigs); } - - virtual void visit(AstNodeModule* nodep, AstNUser*) { - m_modp = nodep; - nodep->iterateChildren(*this); - m_modp = NULL; - } - - virtual void visit(AstNodeFTask* nodep, AstNUser*) { - m_ftaskp = nodep; - nodep->iterateChildren(*this); - m_ftaskp = NULL; - } - - virtual void visit(AstVar* nodep, AstNUser*) { - if (m_state == CONVERT_VARS) { - if (nodep->isTristate() && !m_ftaskp) { - // Add pullups/pulldowns so next stage can ignore wire type - // See test_regress/t/t_tri_pull01.v test, it fails for other reasons - //if (nodep->varType() == AstVarType::TRIWIRE0) { - // nodep->addNext(new AstPull(nodep->fileline(), - // new AstVarRef(nodep->fileline(), nodep, true), - // false)); - //} - //if (nodep->varType() == AstVarType::TRIWIRE1) { - // nodep->addNext(new AstPull(nodep->fileline(), - // new AstVarRef(nodep->fileline(), nodep, true), - // true)); - //} - // create the input var and leave the original as the output var - AstVar* varinp = nodep->cloneTree(false)->castVar(); - varinp->name(varinp->name() + "__in"); - varinp->varType2In(); - - nodep->combineType(AstVarType::OUTPUT); - nodep->varType2Out(); - m_modp->addStmtp(varinp); - nodep->user1p(varinp); - } - } - } - - virtual void visit(AstVarRef* nodep, AstNUser*) { - if (m_state == CONVERT_VARREFS) { - if (!nodep->lvalue() && nodep->varp()->user1p()) { - nodep->varp((AstVar*) nodep->varp()->user1p()); - nodep->name(nodep->varp()->name()); - } - } - } - - virtual void visit(AstCell* nodep, AstNUser*) { - m_cellp = nodep; - nodep->iterateChildren(*this); - m_cellp = NULL; - } - - virtual void visit(AstPin* nodep, AstNUser*) { - if (m_state == CONVERT_PINS) { - if (nodep->modVarp()->user1p()) { - // create the input pin - AstVarRef* refp = nodep->exprp()->castVarRef(); - if (!refp) nodep->v3fatal("Unsupported: Tristate pin not connected to simple net"); - AstVar* inp; - if (refp->varp()->user1p()) { // this is a tristate - inp = (AstVar*) refp->varp()->user1p(); - } else { - inp = refp->varp(); - } - AstPin* pinp = new AstPin(nodep->fileline(), - nodep->pinNum(), - nodep->name() + "__in", - new AstVarRef(nodep->fileline(), inp, false)); - m_cellp->addPinsp(pinp); - - // now link it - pinp->modVarp((AstVar*) nodep->modVarp()->user1p()); - } - } - } - - // Default: Just iterate - virtual void visit(AstNode* nodep, AstNUser*) { - nodep->iterateChildren(*this); - } - -public: - // CONSTUCTORS - InoutVisitor(AstNode* nodep) { - m_modp = NULL; - m_cellp = NULL; - m_ftaskp = NULL; - m_state = CONVERT_VARS; - nodep->accept(*this); - } - virtual ~InoutVisitor() { } }; //###################################################################### @@ -745,8 +821,3 @@ void V3Tristate::tristateAll(AstNetlist* nodep) { UINFO(2,__FUNCTION__<<": "<dumpTreeFile(v3Global.debugFilename("const.tree")); - if (!v3Global.opt.xmlOnly()) { - // Expand Inouts - V3Tristate::inoutAll(v3Global.rootp()); - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inouts.tree")); - } - if (!v3Global.opt.xmlOnly()) { // Remove cell arrays (must be between V3Width and scoping) V3Inst::dearrayAll(v3Global.rootp()); diff --git a/src/verilog.l b/src/verilog.l index 3524593c0..3bc81dbfc 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -296,6 +296,8 @@ word [a-zA-Z0-9_]+ "tranif0" { FL; return yTRANIF0; } "tranif1" { FL; return yTRANIF1; } "tri" { FL; return yTRI; } + "tri0" { FL; return yTRI0; } + "tri1" { FL; return yTRI1; } "vectored" { FL; return yVECTORED; } "while" { FL; return yWHILE; } "wire" { FL; return yWIRE; } @@ -333,8 +335,6 @@ word [a-zA-Z0-9_]+ "triand" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "trior" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "trireg" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } - "tri0" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } - "tri1" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "wait" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "wand" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } "weak0" { yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext); } diff --git a/src/verilog.y b/src/verilog.y index 92f8cfbe5..fe4478d18 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -376,6 +376,8 @@ class AstSenTree; %token yTRANIF0 "tranif0" %token yTRANIF1 "tranif1" %token yTRI "tri" +%token yTRI0 "tri0" +%token yTRI1 "tri1" %token yTRUE "true" %token yTYPEDEF "typedef" %token yUNIQUE "unique" @@ -1007,8 +1009,8 @@ net_type: // ==IEEE: net_type ySUPPLY0 { VARDECL(SUPPLY0); } | ySUPPLY1 { VARDECL(SUPPLY1); } | yTRI { VARDECL(TRIWIRE); } - //UNSUP yTRI0 { VARDECL(TRI0); } - //UNSUP yTRI1 { VARDECL(TRI1); } + | yTRI0 { VARDECL(TRI0); } + | yTRI1 { VARDECL(TRI1); } //UNSUP yTRIAND { VARDECL(TRIAND); } //UNSUP yTRIOR { VARDECL(TRIOR); } //UNSUP yTRIREG { VARDECL(TRIREG); } diff --git a/test_regress/t/t_lint_implicit_port.v b/test_regress/t/t_lint_implicit_port.v index 812454659..f99473b97 100644 --- a/test_regress/t/t_lint_implicit_port.v +++ b/test_regress/t/t_lint_implicit_port.v @@ -21,7 +21,7 @@ module set ( input clk, output enable ); - + assign enable = 1'b0; endmodule module read ( diff --git a/test_regress/t/t_tri_eqcase.pl b/test_regress/t/t_tri_eqcase.pl new file mode 100755 index 000000000..7058e622f --- /dev/null +++ b/test_regress/t/t_tri_eqcase.pl @@ -0,0 +1,18 @@ +#!/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. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_tri_eqcase.v b/test_regress/t/t_tri_eqcase.v new file mode 100644 index 000000000..ca86c34fd --- /dev/null +++ b/test_regress/t/t_tri_eqcase.v @@ -0,0 +1,128 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2011 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + reg [63:0] crc; + reg [63:0] sum; + + wire [3:0] drv_a = crc[3:0]; + wire [3:0] drv_b = crc[7:4]; + wire [3:0] drv_e = crc[19:16]; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [8:0] match1; // From test1 of Test1.v + wire [8:0] match2; // From test2 of Test2.v + // End of automatics + + Test1 test1 (/*AUTOINST*/ + // Outputs + .match1 (match1[8:0]), + // Inputs + .drv_a (drv_a[3:0]), + .drv_e (drv_e[3:0])); + Test2 test2 (/*AUTOINST*/ + // Outputs + .match2 (match2[8:0]), + // Inputs + .drv_a (drv_a[3:0]), + .drv_e (drv_e[3:0])); + + // Aggregate outputs into a single result vector + wire [63:0] result = {39'h0, match2, 7'h0, match1}; + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x m1=%x m2=%x (%b??%b:%b)\n",$time, cyc, crc, match1, match2, drv_e,drv_a,drv_b); +`endif + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + sum <= result ^ {sum[62:0],sum[63]^sum[2]^sum[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + sum <= 64'h0; + end + else if (cyc<10) begin + sum <= 64'h0; + end + else if (cyc<90) begin + end + else if (cyc==99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum); + if (crc !== 64'hc77bb9b3784ea091) $stop; + // What checksum will we end up with (above print should match) +`define EXPECTED_SUM 64'hc0c4a2b9aea7c4b4 + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module Test1 + ( + input wire [3:0] drv_a, + input wire [3:0] drv_e, + output wire [8:0] match1 + ); + + wire [2:1] drv_all; + bufif1 bufa [2:1] (drv_all, drv_a[2:1], drv_e[2:1]); + +`ifdef VERILATOR + // At present Verilator only allows comparisons with Zs + assign match1[0] = (drv_a[2:1]== 2'b00 && drv_e[2:1]==2'b11); + assign match1[1] = (drv_a[2:1]== 2'b01 && drv_e[2:1]==2'b11); + assign match1[2] = (drv_a[2:1]== 2'b10 && drv_e[2:1]==2'b11); + assign match1[3] = (drv_a[2:1]== 2'b11 && drv_e[2:1]==2'b11); +`else + assign match1[0] = drv_all === 2'b00; + assign match1[1] = drv_all === 2'b01; + assign match1[2] = drv_all === 2'b10; + assign match1[3] = drv_all === 2'b11; +`endif + assign match1[4] = drv_all === 2'bz0; + assign match1[5] = drv_all === 2'bz1; + assign match1[6] = drv_all === 2'bzz; + assign match1[7] = drv_all === 2'b0z; + assign match1[8] = drv_all === 2'b1z; +endmodule + +module Test2 + ( + input wire [3:0] drv_a, + input wire [3:0] drv_e, + output wire [8:0] match2 + ); + + wire [2:1] drv_all; + bufif1 bufa [2:1] (drv_all, drv_a[2:1], drv_e[2:1]); + +`ifdef VERILATOR + assign match2[0] = (drv_all !== 2'b00 || drv_e[2:1]!=2'b11); + assign match2[1] = (drv_all !== 2'b01 || drv_e[2:1]!=2'b11); + assign match2[2] = (drv_all !== 2'b10 || drv_e[2:1]!=2'b11); + assign match2[3] = (drv_all !== 2'b11 || drv_e[2:1]!=2'b11); +`else + assign match2[0] = drv_all !== 2'b00; + assign match2[1] = drv_all !== 2'b01; + assign match2[2] = drv_all !== 2'b10; + assign match2[3] = drv_all !== 2'b11; +`endif + assign match2[4] = drv_all !== 2'bz0; + assign match2[5] = drv_all !== 2'bz1; + assign match2[6] = drv_all !== 2'bzz; + assign match2[7] = drv_all !== 2'b0z; + assign match2[8] = drv_all !== 2'b1z; +endmodule diff --git a/test_regress/t/t_tri_gate.v b/test_regress/t/t_tri_gate.v index 4980854ba..01364fadf 100644 --- a/test_regress/t/t_tri_gate.v +++ b/test_regress/t/t_tri_gate.v @@ -1,19 +1,20 @@ // This file ONLY is placed into the Public Domain, for any use, // without warranty, 2008 by Lane Brooks -module top (input SEL, input[1:0] A, output Z, output Y, output X, output W); +module top (input SEL, input[1:0] A, output W, output X, output Y, output Z); + mux mux2 (.A(A), .SEL(SEL), .Z(W)); + + pass mux1 (.A(A), .SEL(SEL), .Z(X)); + + tbuf mux0[1:0] (.A(A), .OE({SEL,!SEL}), .Z(Y)); + assign Z = ( SEL) ? A[1] : 1'bz; - tbuf tbuf(.A(A[0]), .OE(!SEL), .Z(Z)); - - tbuf mux0[1:0](.A(A), .OE({SEL,!SEL}), .Z(Y)); - - pass mux1(.A(A), .SEL(SEL), .Z(X)); - mux mux2(.A(A), .SEL(SEL), .Z(W)); + tbuf tbuf (.A(A[0]), .OE(!SEL), .Z(Z)); endmodule module pass (input[1:0] A, input SEL, output Z); - tbuf tbuf1(.A(A[1]), .OE(SEL), .Z(Z)); - tbuf tbuf0(.A(A[0]), .OE(!SEL),.Z(Z)); + tbuf tbuf1 (.A(A[1]), .OE(SEL), .Z(Z)); + tbuf tbuf0 (.A(A[0]), .OE(!SEL),.Z(Z)); endmodule module tbuf (input A, input OE, output Z); @@ -35,4 +36,5 @@ endmodule module mux (input[1:0] A, input SEL, output Z); assign Z = (SEL) ? A[1] : 1'bz; assign Z = (!SEL)? A[0] : 1'bz; + assign Z = 1'bz; endmodule diff --git a/test_regress/t/t_tri_inout.v b/test_regress/t/t_tri_inout.v index 52ae53b39..7322cf43d 100644 --- a/test_regress/t/t_tri_inout.v +++ b/test_regress/t/t_tri_inout.v @@ -4,13 +4,16 @@ module top (input A, input B, input SEL, output Y1, output Y2, output Z); io io1(.A(A), .OE( SEL), .Z(Z), .Y(Y1)); pass io2(.A(B), .OE(!SEL), .Z(Z), .Y(Y2)); + assign Z = 1'bz; endmodule module pass (input A, input OE, inout Z, output Y); io io(.A(A), .OE(OE), .Z(Z), .Y(Y)); + assign Z = 1'bz; endmodule module io (input A, input OE, inout Z, output Y); assign Z = (OE) ? A : 1'bz; assign Y = Z; + assign Z = 1'bz; endmodule diff --git a/test_regress/t/t_tri_inout2.pl b/test_regress/t/t_tri_inout2.pl new file mode 100755 index 000000000..cdae47250 --- /dev/null +++ b/test_regress/t/t_tri_inout2.pl @@ -0,0 +1,17 @@ +#!/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 +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_tri_inout2.v b/test_regress/t/t_tri_inout2.v new file mode 100644 index 000000000..f0d4ae87a --- /dev/null +++ b/test_regress/t/t_tri_inout2.v @@ -0,0 +1,77 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2008 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + reg [2:0] in; + + + wire a,y,y_fixed; + wire b = in[0]; + wire en = in[1]; + + + pullup(a); + + ChildA childa ( .A(a), .B(b), .en(en), .Y(y),.Yfix(y_fixed) ); + + initial in=0; + initial en=0; + + // Test loop + always @ (posedge clk) begin + + + in <= in + 1; + + $display ( "a %d b %d en %d y %d yfix: %d)" , a, b, en, y, y_fixed); + if (en) begin + // driving b + // a should be b + // y and yfix should also be b + if (a!=b || y != b || y_fixed != b) begin + $display ( "Expected a %d y %b yfix %b" , a, y, y_fixed); + $stop; + end + + end else begin + // not driving b + // a should be 1 (pullup) + // y and yfix shold be 1 + if (a!=1 || y != 1 || y_fixed != 1) begin + $display( "Expected a,y,yfix == 1"); + $stop; + end + end + + if (in==3) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +module ChildA(inout A, input B, input en, output Y, output Yfix); + + // workaround + wire a_in = A; + + ChildB childB(.A(A), .Y(Y)); + assign A = en ? B : 1'bz; + + + ChildB childBfix(.A(a_in),.Y(Yfix)); + + +endmodule + +module ChildB(input A, output Y); + assign Y = A; +endmodule + diff --git a/test_regress/t/t_tri_pull01.pl b/test_regress/t/t_tri_pull01.pl index 702b95e91..7058e622f 100755 --- a/test_regress/t/t_tri_pull01.pl +++ b/test_regress/t/t_tri_pull01.pl @@ -7,8 +7,6 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. -$Self->{vlt} and $Self->unsupported("Verilator unsupported, bug462"); - compile ( ); diff --git a/test_regress/t/t_tri_pull2_bad.pl b/test_regress/t/t_tri_pull2_bad.pl index 6227d0bde..6c70a2aa4 100755 --- a/test_regress/t/t_tri_pull2_bad.pl +++ b/test_regress/t/t_tri_pull2_bad.pl @@ -7,15 +7,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. -$Self->{vlt} and $Self->unsupported("Verilator unsupported, tri"); - compile ( fails=>$Self->{v3}, expect=> -'%Error: t/t_tri_bad_pull2.v:19: Unsupported: Conflicting pull directions. -%Error: t/t_tri_bad_pull2.v:9: ... Location of conflicing pull. +'%Error: t/t_tri_pull2_bad.v:\d+: Unsupported: Conflicting pull directions. +%Error: t/t_tri_pull2_bad.v:\d+: ... Location of conflicing pull. %Error: Exiting due to', - ) if + ); ok(1); 1; diff --git a/test_regress/t/t_tri_pull_bad.pl b/test_regress/t/t_tri_pull_bad.pl index 4779e9af9..6f01a3839 100755 --- a/test_regress/t/t_tri_pull_bad.pl +++ b/test_regress/t/t_tri_pull_bad.pl @@ -7,15 +7,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di # Lesser General Public License Version 3 or the Perl Artistic License # Version 2.0. -$Self->{vlt} and $Self->unsupported("Verilator unsupported, tri"); - compile ( fails=>$Self->{v3}, expect=> -'%Error: t/t_tri_bad_pull.v:9: Unsupported: Conflicting pull directions. -%Error: t/t_tri_bad_pull.v:10: ... Location of conflicing pull. +'%Error: t/t_tri_pull_bad.v:\d+: Unsupported: Conflicting pull directions. +%Error: t/t_tri_pull_bad.v:\d+: ... Location of conflicing pull. %Error: Exiting due to', - ) if + ); ok(1); 1; diff --git a/test_regress/t/t_tri_select.v b/test_regress/t/t_tri_select.v index 0f82a9385..0545c8879 100644 --- a/test_regress/t/t_tri_select.v +++ b/test_regress/t/t_tri_select.v @@ -17,8 +17,8 @@ module top ( // have 2 different 'chips' drive the PAD to act like a bi-directional bus wire [`WIDTH-1:0] PAD; - io_ring io_ring1(.OE(OE1), .A(A1), .Y(Y1), .PAD(PAD)); - io_ring io_ring2(.OE(OE2), .A(A2), .Y(Y2), .PAD(PAD)); + io_ring io_ring1 (.OE(OE1), .A(A1), .Y(Y1), .PAD(PAD)); + io_ring io_ring2 (.OE(OE2), .A(A2), .Y(Y2), .PAD(PAD)); pullup p1(PAD); // pulldown p1(PAD); @@ -29,7 +29,7 @@ module top ( endmodule module io_ring (input OE, input [`WIDTH-1:0] A, output [`WIDTH-1:0] Y, inout [`WIDTH-1:0] PAD); - io io[`WIDTH-1:0](.OE(OE), .I(A), .O(Y), .PAD(PAD)); + io io[`WIDTH-1:0] (.OE(OE), .I(A), .O(Y), .PAD(PAD)); endmodule module io (input OE, input I, output O, inout PAD); diff --git a/test_regress/t/t_tri_various.pl b/test_regress/t/t_tri_various.pl index 6fe5bf47c..db0de10ee 100755 --- a/test_regress/t/t_tri_various.pl +++ b/test_regress/t/t_tri_various.pl @@ -1,8 +1,6 @@ #!/usr/bin/perl if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } -$Self->{vlt} and $Self->unsupported("Verilator unsupported, tri"); - compile ( ); diff --git a/test_regress/t/t_tri_various.v b/test_regress/t/t_tri_various.v index 41b3a21fd..2bbf56076 100644 --- a/test_regress/t/t_tri_various.v +++ b/test_regress/t/t_tri_various.v @@ -9,7 +9,9 @@ module t (clk); wire A = state[0]; wire OE = state[1]; wire Z1, Z2, Z3, Z4, Z5, Z6, Z7, Z8, Z9; - + wire [3:0] Z10; + wire Z11; + Test1 test1(/*AUTOINST*/ // Inouts .Z1 (Z1), @@ -45,58 +47,86 @@ module t (clk); .Z9 (Z9), // Inputs .OE (OE)); - - + + Test6 test6(/*AUTOINST*/ + // Inouts + .Z10 (Z10[3:0]), + // Inputs + .OE (OE)); + + Test7 test7(/*AUTOINST*/ + // Outputs + .Z11 (Z11), + // Inputs + .state (state[2:0])); + always @(posedge clk) begin state <= state + 1; `ifdef TEST_VERBOSE - $display(" Z1=%b 2=%b 3=%b 4=%b 5=%b 6=%b 7=%b 8=%b 9=%b",Z1,Z2,Z3,Z4,Z5,Z6,Z7,Z8,Z9); + $write("[%0t] state=%d Z1=%b 2=%b 3=%b 4=%b 5=%b 6=%b 7=%b 8=%b 9=%b 10=%b 11=%b\n", + $time, state, Z1,Z2,Z3,Z4,Z5,Z6,Z7,Z8,Z9,Z10,Z11); `endif if(state == 0) begin if(Z1 !== 1'b1) $stop; // tests pullups if(Z2 !== 1'b1) $stop; if(Z3 !== 1'b1) $stop; +`ifndef VERILATOR if(Z4 !== 1'b1) $stop; +`endif if(Z5 !== 1'b1) $stop; if(Z6 !== 1'b1) $stop; if(Z7 !== 1'b0) $stop; if(Z8 !== 1'b0) $stop; if(Z9 !== 1'b1) $stop; - end + if(Z10 !== 4'b0001) $stop; + if(Z11 !== 1'b0) $stop; + end else if(state == 1) begin if(Z1 !== 1'b1) $stop; // tests pullup if(Z2 !== 1'b1) $stop; if(Z3 !== 1'b1) $stop; +`ifndef VERILATOR if(Z4 !== 1'b1) $stop; +`endif if(Z5 !== 1'b1) $stop; if(Z6 !== 1'b1) $stop; if(Z7 !== 1'b0) $stop; if(Z8 !== 1'b0) $stop; if(Z9 !== 1'b1) $stop; + if(Z10 !== 4'b0001) $stop; + if(Z11 !== 1'b1) $stop; end else if(state == 2) begin if(Z1 !== 1'b0) $stop; // tests output driver low if(Z2 !== 1'b0) $stop; - //if(Z3 !== 1'b1) $stop; // "X" + if(Z3 !== 1'b1 && Z3 !== 1'bx) $stop; // Conflicts +`ifndef VERILATOR if(Z4 !== 1'b1) $stop; +`endif if(Z5 !== 1'b1) $stop; if(Z6 !== 1'b0) $stop; if(Z7 !== 1'b1) $stop; if(Z8 !== 1'b1) $stop; if(Z9 !== 1'b0) $stop; + if(Z10 !== 4'b0010) $stop; + //if(Z11 !== 1'bx) $stop; // Doesn't matter end else if(state == 3) begin if(Z1 !== 1'b1) $stop; // tests output driver high if(Z2 !== 1'b1) $stop; if(Z3 !== 1'b1) $stop; +`ifndef VERILATOR if(Z4 !== 1'b1) $stop; +`endif if(Z5 !== 1'b1) $stop; if(Z6 !== 1'b0) $stop; if(Z7 !== 1'b1) $stop; if(Z8 !== 1'b1) $stop; if(Z9 !== 1'b0) $stop; - end + if(Z10 !== 4'b0010) $stop; + if(Z11 !== 1'b1) $stop; + end else if(state == 4) begin $write("*-* All Finished *-*\n"); $finish; @@ -111,6 +141,7 @@ module t (clk); pulldown(Z7); pullup(Z8); pulldown(Z9); + pulldown pd10[3:0] (Z10); endmodule @@ -143,6 +174,26 @@ module Test5(input OE, inout Z6, inout Z7, inout Z8, inout Z9); assign Z9 = (OE) ? 1'bz : 1'b1; endmodule +// AND gate tristates +module Test6(input OE, inout [3:0] Z10); + wire [1:0] i; + Test6a a (.OE(OE), .Z({Z10[0],Z10[1]})); + Test6a b (.OE(~OE), .Z({Z10[2],Z10[0]})); +endmodule + +module Test6a(input OE, inout [1:0] Z); + assign Z = (OE) ? 2'b01 : 2'bzz; +endmodule + +module Test7(input [2:0] state, output reg Z11); + always @(*) begin + casez (state) + 3'b000: Z11 = 1'b0; + 3'b0?1: Z11 = 1'b1; + default: Z11 = 1'bx; + endcase + end +endmodule // This is not implemented yet //module Test3(input OE, input A, inout Z3);