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

V3Premit.cpp

//*************************************************************************
// DESCRIPTION: Verilator: Add temporaries, such as for premit nodes
//
// Code available from: http://www.veripool.org/verilator
//
// AUTHORS: Wilson Snyder with 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.
//
//*************************************************************************
// V3Premit's Transformations:
//
// Each module:
//    For each wide OP, make a a temporary variable with the wide value
//    For each deep expression, assign expression to temporary.
//
// Each display (independant transformation; here as Premit is a good point)
//    If autoflush, insert a flush
//
//*************************************************************************

#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <algorithm>
#include <list>

#include "V3Global.h"
#include "V3Premit.h"
#include "V3Ast.h"

//######################################################################
// Premit state, as a visitor of each AstNode

class PremitVisitor : public AstNVisitor {
private:
    // NODE STATE
    //  AstNodeMath::user()   -> bool.  True if iterated already
    //  AstShiftL::user2()    -> bool.  True if converted to conditional
    //  AstShiftR::user2()    -> bool.  True if converted to conditional
    AstUser1InUse m_inuser1;
    AstUser2InUse m_inuser2;

    // STATE
    AstNodeModule*      m_modp;           // Current module
    AstCFunc*           m_funcp;    // Current block
    AstNode*            m_stmtp;    // Current statement
    AstWhile*           m_inWhilep; // Inside while loop, special statement additions
    AstTraceInc*  m_inTracep; // Inside while loop, special statement additions
    bool          m_assignLhs;      // Inside assignment lhs, don't breakup extracts

    // METHODS
    static int debug() {
      static int level = -1;
      if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
      return level;
    }

    bool assignNoTemp(AstNodeAssign* nodep) {
      return (nodep->lhsp()->castVarRef()
            && !nodep->lhsp()->castVarRef()->varp()->isSc()
            && nodep->rhsp()->castConst());
    }
    void checkNode(AstNode* nodep) {
      // Consider adding a temp for this expression.
      // We need to avoid adding temps to the following:
      //   ASSIGN(x, *here*)
      //   ASSIGN(CONST*here*, VARREF(!sc))
      //   ARRAYSEL(*here*, ...)   (No wides can be in any argument but first, so we don't check which arg is wide)
      //   ASSIGN(x, SEL*HERE*(ARRAYSEL()...)   (m_assignLhs==true handles this.)
      //UINFO(9, "   Check: "<<nodep<<endl);
      //UINFO(9, "     Detail stmtp="<<(m_stmtp?"Y":"N")<<" U="<<(nodep->user1()?"Y":"N")<<" IW "<<(nodep->isWide()?"Y":"N")<<endl);
      if (m_stmtp
          && !nodep->user1()) {     // Not already done
          if (nodep->isWide()) {          // Else might be cell interconnect or something
            if (m_assignLhs) {
            } else if (nodep->firstAbovep()
                     && nodep->firstAbovep()->castNodeAssign()
                     && assignNoTemp(nodep->firstAbovep()->castNodeAssign())) {
                // Not much point if it's just a direct assignment to a constant
            } else if (nodep->firstAbovep()
                     && nodep->firstAbovep()->castArraySel()) {  // ArraySel's are pointer refs, ignore
            } else {
                UINFO(4,"Cre Temp: "<<nodep<<endl);
                createDeepTemp(nodep);
            }
          }
      }
    }

    AstVar* getBlockTemp(AstNode* nodep) {
      string newvarname = ((string)"__Vtemp"+cvtToStr(m_modp->varNumGetInc()));
      AstVar* varp = new AstVar (nodep->fileline(), AstVarType::STMTTEMP, newvarname,
                           AstLogicPacked(), nodep->widthMin());
      m_funcp->addInitsp(varp);
      return varp;
    }

    void insertBeforeStmt(AstNode* newp) {
      // Insert newp before m_stmtp
      if (m_inWhilep) {
          // Statements that are needed for the 'condition' in a while actually have to
          // be put before & after the loop, since we can't do any statements in a while's (cond).
          m_inWhilep->addPrecondsp(newp);
      } else if (m_inTracep) {
          m_inTracep->addPrecondsp(newp);
      } else {
          AstNRelinker linker;
          m_stmtp->unlinkFrBack(&linker);
          newp->addNext(m_stmtp);
          linker.relink(newp);
      }
    }

    void createDeepTemp(AstNode* nodep) {
      if (debug()>8) nodep->dumpTree(cout,"deepin:");

      AstNRelinker linker;
      nodep->unlinkFrBack(&linker);

      AstVar* varp = getBlockTemp(nodep);
      // Replace node tree with reference to var
      AstVarRef* newp = new AstVarRef (nodep->fileline(), varp, false);
      linker.relink(newp);
      // Put assignment before the referencing statement
      AstAssign* assp = new AstAssign (nodep->fileline(),
                               new AstVarRef(nodep->fileline(), varp, true),
                               nodep);
      insertBeforeStmt(assp);
      if (debug()>8) assp->dumpTree(cout,"deepou:");
      nodep->user1(true);  // Don't add another assignment
    }

    // VISITORS
    virtual void visit(AstNodeModule* nodep, AstNUser*) {
      UINFO(4," MOD   "<<nodep<<endl);
      m_modp = nodep;
      m_funcp = NULL;
      nodep->iterateChildren(*this);
      m_modp = NULL;
    }
    virtual void visit(AstCFunc* nodep, AstNUser*) {
      m_funcp = nodep;
      nodep->iterateChildren(*this);
      m_funcp = NULL;
    }
    void startStatement(AstNode* nodep) {
      m_assignLhs = false;
      if (m_funcp) m_stmtp = nodep;
    }
    virtual void visit(AstWhile* nodep, AstNUser*) {
      UINFO(4,"  WHILE  "<<nodep<<endl);
      startStatement(nodep);
      nodep->precondsp()->iterateAndNext(*this);
      startStatement(nodep);
      m_inWhilep = nodep;
      nodep->condp()->iterateAndNext(*this);
      m_inWhilep = NULL;
      startStatement(nodep);
      nodep->bodysp()->iterateAndNext(*this);
      nodep->incsp()->iterateAndNext(*this);
      m_stmtp = NULL;
    }
    virtual void visit(AstNodeAssign* nodep, AstNUser*) {
      startStatement(nodep);
      nodep->rhsp()->iterateAndNext(*this);
      m_assignLhs = true;
      nodep->lhsp()->iterateAndNext(*this);
      m_assignLhs = false;
      m_stmtp = NULL;
    }
    virtual void visit(AstNodeStmt* nodep, AstNUser*) {
      UINFO(4,"  STMT  "<<nodep<<endl);
      startStatement(nodep);
      nodep->iterateChildren(*this);
      m_stmtp = NULL;
    }
    virtual void visit(AstTraceInc* nodep, AstNUser*) {
      startStatement(nodep);
      m_inTracep = nodep;
      nodep->iterateChildren(*this);
      m_inTracep = NULL;
      m_stmtp = NULL;
    }
    void visitShift (AstNodeBiop* nodep) {
      // Shifts of > 32/64 bits in C++ will wrap-around and generate non-0s
      if (!nodep->user2Inc()) {
          UINFO(4,"  ShiftFix  "<<nodep<<endl);
          if (nodep->widthMin()<=64  // Else we'll use large operators which work right
            // C operator's width must be < maximum shift which is based on Verilog width
            && nodep->width() < (1LL<<nodep->rhsp()->widthMin())) {
            AstNRelinker replaceHandle;
            nodep->unlinkFrBack(&replaceHandle);
            AstNode* constzerop;
            if (nodep->signedFlavor()) {
                // Then over shifting gives the sign bit, not all zeros
                // Note *NOT* clean output -- just like normal shift!
                // Create equivalent of VL_SIGNONES_(node_width)
                constzerop = new AstUnaryMin (nodep->fileline(),
                                      new AstShiftR(nodep->fileline(),
                                                nodep->lhsp()->cloneTree(false),
                                                new AstConst(nodep->fileline(),
                                                           nodep->widthMin()-1),
                                                nodep->width()));
            } else {
                V3Number zeronum  (nodep->fileline(), nodep->width(), 0);
                constzerop = new AstConst(nodep->fileline(), zeronum);
            }
            constzerop->widthFrom (nodep);  // unsigned
            V3Number widthnum (nodep->fileline(), nodep->rhsp()->widthMin(), nodep->width()-1);
            AstNode* constwidthp = new AstConst(nodep->fileline(), widthnum);
            constwidthp->widthFrom (nodep->rhsp());  // unsigned
            AstCond* newp =
                new AstCond (nodep->fileline(),
                         new AstLte (nodep->fileline(),
                                   nodep->rhsp()->cloneTree(false),
                                   constwidthp),
                         nodep,
                         constzerop);
            newp->widthSignedFrom(nodep);
            replaceHandle.relink(newp);
          }
      }
      nodep->iterateChildren(*this); checkNode(nodep);
    }
    virtual void visit(AstShiftL* nodep, AstNUser*) {
      visitShift(nodep);
    }
    virtual void visit(AstShiftR* nodep, AstNUser*) {
      visitShift(nodep);
    }
    virtual void visit(AstShiftRS* nodep, AstNUser*) {
      visitShift(nodep);
    }
    // Operators
    virtual void visit(AstNodeTermop* nodep, AstNUser*) {
      nodep->iterateChildren(*this); checkNode(nodep); }
    virtual void visit(AstNodeUniop* nodep, AstNUser*) {
      nodep->iterateChildren(*this); checkNode(nodep); }
    virtual void visit(AstNodeBiop* nodep, AstNUser*) {
      nodep->iterateChildren(*this); checkNode(nodep); }
    virtual void visit(AstUCFunc* nodep, AstNUser*) {
      nodep->iterateChildren(*this); checkNode(nodep); }
    virtual void visit(AstSel* nodep, AstNUser*) {
      nodep->fromp()->iterateAndNext(*this);
      {   // Only the 'from' is part of the assignment LHS
          bool prevAssign = m_assignLhs;
          m_assignLhs = false;
          nodep->lsbp()->iterateAndNext(*this);
          nodep->widthp()->iterateAndNext(*this);
          m_assignLhs = prevAssign;
      }
      checkNode(nodep); }
    virtual void visit(AstConst* nodep, AstNUser*) {
      nodep->iterateChildren(*this); checkNode(nodep); }
    virtual void visit(AstNodeCond* nodep, AstNUser*) {
      nodep->iterateChildren(*this);
      if (nodep->expr1p()->isWide()
          && !nodep->condp()->castConst()
          && !nodep->condp()->castVarRef()) {
          // We're going to need the expression several times in the expanded code,
          // so might as well make it a common expression
          createDeepTemp(nodep->condp());
      }
      checkNode(nodep);
    }

    // Autoflush
    virtual void visit(AstDisplay* nodep, AstNUser*) {
      startStatement(nodep);
      nodep->iterateChildren(*this);
      m_stmtp = NULL;
      if (v3Global.opt.autoflush()) {
          AstNode* searchp = nodep->nextp();
          while (searchp && searchp->castComment()) searchp = searchp->nextp();
          if (searchp
            && searchp->castDisplay()
            && nodep->filep()->sameTree(searchp->castDisplay()->filep())) {
            // There's another display next; we can just wait to flush
          } else {
            UINFO(4,"Autoflush "<<nodep<<endl);
            nodep->addNextHere(new AstFFlush(nodep->fileline(),
                                     nodep->filep()->cloneTree(true)));
          }
      }
    }

    //--------------------
    // Default: Just iterate
    virtual void visit(AstVar* nodep, AstNUser*) {}   // Don't hit varrefs under vars
    virtual void visit(AstNode* nodep, AstNUser*) {
      nodep->iterateChildren(*this);
    }

public:
    // CONSTUCTORS
    PremitVisitor(AstNetlist* nodep) {
      m_modp = NULL;
      m_funcp = NULL;
      m_stmtp = NULL;
      m_inWhilep = NULL;
      m_inTracep = NULL;
      nodep->accept(*this);
    }
    virtual ~PremitVisitor() {}
};

//----------------------------------------------------------------------
// Top loop

//######################################################################
// Premit class functions

void V3Premit::premitAll(AstNetlist* nodep) {
    UINFO(2,__FUNCTION__<<": "<<endl);
    PremitVisitor visitor (nodep);
}

Generated by  Doxygen 1.6.0   Back to index