mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
566 lines
24 KiB
C++
566 lines
24 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Emit C++ for tree
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// Copyright 2003-2024 by Wilson Snyder. This program is free software; you
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
// Version 2.0.
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "V3PchAstMT.h"
|
|
|
|
#include "V3EmitC.h"
|
|
#include "V3EmitCConstInit.h"
|
|
#include "V3File.h"
|
|
#include "V3UniqueNames.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
//######################################################################
|
|
// Internal EmitC implementation
|
|
|
|
class EmitCHeader final : public EmitCConstInit {
|
|
V3UniqueNames m_names;
|
|
// METHODS
|
|
|
|
void decorateFirst(bool& first, const string& str) {
|
|
if (first) {
|
|
putsDecoration(nullptr, str);
|
|
first = false;
|
|
}
|
|
}
|
|
void emitCellDecls(const AstNodeModule* modp) {
|
|
bool first = true;
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
if (const AstCell* const cellp = VN_CAST(nodep, Cell)) {
|
|
decorateFirst(first, "// CELLS\n");
|
|
putns(cellp,
|
|
prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n");
|
|
}
|
|
}
|
|
}
|
|
void emitDesignVarDecls(const AstNodeModule* modp) {
|
|
bool first = true;
|
|
std::vector<const AstVar*> varList;
|
|
bool lastAnon = false; // initial value is not important, but is used
|
|
|
|
const auto emitCurrentList = [this, &first, &varList, &lastAnon]() {
|
|
if (varList.empty()) return;
|
|
|
|
decorateFirst(first, "\n// DESIGN SPECIFIC STATE\n");
|
|
|
|
if (lastAnon) { // Output as anons
|
|
const int anonMembers = varList.size();
|
|
const int lim = v3Global.opt.compLimitMembers();
|
|
int anonL3s = 1;
|
|
int anonL2s = 1;
|
|
int anonL1s = 1;
|
|
if (anonMembers > (lim * lim * lim)) {
|
|
anonL3s = (anonMembers + (lim * lim * lim) - 1) / (lim * lim * lim);
|
|
anonL2s = lim;
|
|
anonL1s = lim;
|
|
} else if (anonMembers > (lim * lim)) {
|
|
anonL2s = (anonMembers + (lim * lim) - 1) / (lim * lim);
|
|
anonL1s = lim;
|
|
} else if (anonMembers > lim) {
|
|
anonL1s = (anonMembers + lim - 1) / lim;
|
|
}
|
|
if (anonL1s != 1)
|
|
puts("// Anonymous structures to workaround compiler member-count bugs\n");
|
|
auto it = varList.cbegin();
|
|
for (int l3 = 0; l3 < anonL3s && it != varList.cend(); ++l3) {
|
|
if (anonL3s != 1) puts("struct {\n");
|
|
for (int l2 = 0; l2 < anonL2s && it != varList.cend(); ++l2) {
|
|
if (anonL2s != 1) puts("struct {\n");
|
|
for (int l1 = 0; l1 < anonL1s && it != varList.cend(); ++l1) {
|
|
if (anonL1s != 1) puts("struct {\n");
|
|
for (int l0 = 0; l0 < lim && it != varList.cend(); ++l0) {
|
|
emitVarDecl(*it);
|
|
++it;
|
|
}
|
|
if (anonL1s != 1) puts("};\n");
|
|
}
|
|
if (anonL2s != 1) puts("};\n");
|
|
}
|
|
if (anonL3s != 1) puts("};\n");
|
|
}
|
|
// Leftovers, just in case off by one error somewhere above
|
|
for (; it != varList.cend(); ++it) emitVarDecl(*it);
|
|
} else { // Output as nonanons
|
|
for (const auto& pair : varList) emitVarDecl(pair);
|
|
}
|
|
|
|
varList.clear();
|
|
};
|
|
|
|
// Emit variables in consecutive anon and non-anon batches
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
|
if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) {
|
|
const bool anon = isAnonOk(varp);
|
|
if (anon != lastAnon) emitCurrentList();
|
|
lastAnon = anon;
|
|
varList.emplace_back(varp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit final batch
|
|
emitCurrentList();
|
|
}
|
|
void emitInternalVarDecls(const AstNodeModule* modp) {
|
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
|
if (classp->needRNG()) {
|
|
putsDecoration(nullptr, "\n// INTERNAL VARIABLES\n");
|
|
puts("VlRNG __Vm_rng;\n");
|
|
}
|
|
} else { // not class
|
|
putsDecoration(nullptr, "\n// INTERNAL VARIABLES\n");
|
|
puts(symClassName() + "* const vlSymsp;\n");
|
|
}
|
|
}
|
|
void emitParamDecls(const AstNodeModule* modp) {
|
|
bool first = true;
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
|
if (varp->isParam()) {
|
|
decorateFirst(first, "\n// PARAMETERS\n");
|
|
UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?");
|
|
// Only C++ LiteralTypes can be constexpr
|
|
const bool canBeConstexpr = varp->dtypep()->isLiteralType();
|
|
putns(varp, "static ");
|
|
puts(canBeConstexpr ? "constexpr " : "const ");
|
|
puts(varp->dtypep()->cType(varp->nameProtect(), false, false));
|
|
if (canBeConstexpr) {
|
|
puts(" = ");
|
|
iterateConst(varp->valuep());
|
|
}
|
|
puts(";\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void emitCtorDtorDecls(const AstNodeModule* modp) {
|
|
if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor
|
|
const string& name = prefixNameProtect(modp);
|
|
putsDecoration(nullptr, "\n// CONSTRUCTORS\n");
|
|
putns(modp, name + "(" + symClassName() + "* symsp, const char* v__name);\n");
|
|
putns(modp, "~" + name + "();\n");
|
|
putns(modp, "VL_UNCOPYABLE(" + name + ");\n");
|
|
}
|
|
}
|
|
void emitInternalMethodDecls(const AstNodeModule* modp) {
|
|
bool first = true;
|
|
const string section = "\n// INTERNAL METHODS\n";
|
|
|
|
if (!VN_IS(modp, Class)) {
|
|
decorateFirst(first, section);
|
|
puts("void " + protect("__Vconfigure") + "(bool first);\n");
|
|
}
|
|
|
|
if (v3Global.opt.coverage() && !VN_IS(modp, Class)) {
|
|
decorateFirst(first, section);
|
|
puts("void __vlCoverInsert(");
|
|
puts(v3Global.opt.threads() > 1 ? "std::atomic<uint32_t>" : "uint32_t");
|
|
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
|
|
puts("const char* hierp, const char* pagep, const char* commentp, const char* "
|
|
"linescovp);\n");
|
|
}
|
|
|
|
if (v3Global.opt.savable()) {
|
|
decorateFirst(first, section);
|
|
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
|
|
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
|
|
}
|
|
}
|
|
void emitEnums(const AstNodeModule* modp) {
|
|
bool first = true;
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
const AstTypedef* const tdefp = VN_CAST(nodep, Typedef);
|
|
if (!tdefp) continue;
|
|
if (!tdefp->attrPublic()) continue;
|
|
const AstEnumDType* const edtypep
|
|
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), EnumDType);
|
|
if (!edtypep) continue;
|
|
decorateFirst(first, "\n// ENUMS (that were declared public)\n");
|
|
if (edtypep->width() > 64) {
|
|
putsDecoration(tdefp,
|
|
"// enum " + tdefp->nameProtect() + " ignored: Too wide for C++\n");
|
|
} else {
|
|
putns(tdefp, "enum " + tdefp->name() + " {\n");
|
|
for (const AstEnumItem* itemp = edtypep->itemsp(); itemp;
|
|
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
|
if (const AstConst* const constp = VN_CAST(itemp->valuep(), Const)) {
|
|
if (constp->num().isFourState()) {
|
|
putns(itemp, "// " + itemp->nameProtect() + " is four-state\n");
|
|
continue;
|
|
}
|
|
}
|
|
putns(itemp, itemp->nameProtect());
|
|
puts(" = ");
|
|
iterateConst(itemp->valuep());
|
|
if (VN_IS(itemp->nextp(), EnumItem)) puts(",");
|
|
puts("\n");
|
|
}
|
|
puts("};\n");
|
|
}
|
|
}
|
|
}
|
|
void emitStructDecl(const AstNodeModule* modp, AstNodeUOrStructDType* sdtypep,
|
|
std::set<AstNodeUOrStructDType*>& emitted) {
|
|
if (emitted.count(sdtypep) > 0) return;
|
|
emitted.insert(sdtypep);
|
|
for (AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
|
if (subp && (!subp->packed() || sdtypep->packed())) {
|
|
// Recurse if it belongs to the current module
|
|
if (subp->classOrPackagep() == modp) {
|
|
emitStructDecl(modp, subp, emitted);
|
|
puts("\n");
|
|
}
|
|
}
|
|
}
|
|
if (sdtypep->packed()) {
|
|
emitPackedUOrSBody(sdtypep);
|
|
} else {
|
|
emitUnpackedUOrSBody(sdtypep);
|
|
}
|
|
}
|
|
void emitUnpackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
|
|
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
|
|
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false));
|
|
puts(";\n");
|
|
}
|
|
|
|
putns(sdtypep, "\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep)
|
|
+ "& rhs) const {\n");
|
|
puts("return ");
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
if (itemp != sdtypep->membersp()) puts("\n && ");
|
|
putns(itemp, itemp->nameProtect() + " == " + "rhs." + itemp->nameProtect());
|
|
}
|
|
puts(";\n");
|
|
puts("}\n");
|
|
putns(sdtypep, "bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep)
|
|
+ "& rhs) const {\n");
|
|
puts("return !(*this == rhs);\n}\n");
|
|
puts("};\n");
|
|
}
|
|
|
|
// getfunc: VL_ASSIGNSEL_XX(rbits, obits, off, lhsdata, rhsdata);
|
|
// !getfunc: VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, off);
|
|
void emitVlAssign(const AstNodeDType* const lhstype, const AstNodeDType* rhstype,
|
|
const std::string& off, const std::string& lhsdata,
|
|
const std::string& rhsdata, bool getfunc) {
|
|
puts(getfunc ? "VL_ASSIGNSEL_" : "VL_SELASSIGN_");
|
|
puts(lhstype->charIQWN());
|
|
puts(rhstype->charIQWN());
|
|
puts("(" + std::to_string(lhstype->width()) + ", "); // LHS width
|
|
if (getfunc) {
|
|
puts(std::to_string(rhstype->width()) + ", "); // Number of copy bits
|
|
puts(off + ", "); // LHS offset
|
|
} else {
|
|
// Number of copy bits. Use widthTototalBytes to
|
|
// make VL_SELASSIGN_XX clear upper unused bits for us.
|
|
// puts(std::to_string(lhstype->width()) + ", ");
|
|
puts(std::to_string(lhstype->widthTotalBytes() * 8) + ", ");
|
|
}
|
|
puts(lhsdata + ", "); // LHS data
|
|
puts(rhsdata); // RHS data
|
|
if (!getfunc) {
|
|
puts(", " + off); // RHS offset
|
|
}
|
|
puts(");\n");
|
|
}
|
|
|
|
// `retOrArg` should be prefixed by `&` or suffixed by `.data()` depending on its type
|
|
void emitPackedMember(const AstNodeDType* parentDtypep, const AstNodeDType* dtypep,
|
|
const std::string& fieldname, const std::string& offset, bool getfunc,
|
|
const std::string& retOrArg) {
|
|
dtypep = dtypep->skipRefp();
|
|
if (const auto* adtypep = VN_CAST(dtypep, PackArrayDType)) {
|
|
const std::string index = m_names.get("__Vi");
|
|
puts("for (int " + index + " = 0; " + index + " < "
|
|
+ std::to_string(adtypep->elementsConst()) + "; ++" + index + ") {\n");
|
|
|
|
const std::string offsetInLoop
|
|
= offset + " + " + index + " * " + std::to_string(adtypep->subDTypep()->width());
|
|
const std::string newName = fieldname + "[" + index + "]";
|
|
emitPackedMember(parentDtypep, adtypep->subDTypep(), newName, offsetInLoop, getfunc,
|
|
retOrArg);
|
|
puts("}\n");
|
|
} else if (VN_IS(dtypep, NodeUOrStructDType)) {
|
|
const std::string tmp = m_names.get("__Vtmp");
|
|
const std::string suffixName = dtypep->isWide() ? tmp + ".data()" : tmp;
|
|
if (getfunc) { // Emit `get` func;
|
|
// auto __tmp = field.get();
|
|
puts("auto " + tmp + " = " + fieldname + ".get();\n");
|
|
// VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata);
|
|
emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc);
|
|
} else { // Emit `set` func
|
|
const std::string tmptype = AstCDType::typeToHold(dtypep->width());
|
|
// type tmp;
|
|
puts(tmptype + " " + tmp + ";\n");
|
|
// VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset);
|
|
emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc);
|
|
// field.set(__tmp);
|
|
puts(fieldname + ".set(" + tmp + ");\n");
|
|
}
|
|
} else {
|
|
UASSERT_OBJ(VN_IS(dtypep, EnumDType) || VN_IS(dtypep, BasicDType), dtypep,
|
|
"Unsupported type in packed struct or union");
|
|
const std::string suffixName = dtypep->isWide() ? fieldname + ".data()" : fieldname;
|
|
if (getfunc) { // Emit `get` func;
|
|
// VL_ASSIGNSEL_XX(rbits, obits, lsb, lhsdata, rhsdata);
|
|
emitVlAssign(parentDtypep, dtypep, offset, retOrArg, suffixName, getfunc);
|
|
} else { // Emit `set` func
|
|
// VL_SELASSIGN_XX(rbits, obits, lhsdata, rhsdata, roffset);
|
|
emitVlAssign(dtypep, parentDtypep, offset, suffixName, retOrArg, getfunc);
|
|
}
|
|
}
|
|
}
|
|
void emitPackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
|
|
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
|
|
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
|
|
|
|
AstMemberDType* itemp;
|
|
AstMemberDType* lastItemp;
|
|
AstMemberDType* witemp = nullptr;
|
|
// LSB is first field in C, so loop backwards
|
|
for (lastItemp = sdtypep->membersp(); lastItemp && lastItemp->nextp();
|
|
lastItemp = VN_AS(lastItemp->nextp(), MemberDType)) {
|
|
if (lastItemp->width() == sdtypep->width()) witemp = lastItemp;
|
|
}
|
|
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
|
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false, true));
|
|
puts(";\n");
|
|
}
|
|
|
|
const std::string retArgName = m_names.get("__v");
|
|
const std::string suffixName = sdtypep->isWide() ? retArgName + ".data()" : retArgName;
|
|
const std::string retArgType = AstCDType::typeToHold(sdtypep->width());
|
|
|
|
// Emit `get` member function
|
|
puts(retArgType + " get() const {\n");
|
|
puts(retArgType + " " + retArgName + ";\n");
|
|
if (VN_IS(sdtypep, StructDType)) {
|
|
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
|
emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(),
|
|
std::to_string(itemp->lsb()), /*getfunc=*/true, suffixName);
|
|
}
|
|
} else {
|
|
// We only need to fill the widest field of union
|
|
emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(),
|
|
std::to_string(witemp->lsb()), /*getfunc=*/true, suffixName);
|
|
}
|
|
puts("return " + retArgName + ";\n");
|
|
puts("}\n");
|
|
|
|
// Emit `set` member function
|
|
puts("void set(const " + retArgType + "& " + retArgName + ") {\n");
|
|
if (VN_IS(sdtypep, StructDType)) {
|
|
for (itemp = lastItemp; itemp; itemp = VN_CAST(itemp->backp(), MemberDType)) {
|
|
emitPackedMember(sdtypep, itemp->dtypep(), itemp->nameProtect(),
|
|
std::to_string(itemp->lsb()), /*getfunc=*/false, suffixName);
|
|
}
|
|
} else {
|
|
// We only need to fill the widest field of union
|
|
emitPackedMember(sdtypep, witemp->dtypep(), witemp->nameProtect(),
|
|
std::to_string(witemp->lsb()), /*getfunc=*/false, suffixName);
|
|
}
|
|
|
|
puts("}\n");
|
|
|
|
puts("};\n");
|
|
m_names.reset();
|
|
}
|
|
void emitStructs(const AstNodeModule* modp) {
|
|
// Track structs that've been emitted already
|
|
std::set<AstNodeUOrStructDType*> emitted;
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
const AstTypedef* const tdefp = VN_CAST(nodep, Typedef);
|
|
if (!tdefp) continue;
|
|
AstNodeUOrStructDType* const sdtypep
|
|
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), NodeUOrStructDType);
|
|
if (!sdtypep) continue;
|
|
emitStructDecl(modp, sdtypep, emitted);
|
|
}
|
|
}
|
|
void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) {
|
|
std::vector<const AstCFunc*> funcsp;
|
|
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
if (const AstCFunc* const funcp = VN_CAST(nodep, CFunc)) {
|
|
if (funcp->dpiImportPrototype()) // Declared in __Dpi.h
|
|
continue;
|
|
if (funcp->dpiExportDispatcher()) // Declared in __Dpi.h
|
|
continue;
|
|
if (funcp->isMethod() != inClassBody) // Only methods go inside class
|
|
continue;
|
|
if (funcp->isMethod() && funcp->isLoose()) // Loose methods are declared lazily
|
|
continue;
|
|
funcsp.push_back(funcp);
|
|
}
|
|
}
|
|
|
|
std::stable_sort(funcsp.begin(), funcsp.end(), [](const AstNode* ap, const AstNode* bp) {
|
|
return ap->name() < bp->name();
|
|
});
|
|
|
|
for (const AstCFunc* const funcp : funcsp) {
|
|
if (inClassBody) ofp()->putsPrivate(funcp->declPrivate());
|
|
emitCFuncDecl(funcp, modp);
|
|
}
|
|
}
|
|
void emitAll(const AstNodeModule* modp) {
|
|
// Include files required by this AstNodeModule
|
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
|
for (const AstClassExtends* extp = classp->extendsp(); extp;
|
|
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
|
putns(extp, "#include \"" + prefixNameProtect(extp->classp()->classOrPackagep())
|
|
+ ".h\"\n");
|
|
}
|
|
}
|
|
|
|
// Forward declarations required by this AstNodeModule
|
|
puts("\nclass " + symClassName() + ";\n");
|
|
|
|
// From `systemc_header
|
|
emitTextSection(modp, VNType::atScHdr);
|
|
|
|
emitStructs(modp);
|
|
|
|
// Open class body {{{
|
|
puts("\n");
|
|
putns(modp, "class ");
|
|
if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) ");
|
|
puts(prefixNameProtect(modp));
|
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
|
const string virtpub = classp->useVirtualPublic() ? "virtual public " : "public ";
|
|
puts(" : " + virtpub);
|
|
if (classp->extendsp()) {
|
|
for (const AstClassExtends* extp = classp->extendsp(); extp;
|
|
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
|
putns(extp, prefixNameProtect(extp->classp()));
|
|
if (extp->nextp()) puts(", " + virtpub);
|
|
}
|
|
} else {
|
|
puts("VlClass");
|
|
}
|
|
} else {
|
|
puts(" final : public VerilatedModule");
|
|
}
|
|
puts(" {\n");
|
|
ofp()->resetPrivate();
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
// Emit all class body contents
|
|
emitCellDecls(modp);
|
|
emitEnums(modp);
|
|
emitDesignVarDecls(modp);
|
|
emitInternalVarDecls(modp);
|
|
emitParamDecls(modp);
|
|
emitCtorDtorDecls(modp);
|
|
emitInternalMethodDecls(modp);
|
|
emitFuncDecls(modp, /* inClassBody: */ true);
|
|
|
|
// From `systemc_interface
|
|
emitTextSection(modp, VNType::atScInt);
|
|
|
|
// Close class body
|
|
puts("};\n");
|
|
// }}}
|
|
|
|
// Emit out of class function declarations
|
|
puts("\n");
|
|
emitFuncDecls(modp, /* inClassBody: */ false);
|
|
}
|
|
|
|
explicit EmitCHeader(const AstNodeModule* modp) {
|
|
UINFO(5, " Emitting header for " << prefixNameProtect(modp) << endl);
|
|
|
|
// Open output file
|
|
const string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(modp) + ".h";
|
|
AstCFile* const cfilep = newCFile(filename, /* slow: */ false, /* source: */ false);
|
|
V3OutCFile* const ofilep
|
|
= v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename};
|
|
|
|
setOutputFile(ofilep, cfilep);
|
|
|
|
ofp()->putsHeader();
|
|
puts("// DESCRIPTION: Verilator output: Design internal header\n");
|
|
puts("// See " + topClassName() + ".h for the primary calling header\n");
|
|
|
|
ofp()->putsGuard();
|
|
|
|
// Include files
|
|
puts("\n");
|
|
ofp()->putsIntTopInclude();
|
|
puts("#include \"verilated.h\"\n");
|
|
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
|
|
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
|
|
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
|
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
|
|
if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n");
|
|
|
|
std::set<string> cuse_set;
|
|
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
|
|
|
|
forModCUse(modp, VUseType::INT_FWD_CLASS | VUseType::INT_INCLUDE, add_to_cuse_set);
|
|
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
|
|
forModCUse(packagep->classp(), VUseType::INT_INCLUDE | VUseType::INT_FWD_CLASS,
|
|
add_to_cuse_set);
|
|
}
|
|
|
|
for (const string& s : cuse_set) puts(s);
|
|
puts("\n");
|
|
|
|
emitAll(modp);
|
|
|
|
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
|
|
// Put the non-static class implementation in same h file for speed
|
|
emitAll(packagep->classp());
|
|
}
|
|
|
|
ofp()->putsEndGuard();
|
|
|
|
// Close output file
|
|
closeOutputFile();
|
|
}
|
|
~EmitCHeader() override = default;
|
|
|
|
public:
|
|
static void main(const AstNodeModule* modp) { EmitCHeader emitCHeader{modp}; }
|
|
};
|
|
|
|
//######################################################################
|
|
// EmitC class functions
|
|
|
|
void V3EmitC::emitcHeaders() {
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
// Process each module in turn
|
|
for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
|
|
if (VN_IS(nodep, Class)) continue; // Declared with the ClassPackage
|
|
EmitCHeader::main(VN_AS(nodep, NodeModule));
|
|
}
|
|
}
|