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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2023-01-01 16:18:39 +01:00
|
|
|
// Copyright 2003-2023 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
|
|
|
|
|
|
|
|
#include "V3CCtors.h"
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3EmitCBase.h"
|
2023-10-18 04:50:27 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
2022-08-05 11:56:57 +02:00
|
|
|
|
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 {
|
2016-05-12 13:19:02 +02:00
|
|
|
private:
|
2021-05-22 19:50:55 +02:00
|
|
|
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());
|
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()) {
|
2023-03-18 17:05:29 +01:00
|
|
|
funcp->argTypes(EmitCBase::symClassVar());
|
2021-06-13 15:33:11 +02:00
|
|
|
preventUnusedStmt = "if (false && vlSymsp) {} // Prevent unused\n";
|
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");
|
|
|
|
|
preventUnusedStmt = "if (false && first) {} // Prevent unused\n";
|
|
|
|
|
}
|
|
|
|
|
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");
|
2023-09-08 13:34:35 +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 {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
// VISITs
|
|
|
|
|
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()) {
|
|
|
|
|
if (AstCoverDecl* const coverp = VN_CAST(np, CoverDecl)) {
|
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,
|
|
|
|
|
"CoverDecl 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);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstVar* nodep) override {
|
2022-12-23 16:51:52 +01:00
|
|
|
if (!nodep->isIfaceParent() && !nodep->isIfaceRef() && !nodep->noReset()
|
|
|
|
|
&& !nodep->isParam() && !nodep->isStatementTemp()
|
2022-12-23 16:12:11 +01:00
|
|
|
&& !(nodep->basicp()
|
|
|
|
|
&& (nodep->basicp()->isEvent() || nodep->basicp()->isTriggerVec()))) {
|
2022-12-23 16:51:52 +01:00
|
|
|
if (m_varResetp) {
|
|
|
|
|
const auto vrefp = new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE};
|
|
|
|
|
m_varResetp->add(new AstCReset{nodep->fileline(), vrefp});
|
|
|
|
|
} else if (m_cfuncp) {
|
|
|
|
|
const auto vrefp = new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE};
|
|
|
|
|
nodep->addNextHere(new AstCReset{nodep->fileline(), vrefp});
|
|
|
|
|
}
|
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);
|
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};
|
2023-09-08 13:34:35 +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() {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2017-11-06 03:47:55 +01:00
|
|
|
evalAsserts();
|
2022-12-23 16:12:11 +01:00
|
|
|
{ CCtorsVisitor{v3Global.rootp()}; }
|
2023-05-04 00:04:10 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("cctors", 0, dumpTreeLevel() >= 3);
|
2016-05-12 13:19:02 +02:00
|
|
|
}
|