2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-12-21 22:53:51 +01:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Parse module/signal name references
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-12-21 22:53:51 +01:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// 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-12-21 22:53:51 +01:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// LinkParse TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Top-down traversal
|
|
|
|
|
// Move some attributes around
|
2006-12-21 22:53:51 +01:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-21 22:53:51 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
|
|
|
|
#include "V3LinkParse.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include "V3Ast.h"
|
2020-01-12 10:03:17 +01:00
|
|
|
#include "V3Config.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Global.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2018-10-14 13:04:18 +02:00
|
|
|
#include <algorithm>
|
2006-12-21 22:53:51 +01:00
|
|
|
#include <map>
|
2011-01-19 03:28:51 +01:00
|
|
|
#include <set>
|
2006-12-21 22:53:51 +01:00
|
|
|
#include <vector>
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-12-21 22:53:51 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// Link state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class LinkParseVisitor final : public VNVisitor {
|
2006-12-21 22:53:51 +01:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared on netlist
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNode::user1() -> bool. True if processed
|
|
|
|
|
// AstNode::user2() -> bool. True if fileline recomputed
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
|
|
|
|
const VNUser2InUse m_inuser2;
|
2006-12-21 22:53:51 +01:00
|
|
|
|
2010-01-07 01:04:20 +01:00
|
|
|
// TYPES
|
2021-03-13 00:10:45 +01:00
|
|
|
using ImplTypedefMap = std::map<const std::pair<void*, std::string>, AstTypedef*>;
|
2010-01-07 01:04:20 +01:00
|
|
|
|
2006-12-21 22:53:51 +01:00
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstVar* m_varp = nullptr; // Variable we're under
|
2020-04-15 13:58:34 +02:00
|
|
|
ImplTypedefMap m_implTypedef; // Created typedefs for each <container,name>
|
2021-03-12 23:26:53 +01:00
|
|
|
std::unordered_set<FileLine*> m_filelines; // Filelines that have been seen
|
2020-08-16 15:55:36 +02: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-12-12 18:56:59 +01:00
|
|
|
int m_genblkAbove = 0; // Begin block number of if/case/for above
|
|
|
|
|
int m_genblkNum = 0; // Begin block number, 0=none seen
|
2020-10-16 03:08:24 +02:00
|
|
|
VLifetime m_lifetime = VLifetime::STATIC; // Propagating lifetime
|
2006-12-21 22:53:51 +01:00
|
|
|
|
|
|
|
|
// METHODS
|
2011-01-19 03:28:51 +01:00
|
|
|
void cleanFileline(AstNode* nodep) {
|
2019-05-19 22:13:13 +02: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()) {
|
2022-09-16 01:58:01 +02:00
|
|
|
nodep->fileline(new FileLine{nodep->fileline()});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_filelines.insert(nodep->fileline());
|
|
|
|
|
}
|
2011-01-19 03:28:51 +01:00
|
|
|
}
|
|
|
|
|
|
2018-10-08 00:07:42 +02:00
|
|
|
string nameFromTypedef(AstNode* nodep) {
|
|
|
|
|
// Try to find a name for a typedef'ed enum/struct
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstTypedef* const typedefp = VN_CAST(nodep->backp(), Typedef)) {
|
2018-10-08 00:07:42 +02:00
|
|
|
// 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 13:58:34 +02:00
|
|
|
if (m_modp && VN_IS(m_modp, Package)) {
|
|
|
|
|
above = m_modp->name() + "::";
|
|
|
|
|
} else if (m_modp) {
|
|
|
|
|
above = m_modp->name() + ".";
|
|
|
|
|
}
|
2018-10-08 00:07:42 +02:00
|
|
|
return above + typedefp->name();
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visitIterateNodeDType(AstNodeDType* nodep) {
|
|
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
|
|
|
|
cleanFileline(nodep);
|
2020-12-06 19:49:44 +01:00
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_dtypep);
|
|
|
|
|
m_dtypep = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2018-10-08 00:07:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 13:59:00 +01:00
|
|
|
bool nestedIfBegin(AstBegin* nodep) { // Point at begin inside the GenIf
|
|
|
|
|
// IEEE says directly nested item is not a new block
|
|
|
|
|
// The genblk name will get attached to the if true/false LOWER begin block(s)
|
|
|
|
|
// 1: GENIF
|
|
|
|
|
// -> 1:3: BEGIN [GEN] [IMPLIED] // nodep passed to this function
|
|
|
|
|
// 1:3:1: GENIF
|
|
|
|
|
// 1:3:1:2: BEGIN genblk1 [GEN] [IMPLIED]
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstNode* const backp = nodep->backp();
|
2021-11-01 13:59:00 +01:00
|
|
|
return (nodep->implied() // User didn't provide begin/end
|
|
|
|
|
&& VN_IS(backp, GenIf) && VN_CAST(backp, GenIf)->elsesp() == nodep
|
|
|
|
|
&& !nodep->nextp() // No other statements under upper genif else
|
|
|
|
|
&& (VN_IS(nodep->stmtsp(), GenIf)) // Begin has if underneath
|
|
|
|
|
&& !nodep->stmtsp()->nextp()); // Has only one item
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-21 22:53:51 +01:00
|
|
|
// VISITs
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
2020-10-16 03:02:24 +02:00
|
|
|
V3Config::applyFTask(m_modp, nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
cleanFileline(nodep);
|
2020-10-16 03:08:24 +02:00
|
|
|
VL_RESTORER(m_ftaskp);
|
|
|
|
|
VL_RESTORER(m_lifetime);
|
2020-10-16 03:02:24 +02:00
|
|
|
{
|
|
|
|
|
m_ftaskp = nodep;
|
2020-10-16 03:08:24 +02:00
|
|
|
m_lifetime = nodep->lifetime();
|
|
|
|
|
if (m_lifetime.isNone()) {
|
|
|
|
|
// Automatic always until we support static
|
|
|
|
|
m_lifetime = VLifetime::AUTOMATIC;
|
|
|
|
|
}
|
2020-10-16 03:02:24 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2014-03-08 18:26:34 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user1SetOnce()) { // Process only once.
|
|
|
|
|
cleanFileline(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " " << nodep << endl);
|
2020-12-06 19:49:44 +01:00
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_valueModp);
|
|
|
|
|
m_valueModp = nullptr;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-12-21 22:53:51 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeDType* nodep) override { visitIterateNodeDType(nodep); }
|
|
|
|
|
void visit(AstEnumDType* nodep) override {
|
2018-10-08 00:07:42 +02:00
|
|
|
if (nodep->name() == "") {
|
|
|
|
|
nodep->name(nameFromTypedef(nodep)); // Might still remain ""
|
|
|
|
|
}
|
|
|
|
|
visitIterateNodeDType(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeUOrStructDType* nodep) override {
|
2018-10-08 00:07:42 +02:00
|
|
|
if (nodep->name() == "") {
|
|
|
|
|
nodep->name(nameFromTypedef(nodep)); // Might still remain ""
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-10-08 00:07:42 +02:00
|
|
|
visitIterateNodeDType(nodep);
|
2014-11-07 13:50:11 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEnumItem* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Expand ranges
|
|
|
|
|
cleanFileline(nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->rangep()) {
|
2020-12-08 05:15:29 +01:00
|
|
|
if (VL_UNCOVERABLE(!VN_IS(nodep->rangep()->leftp(), Const) // LCOV_EXCL_START
|
|
|
|
|
|| !VN_IS(nodep->rangep()->rightp(), Const))) {
|
|
|
|
|
// We check this rule in the parser, so shouldn't fire
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->v3error("Enum ranges must be integral, per spec");
|
2020-12-08 05:15:29 +01:00
|
|
|
} // LCOV_EXCL_STOP
|
2021-06-21 00:32:57 +02:00
|
|
|
const int left = nodep->rangep()->leftConst();
|
|
|
|
|
const int right = nodep->rangep()->rightConst();
|
|
|
|
|
const int increment = (left > right) ? -1 : 1;
|
2019-05-19 22:13:13 +02:00
|
|
|
int offset_from_init = 0;
|
2022-09-17 14:48:51 +02:00
|
|
|
AstEnumItem* addp = nullptr;
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
2020-12-07 03:13:56 +01:00
|
|
|
for (int i = left; i != (right + increment); i += increment, offset_from_init++) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const string name = nodep->name() + cvtToStr(i);
|
2020-08-15 16:12:55 +02:00
|
|
|
AstNode* valuep = nullptr;
|
2020-08-16 20:55:46 +02:00
|
|
|
if (nodep->valuep()) {
|
2022-09-17 14:48:51 +02:00
|
|
|
valuep
|
|
|
|
|
= new AstAdd(flp, nodep->valuep()->cloneTree(true),
|
|
|
|
|
new AstConst(flp, AstConst::Unsized32(), offset_from_init));
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2022-09-17 14:48:51 +02:00
|
|
|
addp = AstNode::addNext(addp, new AstEnumItem{flp, name, nullptr, valuep});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->replaceWith(addp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-12-27 14:29:55 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
cleanFileline(nodep);
|
2020-10-16 03:08:24 +02:00
|
|
|
if (nodep->lifetime().isNone()) {
|
2020-11-07 01:51:21 +01:00
|
|
|
if (m_ftaskp) {
|
2020-10-16 03:08:24 +02:00
|
|
|
nodep->lifetime(VLifetime::AUTOMATIC);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->lifetime(m_lifetime);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-26 00:41:47 +02: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 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->subDTypep(), ParseTypeDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// It's a parameter type. Use a different node type for this.
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeDType* const dtypep = VN_CAST(nodep->valuep(), NodeDType);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!dtypep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3error(
|
|
|
|
|
"Parameter type's initial value isn't a type: " << nodep->prettyNameQ());
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
} else {
|
|
|
|
|
dtypep->unlinkFrBack();
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const newp = new AstParamTypeDType(
|
|
|
|
|
nodep->fileline(), nodep->varType(), nodep->name(), VFlagChildDType(), dtypep);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2016-03-15 02:51:31 +01:00
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
// Maybe this variable has a signal attribute
|
|
|
|
|
V3Config::applyVarAttr(m_modp, m_ftaskp, nodep);
|
|
|
|
|
|
2019-09-23 13:56:07 +02:00
|
|
|
if (v3Global.opt.publicFlatRW()) {
|
|
|
|
|
switch (nodep->varType()) {
|
2022-01-02 19:56:40 +01:00
|
|
|
case VVarType::VAR: // FALLTHRU
|
|
|
|
|
case VVarType::GPARAM: // FALLTHRU
|
|
|
|
|
case VVarType::LPARAM: // FALLTHRU
|
|
|
|
|
case VVarType::PORT: // FALLTHRU
|
|
|
|
|
case VVarType::WIRE: nodep->sigUserRWPublic(true); break;
|
2019-09-23 13:56:07 +02:00
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// We used modTrace before leveling, and we may now
|
|
|
|
|
// want to turn it off now that we know the levelizations
|
2020-04-15 13:58:34 +02:00
|
|
|
if (v3Global.opt.traceDepth() && m_modp
|
|
|
|
|
&& (m_modp->level() - 1) > v3Global.opt.traceDepth()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->modTrace(false);
|
|
|
|
|
nodep->trace(false);
|
|
|
|
|
}
|
|
|
|
|
m_varp = nodep;
|
2019-09-23 13:56:07 +02:00
|
|
|
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_varp = nullptr;
|
2019-05-19 22:13:13 +02: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:
|
2021-11-26 23:55:36 +01:00
|
|
|
FileLine* const fl = nodep->valuep()->fileline();
|
2018-10-27 23:29:00 +02:00
|
|
|
if (nodep->isParam() || (m_ftaskp && nodep->isNonOutput())) {
|
2020-04-05 15:30:23 +02:00
|
|
|
// 1. Parameters and function inputs: It's a default to use if not overridden
|
2020-10-16 03:08:24 +02:00
|
|
|
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Default value on module input: "
|
|
|
|
|
<< nodep->prettyNameQ());
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->valuep()->unlinkFrBack()->deleteTree();
|
2020-10-16 03:08:24 +02:00
|
|
|
} // 2. Under modules/class, it's an initial value to be loaded at time 0 via an
|
|
|
|
|
// AstInitial
|
2018-11-26 23:58:18 +01:00
|
|
|
else if (m_valueModp) {
|
|
|
|
|
// Making an AstAssign (vs AstAssignW) to a wire is an error, suppress it
|
2021-11-26 23:55:36 +01:00
|
|
|
FileLine* const newfl = new FileLine(fl);
|
2018-11-26 23:58:18 +01:00
|
|
|
newfl->warnOff(V3ErrorCode::PROCASSWIRE, true);
|
2021-11-26 23:55:36 +01:00
|
|
|
auto* const assp
|
2020-10-16 03:08:24 +02:00
|
|
|
= new AstAssign(newfl, new AstVarRef(newfl, nodep->name(), VAccess::WRITE),
|
|
|
|
|
nodep->valuep()->unlinkFrBack());
|
2022-01-02 18:35:44 +01:00
|
|
|
if (nodep->lifetime().isAutomatic()) {
|
|
|
|
|
nodep->addNextHere(new AstInitialAutomatic{newfl, assp});
|
|
|
|
|
} else {
|
2022-04-23 15:44:48 +02:00
|
|
|
nodep->addNextHere(new AstInitialStatic{newfl, assp});
|
2022-01-02 18:35:44 +01:00
|
|
|
}
|
2020-04-19 02:20:17 +02:00
|
|
|
} // 4. Under blocks, it's an initial value to be under an assign
|
2018-11-26 23:58:18 +01:00
|
|
|
else {
|
2020-09-07 23:09:25 +02:00
|
|
|
nodep->addNextHere(new AstAssign(fl,
|
|
|
|
|
new AstVarRef(fl, nodep->name(), VAccess::WRITE),
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->valuep()->unlinkFrBack()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-10-01 16:48:37 +02:00
|
|
|
if (nodep->isIfaceRef() && !nodep->isIfaceParent() && !v3Global.opt.topIfacesSupported()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// 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 13:58:34 +02:00
|
|
|
if (m_modp->level() <= 2) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Interfaced port on top level module");
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-05-08 00:28:05 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAttrOf* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
cleanFileline(nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
if (nodep->attrType() == VAttrType::DT_PUBLIC) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstTypedef* const typep = VN_AS(nodep->backp(), Typedef);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(typep, nodep, "Attribute not attached to typedef");
|
2019-05-19 22:13:13 +02:00
|
|
|
typep->attrPublic(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_CLOCK_ENABLE) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2022-05-16 19:47:15 +02:00
|
|
|
// Accepted and silently ignored for backward compatibility, but has no effect
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2021-12-19 20:45:06 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_FORCEABLE) {
|
|
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
|
|
|
|
m_varp->setForceable();
|
|
|
|
|
v3Global.setHasForceableSignals();
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_PUBLIC) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2020-04-15 13:58:34 +02:00
|
|
|
m_varp->sigUserRWPublic(true);
|
|
|
|
|
m_varp->sigModPublic(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_PUBLIC_FLAT) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_varp->sigUserRWPublic(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_PUBLIC_FLAT_RD) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_varp->sigUserRdPublic(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_PUBLIC_FLAT_RW) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_varp->sigUserRWPublic(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_ISOLATE_ASSIGNMENTS) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_varp->attrIsolateAssign(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_SFORMAT) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_varp->attrSFormat(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_SPLIT_VAR) {
|
2020-02-29 01:15:08 +01:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
|
|
|
|
if (!VN_IS(m_modp, Module)) {
|
2020-04-15 13:58:34 +02: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 01:15:08 +01:00
|
|
|
} else {
|
|
|
|
|
m_varp->attrSplitVar(true);
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_SC_BV) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_varp->attrScBv(true);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_CLOCKER) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-10-05 13:54:14 +02:00
|
|
|
m_varp->attrClocker(VVarAttrClocker::CLOCKER_YES);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->attrType() == VAttrType::VAR_NO_CLOCKER) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_varp, nodep, "Attribute not attached to variable");
|
2019-10-05 13:54:14 +02:00
|
|
|
m_varp->attrClocker(VVarAttrClocker::CLOCKER_NO);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-05-08 00:28:05 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlwaysPublic* nodep) override {
|
2019-05-19 22:13:13 +02: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 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_varp) {
|
|
|
|
|
nodep->unlinkFrBack();
|
2022-09-17 14:48:51 +02:00
|
|
|
AstNode::addNext<AstNode, AstNode>(m_varp, nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// lvalue is true, because we know we have a verilator public_flat_rw
|
|
|
|
|
// but someday we may be more general
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool lvalue = m_varp->isSigUserRWPublic();
|
2022-09-15 20:43:56 +02:00
|
|
|
nodep->addStmtsp(
|
2020-09-07 23:09:25 +02:00
|
|
|
new AstVarRef(nodep->fileline(), m_varp, lvalue ? VAccess::WRITE : VAccess::READ));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2010-04-06 02:01:17 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDefImplicitDType* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
cleanFileline(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " DEFIMPLICIT " << nodep << endl);
|
2019-05-19 22:13:13 +02: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 16:12:55 +02:00
|
|
|
AstTypedef* defp = nullptr;
|
2021-11-26 23:55:36 +01:00
|
|
|
const ImplTypedefMap::iterator it
|
2021-03-13 00:17:49 +01:00
|
|
|
= m_implTypedef.find(std::make_pair(nodep->containerp(), nodep->name()));
|
2019-05-19 22:13:13 +02: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 13:58:34 +02:00
|
|
|
for (; backp; backp = backp->backp()) {
|
|
|
|
|
if (VN_IS(backp, Var) || VN_IS(backp, Typedef) || VN_IS(backp, NodeFTask)) break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(backp, nodep,
|
|
|
|
|
"Implicit enum/struct type created under unexpected node type");
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeDType* const dtypep = nodep->childDTypep();
|
2020-04-15 13:58:34 +02:00
|
|
|
dtypep->unlinkFrBack();
|
|
|
|
|
if (VN_IS(backp, Typedef)) {
|
|
|
|
|
// A typedef doesn't need us to make yet another level of typedefing
|
2019-05-19 22:13:13 +02:00
|
|
|
// For typedefs just remove the AstRefDType level of abstraction
|
|
|
|
|
nodep->replaceWith(dtypep);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
} else {
|
2020-08-15 16:12:55 +02:00
|
|
|
defp = new AstTypedef(nodep->fileline(), nodep->name(), nullptr, VFlagChildDType(),
|
2020-04-15 13:58:34 +02:00
|
|
|
dtypep);
|
|
|
|
|
m_implTypedef.insert(
|
2021-03-13 00:17:49 +01:00
|
|
|
std::make_pair(std::make_pair(nodep->containerp(), defp->name()), defp));
|
2019-05-19 22:13:13 +02:00
|
|
|
backp->addNextHere(defp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nodep->replaceWith(new AstRefDType(nodep->fileline(), defp->name()));
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2010-01-07 01:04:20 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstForeach* nodep) override {
|
2021-12-11 21:06:33 +01:00
|
|
|
// FOREACH(array, loopvars, body)
|
2020-06-10 04:05:06 +02:00
|
|
|
UINFO(9, "FOREACH " << nodep << endl);
|
|
|
|
|
// 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();
|
2021-12-11 21:06:33 +01:00
|
|
|
while (AstDot* dotp = VN_CAST(bracketp, Dot)) bracketp = dotp->rhsp();
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstSelBit* const selp = VN_CAST(bracketp, SelBit)) {
|
2021-12-11 21:06:33 +01:00
|
|
|
// Convert to AstSelLoopVars so V3LinkDot knows what's being defined
|
|
|
|
|
AstNode* const newp
|
|
|
|
|
= new AstSelLoopVars{selp->fileline(), selp->fromp()->unlinkFrBack(),
|
|
|
|
|
selp->rhsp()->unlinkFrBackWithNext()};
|
|
|
|
|
selp->replaceWith(newp);
|
2020-06-10 04:05:06 +02:00
|
|
|
VL_DO_DANGLING(selp->deleteTree(), selp);
|
2022-07-30 16:01:25 +02:00
|
|
|
} else if (VN_IS(bracketp, SelLoopVars)) {
|
2021-12-11 21:06:33 +01:00
|
|
|
// Ok
|
2020-06-10 04:05:06 +02:00
|
|
|
} else {
|
|
|
|
|
nodep->v3error(
|
2021-12-11 21:06:33 +01:00
|
|
|
"Syntax error; foreach missing bracketed loop variable (IEEE 1800-2017 12.7.3)");
|
2020-06-10 04:05:06 +02:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-12-13 02:43:15 +01:00
|
|
|
iterateChildren(nodep);
|
2016-09-20 04:00:13 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-01-12 10:03:17 +01:00
|
|
|
V3Config::applyModule(nodep);
|
|
|
|
|
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2020-12-12 18:56:59 +01:00
|
|
|
VL_RESTORER(m_genblkAbove);
|
|
|
|
|
VL_RESTORER(m_genblkNum);
|
2020-10-16 03:08:24 +02:00
|
|
|
VL_RESTORER(m_lifetime);
|
2020-01-20 19:27:27 +01:00
|
|
|
{
|
|
|
|
|
// Module: Create sim table for entire module and iterate
|
|
|
|
|
cleanFileline(nodep);
|
2021-09-09 01:31:26 +02:00
|
|
|
// Classes inherit from upper package
|
|
|
|
|
if (m_modp && nodep->timeunit().isNone()) nodep->timeunit(m_modp->timeunit());
|
2020-01-20 19:27:27 +01:00
|
|
|
m_modp = nodep;
|
2020-12-12 18:56:59 +01:00
|
|
|
m_genblkAbove = 0;
|
|
|
|
|
m_genblkNum = 0;
|
2020-01-20 19:27:27 +01:00
|
|
|
m_valueModp = nodep;
|
2020-10-16 03:08:24 +02:00
|
|
|
m_lifetime = nodep->lifetime();
|
|
|
|
|
if (m_lifetime.isNone()) {
|
|
|
|
|
m_lifetime = VN_IS(nodep, Class) ? VLifetime::AUTOMATIC : VLifetime::STATIC;
|
|
|
|
|
}
|
2020-01-20 19:27:27 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
m_valueModp = nodep;
|
2012-06-14 05:08:45 +02:00
|
|
|
}
|
|
|
|
|
void visitIterateNoValueMod(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate a node which shouldn't have any local variables moved to an Initial
|
|
|
|
|
cleanFileline(nodep);
|
2020-12-06 19:49:44 +01:00
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_valueModp);
|
|
|
|
|
m_valueModp = nullptr;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2012-06-14 05:08:45 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeProcedure* nodep) override { visitIterateNoValueMod(nodep); }
|
|
|
|
|
void visit(AstAlways* nodep) override {
|
2020-12-06 19:49:44 +01:00
|
|
|
VL_RESTORER(m_inAlways);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_inAlways = true;
|
|
|
|
|
visitIterateNoValueMod(nodep);
|
2012-06-14 05:08:45 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCover* nodep) override { visitIterateNoValueMod(nodep); }
|
|
|
|
|
void visit(AstRestrict* nodep) override { visitIterateNoValueMod(nodep); }
|
2012-06-14 05:08:45 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstBegin* nodep) override {
|
2020-01-12 10:03:17 +01:00
|
|
|
V3Config::applyCoverageBlock(m_modp, nodep);
|
|
|
|
|
cleanFileline(nodep);
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstNode* const backp = nodep->backp();
|
2020-02-26 04:21:16 +01:00
|
|
|
// IEEE says directly nested item is not a new block
|
2021-11-01 13:59:00 +01:00
|
|
|
// The genblk name will get attached to the if true/false LOWER begin block(s)
|
|
|
|
|
const bool nestedIf = nestedIfBegin(nodep);
|
2020-02-26 04:21:16 +01:00
|
|
|
// It's not FOR(BEGIN(...)) but we earlier changed it to BEGIN(FOR(...))
|
2020-12-12 18:56:59 +01:00
|
|
|
if (nodep->genforp()) {
|
|
|
|
|
++m_genblkNum;
|
|
|
|
|
if (nodep->name() == "") nodep->name("genblk" + cvtToStr(m_genblkNum));
|
|
|
|
|
}
|
|
|
|
|
if (nodep->generate() && nodep->name() == ""
|
2020-12-13 01:19:16 +01:00
|
|
|
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
|
2020-12-12 18:56:59 +01:00
|
|
|
nodep->name("genblk" + cvtToStr(m_genblkAbove));
|
|
|
|
|
}
|
|
|
|
|
if (nodep->name() != "") {
|
|
|
|
|
VL_RESTORER(m_genblkAbove);
|
|
|
|
|
VL_RESTORER(m_genblkNum);
|
|
|
|
|
m_genblkAbove = 0;
|
|
|
|
|
m_genblkNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstGenCase* nodep) override {
|
2020-12-12 18:56:59 +01:00
|
|
|
++m_genblkNum;
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
{
|
|
|
|
|
VL_RESTORER(m_genblkAbove);
|
|
|
|
|
VL_RESTORER(m_genblkNum);
|
|
|
|
|
m_genblkAbove = m_genblkNum;
|
|
|
|
|
m_genblkNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstGenIf* nodep) override {
|
2020-12-12 18:56:59 +01:00
|
|
|
cleanFileline(nodep);
|
2021-11-26 23:55:36 +01:00
|
|
|
const bool nestedIf
|
2021-11-01 13:59:00 +01:00
|
|
|
= (VN_IS(nodep->backp(), Begin) && nestedIfBegin(VN_CAST(nodep->backp(), Begin)));
|
|
|
|
|
if (nestedIf) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
++m_genblkNum;
|
2020-12-12 18:56:59 +01:00
|
|
|
VL_RESTORER(m_genblkAbove);
|
|
|
|
|
VL_RESTORER(m_genblkNum);
|
|
|
|
|
m_genblkAbove = m_genblkNum;
|
|
|
|
|
m_genblkNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
2020-02-26 04:21:16 +01:00
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCase* nodep) override {
|
2020-01-12 10:03:17 +01:00
|
|
|
V3Config::applyCase(nodep);
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPrintTimeScale* nodep) override {
|
2020-04-16 01:39:03 +02:00
|
|
|
// Inlining may change hierarchy, so just save timescale where needed
|
2020-06-06 21:54:44 +02:00
|
|
|
cleanFileline(nodep);
|
2020-04-16 01:39:03 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->name(m_modp->name());
|
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSFormatF* nodep) override {
|
2020-06-06 21:54:44 +02:00
|
|
|
cleanFileline(nodep);
|
2020-04-16 01:39:03 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTime* nodep) override {
|
2020-06-06 21:54:44 +02:00
|
|
|
cleanFileline(nodep);
|
2020-04-16 01:39:03 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTimeD* nodep) override {
|
2020-06-06 21:54:44 +02:00
|
|
|
cleanFileline(nodep);
|
2020-04-16 01:39:03 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTimeImport* nodep) override {
|
2020-06-06 21:54:44 +02:00
|
|
|
cleanFileline(nodep);
|
2020-04-16 01:39:03 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEventControl* nodep) override {
|
2020-06-06 21:54:44 +02:00
|
|
|
cleanFileline(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
2021-11-26 23:55:36 +01:00
|
|
|
AstAlways* const alwaysp = VN_CAST(nodep->backp(), Always);
|
2020-06-06 21:54:44 +02:00
|
|
|
if (alwaysp && alwaysp->keyword() == VAlwaysKwd::ALWAYS_COMB) {
|
2022-05-16 21:44:41 +02:00
|
|
|
alwaysp->v3error("Event control statements not legal under always_comb "
|
2020-08-23 14:56:35 +02:00
|
|
|
"(IEEE 1800-2017 9.2.2.2.2)\n"
|
2020-06-06 21:54:44 +02:00
|
|
|
<< nodep->warnMore() << "... Suggest use a normal 'always'");
|
2020-08-23 14:56:35 +02:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-06-06 21:54:44 +02:00
|
|
|
} else if (alwaysp && !alwaysp->sensesp()) {
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
// If the event control is at the top, move the sentree to the always
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstSenTree* const sensesp = nodep->sensesp()) {
|
2020-06-06 21:54:44 +02:00
|
|
|
sensesp->unlinkFrBackWithNext();
|
|
|
|
|
alwaysp->sensesp(sensesp);
|
|
|
|
|
}
|
2022-09-15 20:43:56 +02:00
|
|
|
if (nodep->stmtsp()) alwaysp->addStmtsp(nodep->stmtsp()->unlinkFrBackWithNext());
|
2020-08-23 14:56:35 +02:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2020-06-06 21:54:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 10:03:17 +01:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Default: Just iterate
|
|
|
|
|
cleanFileline(nodep);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-12-21 22:53:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit LinkParseVisitor(AstNetlist* rootp) { iterate(rootp); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~LinkParseVisitor() override = default;
|
2006-12-21 22:53:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Link class functions
|
|
|
|
|
|
|
|
|
|
void V3LinkParse::linkParse(AstNetlist* rootp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, __FUNCTION__ << ": " << endl);
|
2021-11-26 16:52:36 +01:00
|
|
|
{ LinkParseVisitor{rootp}; } // Destruct before checking
|
2022-09-18 21:53:42 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("linkparse", 0, dumpTree() >= 6);
|
2006-12-21 22:53:51 +01:00
|
|
|
}
|