2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Resolve module/signal name references
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2020-03-21 16:24:24 +01:00
|
|
|
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
|
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// LinkResolve TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
2015-11-11 03:12:15 +01:00
|
|
|
#include "V3String.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3LinkResolve.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <map>
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Link state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class LinkResolveVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Entire netlist:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstCaseItem::user2() // bool Moved default caseitems
|
2020-01-16 02:18:12 +01:00
|
|
|
AstUser2InUse m_inuser2;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
// Below state needs to be preserved between each module call.
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
|
AstClass* m_classp = nullptr; // Class we're inside
|
|
|
|
|
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
|
|
|
|
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
|
|
|
|
int m_senitemCvtNum = 0; // Temporary signal counter
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// VISITs
|
2012-07-21 23:12:42 +02:00
|
|
|
// 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
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeModule* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Module: Create sim table for entire module and iterate
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, "MODULE " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->dead()) return;
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
VL_RESTORER(m_senitemCvtNum);
|
2020-01-20 19:27:27 +01:00
|
|
|
{
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_senitemCvtNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2020-04-26 18:45:06 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstClass* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_classp);
|
2020-04-26 18:45:06 +02:00
|
|
|
{
|
|
|
|
|
m_classp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstInitial* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Initial assignments under function/tasks can just be simple
|
|
|
|
|
// assignments without the initial
|
|
|
|
|
if (m_ftaskp) {
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeCoverOrAssert* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
|
|
|
|
|
m_assertp = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_assertp = nullptr;
|
2007-03-06 22:43:38 +01:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstVar* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-04-20 00:36:55 +02:00
|
|
|
if (m_classp && !nodep->isParam()) nodep->varType(AstVarType::MEMBER);
|
2020-06-10 01:20:16 +02:00
|
|
|
if (m_classp && nodep->isParam())
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: class parameter");
|
2019-05-19 22:13:13 +02:00
|
|
|
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
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeVarRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// VarRef: Resolve its reference
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->varp()) { nodep->varp()->usedParam(true); }
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeFTask* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// NodeTask: Remember its name for later resolution
|
|
|
|
|
// Remember the existing symbol table scope
|
2020-04-05 15:30:23 +02:00
|
|
|
if (m_classp) nodep->classMethod(true);
|
2020-08-23 01:46:21 +02:00
|
|
|
// V3LinkDot moved the isExternDef into the class, the extern proto was
|
|
|
|
|
// checked to exist, and now isn't needed
|
|
|
|
|
nodep->isExternDef(false);
|
|
|
|
|
if (nodep->isExternProto()) {
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-04-26 18:45:06 +02:00
|
|
|
{
|
|
|
|
|
m_ftaskp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2020-08-15 16:12:55 +02:00
|
|
|
m_ftaskp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->dpiExport()) { nodep->scopeNamep(new AstScopeName(nodep->fileline())); }
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeFTaskRef* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
|
|
|
|
|
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
|
|
|
|
|
}
|
2009-12-05 16:38:49 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstSenItem* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Remove bit selects, and bark if it's not a simple variable
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
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();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (sensp && !VN_IS(sensp, NodeVarRef) && !VN_IS(sensp, Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Make a new temp wire
|
2020-04-15 13:58:34 +02:00
|
|
|
string newvarname = "__Vsenitemexpr" + cvtToStr(++m_senitemCvtNum);
|
|
|
|
|
AstVar* newvarp = new AstVar(sensp->fileline(), AstVarType::MODULETEMP, newvarname,
|
2018-08-25 15:52:45 +02:00
|
|
|
VFlagLogicPacked(), 1);
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2020-04-15 13:58:34 +02:00
|
|
|
while (VN_IS(addwherep, SenItem) || VN_IS(addwherep, SenTree)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
addwherep = addwherep->backp();
|
|
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(addwherep, Always)) { // Assertion perhaps?
|
2020-06-10 01:20:16 +02:00
|
|
|
sensp->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Non-single-bit pos/negedge clock statement under "
|
|
|
|
|
"some complicated block");
|
2019-05-19 22:13:13 +02:00
|
|
|
addwherep = m_modp;
|
|
|
|
|
}
|
|
|
|
|
addwherep->addNext(newvarp);
|
2008-11-23 03:10:20 +01:00
|
|
|
|
2020-09-07 23:09:25 +02:00
|
|
|
sensp->replaceWith(new AstVarRef(sensp->fileline(), newvarp, VAccess::READ));
|
2020-04-15 13:58:34 +02:00
|
|
|
AstAssignW* assignp = new AstAssignW(
|
2020-09-07 23:09:25 +02:00
|
|
|
sensp->fileline(), new AstVarRef(sensp->fileline(), newvarp, VAccess::WRITE),
|
|
|
|
|
sensp);
|
2019-05-19 22:13:13 +02:00
|
|
|
addwherep->addNext(assignp);
|
|
|
|
|
}
|
|
|
|
|
} else { // Old V1995 sensitivity list; we'll probably mostly ignore
|
|
|
|
|
bool did = 1;
|
|
|
|
|
while (did) {
|
|
|
|
|
did = 0;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (AstNodeSel* selp = VN_CAST(nodep->sensp(), NodeSel)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode* fromp = selp->fromp()->unlinkFrBack();
|
2020-04-15 13:58:34 +02:00
|
|
|
selp->replaceWith(fromp);
|
|
|
|
|
VL_DO_DANGLING(selp->deleteTree(), selp);
|
2019-05-19 22:13:13 +02:00
|
|
|
did = 1;
|
|
|
|
|
}
|
|
|
|
|
// NodeSel doesn't include AstSel....
|
2018-02-02 03:32:58 +01:00
|
|
|
if (AstSel* selp = VN_CAST(nodep->sensp(), Sel)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode* fromp = selp->fromp()->unlinkFrBack();
|
2020-04-15 13:58:34 +02:00
|
|
|
selp->replaceWith(fromp);
|
|
|
|
|
VL_DO_DANGLING(selp->deleteTree(), selp);
|
2019-05-19 22:13:13 +02:00
|
|
|
did = 1;
|
|
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
if (AstNodePreSel* selp = VN_CAST(nodep->sensp(), NodePreSel)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstNode* fromp = selp->lhsp()->unlinkFrBack();
|
2020-04-15 13:58:34 +02:00
|
|
|
selp->replaceWith(fromp);
|
|
|
|
|
VL_DO_DANGLING(selp->deleteTree(), selp);
|
2019-05-19 22:13:13 +02:00
|
|
|
did = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(nodep->sensp(), NodeVarRef)
|
|
|
|
|
&& !VN_IS(nodep->sensp(), EnumItemRef) // V3Const will cleanup
|
2019-05-19 22:13:13 +02:00
|
|
|
&& !nodep->isIllegal()) {
|
|
|
|
|
if (debug()) nodep->dumpTree(cout, "-tree: ");
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Complex statement in sensitivity list");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-12-21 22:53:51 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodePreSel* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->attrp()) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (AstNodeVarRef* varrefp
|
|
|
|
|
= VN_CAST(basefromp, NodeVarRef)) { // Maybe varxref - so need to clone
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
|
|
|
|
|
varrefp->cloneTree(false)));
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (AstUnlinkedRef* uvxrp
|
|
|
|
|
= VN_CAST(basefromp, UnlinkedRef)) { // Maybe unlinked - so need to clone
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::VAR_BASE,
|
|
|
|
|
uvxrp->cloneTree(false)));
|
2018-02-02 03:32:58 +01:00
|
|
|
} else if (AstMemberSel* fromp = VN_CAST(basefromp, MemberSel)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::MEMBER_BASE,
|
|
|
|
|
fromp->cloneTree(false)));
|
2018-02-02 03:32:58 +01:00
|
|
|
} else if (AstEnumItemRef* fromp = VN_CAST(basefromp, EnumItemRef)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->attrp(new AstAttrOf(nodep->fileline(), AstAttrType::ENUM_BASE,
|
|
|
|
|
fromp->cloneTree(false)));
|
2019-03-14 00:52:23 +01:00
|
|
|
} else if (VN_IS(basefromp, Replicate)) {
|
|
|
|
|
// From {...}[...] syntax in IEEE 2017
|
2020-04-15 13:58:34 +02:00
|
|
|
if (basefromp) { UINFO(1, " Related node: " << basefromp << endl); }
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Select of concatenation");
|
2020-08-15 16:12:55 +02:00
|
|
|
nodep = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (basefromp) { UINFO(1, " Related node: " << basefromp << endl); }
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->v3fatalSrc("Illegal bit select; no signal/member being extracted from");
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstCaseItem* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
|
|
|
|
|
nodep->user2(true);
|
|
|
|
|
AstNode* nextp = nodep->nextp();
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
nextp->addNext(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstPragma* nodep) override {
|
2020-08-15 15:43:53 +02:00
|
|
|
if (nodep->pragType() == AstPragmaType::HIER_BLOCK) {
|
|
|
|
|
UASSERT_OBJ(m_modp, nodep, "HIER_BLOCK not under a module");
|
|
|
|
|
// If this is hierarchical mode which is to create protect-lib,
|
|
|
|
|
// sub modules do not have hier_block meta comment in the source code.
|
|
|
|
|
// But .vlt files may still mark a module which is actually a protect-lib wrapper
|
|
|
|
|
// hier_block. AstNodeModule::hierBlock() can be true only when --hierarchical is
|
|
|
|
|
// specified.
|
|
|
|
|
m_modp->hierBlock(v3Global.opt.hierarchical());
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
} else if (nodep->pragType() == AstPragmaType::PUBLIC_MODULE) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_modp, nodep, "PUBLIC_MODULE not under a module");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->modPublic(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
} else if (nodep->pragType() == AstPragmaType::PUBLIC_TASK) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_ftaskp, nodep, "PUBLIC_TASK not under a task");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_ftaskp->taskPublic(true);
|
|
|
|
|
m_modp->modPublic(true); // Need to get to the task...
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
} else if (nodep->pragType() == AstPragmaType::COVERAGE_BLOCK_OFF) {
|
|
|
|
|
if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize
|
|
|
|
|
// better without
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2015-10-28 01:58:31 +01:00
|
|
|
string expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Check display arguments, return new format string
|
|
|
|
|
string newFormat;
|
|
|
|
|
bool inPct = false;
|
2020-05-12 04:13:59 +02:00
|
|
|
bool inIgnore = false;
|
2018-10-14 05:06:36 +02:00
|
|
|
string fmt;
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const char ch : format) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!inPct && ch == '%') {
|
2019-05-19 22:13:13 +02:00
|
|
|
inPct = true;
|
2020-05-12 04:13:59 +02:00
|
|
|
inIgnore = false;
|
2019-05-19 22:13:13 +02:00
|
|
|
fmt = ch;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (inPct && (isdigit(ch) || ch == '.' || ch == '-')) {
|
2019-05-19 22:13:13 +02:00
|
|
|
fmt += ch;
|
|
|
|
|
} else if (inPct) {
|
|
|
|
|
inPct = false;
|
|
|
|
|
fmt += ch;
|
|
|
|
|
switch (tolower(ch)) {
|
|
|
|
|
case '%': // %% - just output a %
|
|
|
|
|
break;
|
2020-05-12 04:13:59 +02:00
|
|
|
case '*':
|
|
|
|
|
inPct = true;
|
|
|
|
|
inIgnore = true;
|
|
|
|
|
break;
|
2019-05-19 22:13:13 +02:00
|
|
|
case 'm': // %m - auto insert "name"
|
2020-04-15 13:58:34 +02:00
|
|
|
if (isScan) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %m in $fscanf");
|
2020-04-15 13:58:34 +02:00
|
|
|
fmt = "";
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
break;
|
|
|
|
|
case 'l': // %l - auto insert "library"
|
2020-04-15 13:58:34 +02:00
|
|
|
if (isScan) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %l in $fscanf");
|
2020-04-15 13:58:34 +02:00
|
|
|
fmt = "";
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_modp) fmt = VString::quotePercent(m_modp->prettyName());
|
|
|
|
|
break;
|
|
|
|
|
default: // Most operators, just move to next argument
|
2020-05-12 04:13:59 +02:00
|
|
|
if (!V3Number::displayedFmtLegal(ch, isScan)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3error("Unknown $display-like format code: '%" << ch << "'");
|
2020-05-12 04:13:59 +02:00
|
|
|
} else if (!inIgnore) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!argp) {
|
|
|
|
|
nodep->v3error("Missing arguments for $display-like format");
|
|
|
|
|
} else {
|
|
|
|
|
argp = argp->nextp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} // switch
|
|
|
|
|
newFormat += fmt;
|
|
|
|
|
} else {
|
|
|
|
|
newFormat += ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-11 03:12:15 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
if (argp && !isScan) {
|
|
|
|
|
int skipCount = 0; // number of args consume by any additional format strings
|
|
|
|
|
while (argp) {
|
|
|
|
|
if (skipCount) {
|
|
|
|
|
argp = argp->nextp();
|
|
|
|
|
skipCount--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
AstConst* constp = VN_CAST(argp, Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
bool isFromString = (constp) ? constp->num().isFromString() : false;
|
|
|
|
|
if (isFromString) {
|
2020-04-15 13:58:34 +02:00
|
|
|
int numchars = argp->dtypep()->width() / 8;
|
2019-05-19 22:13:13 +02:00
|
|
|
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) {
|
2020-05-28 23:39:20 +02:00
|
|
|
case '0': // FALLTHRU
|
|
|
|
|
case '1': // FALLTHRU
|
|
|
|
|
case '2': // FALLTHRU
|
|
|
|
|
case '3': // FALLTHRU
|
|
|
|
|
case '4': // FALLTHRU
|
|
|
|
|
case '5': // FALLTHRU
|
|
|
|
|
case '6': // FALLTHRU
|
|
|
|
|
case '7': // FALLTHRU
|
|
|
|
|
case '8': // FALLTHRU
|
|
|
|
|
case '9': // FALLTHRU
|
2020-04-15 13:58:34 +02:00
|
|
|
case '.': inpercent = true; break;
|
|
|
|
|
case '%': break;
|
2019-05-19 22:13:13 +02:00
|
|
|
default:
|
2020-05-12 04:13:59 +02:00
|
|
|
if (V3Number::displayedFmtLegal(c, isScan)) ++skipCount;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
newFormat.append(str);
|
2020-04-15 13:58:34 +02:00
|
|
|
AstNode* nextp = argp->nextp();
|
|
|
|
|
argp->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(argp), argp);
|
2019-05-19 22:13:13 +02:00
|
|
|
argp = nextp;
|
2019-05-17 03:21:38 +02:00
|
|
|
} else {
|
|
|
|
|
newFormat.append("%?"); // V3Width to figure it out
|
|
|
|
|
argp = argp->nextp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return newFormat;
|
2008-06-30 20:31:58 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void expectDescriptor(AstNode* nodep, AstNodeVarRef* filep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!filep) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: $fopen/$fclose/$f* descriptor must be a simple variable");
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
if (filep && filep->varp()) filep->varp()->attrFileDescr(true);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2008-06-30 20:31:58 +02:00
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstFOpen* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2018-02-02 03:32:58 +01:00
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstFOpenMcd* nodep) override {
|
2020-05-15 00:03:00 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
|
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstFClose* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2018-02-02 03:32:58 +01:00
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
2006-12-21 22:53:51 +01:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstFError* nodep) override {
|
2020-04-05 17:22:05 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
|
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstFEof* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-03-08 02:56:53 +01:00
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
|
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstFRead* nodep) override {
|
2019-03-08 02:56:53 +01:00
|
|
|
iterateChildren(nodep);
|
2018-02-02 03:32:58 +01:00
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
2008-06-26 14:52:02 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstFScanF* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstSScanF* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstSFormatF* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Cleanup old-school displays without format arguments
|
|
|
|
|
if (!nodep->hasFormat()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT_OBJ(nodep->text() == "", nodep,
|
|
|
|
|
"Non-format $sformatf should have \"\" format");
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->exprsp(), Const)
|
|
|
|
|
&& VN_CAST(nodep->exprsp(), Const)->num().isFromString()) {
|
|
|
|
|
AstConst* fmtp = VN_CAST(nodep->exprsp()->unlinkFrBack(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->text(fmtp->num().toString());
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(fmtp), fmtp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->hasFormat(true);
|
|
|
|
|
}
|
|
|
|
|
string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false);
|
|
|
|
|
nodep->text(newFormat);
|
2018-02-02 03:32:58 +01:00
|
|
|
if ((VN_IS(nodep->backp(), Display)
|
|
|
|
|
&& VN_CAST(nodep->backp(), Display)->displayType().needScopeTracking())
|
2019-05-19 22:13:13 +02:00
|
|
|
|| nodep->formatScopeTracking()) {
|
|
|
|
|
nodep->scopeNamep(new AstScopeName(nodep->fileline()));
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstUdpTable* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, "UDPTABLE " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
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
|
2020-08-15 16:12:55 +02:00
|
|
|
AstVar* varoutp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = m_modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (AstVar* varp = VN_CAST(stmtp, Var)) {
|
2018-10-27 23:29:00 +02:00
|
|
|
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(
|
2020-09-07 23:09:25 +02:00
|
|
|
varp->fileline(),
|
|
|
|
|
new AstVarRef(varp->fileline(), varp, VAccess::WRITE),
|
2020-04-15 13:58:34 +02:00
|
|
|
new AstConst(varp->fileline(), AstConst::LogicFalse())));
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
varp->v3error("Only inputs and outputs are allowed in udp modules");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-11-21 01:53:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstScCtor* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Constructor info means the module must remain public
|
|
|
|
|
m_modp->modPublic(true);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-30 19:27:53 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstScDtor* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Destructor info means the module must remain public
|
|
|
|
|
m_modp->modPublic(true);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstScInt* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Special class info means the module must remain public
|
|
|
|
|
m_modp->modPublic(true);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit LinkResolveVisitor(AstNetlist* rootp) { iterate(rootp); }
|
2020-08-15 17:44:10 +02:00
|
|
|
virtual ~LinkResolveVisitor() override {}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// LinkBotupVisitor
|
2019-05-19 22:13:13 +02:00
|
|
|
// Recurses cells backwards, so we can pick up those things that propagate
|
|
|
|
|
// from child cells up to the top module.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
class LinkBotupVisitor : public AstNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2009-01-21 22:56:50 +01:00
|
|
|
|
|
|
|
|
// METHODS
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// VISITs
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNetlist* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate modules backwards, in bottom-up order.
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildrenBackwards(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2020-01-20 19:27:27 +01:00
|
|
|
{
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstCell* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Parent module inherits child's publicity
|
|
|
|
|
if (nodep->modp()->modPublic()) m_modp->modPublic(true);
|
|
|
|
|
//** No iteration for speed
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeMath*) override {} // Accelerate
|
|
|
|
|
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit LinkBotupVisitor(AstNetlist* rootp) { iterate(rootp); }
|
2020-08-15 17:44:10 +02:00
|
|
|
virtual ~LinkBotupVisitor() override {}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Link class functions
|
|
|
|
|
|
|
|
|
|
void V3LinkResolve::linkResolve(AstNetlist* rootp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, __FUNCTION__ << ": " << endl);
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
|
|
|
|
LinkResolveVisitor visitor(rootp);
|
|
|
|
|
LinkBotupVisitor visitorb(rootp);
|
|
|
|
|
} // Destruct before checking
|
2017-09-18 04:52:57 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("linkresolve", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|