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"
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3EmitC.h"
|
2021-07-07 20:16:40 +02:00
|
|
|
#include "V3EmitCFunc.h"
|
2023-05-05 14:36:20 +02:00
|
|
|
#include "V3ThreadPool.h"
|
2021-07-14 23:37:37 +02:00
|
|
|
#include "V3UniqueNames.h"
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
2021-07-07 20:16:40 +02:00
|
|
|
#include <vector>
|
2021-07-14 23:37:37 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2021-07-07 20:16:40 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Internal EmitC implementation
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
class EmitCImp final : public EmitCFunc {
|
2021-07-07 20:16:40 +02:00
|
|
|
// MEMBERS
|
2025-11-09 18:41:13 +01:00
|
|
|
// Base module (For non-classes, same as m_modp. For classes, it's the ClassPackage.)
|
|
|
|
|
const AstNodeModule* const m_fileModp;
|
2021-07-07 20:16:40 +02:00
|
|
|
const bool m_slow; // Creating __Slow file
|
2025-11-09 18:41:13 +01:00
|
|
|
V3UniqueNames m_uniqueNames; // Generates unique file names
|
|
|
|
|
const std::string m_fileBaseName = EmitCUtil::prefixNameProtect(m_fileModp);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2025-11-09 18:41:13 +01:00
|
|
|
void openNextOutputFile(const std::string& fileName) {
|
|
|
|
|
openNewOutputSourceFile(fileName, m_slow, false, "Design implementation internals");
|
2025-08-26 04:05:40 +02:00
|
|
|
puts("// See " + EmitCUtil::topClassName() + ".h for the primary calling header\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("\n");
|
2025-08-26 04:05:40 +02:00
|
|
|
puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n");
|
2025-11-09 18:41:13 +01:00
|
|
|
emitSystemCSection(m_fileModp, VSystemCSectionType::IMP_HDR);
|
|
|
|
|
// Need to emit new lazy declarations
|
|
|
|
|
m_lazyDecls.reset();
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
void emitStaticVarDefns(const AstNodeModule* modp) {
|
|
|
|
|
// Emit static variable definitions
|
2025-08-26 04:05:40 +02:00
|
|
|
const string modName = EmitCUtil::prefixNameProtect(modp);
|
2021-07-14 23:37:37 +02:00
|
|
|
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-14 23:37:37 +02:00
|
|
|
if (varp->isStatic()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(varp, varp->vlArgType(true, false, false, modName));
|
2021-07-14 23:37:37 +02:00
|
|
|
puts(";\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
void emitParamDefns(const AstNodeModule* modp) {
|
2025-08-26 04:05:40 +02:00
|
|
|
const string modName = EmitCUtil::prefixNameProtect(modp);
|
2021-07-22 19:59:03 +02:00
|
|
|
bool first = true;
|
2021-07-07 20:16:40 +02:00
|
|
|
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->isParam()) {
|
|
|
|
|
if (first) {
|
|
|
|
|
puts("\n");
|
2024-01-25 03:51:47 +01:00
|
|
|
putsDecoration(modp, "// Parameter definitions for " + modName + "\n");
|
2021-07-22 19:59:03 +02:00
|
|
|
first = false;
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
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, canBeConstexpr ? "constexpr " : "const ");
|
2021-07-22 19:59:03 +02:00
|
|
|
const string scopedName = modName + "::" + varp->nameProtect();
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(varp, varp->dtypep()->cType(scopedName, false, false));
|
2021-07-22 19:59:03 +02:00
|
|
|
if (!canBeConstexpr) {
|
|
|
|
|
puts(" = ");
|
|
|
|
|
emitConstInit(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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-22 19:59:03 +02:00
|
|
|
if (!first) puts("\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
void emitCtorImp(const AstNodeModule* modp) {
|
2025-11-07 20:57:10 +01:00
|
|
|
const std::string modName = EmitCUtil::prefixNameProtect(modp);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"),
|
|
|
|
|
"(" + modName + "* vlSelf);");
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
2025-11-07 20:57:10 +01:00
|
|
|
const std::string ctorArgs = EmitCUtil::symClassName() + "* symsp, const char* namep";
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2025-11-07 20:57:10 +01:00
|
|
|
// The root module needs a proper constuctor, everything else uses a
|
|
|
|
|
// 'ctor' function in order to be able to split up constructors
|
|
|
|
|
if (modp->isTop()) {
|
|
|
|
|
putns(modp, modName + "::" + modName + "(" + ctorArgs + ")\n");
|
|
|
|
|
|
|
|
|
|
ofp()->indentInc();
|
|
|
|
|
const char* sepp = " : ";
|
|
|
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
|
|
|
|
if (const AstBasicDType* const dtypep
|
|
|
|
|
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
|
|
|
|
if (dtypep->keyword().isMTaskState()) {
|
|
|
|
|
puts(sepp);
|
|
|
|
|
putns(varp, varp->nameProtect());
|
|
|
|
|
puts("(");
|
|
|
|
|
iterateConst(varp->valuep());
|
|
|
|
|
puts(")\n");
|
|
|
|
|
} else if (varp->isIO() && varp->isSc()) {
|
|
|
|
|
puts(sepp);
|
|
|
|
|
putns(varp, varp->nameProtect());
|
|
|
|
|
puts("(");
|
|
|
|
|
putsQuoted(varp->nameProtect());
|
|
|
|
|
puts(")\n");
|
|
|
|
|
} else if (dtypep->isDelayScheduler()) {
|
|
|
|
|
puts(sepp);
|
|
|
|
|
putns(varp, varp->nameProtect());
|
|
|
|
|
puts("{*symsp->_vm_contextp__}\n");
|
|
|
|
|
} else {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
sepp = ", ";
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-07 20:57:10 +01:00
|
|
|
ofp()->indentDec();
|
|
|
|
|
puts(" {\n");
|
|
|
|
|
} else {
|
|
|
|
|
putns(modp, "void " + modName + "::ctor(" + ctorArgs + ") {\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-07 20:57:10 +01:00
|
|
|
puts("vlSymsp = symsp;\n");
|
|
|
|
|
if (modp->isTop()) {
|
|
|
|
|
puts("vlNamep = strdup(namep);\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("vlNamep = strdup(Verilated::catName(vlSymsp->name(), namep));\n");
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2024-01-25 03:51:47 +01:00
|
|
|
putsDecoration(modp, "// Reset structure values\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n");
|
2025-10-14 12:23:23 +02:00
|
|
|
emitSystemCSection(modp, VSystemCSectionType::CTOR);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
void emitConfigureImp(const AstNodeModule* modp) {
|
2025-08-26 04:05:40 +02:00
|
|
|
const string modName = EmitCUtil::prefixNameProtect(modp);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
if (v3Global.opt.coverage()) {
|
|
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit("void " + modName + "__", protect("_configure_coverage"),
|
|
|
|
|
"(" + modName + "* vlSelf, bool first);");
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-27 16:05:24 +02:00
|
|
|
puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(bool first) {\n");
|
2024-01-13 21:34:59 +01:00
|
|
|
puts("(void)first; // Prevent unused variable warning\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
if (v3Global.opt.coverage()) {
|
|
|
|
|
puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n");
|
|
|
|
|
}
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
void emitCoverageImp() {
|
2025-08-04 14:29:56 +02:00
|
|
|
// Rather than putting out VL_COVER_INSERT calls directly, we do it via this
|
|
|
|
|
// function. This gets around gcc slowness constructing all of the template
|
|
|
|
|
// arguments.
|
2021-07-07 20:16:40 +02:00
|
|
|
if (v3Global.opt.coverage()) {
|
|
|
|
|
puts("\n// Coverage\n");
|
2025-08-26 04:05:40 +02:00
|
|
|
puts("void " + EmitCUtil::prefixNameProtect(m_modp) + "::__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* "
|
2025-08-04 14:29:56 +02:00
|
|
|
"linescovp) {\n");
|
2023-10-21 14:53:56 +02:00
|
|
|
if (v3Global.opt.threads() > 1) {
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("assert(sizeof(uint32_t) == sizeof(std::atomic<uint32_t>));\n");
|
|
|
|
|
puts("uint32_t* count32p = reinterpret_cast<uint32_t*>(countp);\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("uint32_t* count32p = countp;\n");
|
|
|
|
|
}
|
|
|
|
|
// static doesn't need save-restore as is constant
|
|
|
|
|
puts("static uint32_t fake_zero_count = 0;\n");
|
2025-11-07 20:57:10 +01:00
|
|
|
puts("std::string fullhier = std::string{vlNamep} + hierp;\n");
|
2024-09-23 13:34:12 +02:00
|
|
|
puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
// Used for second++ instantiation of identical bin
|
|
|
|
|
puts("if (!enable) count32p = &fake_zero_count;\n");
|
|
|
|
|
puts("*count32p = 0;\n");
|
2025-11-07 20:57:10 +01:00
|
|
|
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), vlNamep, count32p,");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(" \"filename\",filenamep,");
|
|
|
|
|
puts(" \"lineno\",lineno,");
|
|
|
|
|
puts(" \"column\",column,\n");
|
2024-09-23 13:34:12 +02:00
|
|
|
puts("\"hier\",fullhier,");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(" \"page\",pagep,");
|
|
|
|
|
puts(" \"comment\",commentp,");
|
|
|
|
|
puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n");
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
2025-08-04 14:29:56 +02:00
|
|
|
if (v3Global.opt.coverageToggle()) {
|
|
|
|
|
puts("\n// Toggle Coverage\n");
|
2025-08-26 04:05:40 +02:00
|
|
|
puts("void " + EmitCUtil::prefixNameProtect(m_modp) + "::__vlCoverToggleInsert(");
|
2025-08-04 14:29:56 +02:00
|
|
|
puts("int begin, int end, bool ranged, ");
|
|
|
|
|
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) {\n");
|
|
|
|
|
if (v3Global.opt.threads() > 1) {
|
|
|
|
|
puts("assert(sizeof(uint32_t) == sizeof(std::atomic<uint32_t>));\n");
|
|
|
|
|
}
|
|
|
|
|
puts("int step = (end >= begin) ? 1 : -1;\n");
|
|
|
|
|
// range is inclusive
|
|
|
|
|
puts("for (int i = begin; i != end + step; i += step) {\n");
|
2025-08-20 13:31:04 +02:00
|
|
|
puts("for (int j = 0; j < 2; j++) {\n");
|
2025-08-04 14:29:56 +02:00
|
|
|
if (v3Global.opt.threads() > 1) {
|
|
|
|
|
puts("uint32_t* count32p = reinterpret_cast<uint32_t*>(countp);\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("uint32_t* count32p = countp;\n");
|
|
|
|
|
}
|
|
|
|
|
// static doesn't need save-restore as is constant
|
|
|
|
|
puts("static uint32_t fake_zero_count = 0;\n");
|
2025-11-07 20:57:10 +01:00
|
|
|
puts("std::string fullhier = std::string{vlNamep} + hierp;\n");
|
2025-08-04 14:29:56 +02:00
|
|
|
puts("if (!fullhier.empty() && fullhier[0] == '.') fullhier = fullhier.substr(1);\n");
|
|
|
|
|
puts("std::string commentWithIndex = commentp;\n");
|
|
|
|
|
puts("if (ranged) commentWithIndex += '[' + std::to_string(i) + ']';\n");
|
2025-08-20 13:31:04 +02:00
|
|
|
puts("commentWithIndex += j ? \":0->1\" : \":1->0\";\n");
|
2025-08-04 14:29:56 +02:00
|
|
|
// Used for second++ instantiation of identical bin
|
|
|
|
|
puts("if (!enable) count32p = &fake_zero_count;\n");
|
|
|
|
|
puts("*count32p = 0;\n");
|
2025-11-07 20:57:10 +01:00
|
|
|
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), vlNamep, count32p,");
|
2025-08-04 14:29:56 +02:00
|
|
|
puts(" \"filename\",filenamep,");
|
|
|
|
|
puts(" \"lineno\",lineno,");
|
|
|
|
|
puts(" \"column\",column,\n");
|
|
|
|
|
puts("\"hier\",fullhier,");
|
|
|
|
|
puts(" \"page\",pagep,");
|
|
|
|
|
puts(" \"comment\",commentWithIndex.c_str(),");
|
|
|
|
|
puts(" \"\", \"\");\n"); // linescov argument, but in toggle coverage it is always
|
|
|
|
|
// empty
|
|
|
|
|
puts("++countp;\n");
|
|
|
|
|
puts("}\n");
|
|
|
|
|
puts("}\n");
|
2025-08-20 13:31:04 +02:00
|
|
|
puts("}\n");
|
2025-08-04 14:29:56 +02:00
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
void emitDestructorImp(const AstNodeModule* modp) {
|
2025-11-07 20:57:10 +01:00
|
|
|
const std::string modName = EmitCUtil::prefixNameProtect(modp);
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("\n");
|
2025-11-07 20:57:10 +01:00
|
|
|
if (modp->isTop()) {
|
|
|
|
|
putns(modp, modName + "::~" + modName + "() {\n");
|
|
|
|
|
} else {
|
|
|
|
|
putns(modp, "void " + modName + "::dtor() {\n");
|
|
|
|
|
}
|
|
|
|
|
putns(modp, "VL_DO_DANGLING(free(const_cast<char*>(vlNamep)), vlNamep);\n");
|
2025-10-14 12:23:23 +02:00
|
|
|
emitSystemCSection(modp, VSystemCSectionType::DTOR);
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
void emitSavableImp(const AstNodeModule* modp) {
|
|
|
|
|
if (v3Global.opt.savable()) {
|
|
|
|
|
puts("\n// Savable\n");
|
|
|
|
|
for (int de = 0; de < 2; ++de) {
|
|
|
|
|
const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
|
|
|
|
|
const string funcname = de ? "__Vdeserialize" : "__Vserialize";
|
|
|
|
|
const string op = de ? ">>" : "<<";
|
|
|
|
|
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
2025-08-26 04:05:40 +02:00
|
|
|
putns(modp, "void " + EmitCUtil::prefixNameProtect(modp) + "::" + protect(funcname)
|
|
|
|
|
+ "(" + classname + "& os) {\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
// Place a computed checksum to ensure proper structure save/restore formatting
|
|
|
|
|
// OK if this hash includes some things we won't dump, since
|
|
|
|
|
// just looking for loading the wrong model
|
|
|
|
|
VHashSha256 hash;
|
|
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
hash.insert(varp->name());
|
|
|
|
|
hash.insert(varp->dtypep()->width());
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-27 21:27:40 +02:00
|
|
|
ofp()->printf("uint64_t __Vcheckval = 0x%" PRIx64 "ULL;\n",
|
|
|
|
|
static_cast<uint64_t>(hash.digestUInt64()));
|
2021-07-07 20:16:40 +02:00
|
|
|
if (de) {
|
|
|
|
|
puts("os.readAssert(__Vcheckval);\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("os << __Vcheckval;\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save context
|
|
|
|
|
// If multiple models save the same context we'll save it multiple
|
|
|
|
|
// times, but is harmless, and doing it otherwise would break
|
|
|
|
|
// backwards compatibility.
|
|
|
|
|
puts("os " + op + " vlSymsp->_vm_contextp__;\n");
|
|
|
|
|
|
|
|
|
|
// Save all members
|
|
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstVar* const varp = VN_CAST(nodep, Var)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (varp->isIO() && modp->isTop() && optSystemC()) {
|
|
|
|
|
// System C top I/O doesn't need loading, as the
|
|
|
|
|
// lower level subinst code does it.
|
|
|
|
|
} else if (varp->isParam()) {
|
|
|
|
|
} else if (varp->isStatic() && varp->isConst()) {
|
Support NBAs to arrays inside loops (#5092)
For NBAs that might execute a dynamic number of times in a single
evaluation (specifically: those that assign to array elements inside
loops), we introduce a new run-time VlNBACommitQueue data-structure
(currently a vector), which stores all pending updates and the necessary
info to reconstruct the LHS reference of the AstAssignDly at run-time.
All variables needing a commit queue has their corresponding unique
commit queue.
All NBAs to a variable that requires a commit queue go through the
commit queue. This is necessary to preserve update order in sequential
code, e.g.:
a[7] <= 10
for (int i = 1 ; i < 10; ++i) a[i] <= i;
a[2] <= 10
needs to end with array elements 1..9 being 1, 10, 3, 4, 5, 6, 7, 8, 9.
This enables supporting common forms of NBAs to arrays on the left hand
side of <= in non-suspendable/non-fork code. (Suspendable/fork
implementation is unclear to me so I left it unchanged, see #5084).
Any NBA that does not need a commit queue (i.e.: those that were
supported before), use the same scheme as before, and this patch should
have no effect on the generated code for those NBAs.
2024-05-03 13:45:49 +02:00
|
|
|
} else if (VN_IS(varp->dtypep(), NBACommitQueueDType)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
|
|
|
|
int vects = 0;
|
|
|
|
|
AstNodeDType* elementp = varp->dtypeSkipRefp();
|
|
|
|
|
for (AstUnpackArrayDType* arrayp = VN_CAST(elementp, UnpackArrayDType);
|
|
|
|
|
arrayp; arrayp = VN_CAST(elementp, UnpackArrayDType)) {
|
|
|
|
|
const int vecnum = vects++;
|
|
|
|
|
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
|
|
|
|
|
"Should have swapped msb & lsb earlier.");
|
2024-07-14 17:39:45 +02:00
|
|
|
const string ivar = "__Vi"s + cvtToStr(vecnum);
|
2022-10-16 00:47:10 +02:00
|
|
|
puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0));
|
|
|
|
|
puts("; " + ivar + " < " + cvtToStr(arrayp->elementsConst()));
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("; ++" + ivar + ") {\n");
|
|
|
|
|
elementp = arrayp->subDTypep()->skipRefp();
|
|
|
|
|
}
|
|
|
|
|
const AstBasicDType* const basicp = elementp->basicp();
|
|
|
|
|
// Do not save MTask state, only matters within an evaluation
|
|
|
|
|
if (basicp && basicp->keyword().isMTaskState()) continue;
|
|
|
|
|
// Want to detect types that are represented as arrays
|
|
|
|
|
// (i.e. packed types of more than 64 bits).
|
|
|
|
|
if (elementp->isWide()
|
2022-01-02 19:56:40 +01:00
|
|
|
&& !(basicp && basicp->keyword() == VBasicDTypeKwd::STRING)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
const int vecnum = vects++;
|
2024-07-14 17:39:45 +02:00
|
|
|
const string ivar = "__Vi"s + cvtToStr(vecnum);
|
2022-10-16 00:47:10 +02:00
|
|
|
puts("for (int __Vi" + cvtToStr(vecnum) + " = " + cvtToStr(0));
|
|
|
|
|
puts("; " + ivar + " < " + cvtToStr(elementp->widthWords()));
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("; ++" + ivar + ") {\n");
|
|
|
|
|
}
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(varp, "os" + op + varp->nameProtect());
|
2021-07-07 20:16:40 +02:00
|
|
|
for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]");
|
|
|
|
|
puts(";\n");
|
|
|
|
|
for (int v = 0; v < vects; ++v) puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-14 23:37:37 +02:00
|
|
|
// Predicate to check if we actually need to emit anything into the common implementation file.
|
|
|
|
|
// Used to avoid creating empty output files.
|
|
|
|
|
bool hasCommonImp(const AstNodeModule* modp) const {
|
|
|
|
|
// Nothing to emit if no module!
|
|
|
|
|
if (!modp) return false;
|
|
|
|
|
// We always need the slow file
|
|
|
|
|
if (m_slow) return true;
|
2025-10-14 12:23:23 +02:00
|
|
|
// The fast file is only required when we have `systemc_implementation nodes
|
|
|
|
|
if (v3Global.hasSystemCSections()) {
|
|
|
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (const AstSystemCSection* const ssp = VN_CAST(nodep, SystemCSection)) {
|
|
|
|
|
if (ssp->sectionType() == VSystemCSectionType::IMP) return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Actually emit common implementation contents for given AstNodeModule
|
|
|
|
|
void doCommonImp(const AstNodeModule* modp) {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (m_slow) {
|
2021-07-14 23:37:37 +02:00
|
|
|
emitStaticVarDefns(modp);
|
2021-07-07 20:16:40 +02:00
|
|
|
if (!VN_IS(modp, Class)) {
|
|
|
|
|
emitParamDefns(modp);
|
|
|
|
|
emitCtorImp(modp);
|
|
|
|
|
emitConfigureImp(modp);
|
|
|
|
|
emitDestructorImp(modp);
|
2023-03-04 01:26:15 +01:00
|
|
|
emitCoverageImp();
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
emitSavableImp(modp);
|
|
|
|
|
} else {
|
2021-07-14 23:37:37 +02:00
|
|
|
// From `systemc_implementation
|
2025-10-14 12:23:23 +02:00
|
|
|
emitSystemCSection(modp, VSystemCSectionType::IMP);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
void emitCommonImp(const AstNodeModule* modp) {
|
|
|
|
|
const AstClass* const classp
|
2021-10-22 16:15:42 +02:00
|
|
|
= VN_IS(modp, ClassPackage) ? VN_AS(modp, ClassPackage)->classp() : nullptr;
|
2021-07-14 23:37:37 +02:00
|
|
|
|
|
|
|
|
if (hasCommonImp(modp) || hasCommonImp(classp)) {
|
2025-11-09 18:41:13 +01:00
|
|
|
openNextOutputFile(m_fileBaseName);
|
2021-07-14 23:37:37 +02:00
|
|
|
|
|
|
|
|
doCommonImp(modp);
|
|
|
|
|
if (classp) {
|
|
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
m_modp = classp;
|
|
|
|
|
doCommonImp(classp);
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2024-10-01 03:42:36 +02:00
|
|
|
closeOutputFile();
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void emitCFuncImp(const AstNodeModule* modp) {
|
2025-10-17 02:34:09 +02:00
|
|
|
// Functions to be emitted here
|
|
|
|
|
std::vector<AstCFunc*> funcps;
|
2021-07-14 23:37:37 +02:00
|
|
|
|
2025-10-17 02:34:09 +02:00
|
|
|
const auto gather = [this, &funcps](const AstNodeModule* modp) {
|
2021-07-14 23:37:37 +02:00
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2025-10-17 02:34:09 +02:00
|
|
|
AstCFunc* const funcp = VN_CAST(nodep, CFunc);
|
|
|
|
|
if (!funcp) continue;
|
|
|
|
|
// TRACE_* and DPI handled elsewhere
|
|
|
|
|
if (funcp->isTrace()) continue;
|
|
|
|
|
if (funcp->dpiImportPrototype()) continue;
|
|
|
|
|
if (funcp->dpiExportDispatcher()) continue;
|
|
|
|
|
if (funcp->slow() != m_slow) continue;
|
|
|
|
|
funcps.push_back(funcp);
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
gather(modp);
|
2025-05-27 15:31:55 +02:00
|
|
|
VL_RESTORER(m_classOrPackage);
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
|
2025-05-27 15:31:55 +02:00
|
|
|
m_classOrPackage = packagep;
|
2021-07-14 23:37:37 +02:00
|
|
|
gather(packagep->classp());
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-17 02:34:09 +02:00
|
|
|
// Do not create empty files
|
|
|
|
|
if (funcps.empty()) return;
|
|
|
|
|
|
|
|
|
|
// Open output file
|
2025-11-09 18:41:13 +01:00
|
|
|
openNextOutputFile(m_uniqueNames.get(m_fileBaseName));
|
2025-10-17 02:34:09 +02:00
|
|
|
// Emit all functions
|
|
|
|
|
for (AstCFunc* const funcp : funcps) {
|
|
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
m_modp = EmitCParentModule::get(funcp);
|
|
|
|
|
iterateConst(funcp);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2025-10-17 02:34:09 +02:00
|
|
|
// Close output file
|
|
|
|
|
closeOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (splitNeeded()) {
|
|
|
|
|
// Splitting file, so using parallel build.
|
|
|
|
|
v3Global.useParallelBuild(true);
|
|
|
|
|
// Close old file
|
2024-10-01 03:42:36 +02:00
|
|
|
closeOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
// Open a new file
|
2025-11-09 18:41:13 +01:00
|
|
|
openNextOutputFile(m_uniqueNames.get(m_fileBaseName));
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmitCFunc::visit(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
explicit EmitCImp(const AstNodeModule* modp, bool slow)
|
2021-07-07 20:16:40 +02:00
|
|
|
: m_fileModp{modp}
|
2025-11-09 18:41:13 +01:00
|
|
|
, m_slow{slow} {
|
2025-08-26 04:05:40 +02:00
|
|
|
UINFO(5, " Emitting implementation of " << EmitCUtil::prefixNameProtect(modp));
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
m_modp = modp;
|
|
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
// Emit implementation of this module, if this is an AstClassPackage, then put the
|
2022-12-03 00:46:38 +01:00
|
|
|
// corresponding AstClass implementation in the same file as often optimizations are
|
2021-07-14 23:37:37 +02:00
|
|
|
// possible when both are seen by the compiler
|
|
|
|
|
// TODO: is the above comment still true?
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
// Emit implementations of common parts
|
|
|
|
|
emitCommonImp(modp);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
// Emit implementations of all AstCFunc
|
|
|
|
|
emitCFuncImp(modp);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~EmitCImp() override = default;
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
public:
|
2025-11-09 18:41:13 +01:00
|
|
|
static std::vector<AstCFile*> main(const AstNodeModule* modp, bool slow) VL_MT_STABLE {
|
|
|
|
|
EmitCImp emitCImp{modp, slow};
|
|
|
|
|
return emitCImp.getAndClearCfileps();
|
2022-09-13 18:15:34 +02:00
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Tracing routines
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// Trace type descriptors go in a different file as it needs to be written in
|
|
|
|
|
// parallel with the actual trace function source files
|
|
|
|
|
class EmitCTraceTypes final : public EmitCFunc {
|
2021-07-07 20:16:40 +02:00
|
|
|
// NODE STATE/TYPES
|
2022-09-12 18:00:41 +02:00
|
|
|
// None allowed to support threaded emitting
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// STATE
|
2021-07-07 20:16:40 +02:00
|
|
|
int m_enumNum = 0; // Enumeration number (whole netlist)
|
2022-09-12 18:00:41 +02:00
|
|
|
std::unordered_map<AstNode*, int> m_enumNumMap; // EnumDType to enumeration number
|
2023-12-19 17:07:06 +01:00
|
|
|
int m_traceTypeSubs = 0; // Number of trace type declaration sub-functions
|
2025-11-09 18:41:13 +01:00
|
|
|
V3UniqueNames m_uniqueNames; // Generates unique file names
|
|
|
|
|
const std::string m_fileBaseName = EmitCUtil::topClassName() + "_" + protect("_TraceDecls");
|
|
|
|
|
// This one uses CSplitTrace for file splitting, which is incorrect but historically accurates
|
|
|
|
|
const size_t m_splitLimit = v3Global.opt.outputSplitCTrace()
|
|
|
|
|
? static_cast<size_t>(v3Global.opt.outputSplitCTrace())
|
|
|
|
|
: std::numeric_limits<size_t>::max();
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
void openNextOutputFile() {
|
2025-11-09 18:41:13 +01:00
|
|
|
openNewOutputSourceFile(m_uniqueNames.get(m_fileBaseName), true, true,
|
|
|
|
|
"Tracing declarations");
|
|
|
|
|
puts("\n");
|
|
|
|
|
for (const std::string& base : v3Global.opt.traceSourceLangs()) {
|
2025-09-28 02:54:26 +02:00
|
|
|
puts("#include \"" + base + ".h\"\n");
|
2025-11-09 18:41:13 +01:00
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("\n");
|
2025-11-09 18:41:13 +01:00
|
|
|
puts("\nvoid " + EmitCUtil::prefixNameProtect(m_modp) + "__"
|
|
|
|
|
+ protect("traceDeclTypesSub" + std::to_string(m_traceTypeSubs++)) + "("
|
|
|
|
|
+ v3Global.opt.traceClassBase() + "* tracep) {\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
public:
|
|
|
|
|
// METHODS
|
|
|
|
|
int getEnumMapNum(AstEnumDType* nodep) {
|
|
|
|
|
int& enumNumr = m_enumNumMap[nodep];
|
|
|
|
|
if (!enumNumr) {
|
|
|
|
|
if (splitNeeded(m_splitLimit)) {
|
|
|
|
|
// Splitting file, so using parallel build.
|
|
|
|
|
v3Global.useParallelBuild(true);
|
|
|
|
|
puts("}\n");
|
|
|
|
|
closeOutputFile();
|
|
|
|
|
openNextOutputFile();
|
|
|
|
|
}
|
2023-12-19 17:07:06 +01:00
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
enumNumr = ++m_enumNum;
|
|
|
|
|
int nvals = 0;
|
|
|
|
|
puts("{\n");
|
|
|
|
|
putns(nodep, "const char* " + protect("__VenumItemNames") + "[]\n");
|
|
|
|
|
puts("= {");
|
|
|
|
|
for (AstEnumItem* itemp = nodep->itemsp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
|
|
|
|
if (++nvals > 1) puts(", ");
|
|
|
|
|
putbs("\"" + itemp->prettyName() + "\"");
|
|
|
|
|
}
|
|
|
|
|
puts("};\n");
|
|
|
|
|
nvals = 0;
|
|
|
|
|
puts("const char* " + protect("__VenumItemValues") + "[]\n");
|
|
|
|
|
puts("= {");
|
|
|
|
|
for (AstEnumItem* itemp = nodep->itemsp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
|
|
|
|
AstConst* const constp = VN_AS(itemp->valuep(), Const);
|
|
|
|
|
if (++nvals > 1) puts(", ");
|
|
|
|
|
putbs("\"" + constp->num().displayed(nodep, "%0b") + "\"");
|
|
|
|
|
}
|
|
|
|
|
puts("};\n");
|
|
|
|
|
puts("tracep->declDTypeEnum(" + std::to_string(enumNumr) + ", \"" + nodep->prettyName()
|
|
|
|
|
+ "\", " + std::to_string(nvals) + ", " + std::to_string(nodep->widthMin()) + ", "
|
|
|
|
|
+ protect("__VenumItemNames") + ", " + protect("__VenumItemValues") + ");\n");
|
|
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(AstNode::INSTR_COUNT_CALL);
|
2023-12-19 17:07:06 +01:00
|
|
|
}
|
2025-11-09 18:41:13 +01:00
|
|
|
return enumNumr;
|
2023-12-19 17:07:06 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// Close output file
|
|
|
|
|
void finalize() {
|
|
|
|
|
// Close function definition
|
|
|
|
|
puts("}\n");
|
2023-12-19 17:07:06 +01:00
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
const std::string modName = EmitCUtil::prefixNameProtect(m_modp);
|
|
|
|
|
const std::string args = v3Global.opt.traceClassBase() + "* tracep";
|
2023-12-19 17:07:06 +01:00
|
|
|
|
|
|
|
|
// Forward declarations for subs in other files
|
|
|
|
|
for (int i = 0; i < m_traceTypeSubs - 1; ++i) {
|
2025-11-09 18:41:13 +01:00
|
|
|
puts("void " + modName + "__" + protect("traceDeclTypesSub" + std::to_string(i)) + "("
|
|
|
|
|
+ args + ");\n");
|
2023-12-19 17:07:06 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// Create top level trace_decl_types function and call each sub-function
|
|
|
|
|
puts("\nvoid " + modName + "__" + protect("trace_decl_types") + "(" + args + ") {\n");
|
2023-12-19 17:07:06 +01:00
|
|
|
for (int i = 0; i < m_traceTypeSubs; ++i) {
|
2025-11-09 18:41:13 +01:00
|
|
|
puts(modName + "__" + protect("traceDeclTypesSub" + std::to_string(i))
|
|
|
|
|
+ "(tracep);\n");
|
2023-12-19 17:07:06 +01:00
|
|
|
}
|
2025-11-09 18:41:13 +01:00
|
|
|
puts("}\n");
|
|
|
|
|
|
|
|
|
|
closeOutputFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmitCTraceTypes() {
|
|
|
|
|
m_modp = v3Global.rootp()->topModulep();
|
|
|
|
|
openNextOutputFile();
|
2023-12-19 17:07:06 +01:00
|
|
|
}
|
2025-11-09 18:41:13 +01:00
|
|
|
~EmitCTraceTypes() override = default;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class EmitCTrace final : public EmitCFunc {
|
|
|
|
|
// NODE STATE/TYPES
|
|
|
|
|
// None allowed to support threaded emitting
|
|
|
|
|
|
|
|
|
|
// MEMBERS
|
|
|
|
|
const bool m_slow; // Making slow file
|
|
|
|
|
const std::unique_ptr<EmitCTraceTypes> m_emitTypesp{m_slow ? new EmitCTraceTypes{} : nullptr};
|
|
|
|
|
V3UniqueNames m_uniqueNames; // Generates unique file names
|
|
|
|
|
const std::string m_fileBaseName = EmitCUtil::topClassName() + "_" + protect("_Trace");
|
2023-12-19 17:07:06 +01:00
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
// METHODS
|
|
|
|
|
void openNextOutputFile() {
|
|
|
|
|
openNewOutputSourceFile(m_uniqueNames.get(m_fileBaseName), m_slow, true,
|
|
|
|
|
"Tracing implementation internals");
|
|
|
|
|
puts("\n");
|
|
|
|
|
for (const std::string& base : v3Global.opt.traceSourceLangs()) {
|
|
|
|
|
puts("#include \"" + base + ".h\"\n");
|
|
|
|
|
}
|
|
|
|
|
puts("#include \"" + EmitCUtil::symClassName() + ".h\"\n");
|
|
|
|
|
puts("\n");
|
|
|
|
|
// Need to emit new lazy declarations
|
|
|
|
|
m_lazyDecls.reset();
|
2023-12-19 17:07:06 +01:00
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
bool emitTraceIsScBv(const AstTraceInc* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
2021-07-07 20:16:40 +02:00
|
|
|
if (!varrefp) return false;
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstVar* const varp = varrefp->varp();
|
2021-07-07 20:16:40 +02:00
|
|
|
return varp->isSc() && varp->isScBv();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
bool emitTraceIsScBigUint(const AstTraceInc* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
2021-07-07 20:16:40 +02:00
|
|
|
if (!varrefp) return false;
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstVar* const varp = varrefp->varp();
|
2021-07-07 20:16:40 +02:00
|
|
|
return varp->isSc() && varp->isScBigUint();
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
bool emitTraceIsScUint(const AstTraceInc* nodep) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstVarRef* const varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
2021-07-07 20:16:40 +02:00
|
|
|
if (!varrefp) return false;
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstVar* const varp = varrefp->varp();
|
2024-06-25 11:27:09 +02:00
|
|
|
return varp->isSc() && (varp->isScUint() || varp->isScUintBool());
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-19 23:02:10 +02:00
|
|
|
void emitTraceInitOne(const AstTraceDecl* nodep, int enumNum) {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2023-10-24 17:33:29 +02:00
|
|
|
puts("tracep->declDouble(");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->isWide()) {
|
2023-10-24 17:33:29 +02:00
|
|
|
puts("tracep->declArray(");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->isQuad()) {
|
2023-10-24 17:33:29 +02:00
|
|
|
puts("tracep->declQuad(");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->bitRange().ranged()) {
|
2023-10-24 17:33:29 +02:00
|
|
|
puts("tracep->declBus(");
|
2022-11-23 10:07:14 +01:00
|
|
|
} else if (nodep->dtypep()->basicp()->isEvent()) {
|
2023-10-24 17:33:29 +02:00
|
|
|
puts("tracep->declEvent(");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
2023-10-24 17:33:29 +02:00
|
|
|
puts("tracep->declBit(");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
2023-10-24 17:33:29 +02:00
|
|
|
// Code
|
|
|
|
|
puts("c+" + cvtToStr(nodep->code()));
|
2021-07-07 20:16:40 +02:00
|
|
|
if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords()));
|
2023-10-24 17:33:29 +02:00
|
|
|
|
|
|
|
|
// Function index
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(",");
|
2023-10-23 17:01:55 +02:00
|
|
|
puts(cvtToStr(nodep->fidx()));
|
2023-10-24 17:33:29 +02:00
|
|
|
|
|
|
|
|
// Name
|
2023-10-23 17:01:55 +02:00
|
|
|
puts(",");
|
2021-07-07 20:16:40 +02:00
|
|
|
putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect()));
|
2023-10-24 17:33:29 +02:00
|
|
|
|
|
|
|
|
// Enum number
|
|
|
|
|
puts("," + cvtToStr(enumNum));
|
|
|
|
|
|
2021-07-07 20:16:40 +02:00
|
|
|
// Direction
|
2024-11-26 00:25:36 +01:00
|
|
|
if (nodep->declDirection().isInout()) {
|
2023-10-24 17:33:29 +02:00
|
|
|
puts(", VerilatedTraceSigDirection::INOUT");
|
|
|
|
|
} else if (nodep->declDirection().isWritable()) {
|
|
|
|
|
puts(", VerilatedTraceSigDirection::OUTPUT");
|
|
|
|
|
} else if (nodep->declDirection().isNonOutput()) {
|
|
|
|
|
puts(", VerilatedTraceSigDirection::INPUT");
|
|
|
|
|
} else {
|
|
|
|
|
puts(", VerilatedTraceSigDirection::NONE");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2023-10-24 17:33:29 +02:00
|
|
|
|
|
|
|
|
// Kind
|
|
|
|
|
puts(", VerilatedTraceSigKind::");
|
|
|
|
|
puts(nodep->varType().traceSigKind());
|
|
|
|
|
|
|
|
|
|
// Type
|
|
|
|
|
puts(", VerilatedTraceSigType::");
|
|
|
|
|
puts(nodep->dtypep()->basicp()->keyword().traceSigType());
|
|
|
|
|
|
|
|
|
|
// Array range
|
2021-07-07 20:16:40 +02:00
|
|
|
if (nodep->arrayRange().ranged()) {
|
|
|
|
|
puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")");
|
|
|
|
|
} else {
|
|
|
|
|
puts(", false,-1");
|
|
|
|
|
}
|
2023-10-24 17:33:29 +02:00
|
|
|
|
|
|
|
|
// Bit range
|
2021-07-07 20:16:40 +02:00
|
|
|
if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) {
|
|
|
|
|
puts(", " + cvtToStr(nodep->bitRange().left()) + ","
|
|
|
|
|
+ cvtToStr(nodep->bitRange().right()));
|
|
|
|
|
}
|
2023-10-24 17:33:29 +02:00
|
|
|
|
|
|
|
|
//
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(");");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int emitTraceDeclDType(AstNodeDType* nodep) {
|
|
|
|
|
// Return enum number or -1 for none
|
2025-09-28 02:54:26 +02:00
|
|
|
if (v3Global.opt.traceEnabledFst()) {
|
2021-07-07 20:16:40 +02:00
|
|
|
// Skip over refs-to-refs, but stop before final ref so can get data type name
|
|
|
|
|
// Alternatively back in V3Width we could push enum names from upper typedefs
|
2021-11-26 23:55:36 +01:00
|
|
|
if (AstEnumDType* const enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) {
|
2025-11-09 18:41:13 +01:00
|
|
|
return m_emitTypesp->getEnumMapNum(enump);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) {
|
2023-10-23 12:36:24 +02:00
|
|
|
// Note: Both VTraceType::CHANGE and VTraceType::FULL use the 'full' methods
|
|
|
|
|
const std::string func = nodep->traceType() == VTraceType::CHANGE ? "chg" : "full";
|
2021-07-07 20:16:40 +02:00
|
|
|
bool emitWidth = true;
|
2024-01-25 03:51:47 +01:00
|
|
|
string stype;
|
2021-07-07 20:16:40 +02:00
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "Double";
|
2021-07-07 20:16:40 +02:00
|
|
|
emitWidth = false;
|
|
|
|
|
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "WData";
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->isQuad()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "QData";
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 16) {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "IData";
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 8) {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "SData";
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 1) {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "CData";
|
2022-11-23 10:07:14 +01:00
|
|
|
} else if (nodep->dtypep()->basicp()->isEvent()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "Event";
|
2022-11-23 10:07:14 +01:00
|
|
|
emitWidth = false;
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
2024-01-25 03:51:47 +01:00
|
|
|
stype = "Bit";
|
2021-07-07 20:16:40 +02:00
|
|
|
emitWidth = false;
|
|
|
|
|
}
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(nodep, "bufp->" + func + stype);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords());
|
|
|
|
|
const uint32_t code = nodep->declp()->code() + offset;
|
2023-10-23 12:36:24 +02:00
|
|
|
// Note: Both VTraceType::CHANGE and VTraceType::FULL use the 'full' methods
|
|
|
|
|
puts(v3Global.opt.useTraceOffload() && nodep->traceType() == VTraceType::CHANGE
|
|
|
|
|
? "(base+"
|
|
|
|
|
: "(oldp+");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(cvtToStr(code - nodep->baseCode()));
|
|
|
|
|
puts(",");
|
|
|
|
|
emitTraceValue(nodep, arrayindex);
|
|
|
|
|
if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin()));
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-21 10:43:37 +02:00
|
|
|
void emitTraceValue(const AstTraceInc* nodep, int arrayindex) {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) {
|
2025-08-19 23:02:10 +02:00
|
|
|
const AstVar* const varp = varrefp->varp();
|
2024-03-27 22:57:49 +01:00
|
|
|
if (varp->isEvent()) puts("&");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("(");
|
|
|
|
|
if (emitTraceIsScBigUint(nodep)) {
|
2022-03-27 21:27:40 +02:00
|
|
|
puts("(uint32_t*)");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (emitTraceIsScBv(nodep)) {
|
|
|
|
|
puts("VL_SC_BV_DATAP(");
|
|
|
|
|
}
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(varrefp); // Put var name out
|
2021-07-07 20:16:40 +02:00
|
|
|
// Tracing only supports 1D arrays
|
|
|
|
|
if (nodep->declp()->arrayRange().ranged()) {
|
|
|
|
|
if (arrayindex == -2) {
|
|
|
|
|
puts("[i]");
|
|
|
|
|
} else if (arrayindex == -1) {
|
|
|
|
|
puts("[0]");
|
|
|
|
|
} else {
|
|
|
|
|
puts("[" + cvtToStr(arrayindex) + "]");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (varp->isSc()) puts(".read()");
|
|
|
|
|
if (emitTraceIsScUint(nodep)) {
|
|
|
|
|
puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
|
|
|
|
|
} else if (emitTraceIsScBigUint(nodep)) {
|
|
|
|
|
puts(".get_raw()");
|
|
|
|
|
} else if (emitTraceIsScBv(nodep)) {
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
puts(")");
|
|
|
|
|
} else {
|
|
|
|
|
puts("(");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep->valuep());
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(")");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (!nodep->isTrace()) return;
|
|
|
|
|
if (nodep->slow() != m_slow) return;
|
|
|
|
|
|
|
|
|
|
if (splitNeeded()) {
|
|
|
|
|
// Splitting file, so using parallel build.
|
|
|
|
|
v3Global.useParallelBuild(true);
|
|
|
|
|
// Close old file
|
2024-10-01 03:42:36 +02:00
|
|
|
closeOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
// Open a new file
|
2021-07-14 23:37:37 +02:00
|
|
|
openNextOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmitCFunc::visit(nodep);
|
|
|
|
|
}
|
2023-10-24 17:33:29 +02:00
|
|
|
void visit(AstTracePushPrefix* nodep) override {
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(nodep, "tracep->pushPrefix(");
|
2021-12-19 16:15:07 +01:00
|
|
|
putsQuoted(VIdProtect::protectWordsIf(nodep->prefix(), nodep->protect()));
|
2023-10-24 17:33:29 +02:00
|
|
|
puts(", VerilatedTracePrefixType::");
|
|
|
|
|
puts(nodep->prefixType().ascii());
|
2021-12-19 16:15:07 +01:00
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2023-10-24 17:33:29 +02:00
|
|
|
void visit(AstTracePopPrefix* nodep) override { //
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(nodep, "tracep->popPrefix();\n");
|
2021-12-19 16:15:07 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTraceDecl* nodep) override {
|
2021-07-07 20:16:40 +02:00
|
|
|
const int enumNum = emitTraceDeclDType(nodep->dtypep());
|
2024-01-25 03:51:47 +01:00
|
|
|
putns(nodep, "");
|
2021-07-07 20:16:40 +02:00
|
|
|
if (nodep->arrayRange().ranged()) {
|
2021-12-19 16:15:07 +01:00
|
|
|
puts("for (int i = 0; i < " + cvtToStr(nodep->arrayRange().elements()) + "; ++i) {\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
emitTraceInitOne(nodep, enumNum);
|
2021-12-19 16:15:07 +01:00
|
|
|
puts("\n}\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
|
|
|
|
emitTraceInitOne(nodep, enumNum);
|
|
|
|
|
puts("\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTraceInc* nodep) override {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (nodep->declp()->arrayRange().ranged()) {
|
|
|
|
|
// It traces faster if we unroll the loop
|
|
|
|
|
for (int i = 0; i < nodep->declp()->arrayRange().elements(); i++) {
|
|
|
|
|
emitTraceChangeOne(nodep, i);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
emitTraceChangeOne(nodep, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-09 18:41:13 +01:00
|
|
|
explicit EmitCTrace(bool slow)
|
|
|
|
|
: m_slow{slow} {
|
|
|
|
|
m_modp = v3Global.rootp()->topModulep();
|
2021-07-07 20:16:40 +02:00
|
|
|
// Open output file
|
2021-07-14 23:37:37 +02:00
|
|
|
openNextOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
// Emit functions
|
2025-11-09 18:41:13 +01:00
|
|
|
for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2024-01-25 03:51:47 +01:00
|
|
|
if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) iterateConst(funcp);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
// Close output file
|
2024-10-01 03:42:36 +02:00
|
|
|
closeOutputFile();
|
2025-11-09 18:41:13 +01:00
|
|
|
if (m_slow) m_emitTypesp->finalize();
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~EmitCTrace() override = default;
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
public:
|
2025-11-09 18:41:13 +01:00
|
|
|
static std::vector<AstCFile*> main(bool slow) VL_MT_STABLE {
|
|
|
|
|
EmitCTrace emitCTrace{slow};
|
|
|
|
|
std::vector<AstCFile*> cfileps = emitCTrace.getAndClearCfileps();
|
|
|
|
|
if (slow) {
|
|
|
|
|
for (AstCFile* const cfilep : emitCTrace.m_emitTypesp->getAndClearCfileps()) {
|
|
|
|
|
cfileps.emplace_back(cfilep);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return cfileps;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Existing AstCFile emitter
|
|
|
|
|
|
|
|
|
|
class EmitCFile final : public EmitCFunc {
|
|
|
|
|
explicit EmitCFile(AstCFile* cfilep) {
|
|
|
|
|
openOutputFile(cfilep, "Generated C++");
|
|
|
|
|
iterateConst(cfilep->tblockp());
|
|
|
|
|
closeOutputFile();
|
|
|
|
|
}
|
|
|
|
|
~EmitCFile() override = default;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static void main(AstCFile* cfilep) VL_MT_STABLE {
|
|
|
|
|
if (!cfilep->tblockp()) return;
|
|
|
|
|
EmitCFile{cfilep};
|
2022-09-13 18:15:34 +02:00
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// EmitC class functions
|
|
|
|
|
|
|
|
|
|
void V3EmitC::emitcImp() {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2025-11-09 18:41:13 +01:00
|
|
|
std::list<std::vector<AstCFile*>> cfiles;
|
|
|
|
|
{
|
|
|
|
|
// Make parent module pointers available.
|
|
|
|
|
const EmitCParentModule emitCParentModule;
|
|
|
|
|
V3ThreadScope threadScope;
|
|
|
|
|
|
|
|
|
|
// Process each module in turn
|
|
|
|
|
for (const AstNode* nodep = v3Global.rootp()->modulesp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage
|
|
|
|
|
const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
|
|
|
|
|
cfiles.emplace_back();
|
|
|
|
|
std::vector<AstCFile*>& slow = cfiles.back();
|
|
|
|
|
threadScope.enqueue([modp, &slow] { slow = EmitCImp::main(modp, /* slow: */ true); });
|
|
|
|
|
cfiles.emplace_back();
|
|
|
|
|
std::vector<AstCFile*>& fast = cfiles.back();
|
|
|
|
|
threadScope.enqueue([modp, &fast] { fast = EmitCImp::main(modp, /* slow: */ false); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit trace routines (currently they can only exist in the top module)
|
|
|
|
|
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
|
|
|
|
|
cfiles.emplace_back();
|
|
|
|
|
std::vector<AstCFile*>& slow = cfiles.back();
|
|
|
|
|
threadScope.enqueue([&slow] { slow = EmitCTrace::main(/* slow: */ true); });
|
|
|
|
|
cfiles.emplace_back();
|
|
|
|
|
std::vector<AstCFile*>& fast = cfiles.back();
|
|
|
|
|
threadScope.enqueue([&fast] { fast = EmitCTrace::main(/* slow: */ false); });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Add files to netlist
|
|
|
|
|
for (const std::vector<AstCFile*>& cfileps : cfiles) {
|
|
|
|
|
for (AstCFile* const cfilep : cfileps) v3Global.rootp()->addFilesp(cfilep);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3EmitC::emitcFiles() {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
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
|
|
|
for (AstNodeFile *filep = v3Global.rootp()->filesp(), *nextp; filep; filep = nextp) {
|
|
|
|
|
nextp = VN_AS(filep->nextp(), NodeFile);
|
2025-11-09 18:41:13 +01:00
|
|
|
if (AstCFile* const cfilep = VN_CAST(filep, CFile)) EmitCFile::main(cfilep);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|