2012-04-13 01:08:20 +00:00
|
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Add temporaries, such as for inst nodes
|
|
|
|
|
//
|
2008-04-25 12:14:27 +00:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2013-01-01 14:42:59 +00:00
|
|
|
|
// Copyright 2003-2013 by Wilson Snyder. This program is free software; you can
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 21:07:57 +00:00
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
// 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Inst's Transformations:
|
2008-06-10 01:25:10 +00:00
|
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Each module:
|
|
|
|
|
// Pins:
|
|
|
|
|
// Create a wire assign to interconnect to submodule
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2008-06-30 17:11:25 +00:00
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstdarg>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Inst.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Changed.h"
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Inst state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class InstVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared each Cell:
|
2008-11-25 14:03:49 +00:00
|
|
|
|
// AstPin::user1p() -> bool. True if created assignment already
|
|
|
|
|
AstUser1InUse m_inuser1;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// STATE
|
2009-11-07 11:20:20 +00:00
|
|
|
|
AstNodeModule* m_modp; // Current module
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstCell* m_cellp; // Current cell
|
|
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//int m_debug; int debug() { return m_debug; }
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2009-11-07 11:20:20 +00:00
|
|
|
|
virtual void visit(AstNodeModule* nodep, AstNUser*) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
UINFO(4," MOD "<<nodep<<endl);
|
|
|
|
|
//if (nodep->name() == "t_chg") m_debug = 9; else m_debug=0;
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
nodep->iterateChildren(*this);
|
2008-11-12 20:32:09 +00:00
|
|
|
|
m_modp = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstCell* nodep, AstNUser*) {
|
|
|
|
|
UINFO(4," CELL "<<nodep<<endl);
|
|
|
|
|
m_cellp = nodep;
|
2008-11-25 14:03:49 +00:00
|
|
|
|
//VV***** We reset user1p() on each cell!!!
|
|
|
|
|
AstNode::user1ClearTree();
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
m_cellp = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstPin* nodep, AstNUser*) {
|
|
|
|
|
// PIN(p,expr) -> ASSIGNW(VARXREF(p),expr) (if sub's input)
|
|
|
|
|
// or ASSIGNW(expr,VARXREF(p)) (if sub's output)
|
|
|
|
|
UINFO(4," PIN "<<nodep<<endl);
|
2012-04-26 23:01:11 +00:00
|
|
|
|
if (!nodep->exprp()) return; // No-connect
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (debug()>=9) nodep->dumpTree(cout," Pin_oldb: ");
|
2006-09-25 20:40:52 +00:00
|
|
|
|
if (nodep->modVarp()->isOutOnly() && nodep->exprp()->castConst())
|
2009-10-07 01:46:24 +00:00
|
|
|
|
nodep->v3error("Output port is connected to a constant pin, electrical short");
|
2008-11-25 14:03:49 +00:00
|
|
|
|
// Use user1p on the PIN to indicate we created an assign for this pin
|
2012-04-29 12:55:33 +00:00
|
|
|
|
if (!nodep->user1SetOnce()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Simplify it
|
2012-04-27 01:11:48 +00:00
|
|
|
|
V3Inst::pinReconnectSimple(nodep, m_cellp, m_modp, false);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Make a ASSIGNW (expr, pin)
|
|
|
|
|
AstNode* exprp = nodep->exprp()->cloneTree(false);
|
2012-04-29 14:14:13 +00:00
|
|
|
|
if (exprp->width() != nodep->modVarp()->width())
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->v3fatalSrc("Width mismatch, should have been handled in pinReconnectSimple\n");
|
2006-09-25 20:40:52 +00:00
|
|
|
|
if (nodep->modVarp()->isInout()) {
|
|
|
|
|
nodep->v3fatalSrc("Unsupported: Verilator is a 2-state simulator");
|
|
|
|
|
} else if (nodep->modVarp()->isOutput()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* rhsp = new AstVarXRef (exprp->fileline(), nodep->modVarp(), m_cellp->name(), false);
|
|
|
|
|
AstAssignW* assp = new AstAssignW (exprp->fileline(), exprp, rhsp);
|
|
|
|
|
m_modp->addStmtp(assp);
|
|
|
|
|
} else if (nodep->modVarp()->isInput()) {
|
|
|
|
|
// Don't bother moving constants now,
|
|
|
|
|
// we'll be pushing the const down to the cell soon enough.
|
|
|
|
|
AstNode* assp = new AstAssignW
|
|
|
|
|
(exprp->fileline(),
|
|
|
|
|
new AstVarXRef(exprp->fileline(), nodep->modVarp(), m_cellp->name(), true),
|
|
|
|
|
exprp);
|
|
|
|
|
m_modp->addStmtp(assp);
|
|
|
|
|
if (debug()>=9) assp->dumpTree(cout," _new: ");
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3error("Assigned pin is neither input nor output");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We're done with the pin
|
|
|
|
|
nodep->unlinkFrBack()->deleteTree(); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-07 21:41:19 +00:00
|
|
|
|
virtual void visit(AstUdpTable* nodep, AstNUser*) {
|
|
|
|
|
if (!v3Global.opt.bboxUnsup()) {
|
2010-12-30 01:24:31 +00:00
|
|
|
|
// If we support primitives, update V3Undriven to remove special case
|
2010-01-07 21:41:19 +00:00
|
|
|
|
nodep->v3error("Unsupported: Verilog 1995 UDP Tables. Use --bbox-unsup to ignore tables.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Save some time
|
|
|
|
|
virtual void visit(AstNodeAssign*, AstNUser*) {}
|
|
|
|
|
virtual void visit(AstAlways*, AstNUser*) {}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
InstVisitor(AstNode* nodep) {
|
|
|
|
|
m_modp=NULL;
|
|
|
|
|
m_cellp=NULL;
|
|
|
|
|
//
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~InstVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class InstDeVisitor : public AstNVisitor {
|
|
|
|
|
// Find all cells with arrays, and convert to non-arrayed
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
|
|
|
|
AstRange* m_cellRangep; // Range for arrayed instantiations, NULL for normal instantiations
|
|
|
|
|
int m_instNum; // Current instantiation number
|
|
|
|
|
int m_instLsb; // Current instantiation number
|
|
|
|
|
|
2009-01-21 21:56:50 +00:00
|
|
|
|
static int debug() {
|
|
|
|
|
static int level = -1;
|
|
|
|
|
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
|
|
|
|
return level;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
virtual void visit(AstCell* nodep, AstNUser*) {
|
|
|
|
|
if (nodep->rangep()) {
|
|
|
|
|
m_cellRangep = nodep->rangep();
|
|
|
|
|
UINFO(4," CELL "<<nodep<<endl);
|
|
|
|
|
// Make all of the required clones
|
|
|
|
|
m_instLsb = m_cellRangep->lsbConst();
|
|
|
|
|
for (m_instNum = m_instLsb; m_instNum<=m_cellRangep->msbConst(); m_instNum++) {
|
2008-11-20 01:15:05 +00:00
|
|
|
|
AstCell* newp = nodep->cloneTree(false);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->addNextHere(newp);
|
|
|
|
|
// Remove ranging and fix name
|
|
|
|
|
newp->rangep()->unlinkFrBack()->deleteTree();
|
2006-09-01 15:31:07 +00:00
|
|
|
|
// Somewhat illogically, we need to rename the orignal name of the cell too.
|
|
|
|
|
// as that is the name users expect for dotting
|
|
|
|
|
// The spec says we add [x], but that won't work in C...
|
2006-12-22 15:06:13 +00:00
|
|
|
|
newp->name(newp->name()+"__BRA__"+cvtToStr(m_instNum)+"__KET__");
|
|
|
|
|
newp->origName(newp->origName()+"__BRA__"+cvtToStr(m_instNum)+"__KET__");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Fixup pins
|
|
|
|
|
newp->pinsp()->iterateAndNext(*this);
|
|
|
|
|
if (debug()==9) { newp->dumpTree(cout,"newcell: "); cout<<endl; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Done. Delete original
|
|
|
|
|
m_cellRangep=NULL;
|
|
|
|
|
nodep->unlinkFrBack(); pushDeletep(nodep); nodep=NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
virtual void visit(AstPin* nodep, AstNUser*) {
|
|
|
|
|
// Any non-direct pins need reconnection with a part-select
|
2012-04-26 23:01:11 +00:00
|
|
|
|
if (!nodep->exprp()) return; // No-connect
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (m_cellRangep) {
|
|
|
|
|
UINFO(4," PIN "<<nodep<<endl);
|
|
|
|
|
int pinwidth = nodep->modVarp()->width();
|
|
|
|
|
int expwidth = nodep->exprp()->width();
|
|
|
|
|
if (expwidth == pinwidth) {
|
|
|
|
|
// NOP: Arrayed instants: widths match so connect to each instance
|
2011-11-30 12:51:05 +00:00
|
|
|
|
} else if (expwidth == pinwidth*m_cellRangep->elementsConst()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Arrayed instants: one bit for each of the instants (each assign is 1 pinwidth wide)
|
|
|
|
|
AstNode* exprp = nodep->exprp()->unlinkFrBack();
|
|
|
|
|
bool inputPin = nodep->modVarp()->isInput();
|
2011-11-29 03:10:43 +00:00
|
|
|
|
if (!inputPin && !exprp->castVarRef()
|
2013-02-02 23:33:10 +00:00
|
|
|
|
&& !exprp->castConcat() // V3Const will collapse the SEL with the one we're about to make
|
2011-11-29 03:10:43 +00:00
|
|
|
|
&& !exprp->castSel()) { // V3Const will collapse the SEL with the one we're about to make
|
2006-08-26 11:35:28 +00:00
|
|
|
|
nodep->v3error("Unsupported: Per-bit array instantiations with output connections to non-wires.");
|
2011-11-29 03:10:43 +00:00
|
|
|
|
// Note spec allows more complicated matches such as slices and such
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
exprp = new AstSel (exprp->fileline(), exprp,
|
|
|
|
|
pinwidth*(m_instNum-m_instLsb),
|
|
|
|
|
pinwidth);
|
|
|
|
|
nodep->exprp(exprp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("Width mismatch; V3Width should have errored out.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save some time
|
2006-12-12 18:25:33 +00:00
|
|
|
|
virtual void visit(AstNodeMath*, AstNUser*) {}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
|
|
|
|
virtual void visit(AstNode* nodep, AstNUser*) {
|
|
|
|
|
nodep->iterateChildren(*this);
|
|
|
|
|
}
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
|
|
|
|
InstDeVisitor(AstNode* nodep) {
|
|
|
|
|
m_cellRangep=NULL;
|
|
|
|
|
m_instNum=0;
|
|
|
|
|
m_instLsb=0;
|
|
|
|
|
//
|
|
|
|
|
nodep->accept(*this);
|
|
|
|
|
}
|
|
|
|
|
virtual ~InstDeVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Inst class functions
|
|
|
|
|
|
2012-05-08 03:42:58 +00:00
|
|
|
|
AstAssignW* V3Inst::pinReconnectSimple(AstPin* pinp, AstCell* cellp, AstNodeModule*,
|
|
|
|
|
bool forTristate, bool alwaysCvt) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// If a pin connection is "simple" leave it as-is
|
|
|
|
|
// Else create a intermediate wire to perform the interconnect
|
2012-04-21 23:30:08 +00:00
|
|
|
|
// Return the new assignment, if one was made
|
2009-11-02 13:06:04 +00:00
|
|
|
|
// Note this module calles cloneTree() via new AstVar
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstVar* pinVarp = pinp->modVarp();
|
|
|
|
|
AstVarRef* connectRefp = pinp->exprp()->castVarRef();
|
2009-11-15 13:52:19 +00:00
|
|
|
|
AstBasicDType* pinBasicp = pinVarp->dtypep()->basicp(); // Maybe NULL
|
|
|
|
|
AstBasicDType* connBasicp = NULL;
|
2012-04-21 23:30:08 +00:00
|
|
|
|
AstAssignW* assignp = NULL;
|
2009-11-15 13:52:19 +00:00
|
|
|
|
if (connectRefp) connBasicp = connectRefp->varp()->dtypep()->basicp();
|
|
|
|
|
//
|
2012-05-08 03:42:58 +00:00
|
|
|
|
if (!alwaysCvt
|
|
|
|
|
&& connectRefp
|
2009-11-15 13:52:19 +00:00
|
|
|
|
&& connectRefp->varp()->dtypep()->sameTree(pinVarp->dtypep())
|
|
|
|
|
&& !connectRefp->varp()->isSc()) { // Need the signal as a 'shell' to convert types
|
|
|
|
|
// Done. Same data type
|
2012-05-08 03:42:58 +00:00
|
|
|
|
} else if (!alwaysCvt
|
|
|
|
|
&& connBasicp
|
2009-11-15 13:52:19 +00:00
|
|
|
|
&& pinBasicp
|
|
|
|
|
&& connBasicp->width() == pinBasicp->width()
|
|
|
|
|
&& connBasicp->lsb() == pinBasicp->lsb()
|
|
|
|
|
&& !connectRefp->varp()->isSc() // Need the signal as a 'shell' to convert types
|
2012-04-29 14:14:13 +00:00
|
|
|
|
&& connBasicp->width() == pinVarp->width()
|
2009-11-15 13:52:19 +00:00
|
|
|
|
&& 1) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Done. One to one interconnect won't need a temporary variable.
|
2012-05-08 03:42:58 +00:00
|
|
|
|
} else if (!alwaysCvt && !forTristate && pinp->exprp()->castConst()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Done. Constant.
|
|
|
|
|
} else {
|
|
|
|
|
// Make a new temp wire
|
2013-02-21 03:28:56 +00:00
|
|
|
|
//if (1||debug()>=9) { pinp->dumpTree(cout,"-in_pin:"); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* pinexprp = pinp->exprp()->unlinkFrBack();
|
2013-02-21 03:28:56 +00:00
|
|
|
|
string newvarname = ((string)(pinVarp->isOutput() ? "__Vcellout" : "__Vcellinp")
|
|
|
|
|
+(forTristate?"t":"") // Prevent name conflict if both tri & non-tri add signals
|
|
|
|
|
+"__"+cellp->name()+"__"+pinp->name());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstVar* newvarp = new AstVar (pinVarp->fileline(), AstVarType::MODULETEMP, newvarname, pinVarp);
|
2012-04-27 23:41:13 +00:00
|
|
|
|
// Important to add statement next to cell, in case there is a generate with same named cell
|
|
|
|
|
cellp->addNextHere(newvarp);
|
2006-09-25 20:40:52 +00:00
|
|
|
|
if (pinVarp->isInout()) {
|
|
|
|
|
pinVarp->v3fatalSrc("Unsupported: Inout connections to pins must be direct one-to-one connection (without any expression)");
|
|
|
|
|
} else if (pinVarp->isOutput()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// See also V3Inst
|
|
|
|
|
AstNode* rhsp = new AstVarRef(pinp->fileline(), newvarp, false);
|
2012-04-29 14:14:13 +00:00
|
|
|
|
if (pinVarp->width() > rhsp->width()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (rhsp->isSigned()) {
|
|
|
|
|
rhsp = new AstExtendS(pinp->fileline(), rhsp);
|
|
|
|
|
} else {
|
|
|
|
|
rhsp = new AstExtend (pinp->fileline(), rhsp);
|
|
|
|
|
}
|
2012-04-29 14:14:13 +00:00
|
|
|
|
} else if (pinVarp->width() < rhsp->width()) {
|
|
|
|
|
rhsp = new AstSel (pinp->fileline(), rhsp, 0, pinVarp->width());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2012-04-29 14:14:13 +00:00
|
|
|
|
rhsp->dtypeFrom(pinVarp); // Need proper widthMin, which may differ from AstSel created above
|
2006-08-26 11:35:28 +00:00
|
|
|
|
assignp = new AstAssignW (pinp->fileline(), pinexprp, rhsp);
|
|
|
|
|
pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, true));
|
|
|
|
|
} else {
|
|
|
|
|
// V3 width should have range/extended to make the widths correct
|
|
|
|
|
assignp = new AstAssignW (pinp->fileline(),
|
|
|
|
|
new AstVarRef(pinp->fileline(), newvarp, true),
|
|
|
|
|
pinexprp);
|
|
|
|
|
pinp->exprp(new AstVarRef (pinexprp->fileline(), newvarp, false));
|
|
|
|
|
}
|
2012-04-27 23:41:13 +00:00
|
|
|
|
if (assignp) cellp->addNextHere(assignp);
|
2013-02-21 03:28:56 +00:00
|
|
|
|
//if (debug()) { pinp->dumpTree(cout,"- out:"); }
|
|
|
|
|
//if (debug()) { assignp->dumpTree(cout,"- aout:"); }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2012-04-21 23:30:08 +00:00
|
|
|
|
return assignp;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Inst class visitor
|
|
|
|
|
|
|
|
|
|
void V3Inst::instAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
InstVisitor visitor (nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3Inst::dearrayAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
|
|
|
|
InstDeVisitor visitor (nodep);
|
|
|
|
|
}
|