Tristate support; merge from branch.
This commit is contained in:
parent
6f81a9cb1e
commit
0e4f9170fa
5
Changes
5
Changes
|
|
@ -5,6 +5,11 @@ indicates the contributor was also the author of the fix; Thanks!
|
|||
|
||||
* Verilator 3.700***
|
||||
|
||||
** Add limited support for tristate inouts. Written by Lane Brooks.
|
||||
This allows common pad ring and tristate-mux structures to be
|
||||
Verilated. See the documentation for more information on supported
|
||||
constructs.
|
||||
|
||||
** Add --coverage_toggle for toggle coverage analysis.
|
||||
Running coverage now requires SystemPerl 1.301 or newer.
|
||||
|
||||
|
|
|
|||
|
|
@ -1495,9 +1495,24 @@ all variables to one at startup finds most problems.
|
|||
|
||||
=head2 Tri/Inout
|
||||
|
||||
As a 2 state compiler, tristate and inouts are not supported.
|
||||
Traditionally only chip "cores" are Verilated, the pad rings have been
|
||||
written by hand in C++.
|
||||
Verilator converts some simple tristate structures into two state. 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
|
||||
|
||||
Pullup and pulldown are also supported. External logic will be needed to
|
||||
combine these signals with any external drivers.
|
||||
|
||||
Tristate drivers are not supported inside functions and tasks; a inout
|
||||
there will be considered a two state variable that is read and written
|
||||
instead of a four state variable.
|
||||
|
||||
=head2 Functions & Tasks
|
||||
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@ RAW_OBJS = \
|
|||
V3Task.o \
|
||||
V3Trace.o \
|
||||
V3TraceDecl.o \
|
||||
V3Tristate.o \
|
||||
V3Unknown.o \
|
||||
V3Unroll.o \
|
||||
V3Width.o \
|
||||
|
|
|
|||
|
|
@ -283,6 +283,8 @@ public:
|
|||
virtual string name() const { return m_name; } // * = Var name
|
||||
virtual bool maybePointedTo() const { return true; }
|
||||
AstVarType varType() const { return m_varType; } // * = Type of variable
|
||||
void varType2Out() { m_tristate=0; m_input=0; m_output=1; }
|
||||
void varType2In() { m_tristate=0; m_input=1; m_output=0; }
|
||||
string cType() const; // Return C type for declaration: bool, uint32_t, uint64_t, etc.
|
||||
string scType() const; // Return SysC type: bool, uint32_t, uint64_t, sc_bv
|
||||
void combineType(AstVarType type);
|
||||
|
|
@ -955,6 +957,23 @@ public:
|
|||
void allowImplicit(bool flag) { m_allowImplicit = flag; }
|
||||
};
|
||||
|
||||
struct AstPull : public AstNode {
|
||||
private:
|
||||
bool m_direction;
|
||||
public:
|
||||
AstPull(FileLine* fileline, AstNode* lhsp, bool direction)
|
||||
: AstNode(fileline) {
|
||||
setOp1p(lhsp);
|
||||
m_direction = direction;
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(Pull, PULL)
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return direction()==samep->castPull()->direction(); }
|
||||
void lhsp(AstNode* np) { setOp1p(np); }
|
||||
AstNode* lhsp() const { return op1p()->castNode(); } // op1 = Assign to
|
||||
uint32_t direction() const { return (uint32_t) m_direction; }
|
||||
};
|
||||
|
||||
struct AstAssignPre : public AstNodeAssign {
|
||||
// Like Assign, but predelayed assignment requiring special order handling
|
||||
AstAssignPre(FileLine* fileline, AstNode* lhsp, AstNode* rhsp)
|
||||
|
|
|
|||
|
|
@ -291,6 +291,14 @@ V3Number& V3Number::setSingleBits(char value) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
V3Number& V3Number::setAllBits0() {
|
||||
for (int i=0; i<words(); i++) { m_value[i] = m_valueX[i]=0; }
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setAllBits1() {
|
||||
for (int i=0; i<words(); i++) { m_value[i]= ~0; m_valueX[i] = 0; }
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::setAllBitsX() {
|
||||
for (int i=0; i<words(); i++) { m_value[i]=m_valueX[i] = ~0; }
|
||||
return *this;
|
||||
|
|
@ -584,6 +592,22 @@ V3Number& V3Number::opBitsXZ (const V3Number& lhs) { // 0/1->1, X/Z->0
|
|||
}
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::opBitsZ (const V3Number& lhs) { // 0/1->1, X/Z->0
|
||||
// op i, L(lhs) bit return
|
||||
setZero();
|
||||
for(int bit=0; bit<this->width(); bit++) {
|
||||
if (lhs.bitIsZ(bit)) { setBit(bit,1); }
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
V3Number& V3Number::opBitsNonZ (const V3Number& lhs) { // 0/1->1, X/Z->0
|
||||
// op i, L(lhs) bit return
|
||||
setZero();
|
||||
for(int bit=0; bit<this->width(); bit++) {
|
||||
if (!lhs.bitIsZ(bit)) { setBit(bit,1); }
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
// Operators - Simple per-bit logical ops
|
||||
|
|
|
|||
|
|
@ -115,6 +115,8 @@ public:
|
|||
// SETTERS
|
||||
V3Number& setAllBitsX();
|
||||
V3Number& setAllBitsZ();
|
||||
V3Number& setAllBits0();
|
||||
V3Number& setAllBits1();
|
||||
V3Number& setMask(int nbits); // IE if nbits=1, then 0b1, if 2->0b11, if 3->0b111 etc
|
||||
|
||||
// ACCESSORS
|
||||
|
|
@ -129,6 +131,8 @@ public:
|
|||
bool isSigned() const { return m_signed; } // Only correct for parsing of numbers from strings, otherwise not used (use AstConst::isSigned())
|
||||
bool isNegative() const { return bitIs1(width()-1); }
|
||||
bool isFourState() const { for (int i=0;i<words();i++) {if (m_valueX[i]) return true;} return false; }
|
||||
bool hasZ() const { for(int i=0;i<words();i++) {if((~m_value[i]) & m_valueX[i]) return true;} return false;}
|
||||
bool isAllZ() const { for(int i=0;i<width();i++) { if(!bitIsZ(i)){return false;} } return true; }
|
||||
bool isEqZero() const;
|
||||
bool isNeqZero() const;
|
||||
bool isEqOne() const;
|
||||
|
|
@ -158,6 +162,8 @@ public:
|
|||
V3Number& opBitsNonX(const V3Number& lhs); // 0/1->1, X/Z->0
|
||||
V3Number& opBitsOne (const V3Number& lhs); // 1->1, 0/X/Z->0
|
||||
V3Number& opBitsXZ (const V3Number& lhs); // 0/1->0, X/Z->1
|
||||
V3Number& opBitsZ (const V3Number& lhs); // Z->1, 0/1/X->0
|
||||
V3Number& opBitsNonZ(const V3Number& lhs); // Z->0, 0/1/X->1
|
||||
//
|
||||
V3Number& opAssign (const V3Number& lhs);
|
||||
V3Number& opExtendS (const V3Number& lhs); // Sign extension
|
||||
|
|
|
|||
|
|
@ -0,0 +1,692 @@
|
|||
//*************************************************************************
|
||||
// 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-2009 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.
|
||||
//
|
||||
// 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"
|
||||
|
||||
//######################################################################
|
||||
|
||||
typedef std::vector<AstVar*> VarVec;
|
||||
typedef std::vector<AstVarRef*> RefVec;
|
||||
typedef std::map<AstVar*, RefVec*> VarMap;
|
||||
|
||||
//######################################################################
|
||||
|
||||
class TristateBaseVisitor : public AstNVisitor {
|
||||
public:
|
||||
//int debug() { return 9; }
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
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;
|
||||
AstModule* 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 {
|
||||
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 seperate 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 {
|
||||
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++),
|
||||
(width>1) ? new AstRange(outp->fileline(), width-1, 0) : (AstRange *) NULL);
|
||||
|
||||
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 {
|
||||
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(AstModule* 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;
|
||||
|
||||
AstModule* 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(AstModule* 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.
|
||||
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++),
|
||||
(w>1) ? new AstRange(nodep->fileline(), w-1, 0) : (AstRange *) NULL);
|
||||
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),
|
||||
(ws>1) ? new AstRange(nodep->fileline(), ws-1, 0) : (AstRange*) NULL);
|
||||
//
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
AstModule* 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(AstModule* 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();
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// -*- C++ -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Add Unknown assigns
|
||||
//
|
||||
// Code available from: http://www.veripool.org/verilator
|
||||
//
|
||||
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2003-2009 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.
|
||||
//
|
||||
// Verilator is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef _V3TRISTATE_H_
|
||||
#define _V3TRISTATE_H_ 1
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
#include "V3Error.h"
|
||||
#include "V3Ast.h"
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3Tristate {
|
||||
public:
|
||||
static void tristateAll(AstNetlist* nodep);
|
||||
static void inoutAll(AstNetlist* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -700,8 +700,15 @@ private:
|
|||
}
|
||||
nodep->width(awidth,awidth);
|
||||
} else {
|
||||
if (nodep->modVarp()->isTristate()) nodep->v3error("Unsupported: inouts: Verilator is a 2 state simulator\n");
|
||||
// if support tristates need some mess to force both sides to proper size
|
||||
if (nodep->modVarp()->isTristate()) {
|
||||
if (pinwidth != expwidth) {
|
||||
nodep->v3error("Unsupported: Port connection "<<nodep->prettyName()<<" to inout signal "
|
||||
<<" requires "<<pinwidth
|
||||
<<" bits, but connection's "<<nodep->exprp()->typeName()
|
||||
<<" generates "<<expwidth<<" bits.");
|
||||
// otherwise would need some mess to force both sides to proper size
|
||||
}
|
||||
}
|
||||
if (inputPin) {
|
||||
// input pin is lhs, expr is rhs; resize expr to match
|
||||
awidth = pinwidth;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@
|
|||
#include "V3Task.h"
|
||||
#include "V3Trace.h"
|
||||
#include "V3TraceDecl.h"
|
||||
#include "V3Tristate.h"
|
||||
#include "V3Unknown.h"
|
||||
#include "V3Unroll.h"
|
||||
#include "V3Width.h"
|
||||
|
|
@ -182,10 +183,17 @@ void process () {
|
|||
V3Const::constifyAllLint(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("const.tree"));
|
||||
|
||||
// Expand Inouts
|
||||
V3Tristate::inoutAll(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("inouts.tree"));
|
||||
|
||||
// Remove cell arrays (must be between V3Width and scoping)
|
||||
V3Inst::dearrayAll(v3Global.rootp());
|
||||
//v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("dearray.tree"));
|
||||
|
||||
V3Tristate::tristateAll(v3Global.rootp());
|
||||
v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("tristate.tree"));
|
||||
|
||||
// Move assignments from X into MODULE temps.
|
||||
// (Before flattening, so each new X variable is shared between all scopes of that module.)
|
||||
V3Unknown::unknownAll(v3Global.rootp());
|
||||
|
|
|
|||
|
|
@ -213,6 +213,8 @@ escid \\[^ \t\f\r\n]+
|
|||
"output" {yylval.fileline = CRELINE(); return yOUTPUT;}
|
||||
"parameter" {yylval.fileline = CRELINE(); return yPARAMETER;}
|
||||
"posedge" {yylval.fileline = CRELINE(); return yPOSEDGE;}
|
||||
"pulldown" {yylval.fileline = CRELINE(); return yPULLDOWN;}
|
||||
"pullup" {yylval.fileline = CRELINE(); return yPULLUP;}
|
||||
"reg" {yylval.fileline = CRELINE(); return yREG;}
|
||||
"scalared" {yylval.fileline = CRELINE(); return ySCALARED;}
|
||||
"specify" {yylval.fileline = CRELINE(); return ySPECIFY;}
|
||||
|
|
@ -260,8 +262,6 @@ escid \\[^ \t\f\r\n]+
|
|||
"notif1" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
"pmos" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
"primitive" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
"pulldown" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
"pullup" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
"pull0" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
"pull1" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
"rcmos" {yyerrorf("Unsupported: Verilog 1995 reserved word not implemented: %s",yytext);}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ class AstSenTree;
|
|||
AstNode* nodep;
|
||||
|
||||
AstAssignW* assignwp;
|
||||
AstPull* pullp;
|
||||
AstBegin* beginp;
|
||||
AstCase* casep;
|
||||
AstCaseItem* caseitemp;
|
||||
|
|
@ -205,6 +206,8 @@ class AstSenTree;
|
|||
%token<fileline> yPOSEDGE "posedge"
|
||||
%token<fileline> yPRIORITY "priority"
|
||||
%token<fileline> yPROPERTY "property"
|
||||
%token<fileline> yPULLDOWN "pulldown"
|
||||
%token<fileline> yPULLUP "pullup"
|
||||
%token<fileline> yREG "reg"
|
||||
%token<fileline> ySCALARED "scalared"
|
||||
%token<fileline> ySIGNED "signed"
|
||||
|
|
@ -1254,6 +1257,8 @@ gateDecl<nodep>:
|
|||
| yNOR delayE gateNorList ';' { $$ = $3; }
|
||||
| yXOR delayE gateXorList ';' { $$ = $3; }
|
||||
| yXNOR delayE gateXnorList ';' { $$ = $3; }
|
||||
| yPULLUP delayE gatePullupList ';' { $$ = $3; }
|
||||
| yPULLDOWN delayE gatePulldownList ';' { $$ = $3; }
|
||||
;
|
||||
|
||||
gateBufList<nodep>:
|
||||
|
|
@ -1288,6 +1293,14 @@ gateXnorList<nodep>:
|
|||
gateXnor { $$ = $1; }
|
||||
| gateXnorList ',' gateXnor { $$ = $1->addNext($3); }
|
||||
;
|
||||
gatePullupList<nodep>:
|
||||
gatePullup { $$ = $1; }
|
||||
| gatePullupList ',' gatePullup { $$ = $1->addNext($3); }
|
||||
;
|
||||
gatePulldownList<nodep>:
|
||||
gatePulldown { $$ = $1; }
|
||||
| gatePulldownList ',' gatePulldown { $$ = $1->addNext($3); }
|
||||
;
|
||||
|
||||
gateBuf<assignwp>: gateIdE instRangeE '(' varRefDotBit ',' expr ')' { $$ = new AstAssignW ($3,$4,$6); $$->allowImplicit(true); }
|
||||
;
|
||||
|
|
@ -1305,7 +1318,10 @@ gateXor<assignwp>: gateIdE instRangeE '(' varRefDotBit ',' gateXorPinList ')' {
|
|||
;
|
||||
gateXnor<assignwp>: gateIdE instRangeE '(' varRefDotBit ',' gateXorPinList ')' { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); $$->allowImplicit(true); }
|
||||
;
|
||||
|
||||
gatePullup<pullp>: gateIdE instRangeE '(' varRefDotBit ')' { $$ = new AstPull ($3, $4, true); }
|
||||
;
|
||||
gatePulldown<pullp>: gateIdE instRangeE '(' varRefDotBit ')' { $$ = new AstPull ($3, $4, false); }
|
||||
;
|
||||
gateIdE: /*empty*/ {}
|
||||
| yaID {}
|
||||
;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2008 by Lane Brooks
|
||||
|
||||
#include "Vt_tri_inout.h"
|
||||
|
||||
Vt_tri_inout *tb = NULL;
|
||||
|
||||
double sc_time_stamp() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool check() {
|
||||
bool pass;
|
||||
int Z;
|
||||
if (tb->SEL) {
|
||||
Z = tb->A;
|
||||
} else {
|
||||
Z = tb->B;
|
||||
}
|
||||
|
||||
if (tb->Z == tb->Y1 && tb->Z == tb->Y2 && tb->Z == Z) {
|
||||
printf("PASS: ");
|
||||
pass = true;
|
||||
} else {
|
||||
printf("FAIL: ");
|
||||
pass = false;
|
||||
}
|
||||
printf ("SEL=%d A=%d B=%d Z=%d Y1=%d Y2=%d\n", tb->SEL, tb->A, tb->B, tb->Z, tb->Y1, tb->Y2);
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main() {
|
||||
bool pass = true;
|
||||
tb = new Vt_tri_inout("tb");
|
||||
|
||||
// loop through every possibility and check the result
|
||||
for (tb->SEL=0; tb->SEL<2; tb->SEL++) {
|
||||
for (tb->A=0; tb->A<2; tb->A++) {
|
||||
for (tb->B=0; tb->B<2; tb->B++) {
|
||||
tb->eval();
|
||||
if (!check()) {
|
||||
pass = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pass) {
|
||||
cout << "*-* All Finished *-*" << endl;
|
||||
tb->final();
|
||||
} else {
|
||||
vl_fatal(__FILE__,__LINE__,"top", "Unexpected results from inout test\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 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 (
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
v_flags2 => ["--exe t/$Last_Self->{name}.cpp"],
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2008 by Lane Brooks
|
||||
|
||||
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));
|
||||
endmodule
|
||||
|
||||
module pass (input A, input OE, inout Z, output Y);
|
||||
io io(.A(A), .OE(OE), .Z(Z), .Y(Y));
|
||||
endmodule
|
||||
|
||||
module io (input A, input OE, inout Z, output Y);
|
||||
assign Z = (OE) ? A : 1'bz;
|
||||
assign Y = Z;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2008 by Lane Brooks
|
||||
|
||||
#include "Vt_tri_pullup.h"
|
||||
|
||||
Vt_tri_pullup *tb = NULL;
|
||||
|
||||
double sc_time_stamp() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool check() {
|
||||
bool pass;
|
||||
int Z, Y, X;
|
||||
if (tb->OE) {
|
||||
Z = tb->A;
|
||||
Y = tb->A;
|
||||
X = tb->A;
|
||||
} else {
|
||||
Z = 1;
|
||||
Y = 0;
|
||||
X = 1;
|
||||
}
|
||||
|
||||
if (tb->Z == Z && tb->Y == Y && tb->X == X) {
|
||||
printf("PASS: ");
|
||||
pass = true;
|
||||
} else {
|
||||
printf("FAIL: ");
|
||||
pass = false;
|
||||
}
|
||||
printf("OE=%d A=%d X=%d Y=%d Z=%d\n", tb->OE, tb->A, tb->X, tb->Y, tb->Z);
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main() {
|
||||
bool pass = true;
|
||||
tb = new Vt_tri_pullup("tb");
|
||||
|
||||
// loop through every possibility and check the result
|
||||
for (tb->OE=0; tb->OE<2; tb->OE++) {
|
||||
for (tb->A=0; tb->A<2; tb->A++) {
|
||||
tb->eval();
|
||||
if (!check()) {
|
||||
pass = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pass) {
|
||||
cout << "*-* All Finished *-*" << endl;
|
||||
tb->final();
|
||||
} else {
|
||||
vl_fatal(__FILE__,__LINE__,"top", "Unexpected results from pullup test\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 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 (
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
v_flags2 => ["--exe t/$Last_Self->{name}.cpp --debug"],
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2008 by Lane Brooks
|
||||
|
||||
module top (input A, input OE, output X, output Y, output Z);
|
||||
|
||||
pullup p1(Z);
|
||||
assign Z = OE ? A : 1'bz;
|
||||
|
||||
pulldown p2(Y);
|
||||
assign Y = OE ? A : 1'bz;
|
||||
|
||||
pass pass(.A(A), .OE(OE), .X(X));
|
||||
pullup_module p(X);
|
||||
endmodule
|
||||
|
||||
module pass (input A, input OE, inout X);
|
||||
io io(.A(A), .OE(OE), .X(X));
|
||||
endmodule
|
||||
|
||||
module io (input A, input OE, inout X);
|
||||
assign X = (OE) ? A : 1'bz;
|
||||
endmodule
|
||||
|
||||
module pullup_module (output X);
|
||||
pullup p1(X);
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#include "Vt_tri_select.h"
|
||||
|
||||
Vt_tri_select *tb = NULL;
|
||||
|
||||
double sc_time_stamp() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool check() {
|
||||
bool pass = true;
|
||||
|
||||
int Y = (tb->OE1 & !tb->OE2) ? tb->A1
|
||||
: (!tb->OE1 & tb->OE2) ? tb->A2
|
||||
: (tb->OE1 & tb->OE2) ? (tb->A1 | tb->A2)
|
||||
: 3; // pullup
|
||||
|
||||
int W = (((tb->OE2) ? (tb->A2 & 0x1) : 0) << tb->A1)
|
||||
| (((tb->OE1) ? (tb->A2 >> 1)&0x1 : 0) << tb->A2);
|
||||
|
||||
if(tb->Y1 == tb->Y2 && tb->Y1 == Y && tb->W == W) {
|
||||
pass = true;
|
||||
printf("Pass: ");
|
||||
} else {
|
||||
pass = false;
|
||||
printf("Fail: ");
|
||||
}
|
||||
|
||||
printf("Read: OE1=%d OE2=%d A1=0x%x A2=0x%x Y1=0x%x Y2=0x%x W=0x%x Expected: Y1=Y2=%d and W=0x%x\n", tb->OE1, tb->OE2, tb->A1, tb->A2, tb->Y1, tb->Y2, tb->W, Y,W);
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main() {
|
||||
bool pass = true;
|
||||
tb = new Vt_tri_select("tb");
|
||||
|
||||
// loop through every possibility and check the result
|
||||
for (tb->OE1=0; tb->OE1<2; tb->OE1++) {
|
||||
for (tb->OE2=0; tb->OE2<2; tb->OE2++) {
|
||||
for (tb->A1=0; tb->A1<4; tb->A1++) {
|
||||
for (tb->A2=0; tb->A2<4; tb->A2++) {
|
||||
tb->eval();
|
||||
if(!check()) {
|
||||
pass = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pass) {
|
||||
cout << "*-* All Finished *-*" << endl;
|
||||
tb->final();
|
||||
} else {
|
||||
vl_fatal(__FILE__,__LINE__,"top", "Unexpected results from t_tri_select\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 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 (
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
v_flags2 => ["--exe t/$Last_Self->{name}.cpp --debug"],
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2008 by Lane Brooks
|
||||
|
||||
`define WIDTH 2
|
||||
|
||||
module top (
|
||||
input OE1,
|
||||
input OE2,
|
||||
input [`WIDTH-1:0] A1,
|
||||
input [`WIDTH-1:0] A2,
|
||||
output [`WIDTH-1:0] Y1,
|
||||
output [`WIDTH-1:0] Y2,
|
||||
output [`WIDTH**2-1:0] W);
|
||||
|
||||
assign W[A1] = (OE2) ? A2[0] : 1'bz;
|
||||
assign W[A2] = (OE1) ? A2[1] : 1'bz;
|
||||
|
||||
// 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));
|
||||
|
||||
pullup p1(PAD);
|
||||
// pulldown p1(PAD);
|
||||
|
||||
|
||||
wire [5:0] fill = { 4'b0, A1 };
|
||||
|
||||
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));
|
||||
endmodule
|
||||
|
||||
module io (input OE, input I, output O, inout PAD);
|
||||
assign O = PAD;
|
||||
assign PAD = OE ? I : 1'bz;
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2008 by Lane Brooks
|
||||
|
||||
#include "Vt_tristate.h"
|
||||
|
||||
Vt_tristate *tb = NULL;
|
||||
|
||||
double sc_time_stamp() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool check() {
|
||||
bool pass;
|
||||
int c = (tb->A >> tb->SEL) & 0x1;
|
||||
|
||||
if(tb->Z == c && tb->Y == c && tb->X == c && tb->W == c) {
|
||||
pass = true;
|
||||
printf("PASS: ");
|
||||
} else {
|
||||
pass = false;
|
||||
printf("FAIL: ");
|
||||
}
|
||||
printf("SEL=%d A=%d W=%d X=%d Y=%d Z=%d\n", tb->SEL, tb->A, tb->W, tb->X, tb->Y, tb->Z);
|
||||
return pass;
|
||||
}
|
||||
|
||||
int main() {
|
||||
bool pass = true;
|
||||
tb = new Vt_tristate("tb");
|
||||
|
||||
// loop through every possibility and check the result
|
||||
for (tb->SEL=0; tb->SEL<2; tb->SEL++) {
|
||||
for (tb->A=0; tb->A<4; tb->A++) {
|
||||
tb->eval();
|
||||
if(!check()) {
|
||||
pass =false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pass) {
|
||||
cout << "*-* All Finished *-*" << endl;
|
||||
tb->final();
|
||||
} else {
|
||||
vl_fatal(__FILE__,__LINE__,"top", "Unexpected results from tristate test\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2003-2009 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 (
|
||||
make_top_shell => 0,
|
||||
make_main => 0,
|
||||
v_flags2 => ["--exe t/$Last_Self->{name}.cpp"],
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
execute (
|
||||
check_finished=>1,
|
||||
) if $Last_Self->{v3};
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// 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);
|
||||
assign Z = ( SEL) ? A[1] : 1'bz;
|
||||
tbuf tbuf(.A(A[0]), .OE(!SEL), .Z(Z));
|
||||
// assign Z = (!SEL) ? A[0] : 1'bz;
|
||||
|
||||
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));
|
||||
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));
|
||||
endmodule
|
||||
|
||||
module tbuf (input A, input OE, output Z);
|
||||
assign Z = (OE) ? A : 1'bz;
|
||||
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;
|
||||
endmodule
|
||||
Loading…
Reference in New Issue