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: Emit C++ for tree
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2026-01-01 13:22:09 +01:00
|
|
|
// Copyright 2003-2026 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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2021-03-04 03:57:07 +01:00
|
|
|
#ifndef VERILATOR_V3EMITCBASE_H_
|
|
|
|
|
#define VERILATOR_V3EMITCBASE_H_
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Ast.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3File.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <cmath>
|
2022-08-05 11:56:57 +02:00
|
|
|
#include <cstdarg>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2021-07-22 16:53:42 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Set user4p in all CFunc and Var to point to the containing AstNodeModule
|
|
|
|
|
|
|
|
|
|
class EmitCParentModule final {
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// AstFunc::user4p() AstNodeModule* Parent module pointer
|
|
|
|
|
// AstVar::user4p() AstNodeModule* Parent module pointer
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser4InUse user4InUse;
|
2021-07-22 16:53:42 +02:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
EmitCParentModule();
|
|
|
|
|
VL_UNCOPYABLE(EmitCParentModule);
|
|
|
|
|
|
2023-03-17 00:48:56 +01:00
|
|
|
static const AstNodeModule* get(const AstNode* nodep) VL_MT_STABLE {
|
2021-10-22 16:15:42 +02:00
|
|
|
return VN_AS(nodep->user4p(), NodeModule);
|
2021-07-22 16:53:42 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2025-08-26 04:05:40 +02:00
|
|
|
// EmitC-related utility functions
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2025-08-26 04:05:40 +02:00
|
|
|
class EmitCUtil final {
|
2023-03-18 17:05:29 +01:00
|
|
|
public:
|
|
|
|
|
static string voidSelfAssign(const AstNodeModule* modp) {
|
|
|
|
|
const string className = prefixNameProtect(modp);
|
|
|
|
|
return className + "* const __restrict vlSelf VL_ATTR_UNUSED = static_cast<" + className
|
|
|
|
|
+ "*>(voidSelf);\n";
|
|
|
|
|
}
|
2023-10-18 14:08:15 +02:00
|
|
|
static string pchClassName() VL_MT_STABLE { return v3Global.opt.prefix() + "__pch"; }
|
2023-04-20 13:02:31 +02:00
|
|
|
static string symClassName() VL_MT_STABLE {
|
2023-03-18 17:05:29 +01:00
|
|
|
return v3Global.opt.prefix() + "_" + VIdProtect::protect("_Syms");
|
|
|
|
|
}
|
|
|
|
|
static string symClassVar() { return symClassName() + "* __restrict vlSymsp"; }
|
|
|
|
|
static string symClassAssign() {
|
|
|
|
|
return symClassName() + "* const __restrict vlSymsp VL_ATTR_UNUSED = vlSelf->vlSymsp;\n";
|
|
|
|
|
}
|
2023-10-18 14:08:15 +02:00
|
|
|
static string topClassName() VL_MT_SAFE { // Return name of top wrapper module
|
|
|
|
|
return v3Global.opt.prefix();
|
|
|
|
|
}
|
2024-07-14 19:57:16 +02:00
|
|
|
// Return C++ class name for a module/class object
|
|
|
|
|
static string prefixNameProtect(const AstNode* nodep) VL_MT_STABLE;
|
2024-09-06 14:04:26 +02:00
|
|
|
static bool isAnonOk(const AstVar* varp) VL_MT_STABLE {
|
2024-02-08 14:44:27 +01:00
|
|
|
AstNodeDType* const dtp = varp->dtypep()->skipRefp();
|
2023-03-18 17:05:29 +01:00
|
|
|
return v3Global.opt.compLimitMembers() != 0 // Enabled
|
|
|
|
|
&& !varp->isStatic() // Not a static variable
|
|
|
|
|
&& !varp->isSc() // Aggregates can't be anon
|
2024-02-08 14:44:27 +01:00
|
|
|
&& !VN_IS(dtp, SampleQueueDType) // Aggregates can't be anon
|
|
|
|
|
&& !(VN_IS(dtp, NodeUOrStructDType) && !VN_CAST(dtp, NodeUOrStructDType)->packed())
|
2023-03-18 17:05:29 +01:00
|
|
|
&& (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon
|
|
|
|
|
}
|
|
|
|
|
static bool isConstPoolMod(const AstNode* modp) {
|
|
|
|
|
return modp == v3Global.rootp()->constPoolp()->modp();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-26 04:05:40 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Base Visitor class -- holds output file pointer
|
|
|
|
|
|
|
|
|
|
class EmitCBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2025-11-09 18:41:13 +01:00
|
|
|
V3OutCFile* m_ofp = nullptr; // File handle to emit to
|
|
|
|
|
AstCFile* m_cfilep = nullptr; // Current AstCFile being emitted
|
|
|
|
|
std::vector<AstCFile*> m_newCfileps; // AstCFiles created
|
|
|
|
|
size_t m_splitSize = 0; // Complexity of this file
|
|
|
|
|
const size_t m_splitLimit = v3Global.opt.outputSplit()
|
|
|
|
|
? static_cast<size_t>(v3Global.opt.outputSplit())
|
|
|
|
|
: std::numeric_limits<size_t>::max();
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2024-10-01 03:42:36 +02:00
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// Create new AstCFile and open it for writing
|
|
|
|
|
void openNewOutputFile(const std::string& baseName, bool slow, bool support, bool source,
|
|
|
|
|
const char* const descriptionp) {
|
|
|
|
|
std::string fileName = v3Global.opt.makeDir() + "/" + baseName;
|
|
|
|
|
if (source) {
|
|
|
|
|
if (slow) fileName += "__Slow";
|
|
|
|
|
fileName += ".cpp";
|
|
|
|
|
} else {
|
|
|
|
|
fileName += ".h";
|
|
|
|
|
}
|
|
|
|
|
AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), fileName};
|
|
|
|
|
cfilep->slow(slow);
|
|
|
|
|
cfilep->source(source);
|
|
|
|
|
cfilep->support(support);
|
|
|
|
|
m_newCfileps.emplace_back(cfilep);
|
|
|
|
|
openOutputFile(cfilep, descriptionp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// Create new source AstCFile and open it for writing
|
|
|
|
|
void openNewOutputSourceFile(const std::string& baseName, bool slow, bool support,
|
|
|
|
|
const char* descriptionp) {
|
|
|
|
|
V3Stats::addStatSum(V3Stats::STAT_CPP_FILES, 1);
|
|
|
|
|
openNewOutputFile(baseName, slow, support, true, descriptionp);
|
|
|
|
|
}
|
2024-10-01 03:42:36 +02:00
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// Create new header AstCFile and open it for writing
|
|
|
|
|
void openNewOutputHeaderFile(const std::string& baseName, const char* descriptionp) {
|
|
|
|
|
openNewOutputFile(baseName, false, false, false, descriptionp);
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// Open exisitng AstCFile for writing
|
|
|
|
|
void openOutputFile(AstCFile* cfilep, const char* descriptionp) {
|
|
|
|
|
UASSERT(!m_ofp, "Output file is already open");
|
|
|
|
|
m_cfilep = cfilep;
|
|
|
|
|
m_splitSize = 0;
|
|
|
|
|
if (v3Global.opt.lintOnly()) {
|
|
|
|
|
// Unfortunately we have some lint checks in EmitCImp, so we can't
|
|
|
|
|
// just skip processing. TODO: Move them to an earlier stage.
|
|
|
|
|
m_ofp = new V3OutCFile{VL_DEV_NULL};
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_ofp = new V3OutCFile{cfilep->name()};
|
|
|
|
|
putsHeader();
|
|
|
|
|
// Emit description
|
|
|
|
|
m_ofp->putsNoTracking("// DESCR" /* keep this comment */ "IPTION: Verilator output: ");
|
|
|
|
|
m_ofp->putsNoTracking(descriptionp);
|
|
|
|
|
m_ofp->putsNoTracking("\n");
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// Close current output file. Sets ofp() and outFileNodep() to nullptr.
|
2024-10-01 03:42:36 +02:00
|
|
|
void closeOutputFile() {
|
2025-11-09 18:41:13 +01:00
|
|
|
UASSERT(m_ofp, "No currently open output file");
|
2024-10-01 03:42:36 +02:00
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
2025-11-09 18:41:13 +01:00
|
|
|
m_cfilep->complexityScore(m_splitSize);
|
|
|
|
|
m_cfilep = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<AstCFile*> getAndClearCfileps() {
|
|
|
|
|
UASSERT(!m_ofp, "Output file is open");
|
|
|
|
|
return std::move(m_newCfileps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void splitSizeInc(size_t count) { m_splitSize += count; }
|
|
|
|
|
void splitSizeInc(const AstNode* nodep) {
|
|
|
|
|
splitSizeInc(static_cast<size_t>(nodep->nodeCount()));
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
2025-11-09 18:41:13 +01:00
|
|
|
bool splitNeeded(size_t splitLimit) const { return m_splitSize >= splitLimit; }
|
|
|
|
|
bool splitNeeded() const { return splitNeeded(m_splitLimit); }
|
|
|
|
|
|
|
|
|
|
// Returns pointer to current output file object.
|
|
|
|
|
V3OutCFile* ofp() const VL_MT_SAFE { return m_ofp; }
|
2024-10-01 03:42:36 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void puts(const string& str) { ofp()->puts(str); }
|
2024-01-25 03:51:47 +01:00
|
|
|
void putns(const AstNode* nodep, const string& str) { ofp()->putns(nodep, str); }
|
2023-04-11 13:25:10 +02:00
|
|
|
void putsHeader() { ofp()->putsHeader(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
void putbs(const string& str) { ofp()->putbs(str); }
|
2024-01-25 03:51:47 +01:00
|
|
|
void putnbs(const AstNode* nodep, const string& str) { ofp()->putnbs(nodep, str); }
|
|
|
|
|
void putsDecoration(const AstNode* nodep, const string& str) {
|
|
|
|
|
if (v3Global.opt.decoration()) putns(nodep, str);
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2009-05-08 19:16:19 +02:00
|
|
|
void putsQuoted(const string& str) { ofp()->putsQuoted(str); }
|
2021-06-13 15:33:11 +02:00
|
|
|
void ensureNewLine() { ofp()->ensureNewLine(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
bool optSystemC() { return v3Global.opt.systemC(); }
|
2023-03-18 17:05:29 +01:00
|
|
|
static string protect(const string& name) VL_MT_SAFE { return VIdProtect::protect(name); }
|
2021-06-24 17:58:30 +02:00
|
|
|
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr);
|
|
|
|
|
string cFuncArgs(const AstCFunc* nodep);
|
|
|
|
|
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
|
|
|
|
|
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
|
Add V3VariableOrder pass
A separate V3VariableOrder pass is now used to order module variables
before Emit. All variables are now ordered together, without
consideration for whether they are ports, signals form the design, or
additional internal variables added by Verilator (which used to be
ordered and emitted as separate groups in Emit). For single threaded
models, this is performance neutral. For multi-threaded models, the
MTask affinity based sorting was slightly modified, so variables with no
MTask affinity are emitted last, otherwise the MTask affinity sets are
sorted using the TSP sorter as before, but again, ports, signals, and
internal variables are not differentiated. This yields a 2%+ speedup for
the multithreaded model on OpenTitan.
2021-06-29 18:57:07 +02:00
|
|
|
void emitVarDecl(const AstVar* nodep, bool asRef = false);
|
2024-07-06 14:12:53 +02:00
|
|
|
void emitVarAccessors(const AstVar* nodep);
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_Callable>
|
|
|
|
|
static void forModCUse(const AstNodeModule* modp, VUseType useType, T_Callable action) {
|
2025-08-19 23:02:10 +02:00
|
|
|
for (const AstNode* itemp = modp->stmtsp(); itemp; itemp = itemp->nextp()) {
|
|
|
|
|
if (const AstCUse* const usep = VN_CAST(itemp, CUse)) {
|
2023-09-12 17:59:57 +02:00
|
|
|
if (usep->useType().containsAny(useType)) {
|
|
|
|
|
if (usep->useType().containsAny(VUseType::INT_INCLUDE)) {
|
2025-08-26 04:05:40 +02:00
|
|
|
action("#include \"" + EmitCUtil::prefixNameProtect(usep) + ".h\"\n");
|
2023-09-12 17:59:57 +02:00
|
|
|
continue; // Forward declaration is not necessary
|
2023-05-22 14:29:01 +02:00
|
|
|
}
|
2023-09-12 17:59:57 +02:00
|
|
|
if (usep->useType().containsAny(VUseType::INT_FWD_CLASS)) {
|
2025-08-26 04:05:40 +02:00
|
|
|
action("class " + EmitCUtil::prefixNameProtect(usep) + ";\n");
|
2023-05-22 14:29:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
void emitModCUse(const AstNodeModule* modp, VUseType useType);
|
2025-10-14 12:23:23 +02:00
|
|
|
void emitSystemCSection(const AstNodeModule* modp, VSystemCSectionType type);
|
|
|
|
|
static std::pair<string, FileLine*> scSection(const AstNodeModule* modp,
|
|
|
|
|
VSystemCSectionType type);
|
2021-06-13 15:33:11 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// CONSTRUCTORS
|
2023-03-18 17:17:25 +01:00
|
|
|
EmitCBaseVisitorConst() = default;
|
2025-11-09 18:41:13 +01:00
|
|
|
~EmitCBaseVisitorConst() override {
|
|
|
|
|
UASSERT(!m_ofp, "Did not close output file");
|
|
|
|
|
// Add files cerated to the netlist (unless retrieved before destruction)
|
|
|
|
|
for (AstCFile* const cfilep : m_newCfileps) v3Global.rootp()->addFilesp(cfilep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
#endif // guard
|