2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Resolve module/signal name references
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 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-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// LinkResolve TRANSFORMATIONS:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Top-down traversal
|
|
|
|
|
// Extracts:
|
|
|
|
|
// Add SUB so that we subtract off the "base 0-start" of the array
|
|
|
|
|
// SelBit: Convert to ArraySel
|
|
|
|
|
// Add SUB so that we subtract off the "base 0-start" of the array
|
|
|
|
|
// File operations
|
|
|
|
|
// Convert normal var to FILE* type
|
|
|
|
|
// SenItems: Convert pos/negedge of non-simple signals to temporaries
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3LinkResolve.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
|
|
|
|
#include "V3String.h"
|
2023-09-08 03:45:51 +02:00
|
|
|
#include "V3Task.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Link state, as a visitor of each AstNode
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class LinkResolveVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Entire netlist:
|
2023-09-08 03:45:51 +02:00
|
|
|
// AstCaseItem::user2() // bool Moved default caseitems
|
|
|
|
|
// AstNodeFTaskRef::user2() // bool Processing - to check for recursion
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser2InUse m_inuser2;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
// Below state needs to be preserved between each module call.
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
|
AstClass* m_classp = nullptr; // Class we're inside
|
2024-12-14 17:47:46 +01:00
|
|
|
string m_randcIllegalWhy; // Why randc illegal
|
|
|
|
|
AstNode* m_randcIllegalp = nullptr; // Node causing randc illegal
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
|
|
|
|
AstNodeCoverOrAssert* m_assertp = nullptr; // Current assertion
|
|
|
|
|
int m_senitemCvtNum = 0; // Temporary signal counter
|
2025-08-03 22:57:12 +02:00
|
|
|
std::deque<AstGenFor*> m_underGenFors; // Stack of GenFor underneath
|
2021-08-21 16:33:20 +02:00
|
|
|
bool m_underGenerate = false; // Under GenFor/GenIf
|
2025-08-22 13:58:03 +02:00
|
|
|
AstNodeExpr* m_currentRandomizeSelectp = nullptr; // fromp() of current `randomize()` call
|
|
|
|
|
bool m_inRandomizeWith = false; // If in randomize() with (and no other with afterwards)
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2025-03-29 03:04:29 +01:00
|
|
|
// VISITORS
|
2012-07-21 23:12:42 +02:00
|
|
|
// TODO: Most of these visitors are here for historical reasons.
|
2022-12-23 17:32:38 +01:00
|
|
|
// TODO: ExpectDescriptor can move to data type resolution, and the rest
|
2012-07-21 23:12:42 +02:00
|
|
|
// TODO: could move to V3LinkParse to get them out of the way of elaboration
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Module: Create sim table for entire module and iterate
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(8, "MODULE " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->dead()) return;
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
VL_RESTORER(m_senitemCvtNum);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_modp = nodep;
|
|
|
|
|
m_senitemCvtNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
2020-04-26 18:45:06 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClass* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_classp);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_classp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2024-12-12 14:16:19 +01:00
|
|
|
void visit(AstConstraint* nodep) override {
|
|
|
|
|
// V3LinkDot moved the isExternDef into the class, the extern proto was
|
|
|
|
|
// checked to exist, and now isn't needed
|
|
|
|
|
nodep->isExternDef(false);
|
|
|
|
|
if (nodep->isExternProto()) {
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2024-12-14 17:47:46 +01:00
|
|
|
void visit(AstConstraintBefore* nodep) override {
|
|
|
|
|
VL_RESTORER(m_randcIllegalWhy);
|
|
|
|
|
VL_RESTORER(m_randcIllegalp);
|
|
|
|
|
m_randcIllegalWhy = "'solve before' (IEEE 1800-2023 18.5.9)";
|
|
|
|
|
m_randcIllegalp = nodep;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstDist* nodep) override {
|
|
|
|
|
VL_RESTORER(m_randcIllegalWhy);
|
|
|
|
|
VL_RESTORER(m_randcIllegalp);
|
|
|
|
|
m_randcIllegalWhy = "'constraint dist' (IEEE 1800-2023 18.5.3)";
|
|
|
|
|
m_randcIllegalp = nodep;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstConstraintExpr* nodep) override {
|
|
|
|
|
VL_RESTORER(m_randcIllegalWhy);
|
|
|
|
|
VL_RESTORER(m_randcIllegalp);
|
|
|
|
|
if (nodep->isSoft()) {
|
|
|
|
|
m_randcIllegalWhy = "'constraint soft' (IEEE 1800-2023 18.5.13.1)";
|
|
|
|
|
m_randcIllegalp = nodep;
|
|
|
|
|
}
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstInitialAutomatic* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Initial assignments under function/tasks can just be simple
|
|
|
|
|
// assignments without the initial
|
|
|
|
|
if (m_ftaskp) {
|
2024-03-23 23:12:43 +01:00
|
|
|
nodep->replaceWith(nodep->stmtsp()->unlinkFrBackWithNext());
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2010-02-14 16:01:21 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeCoverOrAssert* nodep) override {
|
2023-09-03 15:08:43 +02:00
|
|
|
if (m_assertp) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Assert not allowed under another assert");
|
|
|
|
|
}
|
2025-02-28 03:18:27 +01:00
|
|
|
VL_RESTORER(m_assertp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_assertp = nodep;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2007-03-06 22:43:38 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
if (m_classp && !nodep->isParam()) nodep->varType(VVarType::MEMBER);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_ftaskp) nodep->funcLocal(true);
|
|
|
|
|
if (nodep->isSigModPublic()) {
|
|
|
|
|
nodep->sigModPublic(false); // We're done with this attribute
|
|
|
|
|
m_modp->modPublic(true); // Avoid flattening if signals are exposed
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2024-12-14 17:47:46 +01:00
|
|
|
if (nodep->varp()) { // Else due to dead code, might not have var pointer
|
|
|
|
|
// VarRef: Resolve its reference
|
|
|
|
|
nodep->varp()->usedParam(true);
|
2025-08-03 22:57:12 +02:00
|
|
|
// Look for where genvar is valid
|
|
|
|
|
bool ok = false;
|
2025-08-21 10:43:37 +02:00
|
|
|
// cppcheck-suppress constVariablePointer
|
|
|
|
|
for (AstGenFor* const forp : m_underGenFors) {
|
2025-08-03 22:57:12 +02:00
|
|
|
if (ok) break;
|
|
|
|
|
if (forp->initsp())
|
2025-08-21 10:43:37 +02:00
|
|
|
forp->initsp()->foreach([&](const AstVarRef* refp) { //
|
2025-08-03 22:57:12 +02:00
|
|
|
if (refp->varp() == nodep->varp()) ok = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (nodep->varp()->isGenVar() && !ok) {
|
2024-12-14 17:47:46 +01:00
|
|
|
nodep->v3error("Genvar "
|
|
|
|
|
<< nodep->prettyNameQ()
|
|
|
|
|
<< " used outside generate for loop (IEEE 1800-2023 27.4)");
|
|
|
|
|
}
|
|
|
|
|
if (nodep->varp()->isRandC() && m_randcIllegalp) {
|
|
|
|
|
nodep->v3error("Randc variables not allowed in "
|
|
|
|
|
<< m_randcIllegalWhy << '\n'
|
|
|
|
|
<< nodep->warnContextPrimary() << '\n'
|
|
|
|
|
<< m_randcIllegalp->warnOther()
|
|
|
|
|
<< "... Location of restricting expression\n"
|
|
|
|
|
<< m_randcIllegalp->warnContextSecondary());
|
|
|
|
|
}
|
2024-10-04 00:54:30 +02:00
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2023-09-08 03:45:51 +02:00
|
|
|
// Note AstLet is handled specifically elsewhere
|
2019-05-19 22:13:13 +02:00
|
|
|
// NodeTask: Remember its name for later resolution
|
2021-08-21 16:33:20 +02:00
|
|
|
if (m_underGenerate) nodep->underGenerate(true);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Remember the existing symbol table scope
|
2024-09-10 15:10:36 +02:00
|
|
|
if (m_classp) nodep->classMethod(true);
|
2020-08-23 01:46:21 +02:00
|
|
|
// V3LinkDot moved the isExternDef into the class, the extern proto was
|
|
|
|
|
// checked to exist, and now isn't needed
|
|
|
|
|
nodep->isExternDef(false);
|
|
|
|
|
if (nodep->isExternProto()) {
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-02-28 03:18:27 +01:00
|
|
|
VL_RESTORER(m_ftaskp);
|
|
|
|
|
m_ftaskp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
2021-11-27 02:38:48 +01:00
|
|
|
if (nodep->dpiExport()) nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2025-08-22 13:58:03 +02:00
|
|
|
VL_RESTORER(m_currentRandomizeSelectp);
|
|
|
|
|
|
|
|
|
|
if (nodep->name() == "randomize") {
|
|
|
|
|
if (const AstMethodCall* const methodcallp = VN_CAST(nodep, MethodCall)) {
|
|
|
|
|
if (m_inRandomizeWith) {
|
|
|
|
|
nodep->v3warn(
|
|
|
|
|
E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: randomize() nested in inline randomize() constraints");
|
|
|
|
|
}
|
|
|
|
|
m_currentRandomizeSelectp = methodcallp->fromp();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2023-09-08 03:45:51 +02:00
|
|
|
if (AstLet* letp = VN_CAST(nodep->taskp(), Let)) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(7, "letSubstitute() " << nodep << " <- " << letp);
|
2023-09-08 03:45:51 +02:00
|
|
|
if (letp->user2()) {
|
|
|
|
|
nodep->v3error("Recursive let substitution " << letp->prettyNameQ());
|
|
|
|
|
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
letp->user2(true);
|
2025-02-26 15:08:41 +01:00
|
|
|
if (VN_IS(nodep->backp(), StmtExpr)) {
|
|
|
|
|
nodep->v3error("Expected statement, not let substitution " << letp->prettyNameQ());
|
|
|
|
|
}
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, letp, "", "let-let");
|
|
|
|
|
// UINFOTREE(1, nodep, "", "let-ref");
|
2025-08-21 10:43:37 +02:00
|
|
|
// cppcheck-suppress constVariablePointer
|
2023-09-08 03:45:51 +02:00
|
|
|
AstStmtExpr* const letStmtp = VN_AS(letp->stmtsp(), StmtExpr);
|
|
|
|
|
AstNodeExpr* const newp = letStmtp->exprp()->cloneTree(false);
|
|
|
|
|
const V3TaskConnects tconnects = V3Task::taskConnects(nodep, letp->stmtsp());
|
|
|
|
|
std::map<const AstVar*, AstNodeExpr*> portToExprs;
|
|
|
|
|
for (const auto& tconnect : tconnects) {
|
|
|
|
|
const AstVar* const portp = tconnect.first;
|
|
|
|
|
const AstArg* const argp = tconnect.second;
|
|
|
|
|
AstNodeExpr* const pinp = argp->exprp();
|
|
|
|
|
if (!pinp) continue; // Argument error we'll find later
|
|
|
|
|
portToExprs.emplace(portp, pinp);
|
|
|
|
|
}
|
|
|
|
|
// Replace VarRefs of arguments with the argument values
|
|
|
|
|
newp->foreach([&](AstVarRef* refp) { //
|
|
|
|
|
const auto it = portToExprs.find(refp->varp());
|
|
|
|
|
if (it != portToExprs.end()) {
|
|
|
|
|
AstNodeExpr* const pinp = it->second;
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "let pin subst " << refp << " <- " << pinp);
|
2023-09-08 03:45:51 +02:00
|
|
|
// Side effects are copied into pins, to match other simulators
|
|
|
|
|
refp->replaceWith(pinp->cloneTree(false));
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(refp), refp);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-08-02 19:44:40 +02:00
|
|
|
// UINFOTREE(1, newp, "", "let-new");
|
2023-09-08 03:45:51 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
// Iterate to expand further now, so we can look for recursions
|
|
|
|
|
visit(newp);
|
|
|
|
|
letp->user2(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-02-20 10:15:09 +01:00
|
|
|
if (nodep->taskp() && !nodep->scopeNamep()
|
|
|
|
|
&& (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) {
|
2021-11-27 02:38:48 +01:00
|
|
|
nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-12-05 16:38:49 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCaseItem* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Move default caseItems to the bottom of the list
|
|
|
|
|
// That saves us from having to search each case list twice, for non-defaults and defaults
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
|
|
|
|
|
nodep->user2(true);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const nextp = nodep->nextp();
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
nextp->addNext(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2023-09-08 03:45:51 +02:00
|
|
|
void visit(AstLet* nodep) override {
|
|
|
|
|
// Lets have been (or about to be) substituted, we can remove
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPragma* nodep) override {
|
2022-01-02 19:56:40 +01:00
|
|
|
if (nodep->pragType() == VPragmaType::HIER_BLOCK) {
|
2020-08-15 15:43:53 +02:00
|
|
|
UASSERT_OBJ(m_modp, nodep, "HIER_BLOCK not under a module");
|
2021-11-14 15:39:31 +01:00
|
|
|
// If this is hierarchical mode which is to lib-create,
|
2020-08-15 15:43:53 +02:00
|
|
|
// sub modules do not have hier_block meta comment in the source code.
|
2021-11-14 15:39:31 +01:00
|
|
|
// But .vlt files may still mark a module which is actually a lib-create wrapper
|
2020-08-15 15:43:53 +02:00
|
|
|
// hier_block. AstNodeModule::hierBlock() can be true only when --hierarchical is
|
|
|
|
|
// specified.
|
|
|
|
|
m_modp->hierBlock(v3Global.opt.hierarchical());
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->pragType() == VPragmaType::PUBLIC_MODULE) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_modp, nodep, "PUBLIC_MODULE not under a module");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->modPublic(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->pragType() == VPragmaType::PUBLIC_TASK) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_ftaskp, nodep, "PUBLIC_TASK not under a task");
|
2019-05-19 22:13:13 +02:00
|
|
|
m_ftaskp->taskPublic(true);
|
|
|
|
|
m_modp->modPublic(true); // Need to get to the task...
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2022-01-02 19:56:40 +01:00
|
|
|
} else if (nodep->pragType() == VPragmaType::COVERAGE_BLOCK_OFF) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!v3Global.opt.coverageLine()) { // No need for block statements; may optimize
|
|
|
|
|
// better without
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2015-10-28 01:58:31 +01:00
|
|
|
string expectFormat(AstNode* nodep, const string& format, AstNode* argp, bool isScan) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Check display arguments, return new format string
|
|
|
|
|
string newFormat;
|
|
|
|
|
bool inPct = false;
|
2020-05-12 04:13:59 +02:00
|
|
|
bool inIgnore = false;
|
2018-10-14 05:06:36 +02:00
|
|
|
string fmt;
|
2025-10-14 01:47:08 +02:00
|
|
|
string parseFormat = format;
|
|
|
|
|
while (!parseFormat.empty() || argp) {
|
|
|
|
|
for (const char ch : parseFormat) {
|
|
|
|
|
if (!inPct && ch == '%') {
|
2020-05-12 04:13:59 +02:00
|
|
|
inPct = true;
|
2025-10-14 01:47:08 +02:00
|
|
|
inIgnore = false;
|
|
|
|
|
fmt = ch;
|
|
|
|
|
} else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
|
|
|
|
|
fmt += ch;
|
|
|
|
|
} else if (inPct) {
|
|
|
|
|
inPct = false;
|
|
|
|
|
fmt += ch;
|
|
|
|
|
switch (std::tolower(ch)) {
|
|
|
|
|
case '%': // %% - just output a %
|
|
|
|
|
break;
|
|
|
|
|
case '*':
|
|
|
|
|
inPct = true;
|
|
|
|
|
inIgnore = true;
|
|
|
|
|
break;
|
|
|
|
|
case 'm': // %m - auto insert "name"
|
|
|
|
|
if (isScan) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %m in $fscanf");
|
|
|
|
|
fmt = "";
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2025-10-14 01:47:08 +02:00
|
|
|
break;
|
|
|
|
|
case 'l': // %l - auto insert "library"
|
|
|
|
|
if (isScan) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %l in $fscanf");
|
|
|
|
|
fmt = "";
|
|
|
|
|
}
|
|
|
|
|
if (m_modp)
|
|
|
|
|
fmt = AstNode::prettyName(m_modp->libname()) + "."
|
|
|
|
|
+ m_modp->prettyName();
|
|
|
|
|
break;
|
|
|
|
|
default: // Most operators, just move to next argument
|
|
|
|
|
if (!V3Number::displayedFmtLegal(ch, isScan)) {
|
|
|
|
|
nodep->v3error("Unknown $display-like format code: '%" << ch << "'");
|
|
|
|
|
} else if (!inIgnore) {
|
|
|
|
|
if (!argp) {
|
|
|
|
|
nodep->v3error("Missing arguments for $display-like format");
|
|
|
|
|
} else {
|
|
|
|
|
argp = argp->nextp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
} // switch
|
|
|
|
|
newFormat += fmt;
|
|
|
|
|
} else {
|
|
|
|
|
newFormat += ch;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2015-11-11 03:12:15 +01:00
|
|
|
|
2025-10-14 01:47:08 +02:00
|
|
|
// Find additional arguments (without format) or additional format strings
|
|
|
|
|
parseFormat = "";
|
|
|
|
|
|
|
|
|
|
if (isScan) break;
|
2019-05-19 22:13:13 +02:00
|
|
|
while (argp) {
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstConst* const constp = VN_CAST(argp, Const);
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool isFromString = (constp) ? constp->num().isFromString() : false;
|
2025-10-14 01:47:08 +02:00
|
|
|
if (!isFromString) {
|
|
|
|
|
newFormat.append("%?"); // V3Width to figure it out
|
|
|
|
|
argp = argp->nextp();
|
|
|
|
|
} else { // New format string
|
|
|
|
|
parseFormat += constp->num().toString();
|
2021-11-13 19:50:44 +01:00
|
|
|
AstNode* const nextp = argp->nextp();
|
2020-04-15 13:58:34 +02:00
|
|
|
argp->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(argp), argp);
|
2019-05-19 22:13:13 +02:00
|
|
|
argp = nextp;
|
2025-10-14 01:47:08 +02:00
|
|
|
break; // And continue at top of parsing the new parseFormat
|
2019-05-17 03:21:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return newFormat;
|
2008-06-30 20:31:58 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-21 10:43:37 +02:00
|
|
|
static void expectDescriptor(AstNode* /*nodep*/, const AstNodeVarRef* filep) {
|
2021-11-24 00:22:16 +01:00
|
|
|
// This might fail on complex expressions like arrays
|
|
|
|
|
// We use attrFileDescr() only for lint suppression, so that's ok
|
2019-05-19 22:13:13 +02:00
|
|
|
if (filep && filep->varp()) filep->varp()->attrFileDescr(true);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2008-06-30 20:31:58 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFClose* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2018-02-02 03:32:58 +01:00
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
2006-12-21 22:53:51 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFError* nodep) override {
|
2020-04-05 17:22:05 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFEof* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-03-08 02:56:53 +01:00
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFRead* nodep) override {
|
2019-03-08 02:56:53 +01:00
|
|
|
iterateChildren(nodep);
|
2018-02-02 03:32:58 +01:00
|
|
|
expectDescriptor(nodep, VN_CAST(nodep->filep(), NodeVarRef));
|
2008-06-26 14:52:02 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstFScanF* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSScanF* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
expectFormat(nodep, nodep->text(), nodep->exprsp(), true);
|
2008-07-01 20:15:10 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSFormatF* nodep) override {
|
2024-08-31 01:35:47 +02:00
|
|
|
if (nodep->user2SetOnce()) return;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Cleanup old-school displays without format arguments
|
|
|
|
|
if (!nodep->hasFormat()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT_OBJ(nodep->text() == "", nodep,
|
|
|
|
|
"Non-format $sformatf should have \"\" format");
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->exprsp(), Const)
|
2021-10-22 14:56:48 +02:00
|
|
|
&& VN_AS(nodep->exprsp(), Const)->num().isFromString()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstConst* const fmtp = VN_AS(nodep->exprsp()->unlinkFrBack(), Const);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->text(fmtp->num().toString());
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(fmtp), fmtp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->hasFormat(true);
|
|
|
|
|
}
|
2021-06-21 00:32:57 +02:00
|
|
|
const string newFormat = expectFormat(nodep, nodep->text(), nodep->exprsp(), false);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->text(newFormat);
|
2018-02-02 03:32:58 +01:00
|
|
|
if ((VN_IS(nodep->backp(), Display)
|
2021-10-22 14:56:48 +02:00
|
|
|
&& VN_AS(nodep->backp(), Display)->displayType().needScopeTracking())
|
2019-05-19 22:13:13 +02:00
|
|
|
|| nodep->formatScopeTracking()) {
|
2021-11-27 02:38:48 +01:00
|
|
|
nodep->scopeNamep(new AstScopeName{nodep->fileline(), true});
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstUdpTable* nodep) override {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(5, "UDPTABLE " << nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!v3Global.opt.bboxUnsup()) {
|
|
|
|
|
// We don't warn until V3Inst, so that UDPs that are in libraries and
|
|
|
|
|
// never used won't result in any warnings.
|
|
|
|
|
} else {
|
|
|
|
|
// Massive hack, just tie off all outputs so our analysis can proceed
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstVar* varoutp = nullptr;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode* stmtp = m_modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
|
2018-10-27 23:29:00 +02:00
|
|
|
if (varp->isReadOnly()) {
|
|
|
|
|
} else if (varp->isWritable()) {
|
|
|
|
|
if (varoutp) {
|
|
|
|
|
varp->v3error("Multiple outputs not allowed in udp modules");
|
|
|
|
|
}
|
|
|
|
|
varoutp = varp;
|
|
|
|
|
// Tie off
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
AstAssignW* const ap
|
|
|
|
|
= new AstAssignW{varp->fileline(),
|
|
|
|
|
new AstVarRef{varp->fileline(), varp, VAccess::WRITE},
|
|
|
|
|
new AstConst{varp->fileline(), AstConst::BitFalse{}}};
|
|
|
|
|
m_modp->addStmtsp(new AstAlways{ap});
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
varp->v3error("Only inputs and outputs are allowed in udp modules");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-11-21 01:53:40 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-14 12:23:23 +02:00
|
|
|
void visit(AstSystemCSection* nodep) override {
|
|
|
|
|
switch (nodep->sectionType()) {
|
|
|
|
|
// Constructor, desctructor or special class info means the module must remain public
|
|
|
|
|
case VSystemCSectionType::CTOR:
|
|
|
|
|
case VSystemCSectionType::DTOR:
|
|
|
|
|
case VSystemCSectionType::INT: m_modp->modPublic(true); break;
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
// Has no children
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstIfaceRefDType* nodep) override {
|
2020-11-09 04:43:32 +01:00
|
|
|
// LinkDot checked modports, now remove references to them
|
|
|
|
|
// Keeping them later caused problems with InstDeArray,
|
|
|
|
|
// as it needed to make new modport arrays and such
|
|
|
|
|
nodep->modportp(nullptr);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
// void visit(AstModport* nodep) override { ... }
|
2020-11-09 04:43:32 +01:00
|
|
|
// We keep Modport's themselves around for XML dump purposes
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstGenFor* nodep) override {
|
2021-08-21 16:33:20 +02:00
|
|
|
VL_RESTORER(m_underGenerate);
|
|
|
|
|
m_underGenerate = true;
|
2025-08-03 22:57:12 +02:00
|
|
|
m_underGenFors.emplace_back(nodep);
|
2021-08-21 16:33:20 +02:00
|
|
|
iterateChildren(nodep);
|
2025-08-03 22:57:12 +02:00
|
|
|
UASSERT_OBJ(!m_underGenFors.empty(), nodep, "Underflow");
|
|
|
|
|
m_underGenFors.pop_back();
|
2021-08-21 16:33:20 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstGenIf* nodep) override {
|
2021-08-21 16:33:20 +02:00
|
|
|
VL_RESTORER(m_underGenerate);
|
|
|
|
|
m_underGenerate = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-22 13:58:03 +02:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
|
|
|
|
if (m_inRandomizeWith && nodep->fromp()->isSame(m_currentRandomizeSelectp)) {
|
|
|
|
|
// Replace member selects to the element
|
|
|
|
|
// on which the randomize() is called with LambdaArgRef
|
|
|
|
|
// This allows V3Randomize to work properly when
|
|
|
|
|
// constrained variables are referred using that object
|
|
|
|
|
AstNodeExpr* const prevFromp = nodep->fromp();
|
|
|
|
|
prevFromp->replaceWith(
|
|
|
|
|
new AstLambdaArgRef{prevFromp->fileline(), prevFromp->name(), false});
|
|
|
|
|
pushDeletep(prevFromp);
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstWith* nodep) override {
|
|
|
|
|
VL_RESTORER(m_inRandomizeWith);
|
|
|
|
|
if (const AstMethodCall* const methodCallp = VN_CAST(nodep->backp(), MethodCall)) {
|
|
|
|
|
m_inRandomizeWith = methodCallp->name() == "randomize";
|
|
|
|
|
} else {
|
|
|
|
|
m_inRandomizeWith = false;
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit LinkResolveVisitor(AstNetlist* rootp) { iterate(rootp); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~LinkResolveVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// LinkBotupVisitor
|
2019-05-19 22:13:13 +02:00
|
|
|
// Recurses cells backwards, so we can pick up those things that propagate
|
|
|
|
|
// from child cells up to the top module.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2023-03-18 17:17:25 +01:00
|
|
|
class LinkBotupVisitor final : public VNVisitorConst {
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2025-03-29 03:04:29 +01:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNetlist* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate modules backwards, in bottom-up order.
|
2023-03-18 00:58:53 +01:00
|
|
|
iterateChildrenBackwardsConst(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2023-03-18 17:17:25 +01:00
|
|
|
m_modp = nodep;
|
|
|
|
|
iterateChildrenConst(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Parent module inherits child's publicity
|
|
|
|
|
if (nodep->modp()->modPublic()) m_modp->modPublic(true);
|
|
|
|
|
//** No iteration for speed
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
void visit(AstNodeExpr*) override {} // Accelerate
|
2023-03-18 17:17:25 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2023-03-18 17:17:25 +01:00
|
|
|
explicit LinkBotupVisitor(AstNetlist* rootp) { iterateConst(rootp); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~LinkBotupVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Link class functions
|
|
|
|
|
|
|
|
|
|
void V3LinkResolve::linkResolve(AstNetlist* rootp) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(4, __FUNCTION__ << ": ");
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
2021-11-26 23:55:36 +01:00
|
|
|
const LinkResolveVisitor visitor{rootp};
|
2021-11-26 16:52:36 +01:00
|
|
|
LinkBotupVisitor{rootp};
|
2018-03-10 18:57:50 +01:00
|
|
|
} // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("linkresolve", 0, dumpTreeEitherLevel() >= 6);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|