2016-05-12 13:19:02 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
2016-10-23 20:27:57 +02:00
|
|
|
// DESCRIPTION: Verilator: Generate C language constructors and AstCReset nodes.
|
2016-05-12 13:19:02 +02:00
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2016-05-12 13:19:02 +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
|
2016-05-12 13:19:02 +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
|
2016-05-12 13:19:02 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2016-10-23 20:27:57 +02:00
|
|
|
// V3CCtors's Transformations:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterates over all modules and
|
|
|
|
|
// for all AstVar, create a creates a AstCReset node in an _ctor_var_reset AstCFunc.
|
|
|
|
|
// for all AstCoverDecl, move the declaration into a _configure_coverage AstCFunc.
|
|
|
|
|
// For each variable that needs reset, add a AstCReset node.
|
2016-10-23 20:27:57 +02:00
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// For primary inputs, add _eval_debug_assertions.
|
2017-11-06 03:47:55 +01:00
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// This transformation honors outputSplitCFuncs.
|
2016-05-12 13:19:02 +02:00
|
|
|
//*************************************************************************
|
2018-10-14 19:43:24 +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 "V3CCtors.h"
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3EmitCBase.h"
|
|
|
|
|
|
2021-05-22 19:50:55 +02:00
|
|
|
#include <list>
|
2016-05-12 13:19:02 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2021-05-22 19:50:55 +02:00
|
|
|
class VCtorType final {
|
|
|
|
|
public:
|
|
|
|
|
enum en : uint8_t { MODULE, CLASS, COVERAGE };
|
|
|
|
|
|
|
|
|
|
private:
|
2021-11-26 23:55:36 +01:00
|
|
|
const enum en m_e;
|
2021-05-22 19:50:55 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// cppcheck-suppress noExplicitConstructor
|
2022-09-23 11:57:01 +02:00
|
|
|
constexpr VCtorType(en _e)
|
2021-05-22 19:50:55 +02:00
|
|
|
: m_e{_e} {}
|
|
|
|
|
bool isClass() const { return m_e == CLASS; }
|
|
|
|
|
bool isCoverage() const { return m_e == COVERAGE; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class V3CCtorsBuilder final {
|
|
|
|
|
AstNodeModule* const m_modp; // Current module/class
|
|
|
|
|
const string m_basename;
|
|
|
|
|
const VCtorType m_type; // What kind of constructor are we creating
|
|
|
|
|
std::list<AstCFunc*> m_newFunctions; // Created functions, latest is at back
|
2020-11-25 03:28:04 +01:00
|
|
|
int m_numStmts = 0; // Number of statements output
|
2021-05-22 19:50:55 +02:00
|
|
|
|
|
|
|
|
AstCFunc* makeNewFunc() {
|
|
|
|
|
const int funcNum = m_newFunctions.size();
|
|
|
|
|
const string funcName = m_basename + "_" + cvtToStr(funcNum);
|
2021-07-12 00:42:01 +02:00
|
|
|
AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"};
|
2021-06-13 15:33:11 +02:00
|
|
|
funcp->isStatic(false);
|
|
|
|
|
funcp->isLoose(!m_type.isClass());
|
2025-01-25 18:13:25 +01:00
|
|
|
funcp->keepIfEmpty(true); // TODO relax
|
2021-05-22 19:50:55 +02:00
|
|
|
funcp->declPrivate(true);
|
|
|
|
|
funcp->slow(!m_type.isClass()); // Only classes construct on fast path
|
|
|
|
|
string preventUnusedStmt;
|
|
|
|
|
if (m_type.isClass()) {
|
2025-08-26 04:05:40 +02:00
|
|
|
funcp->argTypes(EmitCUtil::symClassVar());
|
2025-09-26 14:25:47 +02:00
|
|
|
preventUnusedStmt = "(void)vlSymsp; // Prevent unused variable warning";
|
2021-05-22 19:50:55 +02:00
|
|
|
} else if (m_type.isCoverage()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
funcp->argTypes("bool first");
|
2025-09-26 14:25:47 +02:00
|
|
|
preventUnusedStmt = "(void)first; // Prevent unused variable warning";
|
2021-06-13 15:33:11 +02:00
|
|
|
}
|
|
|
|
|
if (!preventUnusedStmt.empty()) {
|
2021-07-12 00:42:01 +02:00
|
|
|
funcp->addStmtsp(new AstCStmt{m_modp->fileline(), preventUnusedStmt});
|
2021-05-22 19:50:55 +02:00
|
|
|
}
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(funcp);
|
2021-05-22 19:50:55 +02:00
|
|
|
m_numStmts = 0;
|
|
|
|
|
return funcp;
|
|
|
|
|
}
|
2016-05-12 13:19:02 +02:00
|
|
|
|
2016-10-23 20:27:57 +02:00
|
|
|
public:
|
|
|
|
|
void add(AstNode* nodep) {
|
2021-05-22 19:50:55 +02:00
|
|
|
if (v3Global.opt.outputSplitCFuncs() && m_numStmts > v3Global.opt.outputSplitCFuncs()) {
|
|
|
|
|
m_newFunctions.push_back(makeNewFunc());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-05-22 19:50:55 +02:00
|
|
|
m_newFunctions.back()->addStmtsp(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_numStmts += 1;
|
2016-05-12 13:19:02 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-22 19:50:55 +02:00
|
|
|
V3CCtorsBuilder(AstNodeModule* nodep, const string& basename, VCtorType type)
|
2021-07-12 00:42:01 +02:00
|
|
|
: m_modp{nodep}
|
2021-05-22 19:50:55 +02:00
|
|
|
, m_basename{basename}
|
2021-07-12 00:42:01 +02:00
|
|
|
, m_type{type} {
|
2021-05-22 19:50:55 +02:00
|
|
|
// Note: The constructor is always called, even if empty, so we must always create at least
|
|
|
|
|
// one.
|
|
|
|
|
m_newFunctions.push_back(makeNewFunc());
|
2016-05-12 13:19:02 +02:00
|
|
|
}
|
2021-05-22 19:50:55 +02:00
|
|
|
|
|
|
|
|
~V3CCtorsBuilder() {
|
|
|
|
|
if (m_newFunctions.size() == 1) {
|
|
|
|
|
// No split was necessary, rename the one function to the basename
|
|
|
|
|
m_newFunctions.front()->name(m_basename);
|
|
|
|
|
} else {
|
|
|
|
|
// Split was necessary, create root function and call all others from that
|
|
|
|
|
AstCFunc* const rootFuncp = makeNewFunc();
|
|
|
|
|
rootFuncp->name(m_basename);
|
|
|
|
|
for (AstCFunc* const funcp : m_newFunctions) {
|
2021-07-12 00:42:01 +02:00
|
|
|
AstCCall* const callp = new AstCCall{m_modp->fileline(), funcp};
|
2022-10-12 11:19:21 +02:00
|
|
|
callp->dtypeSetVoid();
|
2021-05-22 19:50:55 +02:00
|
|
|
if (m_type.isClass()) {
|
|
|
|
|
callp->argTypes("vlSymsp");
|
2021-06-13 15:33:11 +02:00
|
|
|
} else {
|
|
|
|
|
if (m_type.isCoverage()) callp->argTypes("first");
|
2025-04-07 05:13:24 +02:00
|
|
|
callp->selfPointer(VSelfPointerText{VSelfPointerText::This{}});
|
2021-05-22 19:50:55 +02:00
|
|
|
}
|
2022-10-12 11:19:21 +02:00
|
|
|
rootFuncp->addStmtsp(callp->makeStmt());
|
2021-05-22 19:50:55 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-08 18:01:39 +01:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2017-10-14 20:51:57 +02:00
|
|
|
private:
|
2021-05-22 19:50:55 +02:00
|
|
|
VL_UNCOPYABLE(V3CCtorsBuilder);
|
2016-05-12 13:19:02 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2022-12-23 16:12:11 +01:00
|
|
|
// Link state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class CCtorsVisitor final : public VNVisitor {
|
|
|
|
|
// NODE STATE
|
|
|
|
|
|
2025-04-27 20:17:24 +02:00
|
|
|
// STATE - for current visit position (use VL_RESTORER)
|
2022-12-23 16:12:11 +01:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2022-12-23 16:51:52 +01:00
|
|
|
AstCFunc* m_cfuncp = nullptr; // Current function
|
2022-12-23 16:12:11 +01:00
|
|
|
V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset
|
|
|
|
|
|
2025-03-29 03:04:29 +01:00
|
|
|
// METHODS
|
2025-10-14 12:23:23 +02:00
|
|
|
static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VSystemCSectionType type) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
const auto txtAndFlp = EmitCBaseVisitorConst::scSection(modp, type);
|
|
|
|
|
if (txtAndFlp.first.empty()) return;
|
|
|
|
|
// Use an AstCStmtUser as this is from user input
|
|
|
|
|
AstCStmtUser* const cstmtp = new AstCStmtUser{txtAndFlp.second};
|
|
|
|
|
cstmtp->add(txtAndFlp.first);
|
|
|
|
|
cfuncp->addStmtsp(cstmtp);
|
2025-03-29 03:04:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-12-23 16:12:11 +01:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
|
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
VL_RESTORER(m_varResetp);
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
V3CCtorsBuilder var_reset{nodep, "_ctor_var_reset",
|
|
|
|
|
VN_IS(nodep, Class) ? VCtorType::CLASS : VCtorType::MODULE};
|
2023-04-09 03:36:32 +02:00
|
|
|
// cppcheck-suppress danglingLifetime
|
2022-12-23 16:12:11 +01:00
|
|
|
m_varResetp = &var_reset;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
|
|
|
|
|
if (v3Global.opt.coverage()) {
|
|
|
|
|
V3CCtorsBuilder configure_coverage{nodep, "_configure_coverage", VCtorType::COVERAGE};
|
|
|
|
|
for (AstNode* np = nodep->stmtsp(); np; np = np->nextp()) {
|
2025-08-04 14:29:56 +02:00
|
|
|
if (AstNodeCoverDecl* const coverp = VN_CAST(np, NodeCoverDecl)) {
|
2023-03-04 01:26:15 +01:00
|
|
|
// ... else we don't have a static VlSym to be able to coverage insert
|
|
|
|
|
UASSERT_OBJ(!VN_IS(nodep, Class), coverp,
|
2025-08-04 14:29:56 +02:00
|
|
|
"NodeCoverDecl should be in class's package, not class itself");
|
2022-12-23 16:12:11 +01:00
|
|
|
np = coverp->backp();
|
|
|
|
|
configure_coverage.add(coverp->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (AstClass* const classp = VN_CAST(nodep, Class)) {
|
|
|
|
|
AstCFunc* const funcp = new AstCFunc{classp->fileline(), "~", nullptr, ""};
|
|
|
|
|
funcp->isDestructor(true);
|
|
|
|
|
funcp->isStatic(false);
|
|
|
|
|
// If can be referred to by base pointer, need virtual delete
|
|
|
|
|
funcp->isVirtual(classp->isExtended());
|
|
|
|
|
funcp->slow(false);
|
2025-10-14 12:23:23 +02:00
|
|
|
insertSc(funcp, classp, VSystemCSectionType::DTOR);
|
2022-12-23 16:12:11 +01:00
|
|
|
classp->addStmtsp(funcp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstCFunc* nodep) override {
|
|
|
|
|
VL_RESTORER(m_varResetp);
|
2022-12-23 16:51:52 +01:00
|
|
|
VL_RESTORER(m_cfuncp);
|
2022-12-23 16:12:11 +01:00
|
|
|
m_varResetp = nullptr;
|
2022-12-23 16:51:52 +01:00
|
|
|
m_cfuncp = nodep;
|
2022-12-23 16:12:11 +01:00
|
|
|
iterateChildren(nodep);
|
2025-10-14 12:23:23 +02:00
|
|
|
if (nodep->name() == "new") insertSc(nodep, m_modp, VSystemCSectionType::CTOR);
|
2022-12-23 16:12:11 +01:00
|
|
|
}
|
|
|
|
|
void visit(AstVar* nodep) override {
|
2025-02-26 04:48:53 +01:00
|
|
|
if (nodep->needsCReset()) {
|
2022-12-23 16:51:52 +01:00
|
|
|
if (m_varResetp) {
|
2025-02-26 04:48:53 +01:00
|
|
|
AstVarRef* const vrefp = new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE};
|
|
|
|
|
m_varResetp->add(new AstCReset{nodep->fileline(), vrefp, true});
|
2022-12-23 16:51:52 +01:00
|
|
|
} else if (m_cfuncp) {
|
2025-02-26 04:48:53 +01:00
|
|
|
AstVarRef* const vrefp = new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE};
|
|
|
|
|
nodep->addNextHere(new AstCReset{nodep->fileline(), vrefp, true});
|
2022-12-23 16:51:52 +01:00
|
|
|
}
|
2022-12-23 16:12:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void visit(AstConstPool*) override {}
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2023-03-19 00:28:48 +01:00
|
|
|
explicit CCtorsVisitor(AstNode* nodep) { iterate(nodep); }
|
2022-12-23 16:12:11 +01:00
|
|
|
~CCtorsVisitor() override = default;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2017-11-06 03:47:55 +01:00
|
|
|
void V3CCtors::evalAsserts() {
|
2021-07-12 00:42:01 +02:00
|
|
|
AstNodeModule* const modp = v3Global.rootp()->modulesp(); // Top module wrapper
|
|
|
|
|
AstCFunc* const funcp
|
|
|
|
|
= new AstCFunc{modp->fileline(), "_eval_debug_assertions", nullptr, "void"};
|
2017-11-06 03:47:55 +01:00
|
|
|
funcp->declPrivate(true);
|
|
|
|
|
funcp->isStatic(false);
|
2021-06-13 15:33:11 +02:00
|
|
|
funcp->isLoose(true);
|
2025-01-25 18:13:25 +01:00
|
|
|
funcp->keepIfEmpty(true);
|
2017-11-06 03:47:55 +01:00
|
|
|
funcp->slow(false);
|
|
|
|
|
funcp->ifdef("VL_DEBUG");
|
2022-09-15 20:43:56 +02:00
|
|
|
modp->addStmtsp(funcp);
|
2017-11-06 03:47:55 +01:00
|
|
|
for (AstNode* np = modp->stmtsp(); np; np = np->nextp()) {
|
2021-07-12 00:42:01 +02:00
|
|
|
if (AstVar* const varp = VN_CAST(np, Var)) {
|
2018-10-27 23:29:00 +02:00
|
|
|
if (varp->isPrimaryInish() && !varp->isSc()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstBasicDType* const basicp
|
|
|
|
|
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int storedWidth = basicp->widthAlignBytes() * 8;
|
|
|
|
|
const int lastWordWidth = varp->width() % storedWidth;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (lastWordWidth != 0) {
|
|
|
|
|
// if (signal & CONST(upper_non_clean_mask)) { fail; }
|
2021-06-13 15:33:11 +02:00
|
|
|
AstVarRef* const vrefp
|
2021-07-12 00:42:01 +02:00
|
|
|
= new AstVarRef{varp->fileline(), varp, VAccess::READ};
|
2025-04-07 05:13:24 +02:00
|
|
|
vrefp->selfPointer(VSelfPointerText{VSelfPointerText::This{}});
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* newp = vrefp;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (varp->isWide()) {
|
2021-07-12 00:42:01 +02:00
|
|
|
newp = new AstWordSel{
|
2019-05-19 22:13:13 +02:00
|
|
|
varp->fileline(), newp,
|
2021-07-12 00:42:01 +02:00
|
|
|
new AstConst(varp->fileline(), varp->widthWords() - 1)};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-07-12 00:42:01 +02:00
|
|
|
const uint64_t value = VL_MASK_Q(storedWidth) & ~VL_MASK_Q(lastWordWidth);
|
|
|
|
|
newp = new AstAnd{varp->fileline(), newp,
|
2022-11-20 21:06:49 +01:00
|
|
|
new AstConst(varp->fileline(), AstConst::WidthedValue{},
|
2021-07-12 00:42:01 +02:00
|
|
|
storedWidth, value)};
|
|
|
|
|
AstNodeIf* const ifp = new AstIf{
|
2020-04-15 13:58:34 +02:00
|
|
|
varp->fileline(), newp,
|
2021-07-12 00:42:01 +02:00
|
|
|
new AstCStmt{varp->fileline(), "Verilated::overWidthError(\""
|
|
|
|
|
+ varp->prettyName() + "\");"}};
|
2019-10-05 13:54:14 +02:00
|
|
|
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
2022-11-13 21:33:11 +01:00
|
|
|
funcp->addStmtsp(ifp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-06 03:47:55 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-23 20:27:57 +02:00
|
|
|
void V3CCtors::cctorsAll() {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2017-11-06 03:47:55 +01:00
|
|
|
evalAsserts();
|
2022-12-23 16:12:11 +01:00
|
|
|
{ CCtorsVisitor{v3Global.rootp()}; }
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTreeEitherLevel() >= 3);
|
2016-05-12 13:19:02 +02:00
|
|
|
}
|