verilator/src/V3LinkResolve.cpp
Wilson Snyder b83b606267 Internals: Detab and fix spacing style issues. No functional change.
When diff, recommend using "git diff --ignore-all-space"
When merging, recommend using "git merge -Xignore-all-space"
2019-05-19 16:13:13 -04:00

532 lines
21 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Resolve module/signal name references
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2019 by Wilson Snyder. This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
// LinkResolve TRANSFORMATIONS:
// Top-down traversal
// Extracts:
// Add SUB so that we subtract off the "base 0-start" of the array
// SelBit: Convert to ArraySel
// Add SUB so that we subtract off the "base 0-start" of the array
// File operations
// Convert normal var to FILE* type
// SenItems: Convert pos/negedge of non-simple signals to temporaries
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include "V3Global.h"
#include "V3String.h"
#include "V3LinkResolve.h"
#include "V3Ast.h"
#include <algorithm>
#include <cstdarg>
#include <map>
#include <vector>
//######################################################################
// Link state, as a visitor of each AstNode
class LinkResolveVisitor : public AstNVisitor {
private:
// NODE STATE
// Entire netlist:
// AstCaseItem::user2() // bool Moved default caseitems
AstUser2InUse m_inuser2;
// STATE
// Below state needs to be preserved between each module call.
AstNodeModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Function or task we're inside
AstVAssert* m_assertp; // Current assertion
int m_senitemCvtNum; // Temporary signal counter
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITs
// TODO: Most of these visitors are here for historical reasons.
// TODO: ExpectDecriptor can move to data type resolution, and the rest
// TODO: could move to V3LinkParse to get them out of the way of elaboration
virtual void visit(AstNodeModule* nodep) {
// Module: Create sim table for entire module and iterate
UINFO(8,"MODULE "<<nodep<<endl);
if (nodep->dead()) return;
m_modp = nodep;
m_senitemCvtNum = 0;
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstInitial* nodep) {
iterateChildren(nodep);
// Initial assignments under function/tasks can just be simple
// assignments without the initial
if (m_ftaskp) {
nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); VL_DANGLING(nodep);
}
}
virtual void visit(AstVAssert* nodep) {
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
m_assertp = nodep;
iterateChildren(nodep);
m_assertp = NULL;
}
virtual void visit(AstVar* nodep) {
iterateChildren(nodep);
if (m_ftaskp) nodep->funcLocal(true);
if (nodep->isSigModPublic()) {
nodep->sigModPublic(false); // We're done with this attribute
m_modp->modPublic(true); // Avoid flattening if signals are exposed
}
}
virtual void visit(AstNodeVarRef* nodep) {
// VarRef: Resolve its reference
if (nodep->varp()) {
nodep->varp()->usedParam(true);
}
iterateChildren(nodep);
}
virtual void visit(AstNodeFTask* nodep) {
// NodeTask: Remember its name for later resolution
// Remember the existing symbol table scope
m_ftaskp = nodep;
iterateChildren(nodep);
m_ftaskp = NULL;
if (nodep->dpiExport()) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
virtual void visit(AstNodeFTaskRef* nodep) {
iterateChildren(nodep);
if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
virtual void visit(AstSenItem* nodep) {
// Remove bit selects, and bark if it's not a simple variable
iterateChildren(nodep);
if (nodep->isClocked()) {
// If it's not a simple variable wrap in a temporary
// This is a bit unfortunate as we haven't done width resolution
// and any width errors will look a bit odd, but it works.
AstNode* sensp = nodep->sensp();
if (sensp
&& !VN_IS(sensp, NodeVarRef)
&& !VN_IS(sensp, Const)) {
// Make a new temp wire
string newvarname = "__Vsenitemexpr"+cvtToStr(++m_senitemCvtNum);
AstVar* newvarp = new AstVar(sensp->fileline(),
AstVarType::MODULETEMP, newvarname,
VFlagLogicPacked(), 1);
// We can't just add under the module, because we may be
// inside a generate, begin, etc.
// We know a SenItem should be under a SenTree/Always etc,
// we we'll just hunt upwards
AstNode* addwherep = nodep; // Add to this element's next
while (VN_IS(addwherep, SenItem)
|| VN_IS(addwherep, SenTree)) {
addwherep = addwherep->backp();
}
if (!VN_IS(addwherep, Always)) { // Assertion perhaps?
sensp->v3error("Unsupported: Non-single-bit pos/negedge clock statement under some complicated block");
addwherep = m_modp;
}
addwherep->addNext(newvarp);
sensp->replaceWith(new AstVarRef(sensp->fileline(), newvarp, false));
AstAssignW* assignp = new AstAssignW
(sensp->fileline(),
new AstVarRef(sensp->fileline(), newvarp, true),
sensp);
addwherep->addNext(assignp);
}
} else { // Old V1995 sensitivity list; we'll probably mostly ignore
bool did = 1;
while (did) {
did = 0;
if (AstNodeSel* selp = VN_CAST(nodep->sensp(), NodeSel)) {
AstNode* fromp = selp->fromp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
did = 1;
}
// NodeSel doesn't include AstSel....
if (AstSel* selp = VN_CAST(nodep->sensp(), Sel)) {
AstNode* fromp = selp->fromp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
did = 1;
}
if (AstNodePreSel* selp = VN_CAST(nodep->sensp(), NodePreSel)) {
AstNode* fromp = selp->lhsp()->unlinkFrBack();
selp->replaceWith(fromp); selp->deleteTree(); VL_DANGLING(selp);
did = 1;
}
}
}
if (!VN_IS(nodep->sensp(), NodeVarRef)
&& !VN_IS(nodep->sensp(), EnumItemRef) // V3Const will cleanup
&& !nodep->isIllegal()) {
if (debug()) nodep->dumpTree(cout, "-tree: ");
nodep->v3error("Unsupported: Complex statement in sensitivity list");
}
}
virtual void visit(AstSenGate* nodep) {
nodep->v3fatalSrc("SenGates shouldn't be in tree yet");
}
virtual void visit(AstNodePreSel* nodep) {
if (!nodep->attrp()) {
iterateChildren(nodep);
// Constification may change the fromp() to a constant, which will lose the
// variable we're extracting from (to determine MSB/LSB/endianness/etc.)
// So we replicate it in another node
// Note that V3Param knows not to replace AstVarRef's under AstAttrOf's
AstNode* basefromp = AstArraySel::baseFromp(nodep);
if (AstNodeVarRef* varrefp = VN_CAST(basefromp, NodeVarRef)) { // Maybe varxref - so need to clone
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
varrefp->cloneTree(false)));
} else if (AstUnlinkedRef* uvxrp = VN_CAST(basefromp, UnlinkedRef)) { // Maybe unlinked - so need to clone
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
uvxrp->cloneTree(false)));
} else if (AstMemberSel* fromp = VN_CAST(basefromp, MemberSel)) {
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE,
fromp->cloneTree(false)));
} else if (AstEnumItemRef* fromp = VN_CAST(basefromp, EnumItemRef)) {
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::ENUM_BASE,
fromp->cloneTree(false)));
} else if (VN_IS(basefromp, Replicate)) {
// From {...}[...] syntax in IEEE 2017
if (basefromp) { UINFO(1," Related node: "<<basefromp<<endl); }
nodep->v3error("Unsupported: Select of concatenation");
nodep = NULL;
} else {
if (basefromp) { UINFO(1," Related node: "<<basefromp<<endl); }
nodep->v3fatalSrc("Illegal bit select; no signal/member being extracted from");
}
}
}
virtual void visit(AstCaseItem* nodep) {
// Move default caseItems to the bottom of the list
// That saves us from having to search each case list twice, for non-defaults and defaults
iterateChildren(nodep);
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
nodep->user2(true);
AstNode* nextp = nodep->nextp();
nodep->unlinkFrBack();
nextp->addNext(nodep);
}
}
virtual void visit(AstPragma* nodep) {
if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) {
if (!m_modp) nodep->v3fatalSrc("PUBLIC_MODULE not under a module");
m_modp->modPublic(true);
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
}
else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) {
if (!m_ftaskp) nodep->v3fatalSrc("PUBLIC_TASK not under a task");
m_ftaskp->taskPublic(true);
m_modp->modPublic(true); // Need to get to the task...
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
}
else if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize better without
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
}
}
else {
iterateChildren(nodep);
}
}
string expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) {
// Check display arguments, return new format string
string newFormat;
bool inPct = false;
string fmt;
for (string::const_iterator it = format.begin(); it != format.end(); ++it) {
char ch = *it;
if (!inPct && ch=='%') {
inPct = true;
fmt = ch;
} else if (inPct && (isdigit(ch) || ch=='.')) {
fmt += ch;
} else if (inPct) {
inPct = false;
fmt += ch;
switch (tolower(ch)) {
case '%': // %% - just output a %
break;
case 'm': // %m - auto insert "name"
if (isScan) { nodep->v3error("Unsupported: %m in $fscanf"); fmt = ""; }
break;
case 'l': // %l - auto insert "library"
if (isScan) { nodep->v3error("Unsupported: %l in $fscanf"); fmt = ""; }
if (m_modp) fmt = VString::quotePercent(m_modp->prettyName());
break;
default: // Most operators, just move to next argument
if (!V3Number::displayedFmtLegal(ch)) {
nodep->v3error("Unknown $display-like format code: %"<<ch);
} else {
if (!argp) {
nodep->v3error("Missing arguments for $display-like format");
} else {
argp = argp->nextp();
}
}
break;
} // switch
newFormat += fmt;
} else {
newFormat += ch;
}
}
if (argp && !isScan) {
int skipCount = 0; // number of args consume by any additional format strings
while (argp) {
if (skipCount) {
argp = argp->nextp();
skipCount--;
continue;
}
AstConst *constp = VN_CAST(argp, Const);
bool isFromString = (constp) ? constp->num().isFromString() : false;
if (isFromString) {
int numchars = argp->dtypep()->width()/8;
string str(numchars, ' ');
// now scan for % operators
bool inpercent = false;
for (int i = 0; i < numchars; i++) {
int ii = numchars - i - 1;
char c = constp->num().dataByte(ii);
str[i] = c;
if (!inpercent && c == '%') {
inpercent = true;
} else if (inpercent) {
inpercent = 0;
switch (c) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.':
inpercent = true;
break;
case '%':
break;
default:
if (V3Number::displayedFmtLegal(c)) {
skipCount++;
}
}
}
}
newFormat.append(str);
AstNode *nextp = argp->nextp();
argp->unlinkFrBack(); pushDeletep(argp); VL_DANGLING(argp);
argp = nextp;
} else {
newFormat.append("%?"); // V3Width to figure it out
argp = argp->nextp();
}
}
}
return newFormat;
}
void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) {
if (!filep) nodep->v3error("Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable");
if (filep && filep->varp()) filep->varp()->attrFileDescr(true);
}
virtual void visit(AstFOpen* nodep) {
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFClose* nodep) {
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFEof* nodep) {
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFRead* nodep) {
iterateChildren(nodep);
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
}
virtual void visit(AstFScanF* nodep) {
iterateChildren(nodep);
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
}
virtual void visit(AstSScanF* nodep) {
iterateChildren(nodep);
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
}
virtual void visit(AstSFormatF* nodep) {
iterateChildren(nodep);
// Cleanup old-school displays without format arguments
if (!nodep->hasFormat()) {
if (nodep->text()!="") nodep->v3fatalSrc("Non-format $sformatf should have \"\" format");
if (VN_IS(nodep->exprsp(), Const)
&& VN_CAST(nodep->exprsp(), Const)->num().isFromString()) {
AstConst* fmtp = VN_CAST(nodep->exprsp()->unlinkFrBack(), Const);
nodep->text(fmtp->num().toString());
pushDeletep(fmtp); VL_DANGLING(fmtp);
}
nodep->hasFormat(true);
}
string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false);
nodep->text(newFormat);
if ((VN_IS(nodep->backp(), Display)
&& VN_CAST(nodep->backp(), Display)->displayType().needScopeTracking())
|| nodep->formatScopeTracking()) {
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
}
}
virtual void visit(AstDisplay* nodep) {
iterateChildren(nodep);
}
virtual void visit(AstUdpTable* nodep) {
UINFO(5,"UDPTABLE "<<nodep<<endl);
if (!v3Global.opt.bboxUnsup()) {
// We don't warn until V3Inst, so that UDPs that are in libraries and
// never used won't result in any warnings.
} else {
// Massive hack, just tie off all outputs so our analysis can proceed
AstVar* varoutp = NULL;
for (AstNode* stmtp = m_modp->stmtsp(); stmtp; stmtp=stmtp->nextp()) {
if (AstVar* varp = VN_CAST(stmtp, Var)) {
if (varp->isReadOnly()) {
} else if (varp->isWritable()) {
if (varoutp) {
varp->v3error("Multiple outputs not allowed in udp modules");
}
varoutp = varp;
// Tie off
m_modp->addStmtp(new AstAssignW(
varp->fileline(),
new AstVarRef(varp->fileline(), varp, true),
new AstConst(varp->fileline(),
AstConst::LogicFalse())));
} else {
varp->v3error("Only inputs and outputs are allowed in udp modules");
}
}
}
nodep->unlinkFrBack(); pushDeletep(nodep); VL_DANGLING(nodep);
}
}
virtual void visit(AstScCtor* nodep) {
// Constructor info means the module must remain public
m_modp->modPublic(true);
iterateChildren(nodep);
}
virtual void visit(AstScDtor* nodep) {
// Destructor info means the module must remain public
m_modp->modPublic(true);
iterateChildren(nodep);
}
virtual void visit(AstScInt* nodep) {
// Special class info means the module must remain public
m_modp->modPublic(true);
iterateChildren(nodep);
}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit LinkResolveVisitor(AstNetlist* rootp) {
m_ftaskp = NULL;
m_modp = NULL;
m_assertp = NULL;
m_senitemCvtNum = 0;
//
iterate(rootp);
}
virtual ~LinkResolveVisitor() {}
};
//######################################################################
// LinkBotupVisitor
// Recurses cells backwards, so we can pick up those things that propagate
// from child cells up to the top module.
class LinkBotupVisitor : public AstNVisitor {
private:
// STATE
AstNodeModule* m_modp; // Current module
// METHODS
VL_DEBUG_FUNC; // Declare debug()
// VISITs
virtual void visit(AstNetlist* nodep) {
// Iterate modules backwards, in bottom-up order.
iterateChildrenBackwards(nodep);
}
virtual void visit(AstNodeModule* nodep) {
m_modp = nodep;
iterateChildren(nodep);
m_modp = NULL;
}
virtual void visit(AstCell* nodep) {
// Parent module inherits child's publicity
if (nodep->modp()->modPublic()) m_modp->modPublic(true);
//** No iteration for speed
}
virtual void visit(AstNodeMath* nodep) {
// Speedup
}
virtual void visit(AstNode* nodep) {
// Default: Just iterate
iterateChildren(nodep);
}
public:
// CONSTUCTORS
explicit LinkBotupVisitor(AstNetlist* rootp) {
m_modp = NULL;
//
iterate(rootp);
}
virtual ~LinkBotupVisitor() {}
};
//######################################################################
// Link class functions
void V3LinkResolve::linkResolve(AstNetlist* rootp) {
UINFO(4,__FUNCTION__<<": "<<endl);
{
LinkResolveVisitor visitor(rootp);
LinkBotupVisitor visitorb(rootp);
} // Destruct before checking
V3Global::dumpCheckGlobalTree("linkresolve", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
}