Logo Search packages:      
Sourcecode: verilator version File versions  Download package

V3Tristate.cpp

//*************************************************************************
// DESCRIPTION: Verilator: Deals with tristate logic
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Lane Brooks with Wilson Snyder, Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2010 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.
//
//*************************************************************************
// 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.
//
//*************************************************************************

#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <algorithm>
#include <map>

#include "V3Global.h"
#include "V3Tristate.h"
#include "V3Ast.h"
#include "V3Const.h"
#include "V3Stats.h"
#include "V3Inst.h"

//######################################################################

typedef std::vector<AstVar*> VarVec;
typedef std::vector<AstVarRef*> RefVec;
typedef std::map<AstVar*, RefVec*> VarMap;

//######################################################################

class TristateBaseVisitor : public AstNVisitor {
public:
    static int debug() {
      static int level = -1;
      if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
      return level;
    }
};

//######################################################################

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 (nodep->rhsp()->castCond()) {
          AstCond* condp = nodep->rhsp()->castCond();
          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);
          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 cooresponds 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++),
                          AstLogicPacked(), width);
      enp->varType2Out();

      if (enp->width() != enrhsp->width()) {
          if (enrhsp->width()==1) { // 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->widthMin());
          } else {
            enrhsp->v3error("Don't know how to deal with selection logic wider than 1 bit");
          }
      }
        m_modp->addStmtp(enp);
        m_modp->addStmtp(new AstAssignW (enp->fileline(),
                               new AstVarRef (enp->fileline(), enp, true),
                               enrhsp));

      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.
    //**********************************************************************

    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<AstVar*, RefVec*>(key, refs));
          } else {
            (*it).second->push_back(nodep);
          }
          nodep->user3p(m_sel); // attach the sel to this varref
      }
      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
    }
    virtual ~TristateExpander() { }
};

//######################################################################

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

    AstUser1InUse m_inuser1;
    AstUser2InUse m_inuser2;
    AstUser3InUse m_inuser3;

    AstNodeModule*      m_modp;           // Current module
    AstCell* m_cellp; // current cell
    int m_unique;

    virtual void visit(AstNetlist* nodep, AstNUser*) {
      AstNode::user1ClearTree();
      nodep->iterateChildrenBackwards(*this);
    }

    virtual void visit(AstNodeModule* nodep, AstNUser*) {
      UINFO(9," MOD   "<<nodep<<endl);
      m_unique = 0;
      VarMap* lhsmapp = new VarMap();

      // expand tristate nodes and detect multiple LHS drivers for this module
      TristateExpander(nodep, lhsmapp);

      // iterate the children to grab any  __en signals from subcells
      m_modp = nodep;
      nodep->iterateChildren(*this);
      m_modp = NULL;

      // go through each multiple lhs driver & collapse it to a single driver
      for (VarMap::iterator nextit, it=lhsmapp->begin(); it != lhsmapp->end(); it=nextit) {
          nextit = it; ++nextit;
          m_unique = 0;
          AstVar* lhsp = (*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) {
            AstVarRef* refp = (*ii);
            if (!refp->user1p()) { // if no __en signal, then delete the entry
                complete = false;
            } else {
                found_one++;
            }
          }
          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 <<endl);
            }
            lhsmapp->erase(lhsp);
            delete refs;
            continue;
          }

          UINFO(9, "       TRISTATE LHS DRIVER FOUND:" << lhsp << endl);

          AstNode* orp = NULL,* andp = NULL,* undrivenp = NULL,* newenlogicp = 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();
            AstVar* newlhsp = new AstVar(lhsp->fileline(),
                                   AstVarType::MODULETEMP,
                                   lhsp->name()+"__lhs"+cvtToStr(m_unique++),
                                   AstLogicPacked(), w);
            nodep->addStmtp(newlhsp);

            // 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),
                               AstLogicPacked(), 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));
                selp->deleteTree();
            } else {
                refp->varp(newlhsp); // assign the new var to the varref
                refp->name(newlhsp->name());
            }

            // 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 (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();
            } else {
                num.setAllBitsX();
            }
            undrivenp = new AstAnd(nodep->fileline(), undrivenp,
                               new AstConst(nodep->fileline(), num));
            orp = new AstOr(nodep->fileline(), orp, undrivenp);
          }
          nodep->addStmtp(new AstAssignW(lhsp->fileline(),
                                 new AstVarRef(lhsp->fileline(), lhsp, true), orp));

          // delete the map and vector list now that we have collapsed it.
          lhsmapp->erase(lhsp);
          delete refs;
      }
      delete lhsmapp; // delete the map now that we are done
      nodep->user1p(NULL);
    }

    virtual void visit(AstCell* nodep, AstNUser*) {
      m_cellp = nodep;
      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();
          pinp->width(enp->width(),enp->widthMin());
          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);
    }

    // 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
    }

public:
    // CONSTUCTORS
    TristateVisitor(AstNode* nodep) {
      nodep->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 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) {
            // 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;
      nodep->accept(*this);
    }
    virtual ~InoutVisitor() { }
};

//######################################################################
// Tristate class functions

void V3Tristate::tristateAll(AstNetlist* nodep) {
    UINFO(2,__FUNCTION__<<": "<<endl);
    TristateVisitor visitor (nodep);
}

void V3Tristate::inoutAll(AstNetlist* nodep) {
    UINFO(2,__FUNCTION__<<": "<<endl);
    InoutVisitor visitor (nodep);
}

Generated by  Doxygen 1.6.0   Back to index