2012-04-13 01:08:20 +00:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-12-21 21:53:51 +00:00
|
|
|
//*************************************************************************
|
|
|
|
// DESCRIPTION: Verilator: Parse module/signal name references
|
|
|
|
//
|
2019-11-08 03:33:59 +00:00
|
|
|
// Code available from: https://verilator.org
|
2006-12-21 21:53:51 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
//
|
2020-03-21 15:24:24 +00: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 21:07:57 +00:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
// Version 2.0.
|
2020-03-21 15:24:24 +00:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-12-21 21:53:51 +00:00
|
|
|
//
|
|
|
|
//*************************************************************************
|
|
|
|
// LinkParse TRANSFORMATIONS:
|
2019-05-19 20:13:13 +00:00
|
|
|
// Top-down traversal
|
|
|
|
// Move some attributes around
|
2006-12-21 21:53:51 +00:00
|
|
|
//*************************************************************************
|
2019-10-05 00:17:11 +00:00
|
|
|
|
2006-12-21 21:53:51 +00:00
|
|
|
#include "config_build.h"
|
|
|
|
#include "verilatedos.h"
|
2018-10-14 17:43:24 +00:00
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
#include "V3LinkParse.h"
|
|
|
|
#include "V3Ast.h"
|
2020-01-12 09:03:17 +00:00
|
|
|
#include "V3Config.h"
|
2018-10-14 17:43:24 +00:00
|
|
|
|
2018-10-14 11:04:18 +00:00
|
|
|
#include <algorithm>
|
2006-12-21 21:53:51 +00:00
|
|
|
#include <map>
|
2011-01-19 02:28:51 +00:00
|
|
|
#include <set>
|
2006-12-21 21:53:51 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
// Link state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
class LinkParseVisitor : public AstNVisitor {
|
|
|
|
private:
|
|
|
|
// NODE STATE
|
|
|
|
// Cleared on netlist
|
2019-05-19 20:13:13 +00:00
|
|
|
// AstNode::user1() -> bool. True if processed
|
|
|
|
// AstNode::user2() -> bool. True if fileline recomputed
|
2020-04-15 11:58:34 +00:00
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
AstUser2InUse m_inuser2;
|
2006-12-21 21:53:51 +00:00
|
|
|
|
2010-01-07 00:04:20 +00:00
|
|
|
// TYPES
|
2020-10-30 22:00:40 +00:00
|
|
|
typedef std::map<const std::pair<void*, string>, AstTypedef*> ImplTypedefMap;
|
2018-02-02 02:24:41 +00:00
|
|
|
typedef std::set<FileLine*> FileLineSet;
|
2010-01-07 00:04:20 +00:00
|
|
|
|
2006-12-21 21:53:51 +00:00
|
|
|
// STATE
|
2020-08-16 13:55:36 +00:00
|
|
|
AstVar* m_varp = nullptr; // Variable we're under
|
2020-04-15 11:58:34 +00:00
|
|
|
ImplTypedefMap m_implTypedef; // Created typedefs for each <container,name>
|
|
|
|
FileLineSet m_filelines; // Filelines that have been seen
|
2020-08-16 13:55:36 +00:00
|
|
|
bool m_inAlways = false; // Inside an always
|
|
|
|
AstNodeModule* m_valueModp
|
|
|
|
= nullptr; // If set, move AstVar->valuep() initial values to this module
|
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
AstNodeFTask* m_ftaskp = nullptr; // Current task
|
|
|
|
AstNodeDType* m_dtypep = nullptr; // Current data type
|
2020-10-16 01:08:24 +00:00
|
|
|
VLifetime m_lifetime = VLifetime::STATIC; // Propagating lifetime
|
2006-12-21 21:53:51 +00:00
|
|
|
|
|
|
|
// METHODS
|
2018-05-14 10:50:47 +00:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2009-01-21 21:56:50 +00:00
|
|
|
|
2011-01-19 02:28:51 +00:00
|
|
|
void cleanFileline(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!nodep->user2SetOnce()) { // Process once
|
|
|
|
// We make all filelines unique per AstNode. This allows us to
|
|
|
|
// later turn off messages on a fileline when an issue is found
|
|
|
|
// so that messages on replicated blocks occur only once,
|
|
|
|
// without suppressing other token's messages as a side effect.
|
|
|
|
// We could have verilog.l create a new one on every token,
|
|
|
|
// but that's a lot more structures than only doing AST nodes.
|
|
|
|
if (m_filelines.find(nodep->fileline()) != m_filelines.end()) {
|
|
|
|
nodep->fileline(new FileLine(nodep->fileline()));
|
|
|
|
}
|
|
|
|
m_filelines.insert(nodep->fileline());
|
|
|
|
}
|
2011-01-19 02:28:51 +00:00
|
|
|
}
|
|
|
|
|
2018-10-07 22:07:42 +00:00
|
|
|
string nameFromTypedef(AstNode* nodep) {
|
|
|
|
// Try to find a name for a typedef'ed enum/struct
|
|
|
|
if (AstTypedef* typedefp = VN_CAST(nodep->backp(), Typedef)) {
|
|
|
|
// Create a name for the enum, to aid debug and tracing
|
|
|
|
// This name is not guaranteed to be globally unique (due to later parameterization)
|
|
|
|
string above;
|
2020-04-15 11:58:34 +00:00
|
|
|
if (m_modp && VN_IS(m_modp, Package)) {
|
|
|
|
above = m_modp->name() + "::";
|
|
|
|
} else if (m_modp) {
|
|
|
|
above = m_modp->name() + ".";
|
|
|
|
}
|
2018-10-07 22:07:42 +00:00
|
|
|
return above + typedefp->name();
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
void visitIterateNodeDType(AstNodeDType* nodep) {
|
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
|
|
|
cleanFileline(nodep);
|
|
|
|
AstNodeDType* upperDtypep = m_dtypep;
|
|
|
|
m_dtypep = nodep;
|
|
|
|
iterateChildren(nodep);
|
|
|
|
m_dtypep = upperDtypep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-21 21:53:51 +00:00
|
|
|
// VISITs
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeFTask* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
2020-10-16 01:02:24 +00:00
|
|
|
V3Config::applyFTask(m_modp, nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
cleanFileline(nodep);
|
2020-10-16 01:08:24 +00:00
|
|
|
VL_RESTORER(m_ftaskp);
|
|
|
|
VL_RESTORER(m_lifetime);
|
2020-10-16 01:02:24 +00:00
|
|
|
{
|
|
|
|
m_ftaskp = nodep;
|
2020-10-16 01:08:24 +00:00
|
|
|
m_lifetime = nodep->lifetime();
|
|
|
|
if (m_lifetime.isNone()) {
|
|
|
|
// Automatic always until we support static
|
|
|
|
m_lifetime = VLifetime::AUTOMATIC;
|
|
|
|
}
|
2020-10-16 01:02:24 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2014-03-08 17:26:34 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeFTaskRef* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
|
|
|
cleanFileline(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(5, " " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNodeModule* upperValueModp = m_valueModp;
|
2020-08-15 14:12:55 +00:00
|
|
|
m_valueModp = nullptr;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
m_valueModp = upperValueModp;
|
|
|
|
}
|
2006-12-21 21:53:51 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeDType* nodep) override { visitIterateNodeDType(nodep); }
|
|
|
|
virtual void visit(AstEnumDType* nodep) override {
|
2018-10-07 22:07:42 +00:00
|
|
|
if (nodep->name() == "") {
|
|
|
|
nodep->name(nameFromTypedef(nodep)); // Might still remain ""
|
|
|
|
}
|
|
|
|
visitIterateNodeDType(nodep);
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeUOrStructDType* nodep) override {
|
2018-10-07 22:07:42 +00:00
|
|
|
if (nodep->name() == "") {
|
|
|
|
nodep->name(nameFromTypedef(nodep)); // Might still remain ""
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2018-10-07 22:07:42 +00:00
|
|
|
visitIterateNodeDType(nodep);
|
2014-11-07 12:50:11 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstEnumItem* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Expand ranges
|
|
|
|
cleanFileline(nodep);
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (nodep->rangep()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
if (!VN_IS(nodep->rangep()->msbp(), Const) //
|
2019-05-19 20:13:13 +00:00
|
|
|
|| !VN_IS(nodep->rangep()->lsbp(), Const)) {
|
|
|
|
nodep->v3error("Enum ranges must be integral, per spec");
|
|
|
|
}
|
|
|
|
int msb = nodep->rangep()->msbConst();
|
|
|
|
int lsb = nodep->rangep()->lsbConst();
|
|
|
|
int increment = (msb > lsb) ? -1 : 1;
|
|
|
|
int offset_from_init = 0;
|
2020-08-15 14:12:55 +00:00
|
|
|
AstNode* addp = nullptr;
|
2020-04-15 11:58:34 +00:00
|
|
|
for (int i = msb; i != (lsb + increment); i += increment, offset_from_init++) {
|
2019-05-19 20:13:13 +00:00
|
|
|
string name = nodep->name() + cvtToStr(i);
|
2020-08-15 14:12:55 +00:00
|
|
|
AstNode* valuep = nullptr;
|
2020-08-16 18:55:46 +00:00
|
|
|
if (nodep->valuep()) {
|
2020-04-15 11:58:34 +00:00
|
|
|
valuep = new AstAdd(
|
|
|
|
nodep->fileline(), nodep->valuep()->cloneTree(true),
|
|
|
|
new AstConst(nodep->fileline(), AstConst::Unsized32(), offset_from_init));
|
2020-08-16 18:55:46 +00:00
|
|
|
}
|
2020-08-15 14:12:55 +00:00
|
|
|
AstNode* newp = new AstEnumItem(nodep->fileline(), name, nullptr, valuep);
|
2020-04-15 11:58:34 +00:00
|
|
|
if (addp) {
|
|
|
|
addp = addp->addNextNull(newp);
|
|
|
|
} else {
|
|
|
|
addp = newp;
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
nodep->replaceWith(addp);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2009-12-27 13:29:55 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstVar* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
cleanFileline(nodep);
|
2020-10-16 01:08:24 +00:00
|
|
|
if (nodep->lifetime().isNone()) {
|
2020-11-07 00:51:21 +00:00
|
|
|
if (m_ftaskp) {
|
2020-10-16 01:08:24 +00:00
|
|
|
nodep->lifetime(VLifetime::AUTOMATIC);
|
|
|
|
} else {
|
|
|
|
nodep->lifetime(m_lifetime);
|
|
|
|
}
|
|
|
|
}
|
2020-05-25 22:41:47 +00:00
|
|
|
if (nodep->isParam() && !nodep->valuep()
|
|
|
|
&& nodep->fileline()->language() < V3LangCode::L1800_2009) {
|
|
|
|
nodep->v3error("Parameter requires default value, or use IEEE 1800-2009 or later.");
|
|
|
|
}
|
2018-02-02 02:32:58 +00:00
|
|
|
if (VN_IS(nodep->subDTypep(), ParseTypeDType)) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// It's a parameter type. Use a different node type for this.
|
2018-02-02 02:32:58 +00:00
|
|
|
AstNodeDType* dtypep = VN_CAST(nodep->valuep(), NodeDType);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (!dtypep) {
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->v3error(
|
|
|
|
"Parameter type's initial value isn't a type: " << nodep->prettyNameQ());
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
} else {
|
|
|
|
dtypep->unlinkFrBack();
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* newp = new AstParamTypeDType(nodep->fileline(), nodep->varType(),
|
|
|
|
nodep->name(), VFlagChildDType(), dtypep);
|
|
|
|
nodep->replaceWith(newp);
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2016-03-15 01:51:31 +00:00
|
|
|
|
2020-01-12 09:03:17 +00:00
|
|
|
// Maybe this variable has a signal attribute
|
|
|
|
V3Config::applyVarAttr(m_modp, m_ftaskp, nodep);
|
|
|
|
|
2019-09-23 11:56:07 +00:00
|
|
|
if (v3Global.opt.publicFlatRW()) {
|
|
|
|
switch (nodep->varType()) {
|
2020-06-28 02:35:31 +00:00
|
|
|
case AstVarType::VAR: // FALLTHRU
|
|
|
|
case AstVarType::PORT: // FALLTHRU
|
2020-04-15 11:58:34 +00:00
|
|
|
case AstVarType::WIRE: nodep->sigUserRWPublic(true); break;
|
2019-09-23 11:56:07 +00:00
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-19 20:13:13 +00:00
|
|
|
// We used modTrace before leveling, and we may now
|
|
|
|
// want to turn it off now that we know the levelizations
|
2020-04-15 11:58:34 +00:00
|
|
|
if (v3Global.opt.traceDepth() && m_modp
|
|
|
|
&& (m_modp->level() - 1) > v3Global.opt.traceDepth()) {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_modp->modTrace(false);
|
|
|
|
nodep->trace(false);
|
|
|
|
}
|
|
|
|
m_varp = nodep;
|
2019-09-23 11:56:07 +00:00
|
|
|
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 14:12:55 +00:00
|
|
|
m_varp = nullptr;
|
2019-05-19 20:13:13 +00:00
|
|
|
// temporaries under an always aren't expected to be blocking
|
|
|
|
if (m_inAlways) nodep->fileline()->modifyWarnOff(V3ErrorCode::BLKSEQ, true);
|
|
|
|
if (nodep->valuep()) {
|
|
|
|
// A variable with an = value can be three things:
|
|
|
|
FileLine* fl = nodep->valuep()->fileline();
|
2018-10-27 21:29:00 +00:00
|
|
|
if (nodep->isParam() || (m_ftaskp && nodep->isNonOutput())) {
|
2020-04-05 13:30:23 +00:00
|
|
|
// 1. Parameters and function inputs: It's a default to use if not overridden
|
2020-10-16 01:08:24 +00:00
|
|
|
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Default value on module input: "
|
|
|
|
<< nodep->prettyNameQ());
|
2019-05-19 20:13:13 +00:00
|
|
|
nodep->valuep()->unlinkFrBack()->deleteTree();
|
2020-10-16 01:08:24 +00:00
|
|
|
} // 2. Under modules/class, it's an initial value to be loaded at time 0 via an
|
|
|
|
// AstInitial
|
2018-11-26 22:58:18 +00:00
|
|
|
else if (m_valueModp) {
|
|
|
|
// Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it
|
2019-05-19 20:13:13 +00:00
|
|
|
FileLine* newfl = new FileLine(fl);
|
2018-11-26 22:58:18 +00:00
|
|
|
newfl->warnOff(V3ErrorCode::PROCASSWIRE, true);
|
2020-10-16 01:08:24 +00:00
|
|
|
auto* assp
|
|
|
|
= new AstAssign(newfl, new AstVarRef(newfl, nodep->name(), VAccess::WRITE),
|
|
|
|
nodep->valuep()->unlinkFrBack());
|
|
|
|
nodep->addNextHere(new AstInitial(newfl, assp));
|
2020-04-19 00:20:17 +00:00
|
|
|
} // 4. Under blocks, it's an initial value to be under an assign
|
2018-11-26 22:58:18 +00:00
|
|
|
else {
|
2020-09-07 21:09:25 +00:00
|
|
|
nodep->addNextHere(new AstAssign(fl,
|
|
|
|
new AstVarRef(fl, nodep->name(), VAccess::WRITE),
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->valuep()->unlinkFrBack()));
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nodep->isIfaceRef() && !nodep->isIfaceParent()) {
|
|
|
|
// Only AstIfaceRefDType's at this point correspond to ports;
|
|
|
|
// haven't made additional ones for interconnect yet, so assert is simple
|
|
|
|
// What breaks later is we don't have a Scope/Cell representing
|
|
|
|
// the interface to attach to
|
2020-04-15 11:58:34 +00:00
|
|
|
if (m_modp->level() <= 2) {
|
2020-06-09 23:20:16 +00:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Interfaced port on top level module");
|
2020-04-15 11:58:34 +00:00
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2009-05-07 22:28:05 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstAttrOf* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
cleanFileline(nodep);
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (nodep->attrType() == AstAttrType::DT_PUBLIC) {
|
2018-02-02 02:32:58 +00:00
|
|
|
AstTypedef* typep = VN_CAST(nodep->backp(), Typedef);
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(typep, nodep, "Attribute not attached to typedef");
|
2019-05-19 20:13:13 +00:00
|
|
|
typep->attrPublic(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_CLOCK_ENABLE) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_varp->attrClockEn(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_PUBLIC) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2020-04-15 11:58:34 +00:00
|
|
|
m_varp->sigUserRWPublic(true);
|
|
|
|
m_varp->sigModPublic(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_varp->sigUserRWPublic(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RD) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_varp->sigUserRdPublic(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_PUBLIC_FLAT_RW) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_varp->sigUserRWPublic(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_ISOLATE_ASSIGNMENTS) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_varp->attrIsolateAssign(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_SFORMAT) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_varp->attrSFormat(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_SPLIT_VAR) {
|
2020-02-29 00:15:08 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
|
|
|
if (!VN_IS(m_modp, Module)) {
|
2020-04-15 11:58:34 +00:00
|
|
|
m_varp->v3warn(
|
|
|
|
SPLITVAR,
|
|
|
|
m_varp->prettyNameQ()
|
|
|
|
<< " has split_var metacomment, "
|
|
|
|
"but will not be split because it is not declared in a module.");
|
2020-02-29 00:15:08 +00:00
|
|
|
} else {
|
|
|
|
m_varp->attrSplitVar(true);
|
|
|
|
}
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_SC_BV) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 20:13:13 +00:00
|
|
|
m_varp->attrScBv(true);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_CLOCKER) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-10-05 11:54:14 +00:00
|
|
|
m_varp->attrClocker(VVarAttrClocker::CLOCKER_YES);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->attrType() == AstAttrType::VAR_NO_CLOCKER) {
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-10-05 11:54:14 +00:00
|
|
|
m_varp->attrClocker(VVarAttrClocker::CLOCKER_NO);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2009-05-07 22:28:05 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstAlwaysPublic* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// AlwaysPublic was attached under a var, but it's a statement that should be
|
|
|
|
// at the same level as the var
|
|
|
|
cleanFileline(nodep);
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
if (m_varp) {
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
m_varp->addNext(nodep);
|
|
|
|
// lvalue is true, because we know we have a verilator public_flat_rw
|
|
|
|
// but someday we may be more general
|
|
|
|
bool lvalue = m_varp->isSigUserRWPublic();
|
2020-09-07 21:09:25 +00:00
|
|
|
nodep->addStmtp(
|
|
|
|
new AstVarRef(nodep->fileline(), m_varp, lvalue ? VAccess::WRITE : VAccess::READ));
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2010-04-06 00:01:17 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstDefImplicitDType* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
cleanFileline(nodep);
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(8, " DEFIMPLICIT " << nodep << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
// Must remember what names we've already created, and combine duplicates
|
|
|
|
// so that for "var enum {...} a,b" a & b will share a common typedef
|
|
|
|
// Unique name space under each containerp() so that an addition of
|
|
|
|
// a new type won't change every verilated module.
|
2020-08-15 14:12:55 +00:00
|
|
|
AstTypedef* defp = nullptr;
|
2020-04-15 11:58:34 +00:00
|
|
|
ImplTypedefMap::iterator it
|
|
|
|
= m_implTypedef.find(make_pair(nodep->containerp(), nodep->name()));
|
2019-05-19 20:13:13 +00:00
|
|
|
if (it != m_implTypedef.end()) {
|
|
|
|
defp = it->second;
|
|
|
|
} else {
|
|
|
|
// Definition must be inserted right after the variable (etc) that needed it
|
|
|
|
// AstVar, AstTypedef, AstNodeFTask are common containers
|
|
|
|
AstNode* backp = nodep->backp();
|
2020-04-15 11:58:34 +00:00
|
|
|
for (; backp; backp = backp->backp()) {
|
|
|
|
if (VN_IS(backp, Var) || VN_IS(backp, Typedef) || VN_IS(backp, NodeFTask)) break;
|
2019-05-19 20:13:13 +00:00
|
|
|
}
|
2019-07-06 16:57:50 +00:00
|
|
|
UASSERT_OBJ(backp, nodep,
|
|
|
|
"Implicit enum/struct type created under unexpected node type");
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNodeDType* dtypep = nodep->childDTypep();
|
|
|
|
dtypep->unlinkFrBack();
|
|
|
|
if (VN_IS(backp, Typedef)) {
|
|
|
|
// A typedef doesn't need us to make yet another level of typedefing
|
2019-05-19 20:13:13 +00:00
|
|
|
// For typedefs just remove the AstRefDType level of abstraction
|
|
|
|
nodep->replaceWith(dtypep);
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
return;
|
|
|
|
} else {
|
2020-08-15 14:12:55 +00:00
|
|
|
defp = new AstTypedef(nodep->fileline(), nodep->name(), nullptr, VFlagChildDType(),
|
2020-04-15 11:58:34 +00:00
|
|
|
dtypep);
|
|
|
|
m_implTypedef.insert(
|
|
|
|
make_pair(make_pair(nodep->containerp(), defp->name()), defp));
|
2019-05-19 20:13:13 +00:00
|
|
|
backp->addNextHere(defp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name()));
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2010-01-07 00:04:20 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstForeach* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// FOREACH(array,loopvars,body)
|
|
|
|
// -> BEGIN(declare vars, loopa=lowest; WHILE(loopa<=highest, ... body))
|
2020-04-15 11:58:34 +00:00
|
|
|
// nodep->dumpTree(cout, "-foreach-old:");
|
2020-06-10 02:05:06 +00:00
|
|
|
UINFO(9, "FOREACH " << nodep << endl);
|
|
|
|
// nodep->dumpTree(cout, "-foreach-in:");
|
|
|
|
AstNode* newp = nodep->bodysp();
|
|
|
|
if (newp) newp->unlinkFrBackWithNext();
|
|
|
|
// Separate iteration vars from base from variable
|
|
|
|
// Input:
|
|
|
|
// v--- arrayp
|
|
|
|
// 1. DOT(DOT(first, second), ASTSELLOOPVARS(third, var0..var1))
|
|
|
|
// Separated:
|
|
|
|
// bracketp = ASTSELLOOPVARS(...)
|
|
|
|
// arrayp = DOT(DOT(first, second), third)
|
|
|
|
// firstVarp = var0..var1
|
|
|
|
// Other examples
|
|
|
|
// 2. ASTSELBIT(first, var0))
|
|
|
|
// 3. ASTSELLOOPVARS(first, var0..var1))
|
|
|
|
// 4. DOT(DOT(first, second), ASTSELBIT(third, var0))
|
|
|
|
AstNode* bracketp = nodep->arrayp();
|
2020-08-15 14:12:55 +00:00
|
|
|
AstNode* firstVarsp = nullptr;
|
2020-06-10 02:05:06 +00:00
|
|
|
while (AstDot* dotp = VN_CAST(bracketp, Dot)) { bracketp = dotp->rhsp(); }
|
|
|
|
if (AstSelBit* selp = VN_CAST(bracketp, SelBit)) {
|
|
|
|
firstVarsp = selp->rhsp()->unlinkFrBackWithNext();
|
|
|
|
selp->replaceWith(selp->fromp()->unlinkFrBack());
|
|
|
|
VL_DO_DANGLING(selp->deleteTree(), selp);
|
|
|
|
} else if (AstSelLoopVars* selp = VN_CAST(bracketp, SelLoopVars)) {
|
|
|
|
firstVarsp = selp->elementsp()->unlinkFrBackWithNext();
|
|
|
|
selp->replaceWith(selp->fromp()->unlinkFrBack());
|
|
|
|
VL_DO_DANGLING(selp->deleteTree(), selp);
|
|
|
|
} else {
|
|
|
|
nodep->v3error(
|
|
|
|
"Syntax error; foreach missing bracketed index variable (IEEE 1800-2017 12.7.3)");
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
AstNode* arrayp = nodep->arrayp(); // Maybe different node since bracketp looked
|
2020-08-24 23:33:26 +00:00
|
|
|
if (!VN_IS(arrayp, ParseRef) && !VN_IS(arrayp, Dot)) {
|
2020-06-10 02:05:06 +00:00
|
|
|
// Code below needs to use other then attributes to figure out the bounds
|
|
|
|
// Also need to deal with queues, etc
|
|
|
|
arrayp->v3warn(E_UNSUPPORTED, "Unsupported: foreach on non-simple variable reference");
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Split into for loop
|
2019-05-19 20:13:13 +00:00
|
|
|
// Must do innermost (last) variable first
|
2020-06-10 02:05:06 +00:00
|
|
|
int dimension = 1;
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* lastVarsp = firstVarsp;
|
2020-04-15 11:58:34 +00:00
|
|
|
while (lastVarsp->nextp()) {
|
|
|
|
lastVarsp = lastVarsp->nextp();
|
|
|
|
dimension++;
|
|
|
|
}
|
|
|
|
for (AstNode* varsp = lastVarsp; varsp; varsp = varsp->backp()) {
|
|
|
|
UINFO(9, "foreachVar " << varsp << endl);
|
2019-05-19 20:13:13 +00:00
|
|
|
FileLine* fl = varsp->fileline();
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* varp
|
|
|
|
= new AstVar(fl, AstVarType::BLOCKTEMP, varsp->name(), nodep->findSigned32DType());
|
2019-12-18 23:17:18 +00:00
|
|
|
// These will be the left and right dimensions and size of the array:
|
2020-08-24 23:33:26 +00:00
|
|
|
AstNode* leftp = new AstAttrOf(fl, AstAttrType::DIM_LEFT, arrayp->cloneTree(false),
|
2019-05-19 20:13:13 +00:00
|
|
|
new AstConst(fl, dimension));
|
2020-08-24 23:33:26 +00:00
|
|
|
AstNode* rightp = new AstAttrOf(fl, AstAttrType::DIM_RIGHT, arrayp->cloneTree(false),
|
2019-05-19 20:13:13 +00:00
|
|
|
new AstConst(fl, dimension));
|
2020-08-24 23:33:26 +00:00
|
|
|
AstNode* sizep = new AstAttrOf(fl, AstAttrType::DIM_SIZE, arrayp->cloneTree(false),
|
2019-12-18 23:17:18 +00:00
|
|
|
new AstConst(fl, dimension));
|
2019-05-19 20:13:13 +00:00
|
|
|
AstNode* stmtsp = varp;
|
2018-03-15 12:59:52 +00:00
|
|
|
// Assign left-dimension into the loop var:
|
2020-09-07 21:09:25 +00:00
|
|
|
stmtsp->addNext(
|
|
|
|
new AstAssign(fl, new AstVarRef(fl, varp->name(), VAccess::WRITE), leftp));
|
2019-12-18 23:17:18 +00:00
|
|
|
// This will turn into a constant bool for static arrays
|
|
|
|
AstNode* notemptyp = new AstGt(fl, sizep, new AstConst(fl, 0));
|
2018-03-15 12:59:52 +00:00
|
|
|
// This will turn into a bool constant, indicating whether
|
|
|
|
// we count the loop variable up or down:
|
2020-04-15 11:58:34 +00:00
|
|
|
AstNode* countupp = new AstLte(fl, leftp->cloneTree(true), rightp->cloneTree(true));
|
2018-03-15 12:59:52 +00:00
|
|
|
AstNode* comparep = new AstCond(
|
|
|
|
fl, countupp->cloneTree(true),
|
|
|
|
// Left increments up to right
|
2020-09-07 21:09:25 +00:00
|
|
|
new AstLte(fl, new AstVarRef(fl, varp->name(), VAccess::READ),
|
|
|
|
rightp->cloneTree(true)),
|
2018-03-15 12:59:52 +00:00
|
|
|
// Left decrements down to right
|
2020-09-07 21:09:25 +00:00
|
|
|
new AstGte(fl, new AstVarRef(fl, varp->name(), VAccess::READ), rightp));
|
2019-12-18 23:17:18 +00:00
|
|
|
// This will reduce to comparep for static arrays
|
|
|
|
AstNode* condp = new AstAnd(fl, notemptyp, comparep);
|
2018-03-15 12:59:52 +00:00
|
|
|
AstNode* incp = new AstAssign(
|
2020-09-07 21:09:25 +00:00
|
|
|
fl, new AstVarRef(fl, varp->name(), VAccess::WRITE),
|
|
|
|
new AstAdd(fl, new AstVarRef(fl, varp->name(), VAccess::READ),
|
2020-04-15 11:58:34 +00:00
|
|
|
new AstCond(fl, countupp, new AstConst(fl, 1), new AstConst(fl, -1))));
|
2019-12-18 23:17:18 +00:00
|
|
|
stmtsp->addNext(new AstWhile(fl, condp, newp, incp));
|
2020-02-26 03:21:16 +00:00
|
|
|
newp = new AstBegin(nodep->fileline(), "", stmtsp, false, true);
|
2019-05-19 20:13:13 +00:00
|
|
|
dimension--;
|
|
|
|
}
|
2020-04-15 11:58:34 +00:00
|
|
|
// newp->dumpTree(cout, "-foreach-new:");
|
2020-01-17 01:17:11 +00:00
|
|
|
VL_DO_DANGLING(firstVarsp->deleteTree(), firstVarsp);
|
2020-04-15 11:58:34 +00:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2016-09-20 02:00:13 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeModule* nodep) override {
|
2020-01-12 09:03:17 +00:00
|
|
|
V3Config::applyModule(nodep);
|
|
|
|
|
2020-08-25 01:10:43 +00:00
|
|
|
VL_RESTORER(m_modp);
|
2020-10-16 01:08:24 +00:00
|
|
|
VL_RESTORER(m_lifetime);
|
2020-01-20 18:27:27 +00:00
|
|
|
{
|
|
|
|
// Module: Create sim table for entire module and iterate
|
|
|
|
cleanFileline(nodep);
|
|
|
|
//
|
|
|
|
m_modp = nodep;
|
|
|
|
m_valueModp = nodep;
|
2020-10-16 01:08:24 +00:00
|
|
|
m_lifetime = nodep->lifetime();
|
|
|
|
if (m_lifetime.isNone()) {
|
|
|
|
m_lifetime = VN_IS(nodep, Class) ? VLifetime::AUTOMATIC : VLifetime::STATIC;
|
|
|
|
}
|
2020-01-20 18:27:27 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
}
|
2019-05-19 20:13:13 +00:00
|
|
|
m_valueModp = nodep;
|
2012-06-14 03:08:45 +00:00
|
|
|
}
|
|
|
|
void visitIterateNoValueMod(AstNode* nodep) {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Iterate a node which shouldn't have any local variables moved to an Initial
|
|
|
|
cleanFileline(nodep);
|
|
|
|
//
|
|
|
|
AstNodeModule* upperValueModp = m_valueModp;
|
2020-08-15 14:12:55 +00:00
|
|
|
m_valueModp = nullptr;
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 20:13:13 +00:00
|
|
|
m_valueModp = upperValueModp;
|
2012-06-14 03:08:45 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNodeProcedure* nodep) override { visitIterateNoValueMod(nodep); }
|
|
|
|
virtual void visit(AstAlways* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
m_inAlways = true;
|
|
|
|
visitIterateNoValueMod(nodep);
|
|
|
|
m_inAlways = false;
|
2012-06-14 03:08:45 +00:00
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstCover* nodep) override { visitIterateNoValueMod(nodep); }
|
|
|
|
virtual void visit(AstRestrict* nodep) override { visitIterateNoValueMod(nodep); }
|
2012-06-14 03:08:45 +00:00
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstBegin* nodep) override {
|
2020-01-12 09:03:17 +00:00
|
|
|
V3Config::applyCoverageBlock(m_modp, nodep);
|
|
|
|
cleanFileline(nodep);
|
2020-02-26 03:21:16 +00:00
|
|
|
AstNode* backp = nodep->backp();
|
|
|
|
// IEEE says directly nested item is not a new block
|
|
|
|
bool nestedIf = (nodep->implied() // User didn't provide begin/end
|
|
|
|
&& (VN_IS(nodep->stmtsp(), GenIf)
|
|
|
|
|| VN_IS(nodep->stmtsp(), GenCase)) // Has an if/case
|
|
|
|
&& !nodep->stmtsp()->nextp()); // Has only one item
|
|
|
|
// It's not FOR(BEGIN(...)) but we earlier changed it to BEGIN(FOR(...))
|
|
|
|
if (nodep->genforp() && nodep->name() == "") {
|
|
|
|
nodep->name("genblk");
|
2020-04-15 11:58:34 +00:00
|
|
|
} else if (nodep->generate() && nodep->name() == ""
|
|
|
|
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
|
2020-02-26 03:21:16 +00:00
|
|
|
nodep->name("genblk");
|
|
|
|
}
|
2020-01-12 09:03:17 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstCase* nodep) override {
|
2020-01-12 09:03:17 +00:00
|
|
|
V3Config::applyCase(nodep);
|
|
|
|
cleanFileline(nodep);
|
|
|
|
iterateChildren(nodep);
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstPrintTimeScale* nodep) override {
|
2020-04-15 23:39:03 +00:00
|
|
|
// Inlining may change hierarchy, so just save timescale where needed
|
2020-06-06 19:54:44 +00:00
|
|
|
cleanFileline(nodep);
|
2020-04-15 23:39:03 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
nodep->name(m_modp->name());
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstSFormatF* nodep) override {
|
2020-06-06 19:54:44 +00:00
|
|
|
cleanFileline(nodep);
|
2020-04-15 23:39:03 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstTime* nodep) override {
|
2020-06-06 19:54:44 +00:00
|
|
|
cleanFileline(nodep);
|
2020-04-15 23:39:03 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstTimeD* nodep) override {
|
2020-06-06 19:54:44 +00:00
|
|
|
cleanFileline(nodep);
|
2020-04-15 23:39:03 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstTimeImport* nodep) override {
|
2020-06-06 19:54:44 +00:00
|
|
|
cleanFileline(nodep);
|
2020-04-15 23:39:03 +00:00
|
|
|
iterateChildren(nodep);
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
}
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstTimingControl* nodep) override {
|
2020-06-06 19:54:44 +00:00
|
|
|
cleanFileline(nodep);
|
|
|
|
iterateChildren(nodep);
|
|
|
|
AstAlways* alwaysp = VN_CAST(nodep->backp(), Always);
|
|
|
|
if (alwaysp && alwaysp->keyword() == VAlwaysKwd::ALWAYS_COMB) {
|
2020-08-23 12:56:35 +00:00
|
|
|
alwaysp->v3error("Timing control statements not legal under always_comb "
|
|
|
|
"(IEEE 1800-2017 9.2.2.2.2)\n"
|
2020-06-06 19:54:44 +00:00
|
|
|
<< nodep->warnMore() << "... Suggest use a normal 'always'");
|
2020-08-23 12:56:35 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-06-06 19:54:44 +00:00
|
|
|
} else if (alwaysp && !alwaysp->sensesp()) {
|
|
|
|
// Verilator is still ony supporting SenTrees under an always,
|
|
|
|
// so allow the parser to handle everything and shim to
|
|
|
|
// historical AST here
|
|
|
|
if (AstSenTree* sensesp = nodep->sensesp()) {
|
|
|
|
sensesp->unlinkFrBackWithNext();
|
|
|
|
alwaysp->sensesp(sensesp);
|
|
|
|
}
|
|
|
|
if (nodep->stmtsp()) alwaysp->addStmtp(nodep->stmtsp()->unlinkFrBackWithNext());
|
2020-08-23 12:56:35 +00:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-06-06 19:54:44 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-12 09:03:17 +00:00
|
|
|
|
2020-08-15 14:03:34 +00:00
|
|
|
virtual void visit(AstNode* nodep) override {
|
2019-05-19 20:13:13 +00:00
|
|
|
// Default: Just iterate
|
|
|
|
cleanFileline(nodep);
|
2018-05-11 00:55:37 +00:00
|
|
|
iterateChildren(nodep);
|
2006-12-21 21:53:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2019-09-12 11:22:22 +00:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 13:55:36 +00:00
|
|
|
explicit LinkParseVisitor(AstNetlist* rootp) { iterate(rootp); }
|
2020-11-17 00:56:16 +00:00
|
|
|
virtual ~LinkParseVisitor() override = default;
|
2006-12-21 21:53:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
// Link class functions
|
|
|
|
|
|
|
|
void V3LinkParse::linkParse(AstNetlist* rootp) {
|
2020-04-15 11:58:34 +00:00
|
|
|
UINFO(4, __FUNCTION__ << ": " << endl);
|
|
|
|
{ LinkParseVisitor visitor(rootp); } // Destruct before checking
|
2017-09-18 02:52:57 +00:00
|
|
|
V3Global::dumpCheckGlobalTree("linkparse", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 6);
|
2006-12-21 21:53:51 +00:00
|
|
|
}
|