2021-07-07 20:16:40 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Emit C++ for tree
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2021-07-07 20:16:40 +02:00
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstMT.h"
|
|
|
|
|
|
2021-07-07 20:16:40 +02:00
|
|
|
#include "V3EmitC.h"
|
2021-07-14 14:01:03 +02:00
|
|
|
#include "V3EmitCConstInit.h"
|
2024-10-01 03:42:36 +02:00
|
|
|
#include "V3File.h"
|
2024-03-03 16:23:04 +01:00
|
|
|
#include "V3UniqueNames.h"
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
#include <algorithm>
|
2023-05-29 18:08:39 +02:00
|
|
|
#include <cstdint>
|
2023-05-22 14:29:01 +02:00
|
|
|
#include <set>
|
2023-05-29 18:08:39 +02:00
|
|
|
#include <string>
|
2021-07-07 20:16:40 +02:00
|
|
|
#include <vector>
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2021-07-07 20:16:40 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Internal EmitC implementation
|
|
|
|
|
|
2021-07-14 14:01:03 +02:00
|
|
|
class EmitCHeader final : public EmitCConstInit {
|
2024-03-03 16:23:04 +01:00
|
|
|
V3UniqueNames m_names;
|
2021-07-07 20:16:40 +02:00
|
|
|
// METHODS
|
2021-07-14 14:01:03 +02:00
|
|
|
|
2021-07-07 20:16:40 +02:00
|
|
|
void decorateFirst(bool& first, const string& str) {
|
|
|
|
|
if (first) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putsDecoration(nullptr, str);
|
2021-07-07 20:16:40 +02:00
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void emitCellDecls(const AstNodeModule* modp) {
|
|
|
|
|
bool first = true;
|
|
|
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstCell* const cellp = VN_CAST(nodep, Cell)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
decorateFirst(first, "// CELLS\n");
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(cellp,
|
|
|
|
|
prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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()) {
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
2021-07-22 19:59:03 +02:00
|
|
|
if (varp->isIO() || varp->isSignal() || varp->isClassMember() || varp->isTemp()) {
|
2021-07-07 20:16:40 +02:00
|
|
|
const bool anon = isAnonOk(varp);
|
|
|
|
|
if (anon != lastAnon) emitCurrentList();
|
|
|
|
|
lastAnon = anon;
|
|
|
|
|
varList.emplace_back(varp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit final batch
|
|
|
|
|
emitCurrentList();
|
|
|
|
|
}
|
|
|
|
|
void emitInternalVarDecls(const AstNodeModule* modp) {
|
2023-04-01 16:50:27 +02:00
|
|
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
|
|
|
|
if (classp->needRNG()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putsDecoration(nullptr, "\n// INTERNAL VARIABLES\n");
|
2023-04-01 16:50:27 +02:00
|
|
|
puts("VlRNG __Vm_rng;\n");
|
|
|
|
|
}
|
|
|
|
|
} else { // not class
|
2024-01-25 03:51:47 +01:00
|
|
|
putsDecoration(nullptr, "\n// INTERNAL VARIABLES\n");
|
2022-03-27 16:05:24 +02:00
|
|
|
puts(symClassName() + "* const vlSymsp;\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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)) {
|
2021-07-22 19:59:03 +02:00
|
|
|
if (varp->isParam()) {
|
2021-07-07 20:16:40 +02:00
|
|
|
decorateFirst(first, "\n// PARAMETERS\n");
|
|
|
|
|
UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?");
|
2021-07-22 19:59:03 +02:00
|
|
|
// Only C++ LiteralTypes can be constexpr
|
|
|
|
|
const bool canBeConstexpr = varp->dtypep()->isLiteralType();
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(varp, "static ");
|
2021-07-22 19:59:03 +02:00
|
|
|
puts(canBeConstexpr ? "constexpr " : "const ");
|
|
|
|
|
puts(varp->dtypep()->cType(varp->nameProtect(), false, false));
|
|
|
|
|
if (canBeConstexpr) {
|
|
|
|
|
puts(" = ");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(varp->valuep());
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2021-07-22 19:59:03 +02:00
|
|
|
puts(";\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void emitCtorDtorDecls(const AstNodeModule* modp) {
|
|
|
|
|
if (!VN_IS(modp, Class)) { // Classes use CFuncs with isConstructor/isDestructor
|
|
|
|
|
const string& name = prefixNameProtect(modp);
|
2024-01-25 03:51:47 +01:00
|
|
|
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");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void emitInternalMethodDecls(const AstNodeModule* modp) {
|
|
|
|
|
bool first = true;
|
|
|
|
|
const string section = "\n// INTERNAL METHODS\n";
|
|
|
|
|
|
|
|
|
|
if (!VN_IS(modp, Class)) {
|
|
|
|
|
decorateFirst(first, section);
|
2022-03-27 16:05:24 +02:00
|
|
|
puts("void " + protect("__Vconfigure") + "(bool first);\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
2023-03-04 01:26:15 +01:00
|
|
|
if (v3Global.opt.coverage() && !VN_IS(modp, Class)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
decorateFirst(first, section);
|
|
|
|
|
puts("void __vlCoverInsert(");
|
2023-10-21 14:53:56 +02:00
|
|
|
puts(v3Global.opt.threads() > 1 ? "std::atomic<uint32_t>" : "uint32_t");
|
2021-07-07 20:16:40 +02:00
|
|
|
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()) {
|
2021-10-22 16:15:42 +02:00
|
|
|
const AstTypedef* const tdefp = VN_CAST(nodep, Typedef);
|
2021-07-07 20:16:40 +02:00
|
|
|
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) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putsDecoration(tdefp,
|
|
|
|
|
"// enum " + tdefp->nameProtect() + " ignored: Too wide for C++\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(tdefp, "enum " + tdefp->name() + " {\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
for (const AstEnumItem* itemp = edtypep->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
2022-02-11 01:27:28 +01:00
|
|
|
if (const AstConst* const constp = VN_CAST(itemp->valuep(), Const)) {
|
|
|
|
|
if (constp->num().isFourState()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(itemp, "// " + itemp->nameProtect() + " is four-state\n");
|
2022-02-11 01:27:28 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(itemp, itemp->nameProtect());
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(" = ");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(itemp->valuep());
|
2021-07-07 20:16:40 +02:00
|
|
|
if (VN_IS(itemp->nextp(), EnumItem)) puts(",");
|
|
|
|
|
puts("\n");
|
|
|
|
|
}
|
|
|
|
|
puts("};\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-28 04:41:12 +01:00
|
|
|
void emitStructDecl(const AstNodeModule* modp, AstNodeUOrStructDType* sdtypep,
|
|
|
|
|
std::set<AstNodeUOrStructDType*>& emitted) {
|
2022-12-21 01:22:42 +01:00
|
|
|
if (emitted.count(sdtypep) > 0) return;
|
|
|
|
|
emitted.insert(sdtypep);
|
2024-11-29 14:10:51 +01:00
|
|
|
for (AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
2022-12-21 01:22:42 +01:00
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
2023-05-07 03:41:17 +02:00
|
|
|
AstNodeUOrStructDType* const subp = itemp->getChildStructp();
|
2024-03-03 16:23:04 +01:00
|
|
|
if (subp && (!subp->packed() || sdtypep->packed())) {
|
2022-12-21 01:22:42 +01:00
|
|
|
// Recurse if it belongs to the current module
|
|
|
|
|
if (subp->classOrPackagep() == modp) {
|
|
|
|
|
emitStructDecl(modp, subp, emitted);
|
|
|
|
|
puts("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-03 16:23:04 +01:00
|
|
|
if (sdtypep->packed()) {
|
|
|
|
|
emitPackedUOrSBody(sdtypep);
|
|
|
|
|
} else {
|
|
|
|
|
emitUnpackedUOrSBody(sdtypep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-24 23:51:51 +01:00
|
|
|
enum class AttributeType { Width, Dimension };
|
|
|
|
|
// Get member attribute based on type
|
|
|
|
|
int getNodeAttribute(const AstMemberDType* itemp, AttributeType type) {
|
|
|
|
|
const bool isArrayType
|
|
|
|
|
= VN_IS(itemp->dtypep(), UnpackArrayDType) || VN_IS(itemp->dtypep(), DynArrayDType)
|
|
|
|
|
|| VN_IS(itemp->dtypep(), QueueDType) || VN_IS(itemp->dtypep(), AssocArrayDType);
|
|
|
|
|
switch (type) {
|
|
|
|
|
case AttributeType::Width: {
|
|
|
|
|
if (isArrayType) {
|
|
|
|
|
// For arrays, get innermost element width
|
|
|
|
|
AstNodeDType* dtype = itemp->dtypep();
|
|
|
|
|
while (dtype->subDTypep()) dtype = dtype->subDTypep();
|
|
|
|
|
return dtype->width();
|
|
|
|
|
}
|
|
|
|
|
return itemp->width();
|
|
|
|
|
}
|
|
|
|
|
case AttributeType::Dimension: {
|
|
|
|
|
// Return array dimension or 0 for non-arrays
|
|
|
|
|
return isArrayType ? itemp->dtypep()->dimensions(true).second : 0;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
template <AttributeType T>
|
|
|
|
|
void emitMemberVector(const AstNodeUOrStructDType* sdtypep) {
|
|
|
|
|
puts("return {");
|
|
|
|
|
bool needComma = false;
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
|
|
|
if (!itemp->isConstrainedRand()) continue;
|
|
|
|
|
// Comma handling: add before element except first
|
|
|
|
|
if (needComma) puts(",\n");
|
|
|
|
|
putns(itemp, std::to_string(getNodeAttribute(itemp, T)));
|
|
|
|
|
needComma = true;
|
|
|
|
|
}
|
|
|
|
|
puts("};\n}\n");
|
|
|
|
|
}
|
2024-03-03 16:23:04 +01:00
|
|
|
void emitUnpackedUOrSBody(AstNodeUOrStructDType* sdtypep) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union"
|
2023-03-18 17:05:29 +01:00
|
|
|
puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n");
|
2022-12-21 01:22:42 +01:00
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false));
|
2022-12-21 01:22:42 +01:00
|
|
|
puts(";\n");
|
|
|
|
|
}
|
2025-04-16 13:08:46 +02:00
|
|
|
|
2025-02-03 17:56:00 +01:00
|
|
|
// Three helper functions for struct constrained randomization:
|
|
|
|
|
// - memberNames: Get member names
|
|
|
|
|
// - getMembers: Access member references
|
|
|
|
|
// - memberIndices: Retrieve member indices
|
2025-02-24 23:51:51 +01:00
|
|
|
// - memberWidth: Retrieve member width
|
|
|
|
|
// - memberDimension: Retrieve member dimension
|
2025-02-03 17:56:00 +01:00
|
|
|
if (sdtypep->isConstrainedRand()) {
|
2025-04-16 13:08:46 +02:00
|
|
|
bool needComma = false;
|
2025-02-03 17:56:00 +01:00
|
|
|
putns(sdtypep, "\nstd::vector<std::string> memberNames(void) const {\n");
|
|
|
|
|
puts("return {");
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
2025-04-16 13:08:46 +02:00
|
|
|
if (!itemp->isConstrainedRand()) continue;
|
|
|
|
|
if (needComma) puts(",\n");
|
|
|
|
|
putns(itemp, "\"" + itemp->shortName() + "\"");
|
|
|
|
|
needComma = true;
|
2025-02-03 17:56:00 +01:00
|
|
|
}
|
|
|
|
|
puts("};\n}\n");
|
|
|
|
|
|
2025-02-24 23:51:51 +01:00
|
|
|
putns(sdtypep, "\nstd::vector<int> memberWidth(void) const {\n");
|
|
|
|
|
emitMemberVector<AttributeType::Width>(sdtypep);
|
|
|
|
|
|
|
|
|
|
putns(sdtypep, "\nstd::vector<int> memberDimension(void) const {\n");
|
|
|
|
|
emitMemberVector<AttributeType::Dimension>(sdtypep);
|
|
|
|
|
|
2025-04-16 13:08:46 +02:00
|
|
|
needComma = false;
|
2025-02-03 17:56:00 +01:00
|
|
|
putns(sdtypep, "\nauto memberIndices(void) const {\n");
|
|
|
|
|
puts("return std::index_sequence_for<");
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
2025-04-16 13:08:46 +02:00
|
|
|
if (!itemp->isConstrainedRand()) continue;
|
|
|
|
|
if (needComma) puts(",\n");
|
|
|
|
|
putns(itemp, itemp->dtypep()->cType("", false, false));
|
|
|
|
|
needComma = true;
|
2025-02-03 17:56:00 +01:00
|
|
|
}
|
|
|
|
|
puts(">{};\n}\n");
|
2023-05-29 18:08:39 +02:00
|
|
|
|
2025-04-16 13:08:46 +02:00
|
|
|
needComma = false;
|
2025-02-03 17:56:00 +01:00
|
|
|
putns(sdtypep, "\ntemplate <typename T>");
|
|
|
|
|
putns(sdtypep, "\nauto getMembers(T& obj) {\n");
|
|
|
|
|
puts("return std::tie(");
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
2025-04-16 13:08:46 +02:00
|
|
|
if (!itemp->isConstrainedRand()) continue;
|
|
|
|
|
if (needComma) puts(",\n");
|
|
|
|
|
putns(itemp, "obj." + itemp->nameProtect());
|
|
|
|
|
needComma = true;
|
2025-02-03 17:56:00 +01:00
|
|
|
}
|
|
|
|
|
puts(");\n}\n");
|
|
|
|
|
}
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(sdtypep, "\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep)
|
|
|
|
|
+ "& rhs) const {\n");
|
2023-05-29 18:08:39 +02:00
|
|
|
puts("return ");
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
|
|
|
if (itemp != sdtypep->membersp()) puts("\n && ");
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(itemp, itemp->nameProtect() + " == " + "rhs." + itemp->nameProtect());
|
2023-05-29 18:08:39 +02:00
|
|
|
}
|
|
|
|
|
puts(";\n");
|
|
|
|
|
puts("}\n");
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(sdtypep, "bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep)
|
|
|
|
|
+ "& rhs) const {\n");
|
2023-05-29 18:08:39 +02:00
|
|
|
puts("return !(*this == rhs);\n}\n");
|
2025-01-11 18:07:52 +01:00
|
|
|
putns(sdtypep, "\nbool operator<(const " + EmitCBase::prefixNameProtect(sdtypep)
|
|
|
|
|
+ "& rhs) const {\n");
|
|
|
|
|
puts("return ");
|
|
|
|
|
puts("std::tie(");
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
|
|
|
if (itemp != sdtypep->membersp()) puts(", ");
|
|
|
|
|
putns(itemp, itemp->nameProtect());
|
|
|
|
|
}
|
|
|
|
|
puts(")\n < std::tie(");
|
|
|
|
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
|
|
|
|
if (itemp != sdtypep->membersp()) puts(", ");
|
|
|
|
|
putns(itemp, "rhs." + itemp->nameProtect());
|
|
|
|
|
}
|
|
|
|
|
puts(");\n");
|
|
|
|
|
puts("}\n");
|
2022-12-21 01:22:42 +01:00
|
|
|
puts("};\n");
|
2025-02-03 17:56:00 +01:00
|
|
|
puts("template <>\n");
|
|
|
|
|
putns(sdtypep, "struct VlIsCustomStruct<" + EmitCBase::prefixNameProtect(sdtypep)
|
|
|
|
|
+ "> : public std::true_type {};\n");
|
2022-12-21 01:22:42 +01:00
|
|
|
}
|
2024-03-03 16:23:04 +01:00
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
}
|
2022-12-21 01:22:42 +01:00
|
|
|
void emitStructs(const AstNodeModule* modp) {
|
|
|
|
|
// Track structs that've been emitted already
|
2023-01-28 04:41:12 +01:00
|
|
|
std::set<AstNodeUOrStructDType*> emitted;
|
2022-12-21 01:22:42 +01:00
|
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
const AstTypedef* const tdefp = VN_CAST(nodep, Typedef);
|
|
|
|
|
if (!tdefp) continue;
|
2023-01-28 04:41:12 +01:00
|
|
|
AstNodeUOrStructDType* const sdtypep
|
|
|
|
|
= VN_CAST(tdefp->dtypep()->skipRefToEnump(), NodeUOrStructDType);
|
2022-12-21 01:22:42 +01:00
|
|
|
if (!sdtypep) continue;
|
|
|
|
|
emitStructDecl(modp, sdtypep, emitted);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
void emitFuncDecls(const AstNodeModule* modp, bool inClassBody) {
|
|
|
|
|
std::vector<const AstCFunc*> funcsp;
|
|
|
|
|
|
|
|
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstCFunc* const funcp = VN_CAST(nodep, CFunc)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
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
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
2024-01-12 02:19:48 +01:00
|
|
|
for (const AstClassExtends* extp = classp->extendsp(); extp;
|
|
|
|
|
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(extp, "#include \"" + prefixNameProtect(extp->classp()->classOrPackagep())
|
|
|
|
|
+ ".h\"\n");
|
2022-07-30 16:01:25 +02:00
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Forward declarations required by this AstNodeModule
|
|
|
|
|
puts("\nclass " + symClassName() + ";\n");
|
|
|
|
|
|
|
|
|
|
// From `systemc_header
|
2022-01-02 19:56:40 +01:00
|
|
|
emitTextSection(modp, VNType::atScHdr);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2022-12-21 01:22:42 +01:00
|
|
|
emitStructs(modp);
|
|
|
|
|
|
2021-07-07 20:16:40 +02:00
|
|
|
// Open class body {{{
|
2024-01-25 03:51:47 +01:00
|
|
|
puts("\n");
|
|
|
|
|
putns(modp, "class ");
|
2023-05-03 03:21:10 +02:00
|
|
|
if (!VN_IS(modp, Class)) puts("alignas(VL_CACHE_LINE_BYTES) ");
|
2022-07-12 12:41:15 +02:00
|
|
|
puts(prefixNameProtect(modp));
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
2024-01-24 01:36:11 +01:00
|
|
|
const string virtpub = classp->useVirtualPublic() ? "virtual public " : "public ";
|
|
|
|
|
puts(" : " + virtpub);
|
2021-07-07 20:16:40 +02:00
|
|
|
if (classp->extendsp()) {
|
2024-01-24 01:36:11 +01:00
|
|
|
for (const AstClassExtends* extp = classp->extendsp(); extp;
|
|
|
|
|
extp = VN_AS(extp->nextp(), ClassExtends)) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(extp, prefixNameProtect(extp->classp()));
|
2024-01-24 01:36:11 +01:00
|
|
|
if (extp->nextp()) puts(", " + virtpub);
|
|
|
|
|
}
|
2022-09-29 00:54:18 +02:00
|
|
|
} else {
|
|
|
|
|
puts("VlClass");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
2022-07-12 12:41:15 +02:00
|
|
|
puts(" final : public VerilatedModule");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
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
|
2022-01-02 19:56:40 +01:00
|
|
|
emitTextSection(modp, VNType::atScInt);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
// Close class body
|
2023-05-03 03:21:10 +02:00
|
|
|
puts("};\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
// }}}
|
|
|
|
|
|
|
|
|
|
// Emit out of class function declarations
|
|
|
|
|
puts("\n");
|
|
|
|
|
emitFuncDecls(modp, /* inClassBody: */ false);
|
2025-03-29 03:24:39 +01:00
|
|
|
emitTextSection(modp, VNType::atScHdrPost);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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";
|
2024-10-01 03:42:36 +02:00
|
|
|
AstCFile* const cfilep = newCFile(filename, /* slow: */ false, /* source: */ false);
|
|
|
|
|
V3OutCFile* const ofilep
|
|
|
|
|
= v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename};
|
|
|
|
|
|
|
|
|
|
setOutputFile(ofilep, cfilep);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
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();
|
2021-07-24 16:00:33 +02:00
|
|
|
puts("#include \"verilated.h\"\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
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");
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (v3Global.usesTiming()) puts("#include \"verilated_timing.h\"\n");
|
2024-05-17 16:38:34 +02:00
|
|
|
if (v3Global.useRandomizeMethods()) puts("#include \"verilated_random.h\"\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2023-05-22 14:29:01 +02:00
|
|
|
std::set<string> cuse_set;
|
|
|
|
|
auto add_to_cuse_set = [&](string s) { cuse_set.insert(s); };
|
|
|
|
|
|
2023-09-12 17:59:57 +02:00
|
|
|
forModCUse(modp, VUseType::INT_FWD_CLASS | VUseType::INT_INCLUDE, add_to_cuse_set);
|
2023-05-22 14:29:01 +02:00
|
|
|
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
|
2023-09-12 17:59:57 +02:00
|
|
|
forModCUse(packagep->classp(), VUseType::INT_INCLUDE | VUseType::INT_FWD_CLASS,
|
|
|
|
|
add_to_cuse_set);
|
2023-05-22 14:29:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const string& s : cuse_set) puts(s);
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
2021-07-07 20:16:40 +02:00
|
|
|
emitAll(modp);
|
|
|
|
|
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
// Put the non-static class implementation in same h file for speed
|
|
|
|
|
emitAll(packagep->classp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofp()->putsEndGuard();
|
|
|
|
|
|
|
|
|
|
// Close output file
|
2024-10-01 03:42:36 +02:00
|
|
|
closeOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~EmitCHeader() override = default;
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
public:
|
2022-11-20 19:11:01 +01:00
|
|
|
static void main(const AstNodeModule* modp) { EmitCHeader emitCHeader{modp}; }
|
2021-07-07 20:16:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// 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
|
2021-10-22 16:15:42 +02:00
|
|
|
EmitCHeader::main(VN_AS(nodep, NodeModule));
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|