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: Replicate modules for parameterization
|
|
|
|
|
//
|
2008-04-25 12:14:27 +00:00
|
|
|
|
// Code available from: http://www.veripool.org/verilator
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2018-01-02 23:05:06 +00:00
|
|
|
|
// Copyright 2003-2018 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.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// PARAM TRANSFORMATIONS:
|
|
|
|
|
// Top down traversal:
|
|
|
|
|
// For each cell:
|
|
|
|
|
// If parameterized,
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// Determine all parameter widths, constant values.
|
|
|
|
|
// (Interfaces also matter, as if an interface is parameterized
|
|
|
|
|
// this effectively changes the width behavior of all that
|
|
|
|
|
// reference the iface.)
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Clone module cell calls, renaming with __{par1}_{par2}_...
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// Substitute constants for cell's module's parameters.
|
|
|
|
|
// Relink pins and cell and ifacerefdtype to point to new module.
|
|
|
|
|
//
|
|
|
|
|
// For interface Parent's we have the AstIfaceRefDType::cellp()
|
|
|
|
|
// pointing to this module. If that parent cell's interface
|
|
|
|
|
// module gets parameterized, AstIfaceRefDType::cloneRelink
|
2018-06-22 10:35:27 +00:00
|
|
|
|
// will update AstIfaceRefDType::cellp(), and V3LinkDot will
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// see the new interface.
|
|
|
|
|
//
|
|
|
|
|
// However if a submodule's AstIfaceRefDType::ifacep() points
|
|
|
|
|
// to the old (unparameterized) interface and needs correction.
|
|
|
|
|
// To detect this we must walk all pins looking for interfaces
|
|
|
|
|
// that the parent has changed and propagate down.
|
|
|
|
|
//
|
|
|
|
|
// Then process all modules called by that cell.
|
2012-03-24 19:54:06 +00:00
|
|
|
|
// (Cells never referenced after parameters expanded must be ignored.)
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// After we complete parameters, the varp's will be wrong (point to old module)
|
|
|
|
|
// and must be relinked.
|
|
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
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 <map>
|
|
|
|
|
#include <vector>
|
2012-03-24 19:54:06 +00:00
|
|
|
|
#include <deque>
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Param.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Case.h"
|
|
|
|
|
#include "V3Const.h"
|
|
|
|
|
#include "V3Width.h"
|
|
|
|
|
#include "V3Unroll.h"
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Param state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class ParamVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2012-03-24 19:54:06 +00:00
|
|
|
|
// AstNodeModule::user5() // bool True if processed
|
2012-07-21 21:12:42 +00:00
|
|
|
|
// AstGenFor::user5() // bool True if processed
|
2012-03-24 19:54:06 +00:00
|
|
|
|
// AstVar::user5() // bool True if constant propagated
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// AstVar::user4() // int Global parameter number (for naming new module)
|
2009-10-25 20:53:55 +00:00
|
|
|
|
// // (0=not processed, 1=iterated, but no number, 65+ parameter numbered)
|
2008-11-25 14:03:49 +00:00
|
|
|
|
AstUser4InUse m_inuser4;
|
2010-05-25 23:37:45 +00:00
|
|
|
|
AstUser5InUse m_inuser5;
|
2009-10-25 20:53:55 +00:00
|
|
|
|
// User1/2/3 used by constant function simulations
|
2008-11-21 20:50:33 +00:00
|
|
|
|
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// TYPES
|
2018-02-02 02:24:41 +00:00
|
|
|
|
typedef std::deque<std::pair<AstIfaceRefDType*,AstIfaceRefDType*> > IfaceRefRefs; // Note may have duplicate entries
|
2013-05-28 01:39:19 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// STATE
|
2018-02-02 02:24:41 +00:00
|
|
|
|
typedef std::map<AstNode*,AstNode*> CloneMap;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
struct ModInfo {
|
2009-11-07 11:20:20 +00:00
|
|
|
|
AstNodeModule* m_modp; // Module with specified name
|
2016-03-13 01:54:52 +00:00
|
|
|
|
CloneMap m_cloneMap; // Map of old-varp -> new cloned varp
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit ModInfo(AstNodeModule* modp) { m_modp=modp; }
|
2006-08-26 11:35:28 +00:00
|
|
|
|
};
|
2018-02-02 02:24:41 +00:00
|
|
|
|
typedef std::map<string,ModInfo> ModNameMap;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
ModNameMap m_modNameMap; // Hash of created module flavors by name
|
|
|
|
|
|
2018-02-02 02:24:41 +00:00
|
|
|
|
typedef std::map<string,string> LongMap;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
LongMap m_longMap; // Hash of very long names to unique identity number
|
|
|
|
|
int m_longId;
|
|
|
|
|
|
2018-02-02 02:24:41 +00:00
|
|
|
|
typedef std::map<AstNode*,int> ValueMap;
|
|
|
|
|
typedef std::map<int,int> NextValueMap;
|
2013-05-28 01:39:19 +00:00
|
|
|
|
ValueMap m_valueMap; // Hash of node to param value
|
|
|
|
|
NextValueMap m_nextValueMap;// Hash of param value to next value to be used
|
|
|
|
|
|
2018-02-02 02:24:41 +00:00
|
|
|
|
typedef std::multimap<int,AstNodeModule*> LevelModMap;
|
2012-08-02 11:02:57 +00:00
|
|
|
|
LevelModMap m_todoModps; // Modules left to process
|
2012-03-24 19:54:06 +00:00
|
|
|
|
|
2018-02-02 02:24:41 +00:00
|
|
|
|
typedef std::deque<AstCell*> CellList;
|
2013-05-28 01:39:19 +00:00
|
|
|
|
CellList m_cellps; // Cells left to process (in this module)
|
|
|
|
|
|
2017-11-18 22:42:35 +00:00
|
|
|
|
AstNodeModule* m_modp; // Current module being processed
|
|
|
|
|
|
2015-11-12 01:40:24 +00:00
|
|
|
|
string m_unlinkedTxt; // Text for AstUnlinkedRef
|
2015-10-23 00:13:49 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// METHODS
|
2018-05-14 10:50:47 +00:00
|
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-21 21:56:50 +00:00
|
|
|
|
|
2009-11-07 11:20:20 +00:00
|
|
|
|
void makeSmallNames(AstNodeModule* modp) {
|
2018-02-02 02:24:41 +00:00
|
|
|
|
std::vector<int> usedLetter; usedLetter.resize(256);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Pass 1, assign first letter to each gparam's name
|
|
|
|
|
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (AstVar* varp = VN_CAST(stmtp, Var)) {
|
2013-05-28 01:39:19 +00:00
|
|
|
|
if (varp->isGParam()||varp->isIfaceRef()) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
char ch = varp->name()[0];
|
|
|
|
|
ch = toupper(ch); if (ch<'A' || ch>'Z') ch='Z';
|
2013-02-03 18:27:37 +00:00
|
|
|
|
varp->user4(usedLetter[static_cast<int>(ch)]*256 + ch);
|
|
|
|
|
usedLetter[static_cast<int>(ch)]++;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
|
} else if (AstParamTypeDType* typep = VN_CAST(stmtp, ParamTypeDType)) {
|
2016-11-05 02:58:21 +00:00
|
|
|
|
char ch = 'T';
|
|
|
|
|
typep->user4(usedLetter[static_cast<int>(ch)]*256 + ch);
|
|
|
|
|
usedLetter[static_cast<int>(ch)]++;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-13 01:54:52 +00:00
|
|
|
|
string paramSmallName(AstNodeModule* modp, AstNode* varp) {
|
2009-10-25 20:53:55 +00:00
|
|
|
|
if (varp->user4()<=1) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
makeSmallNames(modp);
|
|
|
|
|
}
|
|
|
|
|
int index = varp->user4()/256;
|
|
|
|
|
char ch = varp->user4()&255;
|
|
|
|
|
string st = cvtToStr(ch);
|
|
|
|
|
while (index) {
|
2015-11-05 12:00:04 +00:00
|
|
|
|
st += cvtToStr(char((index%25)+'A'));
|
2006-08-26 11:35:28 +00:00
|
|
|
|
index /= 26;
|
|
|
|
|
}
|
|
|
|
|
return st;
|
|
|
|
|
}
|
2013-05-28 01:39:19 +00:00
|
|
|
|
string paramValueNumber(AstNode* nodep) {
|
2016-03-13 01:54:52 +00:00
|
|
|
|
// Given a complicated object create a number to use for param module assignment
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// Ideally would be relatively stable if design changes (not use pointer value),
|
|
|
|
|
// and must return same value given same input node
|
2015-11-05 03:01:21 +00:00
|
|
|
|
// Return must presently be numeric so doesn't collide with 'small' alphanumeric parameter names
|
2013-05-28 01:39:19 +00:00
|
|
|
|
ValueMap::iterator it = m_valueMap.find(nodep);
|
|
|
|
|
if (it != m_valueMap.end()) {
|
|
|
|
|
return cvtToStr(it->second);
|
|
|
|
|
} else {
|
|
|
|
|
static int BUCKETS = 1000;
|
|
|
|
|
V3Hash hash (nodep->name());
|
|
|
|
|
int bucket = hash.hshval() % BUCKETS;
|
|
|
|
|
int offset = 0;
|
|
|
|
|
NextValueMap::iterator it = m_nextValueMap.find(bucket);
|
|
|
|
|
if (it != m_nextValueMap.end()) { offset = it->second; it->second = offset + 1; }
|
|
|
|
|
else { m_nextValueMap.insert(make_pair(bucket, offset + 1)); }
|
|
|
|
|
int num = bucket + offset * BUCKETS;
|
|
|
|
|
m_valueMap.insert(make_pair(nodep, num));
|
2015-11-05 12:00:04 +00:00
|
|
|
|
// 'z' just to make sure we don't collide with a normal non-hashed number
|
|
|
|
|
return (string)"z"+cvtToStr(num);
|
2013-05-28 01:39:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-13 01:54:52 +00:00
|
|
|
|
void collectPins(CloneMap* clonemapp, AstNodeModule* modp) {
|
|
|
|
|
// Grab all I/O so we can remap our pins later
|
|
|
|
|
for (AstNode* stmtp=modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (AstVar* varp = VN_CAST(stmtp, Var)) {
|
2016-03-13 01:54:52 +00:00
|
|
|
|
if (varp->isIO() || varp->isGParam() || varp->isIfaceRef()) {
|
|
|
|
|
// Cloning saved a pointer to the new node for us, so just follow that link.
|
2016-11-09 02:16:22 +00:00
|
|
|
|
AstVar* oldvarp = varp->clonep();
|
2016-03-13 01:54:52 +00:00
|
|
|
|
//UINFO(8,"Clone list 0x"<<hex<<(uint32_t)oldvarp<<" -> 0x"<<(uint32_t)varp<<endl);
|
|
|
|
|
clonemapp->insert(make_pair(oldvarp, varp));
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
|
else if (AstParamTypeDType* ptp = VN_CAST(stmtp, ParamTypeDType)) {
|
2016-03-15 01:51:31 +00:00
|
|
|
|
if (ptp->isGParam()) {
|
2016-11-09 02:16:22 +00:00
|
|
|
|
AstParamTypeDType* oldptp = ptp->clonep();
|
2016-03-15 01:51:31 +00:00
|
|
|
|
clonemapp->insert(make_pair(oldptp, ptp));
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-13 01:54:52 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void relinkPins(CloneMap* clonemapp, AstPin* startpinp) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
for (AstPin* pinp = startpinp; pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
2016-03-13 01:54:52 +00:00
|
|
|
|
if (pinp->modVarp()) {
|
|
|
|
|
// Find it in the clone structure
|
|
|
|
|
//UINFO(8,"Clone find 0x"<<hex<<(uint32_t)pinp->modVarp()<<endl);
|
|
|
|
|
CloneMap::iterator cloneiter = clonemapp->find(pinp->modVarp());
|
2018-06-16 22:00:21 +00:00
|
|
|
|
if (cloneiter == clonemapp->end()) {
|
|
|
|
|
pinp->v3fatalSrc("Couldn't find pin in clone list");
|
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
|
pinp->modVarp(VN_CAST(cloneiter->second, Var));
|
2016-03-13 01:54:52 +00:00
|
|
|
|
}
|
2016-03-15 01:51:31 +00:00
|
|
|
|
else if (pinp->modPTypep()) {
|
|
|
|
|
CloneMap::iterator cloneiter = clonemapp->find(pinp->modPTypep());
|
2018-06-16 22:00:21 +00:00
|
|
|
|
if (cloneiter == clonemapp->end()) {
|
|
|
|
|
pinp->v3fatalSrc("Couldn't find pin in clone list");
|
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
|
pinp->modPTypep(VN_CAST(cloneiter->second, ParamTypeDType));
|
2016-03-15 01:51:31 +00:00
|
|
|
|
}
|
2016-03-13 01:54:52 +00:00
|
|
|
|
else {
|
2017-04-29 00:09:27 +00:00
|
|
|
|
pinp->v3fatalSrc("Not linked?");
|
2016-03-13 01:54:52 +00:00
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-28 01:39:19 +00:00
|
|
|
|
void visitCell(AstCell* nodep);
|
2012-03-24 19:54:06 +00:00
|
|
|
|
void visitModules() {
|
|
|
|
|
// Loop on all modules left to process
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// Hitting a cell adds to the appropriate level of this level-sorted list,
|
2012-08-02 11:02:57 +00:00
|
|
|
|
// so since cells originally exist top->bottom we process in top->bottom order too.
|
2012-03-24 19:54:06 +00:00
|
|
|
|
while (!m_todoModps.empty()) {
|
2012-08-02 11:02:57 +00:00
|
|
|
|
LevelModMap::iterator it = m_todoModps.begin();
|
|
|
|
|
AstNodeModule* nodep = it->second;
|
|
|
|
|
m_todoModps.erase(it);
|
2012-04-29 12:55:33 +00:00
|
|
|
|
if (!nodep->user5SetOnce()) { // Process once; note clone() must clear so we do it again
|
2017-11-18 22:42:35 +00:00
|
|
|
|
m_modp = nodep;
|
2012-03-24 19:54:06 +00:00
|
|
|
|
UINFO(4," MOD "<<nodep<<endl);
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateChildren(nodep);
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// Note above iterate may add to m_todoModps
|
|
|
|
|
//
|
|
|
|
|
// Process interface cells, then non-interface which may ref an interface cell
|
|
|
|
|
for (int nonIf=0; nonIf<2; ++nonIf) {
|
|
|
|
|
for (CellList::iterator it=m_cellps.begin(); it!=m_cellps.end(); ++it) {
|
|
|
|
|
AstCell* nodep = *it;
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if ((nonIf==0 && VN_IS(nodep->modp(), Iface))
|
|
|
|
|
|| (nonIf==1 && !VN_IS(nodep->modp(), Iface))) {
|
2013-05-28 01:39:19 +00:00
|
|
|
|
visitCell(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_cellps.clear();
|
2017-11-18 22:42:35 +00:00
|
|
|
|
m_modp = NULL;
|
2012-03-24 19:54:06 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
|
// VISITORS
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNetlist* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Modules must be done in top-down-order
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNodeModule* nodep) {
|
2012-07-21 21:12:42 +00:00
|
|
|
|
if (nodep->dead()) {
|
|
|
|
|
UINFO(4," MOD-dead. "<<nodep<<endl); // Marked by LinkDot
|
2017-11-18 22:42:35 +00:00
|
|
|
|
} else if (nodep->recursiveClone()) {
|
|
|
|
|
UINFO(4," MOD-recursive-dead. "<<nodep<<endl); // Fake, made for recursive elimination
|
|
|
|
|
nodep->dead(true); // So Dead checks won't count references to it
|
2014-07-22 00:44:33 +00:00
|
|
|
|
} else if (nodep->level() <= 2 // Haven't added top yet, so level 2 is the top
|
2018-02-02 02:32:58 +00:00
|
|
|
|
|| VN_IS(nodep, Package)) { // Likewise haven't done wrapTopPackages yet
|
2012-03-24 19:54:06 +00:00
|
|
|
|
// Add request to END of modules left to process
|
2017-11-18 22:40:10 +00:00
|
|
|
|
m_todoModps.insert(make_pair(nodep->level(), nodep));
|
2012-03-24 19:54:06 +00:00
|
|
|
|
visitModules();
|
|
|
|
|
} else if (nodep->user5()) {
|
|
|
|
|
UINFO(4," MOD-done "<<nodep<<endl); // Already did it
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(4," MOD-dead? "<<nodep<<endl); // Should have been done by now, if not dead
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstCell* nodep) {
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// Must do ifaces first, so push to list and do in proper order
|
|
|
|
|
m_cellps.push_back(nodep);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
2009-07-16 18:49:34 +00:00
|
|
|
|
// Make sure all parameters are constantified
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstVar* nodep) {
|
2012-04-29 12:55:33 +00:00
|
|
|
|
if (!nodep->user5SetOnce()) { // Process once
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateChildren(nodep);
|
2009-10-25 20:53:55 +00:00
|
|
|
|
if (nodep->isParam()) {
|
2014-04-02 03:16:16 +00:00
|
|
|
|
if (!nodep->valuep()) { nodep->v3fatalSrc("Parameter without initial value"); }
|
2009-10-25 20:53:55 +00:00
|
|
|
|
V3Const::constifyParamsEdit(nodep); // The variable, not just the var->init()
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (!VN_IS(nodep->valuep(), Const)) { // Complex init, like an array
|
2014-04-02 03:16:16 +00:00
|
|
|
|
// Make a new INITIAL to set the value.
|
|
|
|
|
// This allows the normal array/struct handling code to properly initialize the parameter
|
|
|
|
|
nodep->addNext(new AstInitial(nodep->fileline(),
|
|
|
|
|
new AstAssign(nodep->fileline(),
|
|
|
|
|
new AstVarRef(nodep->fileline(), nodep, true),
|
|
|
|
|
nodep->valuep()->cloneTree(true))));
|
|
|
|
|
}
|
2009-10-25 20:53:55 +00:00
|
|
|
|
}
|
2009-07-16 18:49:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-25 20:53:55 +00:00
|
|
|
|
// Make sure varrefs cause vars to constify before things above
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstVarRef* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
|
if (nodep->varp()) iterate(nodep->varp());
|
2009-10-25 20:53:55 +00:00
|
|
|
|
}
|
2017-05-19 02:49:17 +00:00
|
|
|
|
bool ifaceParamReplace(AstVarXRef* nodep, AstNode* candp) {
|
|
|
|
|
for (; candp; candp = candp->nextp()) {
|
|
|
|
|
if (nodep->name() == candp->name()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (AstVar* varp = VN_CAST(candp, Var)) {
|
2017-05-19 02:49:17 +00:00
|
|
|
|
UINFO(9,"Found interface parameter: "<<varp<<endl);
|
|
|
|
|
nodep->varp(varp);
|
|
|
|
|
return true;
|
2018-02-02 02:32:58 +00:00
|
|
|
|
} else if (AstPin* pinp = VN_CAST(candp, Pin)) {
|
2017-05-19 02:49:17 +00:00
|
|
|
|
UINFO(9,"Found interface parameter: "<<pinp<<endl);
|
|
|
|
|
if (!pinp->exprp()) pinp->v3fatalSrc("Interface parameter pin missing expression");
|
|
|
|
|
nodep->replaceWith(pinp->exprp()->cloneTree(false)); VL_DANGLING(nodep);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstVarXRef* nodep) {
|
2017-05-19 02:49:17 +00:00
|
|
|
|
// Check to see if the scope is just an interface because interfaces are special
|
|
|
|
|
string dotted = nodep->dotted();
|
|
|
|
|
if (!dotted.empty() && nodep->varp() && nodep->varp()->isParam()) {
|
|
|
|
|
AstNode* backp = nodep;
|
|
|
|
|
while ((backp = backp->backp())) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (VN_IS(backp, NodeModule)) {
|
2017-05-19 02:49:17 +00:00
|
|
|
|
UINFO(9,"Hit module boundary, done looking for interface"<<endl);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (VN_IS(backp, Var)
|
|
|
|
|
&& VN_CAST(backp, Var)->isIfaceRef()
|
|
|
|
|
&& VN_CAST(backp, Var)->childDTypep()
|
|
|
|
|
&& VN_CAST(VN_CAST(backp, Var)->childDTypep(), IfaceRefDType)) {
|
|
|
|
|
AstIfaceRefDType* ifacerefp = VN_CAST(VN_CAST(backp, Var)->childDTypep(), IfaceRefDType);
|
2017-05-19 02:49:17 +00:00
|
|
|
|
// Interfaces passed in on the port map have ifaces
|
|
|
|
|
if (AstIface* ifacep = ifacerefp->ifacep()) {
|
|
|
|
|
if (dotted == backp->name()) {
|
|
|
|
|
UINFO(9,"Iface matching scope: "<<ifacep<<endl);
|
|
|
|
|
if (ifaceParamReplace(nodep, ifacep->stmtsp())) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Interfaces declared in this module have cells
|
|
|
|
|
else if (AstCell* cellp = ifacerefp->cellp()) {
|
|
|
|
|
if (dotted == cellp->name()) {
|
|
|
|
|
UINFO(9,"Iface matching scope: "<<cellp<<endl);
|
|
|
|
|
if (ifaceParamReplace(nodep, cellp->paramsp())) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-09-19 00:57:27 +00:00
|
|
|
|
nodep->varp(NULL); // Needs relink, as may remove pointed-to var
|
|
|
|
|
}
|
2015-11-12 01:40:24 +00:00
|
|
|
|
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstUnlinkedRef* nodep) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
AstVarXRef* varxrefp = VN_CAST(nodep->op1p(), VarXRef);
|
|
|
|
|
AstNodeFTaskRef* taskrefp = VN_CAST(nodep->op1p(), NodeFTaskRef);
|
2015-11-12 01:40:24 +00:00
|
|
|
|
if (varxrefp) {
|
2015-12-06 00:58:58 +00:00
|
|
|
|
m_unlinkedTxt = varxrefp->dotted();
|
|
|
|
|
} else if (taskrefp) {
|
|
|
|
|
m_unlinkedTxt = taskrefp->dotted();
|
2015-11-12 01:40:24 +00:00
|
|
|
|
} else {
|
|
|
|
|
nodep->v3fatalSrc("Unexpected AstUnlinkedRef node");
|
2015-12-06 00:58:58 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterate(nodep->cellrefp());
|
2015-12-06 00:58:58 +00:00
|
|
|
|
|
|
|
|
|
if (varxrefp) {
|
|
|
|
|
varxrefp->dotted(m_unlinkedTxt);
|
|
|
|
|
} else {
|
|
|
|
|
taskrefp->dotted(m_unlinkedTxt);
|
2015-11-12 01:40:24 +00:00
|
|
|
|
}
|
|
|
|
|
nodep->replaceWith(nodep->op1p()->unlinkFrBack());
|
2015-10-23 00:13:49 +00:00
|
|
|
|
pushDeletep(nodep); VL_DANGLING(nodep);
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstCellArrayRef* nodep) {
|
2015-10-23 00:13:49 +00:00
|
|
|
|
V3Const::constifyParamsEdit(nodep->selp());
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (const AstConst* constp = VN_CAST(nodep->selp(), Const)) {
|
2015-10-23 00:13:49 +00:00
|
|
|
|
string index = AstNode::encodeNumber(constp->toSInt());
|
2015-12-06 00:58:58 +00:00
|
|
|
|
string replacestr = nodep->name() + "__BRA__??__KET__";
|
|
|
|
|
size_t pos = m_unlinkedTxt.find(replacestr);
|
|
|
|
|
if (pos == string::npos) {
|
|
|
|
|
nodep->v3error("Could not find array index in unlinked text: '" << m_unlinkedTxt << "' for node: " << nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_unlinkedTxt.replace(pos, replacestr.length(), nodep->name() + "__BRA__"+index+"__KET__");
|
2015-10-23 00:13:49 +00:00
|
|
|
|
} else {
|
|
|
|
|
nodep->v3error("Could not expand constant selection inside dotted reference: "<<nodep->selp()->prettyName());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-16 18:49:34 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Generate Statements
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstGenerate* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (debug()>=9) nodep->dumpTree(cout,"-genin: ");
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// After expanding the generate, all statements under it can be moved
|
|
|
|
|
// up, and the generate block deleted as it's not relevant
|
2006-09-11 20:42:47 +00:00
|
|
|
|
if (AstNode* stmtsp = nodep->stmtsp()) {
|
|
|
|
|
stmtsp->unlinkFrBackWithNext();
|
|
|
|
|
nodep->replaceWith(stmtsp);
|
|
|
|
|
if (debug()>=9) stmtsp->dumpTree(cout,"-genout: ");
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstGenIf* nodep) {
|
2012-03-10 00:34:02 +00:00
|
|
|
|
UINFO(9," GENIF "<<nodep<<endl);
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateAndNextNull(nodep->condp());
|
2012-04-20 02:53:52 +00:00
|
|
|
|
// We suppress errors when widthing params since short-circuiting in
|
|
|
|
|
// the conditional evaluation may mean these error can never occur. We
|
|
|
|
|
// then make sure that short-circuiting is used by constifyParamsEdit.
|
|
|
|
|
V3Width::widthGenerateParamsEdit(nodep); // Param typed widthing will
|
|
|
|
|
// NOT recurse the body.
|
|
|
|
|
V3Const::constifyGenerateParamsEdit(nodep->condp()); // condp may change
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (const AstConst* constp = VN_CAST(nodep->condp(), Const)) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* keepp = (constp->isZero()
|
|
|
|
|
? nodep->elsesp()
|
|
|
|
|
: nodep->ifsp());
|
|
|
|
|
if (keepp) {
|
|
|
|
|
keepp->unlinkFrBackWithNext();
|
|
|
|
|
nodep->replaceWith(keepp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
2009-10-16 01:47:15 +00:00
|
|
|
|
// Normal edit rules will now recurse the replacement
|
2006-08-26 11:35:28 +00:00
|
|
|
|
} else {
|
|
|
|
|
nodep->condp()->v3error("Generate If condition must evaluate to constant");
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-20 02:53:52 +00:00
|
|
|
|
|
|
|
|
|
//! Parameter subsitution for generated for loops.
|
|
|
|
|
//! @todo Unlike generated IF, we don't have to worry about short-circuiting the conditional
|
|
|
|
|
//! expression, since this is currently restricted to simple comparisons. If we ever do
|
2013-05-28 01:39:19 +00:00
|
|
|
|
//! move to more generic constant expressions, such code will be needed here.
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstBegin* nodep) {
|
2012-07-21 21:12:42 +00:00
|
|
|
|
if (nodep->genforp()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
AstGenFor* forp = VN_CAST(nodep->genforp(), GenFor);
|
2012-07-21 21:12:42 +00:00
|
|
|
|
if (!forp) nodep->v3fatalSrc("Non-GENFOR under generate-for BEGIN");
|
|
|
|
|
// We should have a GENFOR under here. We will be replacing the begin,
|
|
|
|
|
// so process here rather than at the generate to avoid iteration problems
|
|
|
|
|
UINFO(9," BEGIN "<<nodep<<endl);
|
|
|
|
|
UINFO(9," GENFOR "<<forp<<endl);
|
|
|
|
|
V3Width::widthParamsEdit(forp); // Param typed widthing will NOT recurse the body
|
|
|
|
|
// Outer wrapper around generate used to hold genvar, and to insure genvar
|
|
|
|
|
// doesn't conflict in V3LinkDot resolution with other genvars
|
|
|
|
|
// Now though we need to change BEGIN("zzz",GENFOR(...)) to
|
|
|
|
|
// a BEGIN("zzz__BRA__{loop#}__KET__")
|
|
|
|
|
string beginName = nodep->name();
|
|
|
|
|
// Leave the original Begin, as need a container for the (possible) GENVAR
|
|
|
|
|
// Note V3Unroll will replace some AstVarRef's to the loop variable with constants
|
2015-10-04 17:16:35 +00:00
|
|
|
|
V3Unroll::unrollGen(forp, beginName); VL_DANGLING(forp);
|
2012-07-21 21:12:42 +00:00
|
|
|
|
// Blocks were constructed under the special begin, move them up
|
|
|
|
|
// Note forp is null, so grab statements again
|
|
|
|
|
if (AstNode* stmtsp = nodep->genforp()) {
|
|
|
|
|
stmtsp->unlinkFrBackWithNext();
|
|
|
|
|
nodep->addNextHere(stmtsp);
|
|
|
|
|
// Note this clears nodep->genforp(), so begin is no longer special
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateChildren(nodep);
|
2012-07-21 21:12:42 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstGenFor* nodep) {
|
2012-07-21 21:12:42 +00:00
|
|
|
|
nodep->v3fatalSrc("GENFOR should have been wrapped in BEGIN");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstGenCase* nodep) {
|
2012-03-10 00:34:02 +00:00
|
|
|
|
UINFO(9," GENCASE "<<nodep<<endl);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
AstNode* keepp = NULL;
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateAndNextNull(nodep->exprp());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
V3Case::caseLint(nodep);
|
2012-04-20 02:53:52 +00:00
|
|
|
|
V3Width::widthParamsEdit(nodep); // Param typed widthing will NOT recurse the body,
|
|
|
|
|
// don't trigger errors yet.
|
2009-10-15 00:13:04 +00:00
|
|
|
|
V3Const::constifyParamsEdit(nodep->exprp()); // exprp may change
|
2018-02-02 02:32:58 +00:00
|
|
|
|
AstConst* exprp = VN_CAST(nodep->exprp(), Const);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Constify
|
2018-02-02 02:32:58 +00:00
|
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
for (AstNode* ep = itemp->condsp(); ep; ) {
|
|
|
|
|
AstNode* nextp = ep->nextp(); //May edit list
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateAndNextNull(ep);
|
2015-10-04 17:16:35 +00:00
|
|
|
|
V3Const::constifyParamsEdit(ep); VL_DANGLING(ep); // ep may change
|
2006-08-26 11:35:28 +00:00
|
|
|
|
ep = nextp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Item match
|
2018-02-02 02:32:58 +00:00
|
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (!itemp->isDefault()) {
|
|
|
|
|
for (AstNode* ep = itemp->condsp(); ep; ep=ep->nextp()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
if (const AstConst* ccondp = VN_CAST(ep, Const)) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
V3Number match (nodep->fileline(), 1);
|
|
|
|
|
match.opEq(ccondp->num(), exprp->num());
|
|
|
|
|
if (!keepp && match.isNeqZero()) {
|
|
|
|
|
keepp = itemp->bodysp();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
itemp->v3error("Generate Case item does not evaluate to constant");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Else default match
|
2018-02-02 02:32:58 +00:00
|
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp; itemp=VN_CAST(itemp->nextp(), CaseItem)) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
if (itemp->isDefault()) {
|
|
|
|
|
if (!keepp) keepp=itemp->bodysp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Replace
|
|
|
|
|
if (keepp) {
|
|
|
|
|
keepp->unlinkFrBackWithNext();
|
|
|
|
|
nodep->replaceWith(keepp);
|
|
|
|
|
}
|
|
|
|
|
else nodep->unlinkFrBack();
|
2015-10-04 17:16:35 +00:00
|
|
|
|
nodep->deleteTree(); VL_DANGLING(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Default: Just iterate
|
2016-11-27 13:11:38 +00:00
|
|
|
|
virtual void visit(AstNode* nodep) {
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTUCTORS
|
2015-10-04 02:33:06 +00:00
|
|
|
|
explicit ParamVisitor(AstNetlist* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
m_longId = 0;
|
2017-11-18 22:42:35 +00:00
|
|
|
|
m_modp = NULL;
|
2006-08-26 11:35:28 +00:00
|
|
|
|
//
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterate(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
virtual ~ParamVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
// VISITs
|
|
|
|
|
|
2013-05-28 01:39:19 +00:00
|
|
|
|
void ParamVisitor::visitCell(AstCell* nodep) {
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Cell: Check for parameters in the instantiation.
|
2018-05-11 00:55:37 +00:00
|
|
|
|
iterateChildren(nodep);
|
2013-05-28 01:39:19 +00:00
|
|
|
|
if (!nodep->modp()) nodep->v3fatalSrc("Not linked?");
|
2017-11-18 22:40:10 +00:00
|
|
|
|
// We always run this, even if no parameters, as need to look for interfaces,
|
|
|
|
|
// and remove any recursive references
|
|
|
|
|
{
|
2006-08-26 11:35:28 +00:00
|
|
|
|
UINFO(4,"De-parameterize: "<<nodep<<endl);
|
|
|
|
|
// Create new module name with _'s between the constants
|
2013-01-19 02:35:43 +00:00
|
|
|
|
if (debug()>=10) nodep->dumpTree(cout,"-cell:\t");
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Evaluate all module constants
|
2009-10-15 00:13:04 +00:00
|
|
|
|
V3Const::constifyParamsEdit(nodep);
|
2017-11-18 22:40:10 +00:00
|
|
|
|
AstNodeModule* srcModp = nodep->modp();
|
2009-01-08 14:22:31 +00:00
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Make sure constification worked
|
|
|
|
|
// Must be a separate loop, as constant conversion may have changed some pointers.
|
2013-01-19 02:35:43 +00:00
|
|
|
|
//if (debug()) nodep->dumpTree(cout,"-cel2:\t");
|
2017-11-18 22:40:10 +00:00
|
|
|
|
string longname = srcModp->name();
|
2009-01-08 14:22:31 +00:00
|
|
|
|
bool any_overrides = false;
|
2017-11-18 22:42:35 +00:00
|
|
|
|
if (nodep->recursive()) any_overrides = true; // Must always clone __Vrcm (recursive modules)
|
2006-08-26 11:35:28 +00:00
|
|
|
|
longname += "_";
|
|
|
|
|
if (debug()>8) nodep->paramsp()->dumpTreeAndNext(cout,"-cellparams:\t");
|
2018-02-02 02:32:58 +00:00
|
|
|
|
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
2012-04-26 23:01:11 +00:00
|
|
|
|
if (!pinp->exprp()) continue; // No-connect
|
2016-03-13 01:54:52 +00:00
|
|
|
|
if (AstVar* modvarp = pinp->modVarp()) {
|
|
|
|
|
if (!modvarp->isGParam()) {
|
|
|
|
|
pinp->v3error("Attempted parameter setting of non-parameter: Param "<<pinp->prettyName()<<" of "<<nodep->prettyName());
|
2018-02-02 02:32:58 +00:00
|
|
|
|
} else if (VN_IS(pinp->exprp(), InitArray)
|
|
|
|
|
&& VN_IS(modvarp->subDTypep(), UnpackArrayDType)) {
|
2017-05-02 23:16:54 +00:00
|
|
|
|
// Array assigned to array
|
|
|
|
|
AstNode* exprp = pinp->exprp();
|
2017-11-18 22:40:10 +00:00
|
|
|
|
longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
2017-05-02 23:16:54 +00:00
|
|
|
|
any_overrides = true;
|
2015-11-05 03:01:21 +00:00
|
|
|
|
} else {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
AstConst* exprp = VN_CAST(pinp->exprp(), Const);
|
|
|
|
|
AstConst* origp = VN_CAST(modvarp->valuep(), Const);
|
2016-03-13 01:54:52 +00:00
|
|
|
|
if (!exprp) {
|
|
|
|
|
//if (debug()) pinp->dumpTree(cout,"error:");
|
|
|
|
|
pinp->v3error("Can't convert defparam value to constant: Param "<<pinp->name()<<" of "<<nodep->prettyName());
|
|
|
|
|
pinp->exprp()->replaceWith(new AstConst(pinp->fileline(), V3Number(pinp->fileline(), modvarp->width(), 0)));
|
|
|
|
|
} else if (origp && exprp->sameTree(origp)) {
|
|
|
|
|
// Setting parameter to its default value. Just ignore it.
|
|
|
|
|
// This prevents making additional modules, and makes coverage more
|
|
|
|
|
// obvious as it won't show up under a unique module page name.
|
|
|
|
|
} else if (exprp->num().isDouble()
|
|
|
|
|
|| exprp->num().isString()
|
|
|
|
|
|| exprp->num().isFourState()) {
|
2017-11-18 22:40:10 +00:00
|
|
|
|
longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
2016-03-13 01:54:52 +00:00
|
|
|
|
any_overrides = true;
|
|
|
|
|
} else {
|
2017-11-18 22:40:10 +00:00
|
|
|
|
longname += "_" + paramSmallName(srcModp, modvarp) + exprp->num().ascii(false);
|
2016-03-13 01:54:52 +00:00
|
|
|
|
any_overrides = true;
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2016-03-15 01:51:31 +00:00
|
|
|
|
} else if (AstParamTypeDType* modvarp = pinp->modPTypep()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
AstNodeDType* exprp = VN_CAST(pinp->exprp(), NodeDType);
|
2016-03-15 01:51:31 +00:00
|
|
|
|
AstNodeDType* origp = modvarp->subDTypep();
|
|
|
|
|
if (!exprp) {
|
|
|
|
|
pinp->v3error("Parameter type pin value isn't a type: Param "<<pinp->prettyName()<<" of "<<nodep->prettyName());
|
|
|
|
|
} else if (!origp) {
|
|
|
|
|
pinp->v3error("Parameter type variable isn't a type: Param "<<modvarp->prettyName());
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(9,"Parameter type assignment expr="<<exprp<<" to "<<origp<<endl);
|
|
|
|
|
if (origp && exprp->sameTree(origp)) {
|
|
|
|
|
// Setting parameter to its default value. Just ignore it.
|
|
|
|
|
// This prevents making additional modules, and makes coverage more
|
|
|
|
|
// obvious as it won't show up under a unique module page name.
|
|
|
|
|
} else {
|
2017-11-18 22:40:10 +00:00
|
|
|
|
longname += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
|
2016-03-15 01:51:31 +00:00
|
|
|
|
any_overrides = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-13 01:54:52 +00:00
|
|
|
|
} else {
|
|
|
|
|
pinp->v3error("Parameter not found in sub-module: Param "<<pinp->prettyName()<<" of "<<nodep->prettyName());
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2013-05-28 01:39:19 +00:00
|
|
|
|
IfaceRefRefs ifaceRefRefs;
|
2018-02-02 02:32:58 +00:00
|
|
|
|
for (AstPin* pinp = nodep->pinsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
2013-05-28 01:39:19 +00:00
|
|
|
|
AstVar* modvarp = pinp->modVarp();
|
|
|
|
|
if (modvarp->isIfaceRef()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
AstIfaceRefDType* portIrefp = VN_CAST(modvarp->subDTypep(), IfaceRefDType);
|
|
|
|
|
if (!portIrefp && VN_IS(modvarp->subDTypep(), UnpackArrayDType)) {
|
|
|
|
|
portIrefp = VN_CAST(VN_CAST(modvarp->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType);
|
2015-12-06 00:39:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-24 03:06:24 +00:00
|
|
|
|
AstIfaceRefDType* pinIrefp = NULL;
|
2015-12-05 22:12:03 +00:00
|
|
|
|
AstNode* exprp = pinp->exprp();
|
2015-10-24 03:06:24 +00:00
|
|
|
|
if (exprp
|
2018-02-02 02:32:58 +00:00
|
|
|
|
&& VN_IS(exprp, VarRef)
|
|
|
|
|
&& VN_CAST(exprp, VarRef)->varp()
|
|
|
|
|
&& VN_CAST(exprp, VarRef)->varp()->subDTypep()
|
|
|
|
|
&& VN_IS(VN_CAST(exprp, VarRef)->varp()->subDTypep(), IfaceRefDType))
|
|
|
|
|
pinIrefp = VN_CAST(VN_CAST(exprp, VarRef)->varp()->subDTypep(), IfaceRefDType);
|
2015-10-24 03:06:24 +00:00
|
|
|
|
else if (exprp
|
|
|
|
|
&& exprp->op1p()
|
2018-02-02 02:32:58 +00:00
|
|
|
|
&& VN_IS(exprp->op1p(), VarRef)
|
|
|
|
|
&& VN_CAST(exprp->op1p(), VarRef)->varp()
|
|
|
|
|
&& VN_CAST(exprp->op1p(), VarRef)->varp()->subDTypep()
|
|
|
|
|
&& VN_CAST(VN_CAST(exprp->op1p(), VarRef)->varp()->subDTypep(), UnpackArrayDType)
|
|
|
|
|
&& VN_CAST(VN_CAST(exprp->op1p(), VarRef)->varp()->subDTypep(), UnpackArrayDType)->subDTypep()
|
|
|
|
|
&& VN_CAST(VN_CAST(VN_CAST(exprp->op1p(), VarRef)->varp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType))
|
|
|
|
|
pinIrefp = VN_CAST(VN_CAST(VN_CAST(exprp->op1p(), VarRef)->varp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType);
|
2015-12-06 00:39:40 +00:00
|
|
|
|
else if (exprp
|
2018-02-02 02:32:58 +00:00
|
|
|
|
&& VN_IS(exprp, VarRef)
|
|
|
|
|
&& VN_CAST(exprp, VarRef)->varp()
|
|
|
|
|
&& VN_CAST(exprp, VarRef)->varp()->subDTypep()
|
|
|
|
|
&& VN_CAST(VN_CAST(exprp, VarRef)->varp()->subDTypep(), UnpackArrayDType)
|
|
|
|
|
&& VN_CAST(VN_CAST(exprp, VarRef)->varp()->subDTypep(), UnpackArrayDType)->subDTypep()
|
|
|
|
|
&& VN_CAST(VN_CAST(VN_CAST(exprp, VarRef)->varp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType))
|
|
|
|
|
pinIrefp = VN_CAST(VN_CAST(VN_CAST(exprp, VarRef)->varp()->subDTypep(), UnpackArrayDType)->subDTypep(), IfaceRefDType);
|
2015-12-06 00:39:40 +00:00
|
|
|
|
|
|
|
|
|
UINFO(9," portIfaceRef "<<portIrefp<<endl);
|
2015-10-24 03:06:24 +00:00
|
|
|
|
|
2015-12-06 00:39:40 +00:00
|
|
|
|
if (!portIrefp) {
|
2017-03-17 22:35:53 +00:00
|
|
|
|
pinp->v3error("Interface port '"<<modvarp->prettyName()<<"' is not an interface " << modvarp);
|
2015-12-06 00:39:40 +00:00
|
|
|
|
} else if (!pinIrefp) {
|
2013-05-28 01:39:19 +00:00
|
|
|
|
pinp->v3error("Interface port '"<<modvarp->prettyName()<<"' is not connected to interface/modport pin expression");
|
|
|
|
|
} else {
|
2015-12-06 00:39:40 +00:00
|
|
|
|
UINFO(9," pinIfaceRef "<<pinIrefp<<endl);
|
2013-05-28 01:39:19 +00:00
|
|
|
|
if (portIrefp->ifaceViaCellp() != pinIrefp->ifaceViaCellp()) {
|
|
|
|
|
UINFO(9," IfaceRefDType needs reconnect "<<pinIrefp<<endl);
|
2017-11-18 22:40:10 +00:00
|
|
|
|
longname += "_" + paramSmallName(srcModp, pinp->modVarp()) + paramValueNumber(pinIrefp);
|
2013-05-28 01:39:19 +00:00
|
|
|
|
any_overrides = true;
|
|
|
|
|
ifaceRefRefs.push_back(make_pair(portIrefp,pinIrefp));
|
2018-04-05 01:03:43 +00:00
|
|
|
|
if (portIrefp->ifacep() != pinIrefp->ifacep()
|
|
|
|
|
// Might be different only due to param cloning, so check names too
|
|
|
|
|
&& portIrefp->ifaceName() != pinIrefp->ifaceName()) {
|
|
|
|
|
pinp->v3error("Port '"<<pinp->prettyName()<<"' expects '"
|
|
|
|
|
<<AstNode::prettyName(portIrefp->ifaceName())
|
|
|
|
|
<<"' interface but pin connects '"
|
|
|
|
|
<<AstNode::prettyName(pinIrefp->ifaceName())<<"' interface");
|
|
|
|
|
}
|
2013-05-28 01:39:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
2009-01-08 14:22:31 +00:00
|
|
|
|
if (!any_overrides) {
|
|
|
|
|
UINFO(8,"Cell parameters all match original values, skipping expansion.\n");
|
|
|
|
|
} else {
|
|
|
|
|
// If the name is very long, we don't want to overwhelm the filename limit
|
|
|
|
|
// We don't do this always, as it aids debugability to have intuitive naming.
|
2015-09-20 00:12:35 +00:00
|
|
|
|
// TODO can use new V3Name hash replacement instead of this
|
2009-01-08 14:22:31 +00:00
|
|
|
|
string newname = longname;
|
|
|
|
|
if (longname.length()>30) {
|
|
|
|
|
LongMap::iterator iter = m_longMap.find(longname);
|
|
|
|
|
if (iter != m_longMap.end()) {
|
|
|
|
|
newname = iter->second;
|
|
|
|
|
} else {
|
2017-11-18 22:40:10 +00:00
|
|
|
|
newname = srcModp->name();
|
2009-01-08 14:22:31 +00:00
|
|
|
|
newname += "__pi"+cvtToStr(++m_longId); // We use all upper case above, so lower here can't conflict
|
|
|
|
|
m_longMap.insert(make_pair(longname, newname));
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2017-11-18 22:40:10 +00:00
|
|
|
|
UINFO(4,"Name: "<<srcModp->name()<<"->"<<longname<<"->"<<newname<<endl);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
2009-01-08 14:22:31 +00:00
|
|
|
|
//
|
|
|
|
|
// Already made this flavor?
|
2017-11-18 22:40:10 +00:00
|
|
|
|
AstNodeModule* cellmodp = NULL;
|
2009-01-08 14:22:31 +00:00
|
|
|
|
ModNameMap::iterator iter = m_modNameMap.find(newname);
|
2017-11-18 22:40:10 +00:00
|
|
|
|
if (iter != m_modNameMap.end()) cellmodp = iter->second.m_modp;
|
|
|
|
|
if (!cellmodp) {
|
2009-01-08 14:22:31 +00:00
|
|
|
|
// Deep clone of new module
|
|
|
|
|
// Note all module internal variables will be re-linked to the new modules by clone
|
|
|
|
|
// However links outside the module (like on the upper cells) will not.
|
2017-11-18 22:40:10 +00:00
|
|
|
|
cellmodp = srcModp->cloneTree(false);
|
|
|
|
|
cellmodp->name(newname);
|
|
|
|
|
cellmodp->user5(false); // We need to re-recurse this module once changed
|
2017-11-18 22:42:35 +00:00
|
|
|
|
cellmodp->recursive(false);
|
|
|
|
|
cellmodp->recursiveClone(false);
|
|
|
|
|
nodep->recursive(false);
|
|
|
|
|
// Recursion may need level cleanups
|
|
|
|
|
if (cellmodp->level() <= m_modp->level()) cellmodp->level(m_modp->level()+1);
|
|
|
|
|
if ((cellmodp->level() - srcModp->level()) >= (v3Global.opt.moduleRecursionDepth() - 2)) {
|
|
|
|
|
nodep->v3error("Exceeded maximum --module-recursion-depth of "<<v3Global.opt.moduleRecursionDepth());
|
|
|
|
|
}
|
|
|
|
|
// Keep tree sorted by level
|
|
|
|
|
AstNodeModule* insertp = srcModp;
|
2018-02-02 02:32:58 +00:00
|
|
|
|
while (VN_IS(insertp->nextp(), NodeModule)
|
|
|
|
|
&& VN_CAST(insertp->nextp(), NodeModule)->level() < cellmodp->level()) {
|
|
|
|
|
insertp = VN_CAST(insertp->nextp(), NodeModule);
|
2017-11-18 22:42:35 +00:00
|
|
|
|
}
|
|
|
|
|
insertp->addNextHere(cellmodp);
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2017-11-18 22:40:10 +00:00
|
|
|
|
m_modNameMap.insert(make_pair(cellmodp->name(), ModInfo(cellmodp)));
|
2009-01-08 14:22:31 +00:00
|
|
|
|
iter = m_modNameMap.find(newname);
|
2016-03-13 01:54:52 +00:00
|
|
|
|
CloneMap* clonemapp = &(iter->second.m_cloneMap);
|
2017-11-18 22:40:10 +00:00
|
|
|
|
UINFO(4," De-parameterize to new: "<<cellmodp<<endl);
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-01-08 14:22:31 +00:00
|
|
|
|
// Grab all I/O so we can remap our pins later
|
|
|
|
|
// Note we allow multiple users of a parameterized model, thus we need to stash this info.
|
2017-11-18 22:40:10 +00:00
|
|
|
|
collectPins(clonemapp, cellmodp);
|
2009-01-08 14:22:31 +00:00
|
|
|
|
// Relink parameter vars to the new module
|
|
|
|
|
relinkPins(clonemapp, nodep->paramsp());
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// Fix any interface references
|
|
|
|
|
for (IfaceRefRefs::iterator it=ifaceRefRefs.begin(); it!=ifaceRefRefs.end(); ++it) {
|
|
|
|
|
AstIfaceRefDType* portIrefp = it->first;
|
|
|
|
|
AstIfaceRefDType* pinIrefp = it->second;
|
2016-11-09 02:16:22 +00:00
|
|
|
|
AstIfaceRefDType* cloneIrefp = portIrefp->clonep();
|
2013-05-28 01:39:19 +00:00
|
|
|
|
UINFO(8," IfaceOld "<<portIrefp<<endl);
|
|
|
|
|
UINFO(8," IfaceTo "<<pinIrefp<<endl);
|
|
|
|
|
if (!cloneIrefp) portIrefp->v3fatalSrc("parameter clone didn't hit AstIfaceRefDType");
|
|
|
|
|
UINFO(8," IfaceClo "<<cloneIrefp<<endl);
|
|
|
|
|
cloneIrefp->ifacep(pinIrefp->ifaceViaCellp());
|
|
|
|
|
UINFO(8," IfaceNew "<<cloneIrefp<<endl);
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-08 14:22:31 +00:00
|
|
|
|
// Assign parameters to the constants specified
|
2013-05-28 01:39:19 +00:00
|
|
|
|
// DOES clone() so must be finished with module clonep() before here
|
2018-02-02 02:32:58 +00:00
|
|
|
|
for (AstPin* pinp = nodep->paramsp(); pinp; pinp=VN_CAST(pinp->nextp(), Pin)) {
|
2016-03-13 01:54:52 +00:00
|
|
|
|
if (pinp->exprp()) {
|
|
|
|
|
if (AstVar* modvarp = pinp->modVarp()) {
|
2017-05-02 23:16:54 +00:00
|
|
|
|
AstNode* newp = pinp->exprp(); // Const or InitArray
|
2016-03-13 01:54:52 +00:00
|
|
|
|
// Remove any existing parameter
|
|
|
|
|
if (modvarp->valuep()) modvarp->valuep()->unlinkFrBack()->deleteTree();
|
|
|
|
|
// Set this parameter to value requested by cell
|
2017-05-02 23:16:54 +00:00
|
|
|
|
modvarp->valuep(newp->cloneTree(false));
|
2016-03-13 01:54:52 +00:00
|
|
|
|
}
|
2016-03-15 01:51:31 +00:00
|
|
|
|
else if (AstParamTypeDType* modptp = pinp->modPTypep()) {
|
2018-02-02 02:32:58 +00:00
|
|
|
|
AstNodeDType* dtypep = VN_CAST(pinp->exprp(), NodeDType);
|
2016-03-15 01:51:31 +00:00
|
|
|
|
if (!dtypep) pinp->v3fatalSrc("unlinked param dtype");
|
|
|
|
|
if (modptp->childDTypep()) pushDeletep(modptp->childDTypep()->unlinkFrBack());
|
|
|
|
|
// Set this parameter to value requested by cell
|
|
|
|
|
modptp->childDTypep(dtypep->cloneTree(false));
|
|
|
|
|
// Later V3LinkDot will convert the ParamDType to a Typedef
|
|
|
|
|
// Not done here as may be localparams, etc, that also need conversion
|
|
|
|
|
}
|
2009-01-08 14:22:31 +00:00
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2016-03-13 01:54:52 +00:00
|
|
|
|
|
2009-01-08 14:22:31 +00:00
|
|
|
|
} else {
|
2017-11-18 22:40:10 +00:00
|
|
|
|
UINFO(4," De-parameterize to old: "<<cellmodp<<endl);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-01-08 14:22:31 +00:00
|
|
|
|
// Have child use this module instead.
|
2017-11-18 22:40:10 +00:00
|
|
|
|
nodep->modp(cellmodp);
|
2009-01-08 14:22:31 +00:00
|
|
|
|
nodep->modName(newname);
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2009-01-08 14:22:31 +00:00
|
|
|
|
// We need to relink the pins to the new module
|
2016-03-13 01:54:52 +00:00
|
|
|
|
CloneMap* clonemapp = &(iter->second.m_cloneMap);
|
2009-01-08 14:22:31 +00:00
|
|
|
|
relinkPins(clonemapp, nodep->pinsp());
|
2017-11-18 22:40:10 +00:00
|
|
|
|
UINFO(8," Done with "<<cellmodp<<endl);
|
2009-01-08 14:22:31 +00:00
|
|
|
|
} // if any_overrides
|
2012-03-20 20:01:53 +00:00
|
|
|
|
|
2017-11-18 22:42:35 +00:00
|
|
|
|
nodep->recursive(false);
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
|
// Delete the parameters from the cell; they're not relevant any longer.
|
2013-05-28 01:39:19 +00:00
|
|
|
|
if (nodep->paramsp()) nodep->paramsp()->unlinkFrBackWithNext()->deleteTree();
|
2009-01-08 14:22:31 +00:00
|
|
|
|
UINFO(8," Done with "<<nodep<<endl);
|
2017-10-02 02:17:37 +00:00
|
|
|
|
//if (debug()>=10) v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("param-out.tree"));
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
2012-03-24 19:54:06 +00:00
|
|
|
|
|
|
|
|
|
// Now remember to process the child module at the end of the module
|
2017-11-18 22:40:10 +00:00
|
|
|
|
m_todoModps.insert(make_pair(nodep->modp()->level(), nodep->modp()));
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Param class functions
|
|
|
|
|
|
|
|
|
|
void V3Param::param(AstNetlist* rootp) {
|
|
|
|
|
UINFO(2,__FUNCTION__<<": "<<endl);
|
2018-03-10 17:57:50 +00:00
|
|
|
|
{
|
|
|
|
|
ParamVisitor visitor (rootp);
|
|
|
|
|
} // Destruct before checking
|
2017-09-18 02:52:57 +00:00
|
|
|
|
V3Global::dumpCheckGlobalTree("param", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
2006-08-26 11:35:28 +00:00
|
|
|
|
}
|