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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2024-01-01 09:19:59 +01:00
|
|
|
// Copyright 2003-2024 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
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include "V3LinkParse.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2020-01-12 10:03:17 +01:00
|
|
|
#include "V3Config.h"
|
2024-03-24 14:14:31 +01:00
|
|
|
#include "V3Stats.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
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
|
|
|
// 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
|
2022-12-23 13:34:49 +01:00
|
|
|
AstNodeExpr* m_defaultInSkewp = nullptr; // Current default input skew
|
|
|
|
|
AstNodeExpr* m_defaultOutSkewp = nullptr; // Current default output skew
|
2024-01-07 16:59:56 +01:00
|
|
|
int m_anonUdpId = 0; // Counter for anonymous UDP instances
|
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
|
2023-09-25 17:39:04 +02:00
|
|
|
int m_beginDepth = 0; // How many begin blocks above current node within current AstNodeModule
|
2020-10-16 03:08:24 +02:00
|
|
|
VLifetime m_lifetime = VLifetime::STATIC; // Propagating lifetime
|
2023-03-14 10:03:08 +01:00
|
|
|
bool m_insideLoop = false; // True if the node is inside a loop
|
2024-03-24 14:14:31 +01:00
|
|
|
VDouble0 m_statModules; // Number of modules seen
|
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.
|
2023-07-01 14:59:26 +02:00
|
|
|
// TODO: Many places copy the filename when suppressing warnings,
|
|
|
|
|
// perhaps audit to make consistent and this is no longer needed
|
2019-05-19 22:13:13 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-01 16:45:25 +02:00
|
|
|
void checkIndent(AstNode* nodep, AstNode* childp) {
|
|
|
|
|
// Try very hard to avoid false positives
|
|
|
|
|
AstNode* nextp = nodep->nextp();
|
|
|
|
|
if (!childp) return;
|
|
|
|
|
if (!nextp && VN_IS(nodep, While) && VN_IS(nodep->backp(), Begin))
|
|
|
|
|
nextp = nodep->backp()->nextp();
|
|
|
|
|
if (!nextp) return;
|
|
|
|
|
if (VN_IS(childp, Begin)) return;
|
|
|
|
|
FileLine* const nodeFlp = nodep->fileline();
|
|
|
|
|
FileLine* const childFlp = childp->fileline();
|
|
|
|
|
FileLine* const nextFlp = nextp->fileline();
|
|
|
|
|
// UINFO(0, "checkInd " << nodeFlp->firstColumn() << " " << nodep << endl);
|
|
|
|
|
// UINFO(0, " child " << childFlp->firstColumn() << " " << childp << endl);
|
|
|
|
|
// UINFO(0, " next " << nextFlp->firstColumn() << " " << nextp << endl);
|
|
|
|
|
// Same filename, later line numbers (no macro magic going on)
|
|
|
|
|
if (nodeFlp->filenameno() != childFlp->filenameno()) return;
|
|
|
|
|
if (nodeFlp->filenameno() != nextFlp->filenameno()) return;
|
|
|
|
|
if (nodeFlp->lastLineno() >= childFlp->firstLineno()) return;
|
|
|
|
|
if (childFlp->lastLineno() >= nextFlp->firstLineno()) return;
|
|
|
|
|
// This block has indent 'a'
|
|
|
|
|
// Child block has indent 'b' where indent('b') > indent('a')
|
|
|
|
|
// Next block has indent 'b'
|
|
|
|
|
// (note similar code below)
|
|
|
|
|
if (!(nodeFlp->firstColumn() < childFlp->firstColumn()
|
|
|
|
|
&& nextFlp->firstColumn() >= childFlp->firstColumn()))
|
|
|
|
|
return;
|
|
|
|
|
// Might be a tab difference in spaces up to the node prefix, if so
|
|
|
|
|
// just ignore this warning
|
|
|
|
|
// Note it's correct we look at nodep's column in all of these
|
|
|
|
|
const std::string nodePrefix = nodeFlp->sourcePrefix(nodeFlp->firstColumn());
|
|
|
|
|
const std::string childPrefix = childFlp->sourcePrefix(nodeFlp->firstColumn());
|
|
|
|
|
const std::string nextPrefix = nextFlp->sourcePrefix(nodeFlp->firstColumn());
|
|
|
|
|
if (childPrefix != nodePrefix) return;
|
|
|
|
|
if (nextPrefix != childPrefix) return;
|
|
|
|
|
// Some file lines start after the indentation, so make another check
|
|
|
|
|
// using actual file contents
|
|
|
|
|
const std::string nodeSource = nodeFlp->source();
|
|
|
|
|
const std::string childSource = childFlp->source();
|
|
|
|
|
const std::string nextSource = nextFlp->source();
|
|
|
|
|
if (!(VString::leadingWhitespaceCount(nodeSource)
|
|
|
|
|
< VString::leadingWhitespaceCount(childSource)
|
|
|
|
|
&& VString::leadingWhitespaceCount(nextSource)
|
|
|
|
|
>= VString::leadingWhitespaceCount(childSource)))
|
|
|
|
|
return;
|
|
|
|
|
nextp->v3warn(MISINDENT,
|
|
|
|
|
"Misleading indentation\n"
|
|
|
|
|
<< nextp->warnContextPrimary() << '\n'
|
|
|
|
|
<< nodep->warnOther()
|
|
|
|
|
<< "... Expected indentation matching this earlier statement's line:\n"
|
|
|
|
|
<< nodep->warnContextSecondary());
|
|
|
|
|
}
|
|
|
|
|
|
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.
|
2023-04-11 01:37:24 +02:00
|
|
|
// Mark class methods
|
|
|
|
|
if (VN_IS(m_modp, Class)) nodep->classMethod(true);
|
|
|
|
|
|
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;
|
2023-01-05 23:42:05 +01:00
|
|
|
if (!nodep->lifetime().isNone()) {
|
|
|
|
|
m_lifetime = nodep->lifetime();
|
|
|
|
|
} else {
|
2024-09-21 00:26:23 +02:00
|
|
|
if (nodep->classMethod()) {
|
2023-01-05 23:42:05 +01:00
|
|
|
// Class methods are automatic by default
|
|
|
|
|
m_lifetime = VLifetime::AUTOMATIC;
|
2023-01-23 14:35:10 +01:00
|
|
|
} else if (nodep->dpiImport() || VN_IS(nodep, Property)) {
|
|
|
|
|
// DPI-imported functions and properties don't have lifetime specifiers
|
2023-01-05 23:42:05 +01:00
|
|
|
m_lifetime = VLifetime::NONE;
|
|
|
|
|
}
|
2023-05-07 01:48:22 +02:00
|
|
|
for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
|
|
|
|
AstVar* const varp = VN_CAST(itemp, Var);
|
|
|
|
|
if (varp && varp->valuep() && varp->lifetime().isNone()
|
|
|
|
|
&& m_lifetime.isStatic() && !varp->isIO()) {
|
|
|
|
|
if (VN_IS(m_modp, Module)) {
|
|
|
|
|
nodep->v3warn(IMPLICITSTATIC,
|
|
|
|
|
"Function/task's lifetime implicitly set to static\n"
|
|
|
|
|
<< nodep->warnMore()
|
|
|
|
|
<< "... Suggest use 'function automatic' or "
|
|
|
|
|
"'function static'\n"
|
|
|
|
|
<< nodep->warnContextPrimary() << '\n'
|
|
|
|
|
<< varp->warnOther()
|
|
|
|
|
<< "... Location of implicit static variable\n"
|
|
|
|
|
<< varp->warnContextSecondary() << '\n'
|
|
|
|
|
<< "... Suggest use 'function automatic' or "
|
|
|
|
|
"'function static'");
|
|
|
|
|
} else {
|
|
|
|
|
varp->v3warn(IMPLICITSTATIC,
|
|
|
|
|
"Variable's lifetime implicitly set to static\n"
|
|
|
|
|
<< nodep->warnMore()
|
|
|
|
|
<< "... Suggest use 'static' before "
|
|
|
|
|
"variable declaration'");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-05 23:42:05 +01:00
|
|
|
}
|
|
|
|
|
nodep->lifetime(m_lifetime);
|
2020-10-16 03:08:24 +02:00
|
|
|
}
|
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); }
|
2023-11-12 02:20:37 +01:00
|
|
|
void visit(AstConstraint* nodep) override {
|
|
|
|
|
v3Global.useRandomizeMethods(true);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
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);
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* valuep = nullptr;
|
2020-08-16 20:55:46 +02:00
|
|
|
if (nodep->valuep()) {
|
2022-09-17 14:48:51 +02:00
|
|
|
valuep
|
2022-11-20 21:06:49 +01:00
|
|
|
= 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);
|
2023-03-17 12:48:41 +01:00
|
|
|
if (nodep->lifetime().isStatic() && m_insideLoop && nodep->valuep()) {
|
|
|
|
|
nodep->lifetime(VLifetime::AUTOMATIC);
|
|
|
|
|
nodep->v3warn(STATICVAR, "Static variable with assignment declaration declared in a "
|
|
|
|
|
"loop converted to automatic");
|
2023-03-14 10:03:08 +01:00
|
|
|
}
|
2023-10-31 13:15:54 +01:00
|
|
|
if (nodep->varType() != VVarType::PORT) {
|
|
|
|
|
if (nodep->lifetime().isNone()) nodep->lifetime(m_lifetime);
|
|
|
|
|
} else if (m_ftaskp) {
|
|
|
|
|
nodep->lifetime(VLifetime::AUTOMATIC);
|
2020-10-16 03:08:24 +02:00
|
|
|
}
|
2023-09-25 17:39:04 +02:00
|
|
|
|
|
|
|
|
if (nodep->isGParam() && !nodep->isAnsi()) { // shadow some parameters into localparams
|
|
|
|
|
if (m_beginDepth > 0
|
|
|
|
|
|| (m_beginDepth == 0
|
|
|
|
|
&& (m_modp->hasParameterList() || VN_IS(m_modp, Class)
|
|
|
|
|
|| VN_IS(m_modp, Package)))) {
|
|
|
|
|
nodep->varType(VVarType::LPARAM);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-04 03:06:52 +01:00
|
|
|
if (nodep->isGParam() && m_modp) m_modp->hasGParam(true);
|
2023-09-25 17:39:04 +02:00
|
|
|
|
2020-05-26 00:41:47 +02:00
|
|
|
if (nodep->isParam() && !nodep->valuep()
|
|
|
|
|
&& nodep->fileline()->language() < V3LangCode::L1800_2009) {
|
2023-05-06 04:37:42 +02:00
|
|
|
nodep->v3warn(NEWERSTD,
|
|
|
|
|
"Parameter requires default value, or use IEEE 1800-2009 or later.");
|
2020-05-26 00:41:47 +02:00
|
|
|
}
|
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.
|
2022-10-20 03:59:26 +02:00
|
|
|
AstNodeDType* dtypep = VN_CAST(nodep->valuep(), NodeDType);
|
|
|
|
|
if (dtypep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
dtypep->unlinkFrBack();
|
2022-10-20 03:59:26 +02:00
|
|
|
} else {
|
|
|
|
|
dtypep = new AstVoidDType{nodep->fileline()};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-10-20 03:59:26 +02:00
|
|
|
AstNode* const newp = new AstParamTypeDType{nodep->fileline(), nodep->varType(),
|
|
|
|
|
nodep->name(), VFlagChildDType{}, dtypep};
|
|
|
|
|
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);
|
|
|
|
|
|
2023-03-10 03:48:05 +01:00
|
|
|
if (v3Global.opt.publicFlatRW()
|
|
|
|
|
|| (v3Global.opt.publicDepth() && m_modp
|
|
|
|
|
&& (m_modp->level() - 1) <= v3Global.opt.publicDepth())) {
|
|
|
|
|
|
2019-09-23 13:56:07 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-09 01:38:26 +01:00
|
|
|
if (v3Global.opt.publicParams() && nodep->isParam()) nodep->sigUserRWPublic(true);
|
|
|
|
|
|
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
|
2024-08-15 16:04:07 +02:00
|
|
|
} else if (!m_ftaskp && !VN_IS(m_modp, Class) && nodep->isNonOutput()
|
|
|
|
|
&& !nodep->isInput()) {
|
|
|
|
|
// Module inout/ref/constref: const default to use
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: Default value on module inout/ref/constref: "
|
|
|
|
|
<< 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
|
2022-11-20 21:06:49 +01:00
|
|
|
FileLine* const newfl = new FileLine{fl};
|
2018-11-26 23:58:18 +01:00
|
|
|
newfl->warnOff(V3ErrorCode::PROCASSWIRE, true);
|
2023-08-03 08:52:52 +02:00
|
|
|
// Create a ParseRef to the wire. We cannot use the var as it may be deleted if
|
|
|
|
|
// it's a port (see t_var_set_link.v)
|
|
|
|
|
auto* const assp = new AstAssign{
|
|
|
|
|
newfl, new AstParseRef{newfl, VParseRefExp::PX_TEXT, nodep->name()},
|
|
|
|
|
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)};
|
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 {
|
2022-11-13 21:33:11 +01:00
|
|
|
nodep->addNextHere(
|
2023-08-03 08:52:52 +02:00
|
|
|
new AstAssign{fl, new AstVarRef{fl, nodep, VAccess::WRITE},
|
2022-11-20 21:06:49 +01:00
|
|
|
VN_AS(nodep->valuep()->unlinkFrBack(), NodeExpr)});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-05-08 00:28:05 +02:00
|
|
|
}
|
2023-05-06 04:36:51 +02:00
|
|
|
void visit(AstConst* nodep) override {
|
2023-05-06 04:37:42 +02:00
|
|
|
if (nodep->num().autoExtend() && nodep->fileline()->language() < V3LangCode::L1800_2005) {
|
|
|
|
|
nodep->v3warn(NEWERSTD, "Unbased unsized literals require IEEE 1800-2005 or later.");
|
2023-05-06 04:36:51 +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(
|
2022-11-20 21:06:49 +01: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 {
|
2022-11-20 21:06:49 +01:00
|
|
|
defp = new AstTypedef{nodep->fileline(), nodep->name(), nullptr, VFlagChildDType{},
|
|
|
|
|
dtypep};
|
2023-10-28 12:24:04 +02:00
|
|
|
m_implTypedef.emplace(std::make_pair(nodep->containerp(), defp->name()), defp);
|
2019-05-19 22:13:13 +02:00
|
|
|
backp->addNextHere(defp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-20 21:06:49 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2024-01-06 00:07:24 +01:00
|
|
|
void visit(AstDelay* nodep) override {
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
UASSERT_OBJ(m_modp, nodep, "Delay not under module");
|
|
|
|
|
nodep->timeunit(m_modp->timeunit());
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-12 02:20:37 +01:00
|
|
|
void visit(AstNodeForeach* 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);
|
2023-07-01 14:59:26 +02:00
|
|
|
cleanFileline(nodep);
|
2020-06-10 04:05:06 +02:00
|
|
|
// 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))
|
2023-03-14 10:03:08 +01:00
|
|
|
VL_RESTORER(m_insideLoop);
|
|
|
|
|
m_insideLoop = true;
|
2020-06-10 04:05:06 +02:00
|
|
|
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(),
|
2023-11-12 19:30:48 +01:00
|
|
|
selp->bitp()->unlinkFrBackWithNext()};
|
2021-12-11 21:06:33 +01:00
|
|
|
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 {
|
2023-10-20 01:26:36 +02:00
|
|
|
nodep->v3error("Syntax error; foreach missing bracketed loop variable"
|
2024-03-02 15:05:21 +01:00
|
|
|
" (IEEE 1800-2023 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
|
|
|
}
|
2023-03-14 10:03:08 +01:00
|
|
|
void visit(AstRepeat* nodep) override {
|
2023-07-01 14:59:26 +02:00
|
|
|
cleanFileline(nodep);
|
2023-03-14 10:03:08 +01:00
|
|
|
VL_RESTORER(m_insideLoop);
|
|
|
|
|
{
|
|
|
|
|
m_insideLoop = true;
|
2023-07-01 16:45:25 +02:00
|
|
|
checkIndent(nodep, nodep->stmtsp());
|
2023-03-14 10:03:08 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstDoWhile* nodep) override {
|
2023-07-01 14:59:26 +02:00
|
|
|
cleanFileline(nodep);
|
2023-03-14 10:03:08 +01:00
|
|
|
VL_RESTORER(m_insideLoop);
|
|
|
|
|
{
|
|
|
|
|
m_insideLoop = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-15 14:53:29 +02:00
|
|
|
void visit(AstWait* nodep) override {
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (nodep->condp()->isZero()) {
|
|
|
|
|
// Special case "wait(0)" we won't throw WAITCONST as user wrote
|
|
|
|
|
// it that way with presumed intent - UVM does this.
|
|
|
|
|
FileLine* const newfl = nodep->fileline();
|
|
|
|
|
newfl->warnOff(V3ErrorCode::WAITCONST, true);
|
|
|
|
|
nodep->fileline(newfl);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-03-14 10:03:08 +01:00
|
|
|
void visit(AstWhile* nodep) override {
|
2023-07-01 14:59:26 +02:00
|
|
|
cleanFileline(nodep);
|
2023-03-14 10:03:08 +01:00
|
|
|
VL_RESTORER(m_insideLoop);
|
|
|
|
|
{
|
|
|
|
|
m_insideLoop = true;
|
2023-07-01 16:45:25 +02:00
|
|
|
checkIndent(nodep, nodep->stmtsp());
|
2023-03-14 10:03:08 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-01-12 10:03:17 +01:00
|
|
|
V3Config::applyModule(nodep);
|
2024-03-24 14:14:31 +01:00
|
|
|
++m_statModules;
|
2020-01-12 10:03:17 +01:00
|
|
|
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2024-01-07 16:59:56 +01:00
|
|
|
VL_RESTORER(m_anonUdpId);
|
2020-12-12 18:56:59 +01:00
|
|
|
VL_RESTORER(m_genblkAbove);
|
|
|
|
|
VL_RESTORER(m_genblkNum);
|
2023-09-25 17:39:04 +02:00
|
|
|
VL_RESTORER(m_beginDepth);
|
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;
|
2024-01-07 16:59:56 +01:00
|
|
|
m_anonUdpId = 0;
|
2020-12-12 18:56:59 +01:00
|
|
|
m_genblkAbove = 0;
|
|
|
|
|
m_genblkNum = 0;
|
2023-09-25 17:39:04 +02:00
|
|
|
m_beginDepth = 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;
|
|
|
|
|
}
|
2024-03-01 23:28:12 +01:00
|
|
|
if (nodep->name() == "TOP") {
|
|
|
|
|
// May mess up scope resolution and cause infinite loop
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Module cannot be named 'TOP' as conflicts with "
|
|
|
|
|
"Verilator top-level internals");
|
|
|
|
|
}
|
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);
|
2023-09-25 17:39:04 +02:00
|
|
|
VL_RESTORER(m_beginDepth);
|
|
|
|
|
m_beginDepth++;
|
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(...))
|
2023-07-01 14:31:53 +02:00
|
|
|
int assignGenBlkNum = -1;
|
2020-12-12 18:56:59 +01:00
|
|
|
if (nodep->genforp()) {
|
|
|
|
|
++m_genblkNum;
|
2023-07-01 14:31:53 +02:00
|
|
|
if (nodep->name() == "") assignGenBlkNum = m_genblkNum;
|
|
|
|
|
} else if (nodep->generate() && nodep->name() == "" && assignGenBlkNum == -1
|
|
|
|
|
&& (VN_IS(backp, CaseItem) || VN_IS(backp, GenIf)) && !nestedIf) {
|
|
|
|
|
assignGenBlkNum = m_genblkAbove;
|
2020-12-12 18:56:59 +01:00
|
|
|
}
|
2023-07-01 14:31:53 +02:00
|
|
|
if (assignGenBlkNum != -1) {
|
|
|
|
|
nodep->name("genblk" + cvtToStr(assignGenBlkNum));
|
|
|
|
|
if (nodep->stmtsp()) {
|
|
|
|
|
nodep->v3warn(GENUNNAMED,
|
|
|
|
|
"Unnamed generate block "
|
2024-03-02 15:05:21 +01:00
|
|
|
<< nodep->prettyNameQ() << " (IEEE 1800-2023 27.6)\n"
|
2023-07-01 14:31:53 +02:00
|
|
|
<< nodep->warnMore()
|
|
|
|
|
<< "... Suggest assign a label with 'begin : gen_<label_name>'");
|
|
|
|
|
}
|
2020-12-12 18:56:59 +01:00
|
|
|
}
|
2023-07-01 14:31:53 +02:00
|
|
|
|
2020-12-12 18:56:59 +01:00
|
|
|
if (nodep->name() != "") {
|
|
|
|
|
VL_RESTORER(m_genblkAbove);
|
|
|
|
|
VL_RESTORER(m_genblkNum);
|
|
|
|
|
m_genblkAbove = 0;
|
|
|
|
|
m_genblkNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-07 16:59:56 +01:00
|
|
|
void visit(AstCell* nodep) override {
|
|
|
|
|
if (nodep->origName().empty()) {
|
|
|
|
|
if (!VN_IS(nodep->modp(), Primitive)) { // Module/Program/Iface
|
|
|
|
|
nodep->modNameFileline()->v3error("Instance of " << nodep->modp()->verilogKwd()
|
|
|
|
|
<< " must be named");
|
|
|
|
|
}
|
|
|
|
|
// UDPs can have empty instance names. Assigning unique names for them to prevent any
|
|
|
|
|
// conflicts
|
|
|
|
|
const string newName = "$unnamedudp" + cvtToStr(++m_anonUdpId);
|
|
|
|
|
nodep->name(newName);
|
|
|
|
|
nodep->origName(newName);
|
|
|
|
|
}
|
|
|
|
|
}
|
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);
|
2023-07-01 16:45:25 +02:00
|
|
|
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
|
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-11-30 13:40:06 +01:00
|
|
|
void visit(AstDot* nodep) override {
|
|
|
|
|
cleanFileline(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (VN_IS(nodep->lhsp(), ParseRef) && nodep->lhsp()->name() == "super"
|
|
|
|
|
&& VN_IS(nodep->rhsp(), New)) {
|
|
|
|
|
// Look for other statements until hit function start
|
|
|
|
|
AstNode* scanp = nodep;
|
|
|
|
|
// Skip over the New's statement
|
|
|
|
|
for (; scanp && !VN_IS(scanp, StmtExpr); scanp = scanp->backp()) {}
|
2022-12-03 00:46:38 +01:00
|
|
|
if (VN_IS(scanp, StmtExpr)) { // Ignore warning if something not understood
|
2022-11-30 13:40:06 +01:00
|
|
|
scanp = scanp->backp();
|
|
|
|
|
for (; scanp; scanp = scanp->backp()) {
|
|
|
|
|
if (VN_IS(scanp, NodeStmt) || VN_IS(scanp, NodeModule)
|
|
|
|
|
|| VN_IS(scanp, NodeFTask))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!VN_IS(scanp, NodeFTask)) {
|
|
|
|
|
nodep->rhsp()->v3error(
|
2024-03-02 15:05:21 +01:00
|
|
|
"'super.new' not first statement in new function (IEEE 1800-2023 8.15)\n"
|
2022-11-30 13:40:06 +01:00
|
|
|
<< nodep->rhsp()->warnContextPrimary() << scanp->warnOther()
|
|
|
|
|
<< "... Location of earlier statement\n"
|
|
|
|
|
<< scanp->warnContextSecondary());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-01 14:59:26 +02:00
|
|
|
void visit(AstIf* nodep) override {
|
|
|
|
|
cleanFileline(nodep);
|
2023-07-01 16:45:25 +02:00
|
|
|
checkIndent(nodep, nodep->elsesp() ? nodep->elsesp() : nodep->thensp());
|
2023-07-01 14:59:26 +02:00
|
|
|
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-11-17 03:10:39 +01:00
|
|
|
void visit(AstTimeUnit* nodep) override {
|
2023-07-01 14:59:26 +02:00
|
|
|
cleanFileline(nodep);
|
2022-11-17 03:10:39 +01: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 "
|
2024-03-02 15:05:21 +01:00
|
|
|
"(IEEE 1800-2023 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
|
|
|
}
|
|
|
|
|
}
|
2024-09-21 00:26:23 +02:00
|
|
|
void visit(AstClassOrPackageRef* nodep) override { //
|
|
|
|
|
iterateChildren(nodep);
|
2022-11-28 16:53:55 +01:00
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstClocking* nodep) override {
|
2023-07-01 14:59:26 +02:00
|
|
|
cleanFileline(nodep);
|
2022-12-23 13:34:49 +01:00
|
|
|
VL_RESTORER(m_defaultInSkewp);
|
|
|
|
|
VL_RESTORER(m_defaultOutSkewp);
|
|
|
|
|
// Find default input and output skews
|
|
|
|
|
AstClockingItem* nextItemp = nodep->itemsp();
|
|
|
|
|
for (AstClockingItem* itemp = nextItemp; itemp; itemp = nextItemp) {
|
|
|
|
|
nextItemp = VN_AS(itemp->nextp(), ClockingItem);
|
|
|
|
|
if (itemp->exprp() || itemp->assignp()) continue;
|
|
|
|
|
if (itemp->skewp()) {
|
|
|
|
|
if (itemp->direction() == VDirection::INPUT) {
|
|
|
|
|
// Disallow default redefinition; note some simulators allow this
|
|
|
|
|
if (m_defaultInSkewp) {
|
|
|
|
|
itemp->skewp()->v3error("Multiple default input skews not allowed");
|
|
|
|
|
}
|
|
|
|
|
m_defaultInSkewp = itemp->skewp();
|
|
|
|
|
} else if (itemp->direction() == VDirection::OUTPUT) {
|
|
|
|
|
if (AstConst* const constp = VN_CAST(itemp->skewp(), Const)) {
|
|
|
|
|
if (constp->num().is1Step()) {
|
|
|
|
|
itemp->skewp()->v3error("1step not allowed as output skew");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Disallow default redefinition; note some simulators allow this
|
|
|
|
|
if (m_defaultOutSkewp) {
|
|
|
|
|
itemp->skewp()->v3error("Multiple default output skews not allowed");
|
|
|
|
|
}
|
|
|
|
|
m_defaultOutSkewp = itemp->skewp();
|
|
|
|
|
} else {
|
|
|
|
|
itemp->v3fatalSrc("Incorrect direction");
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-08 14:36:24 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(itemp->unlinkFrBack()), itemp);
|
2022-12-23 13:34:49 +01:00
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstClockingItem* nodep) override {
|
2023-07-01 14:59:26 +02:00
|
|
|
cleanFileline(nodep);
|
2022-12-23 13:34:49 +01:00
|
|
|
if (nodep->direction() == VDirection::OUTPUT) {
|
|
|
|
|
if (!nodep->skewp()) {
|
|
|
|
|
if (m_defaultOutSkewp) {
|
|
|
|
|
nodep->skewp(m_defaultOutSkewp->cloneTree(false));
|
|
|
|
|
} else {
|
2024-03-02 15:05:21 +01:00
|
|
|
// Default is 0 (IEEE 1800-2023 14.3)
|
2022-12-23 13:34:49 +01:00
|
|
|
nodep->skewp(new AstConst{nodep->fileline(), 0});
|
|
|
|
|
}
|
|
|
|
|
} else if (AstConst* const constp = VN_CAST(nodep->skewp(), Const)) {
|
|
|
|
|
if (constp->num().is1Step()) {
|
|
|
|
|
nodep->skewp()->v3error("1step not allowed as output skew");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (nodep->direction() == VDirection::INPUT) {
|
|
|
|
|
if (!nodep->skewp()) {
|
|
|
|
|
if (m_defaultInSkewp) {
|
|
|
|
|
nodep->skewp(m_defaultInSkewp->cloneTree(false));
|
|
|
|
|
} else {
|
2024-03-02 15:05:21 +01:00
|
|
|
// Default is 1step (IEEE 1800-2023 14.3)
|
2022-12-23 13:34:49 +01:00
|
|
|
nodep->skewp(new AstConst{nodep->fileline(), AstConst::OneStep{}});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
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
|
2024-09-21 00:26:23 +02:00
|
|
|
explicit LinkParseVisitor(AstNetlist* rootp) { iterate(rootp); }
|
2024-03-24 14:14:31 +01:00
|
|
|
~LinkParseVisitor() override {
|
|
|
|
|
V3Stats::addStatSum(V3Stats::STAT_SOURCE_MODULES, m_statModules);
|
|
|
|
|
}
|
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
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("linkparse", 0, dumpTreeEitherLevel() >= 6);
|
2006-12-21 22:53:51 +01:00
|
|
|
}
|