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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2023-01-01 16:18:39 +01:00
|
|
|
// Copyright 2003-2023 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
#include "V3Ast.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3EmitC.h"
|
2021-07-07 20:16:40 +02:00
|
|
|
#include "V3EmitCFunc.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Global.h"
|
2021-07-14 23:37:37 +02:00
|
|
|
#include "V3String.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-14 23:37:37 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Visitor that gathers the headers required by an AstCFunc
|
|
|
|
|
|
2023-03-18 00:58:53 +01:00
|
|
|
class EmitCGatherDependencies final : VNVisitorConst {
|
2021-07-14 23:37:37 +02:00
|
|
|
// Ordered set, as it is used as a key in another map.
|
|
|
|
|
std::set<string> m_dependencies; // Header names to be included in output C++ file
|
|
|
|
|
|
|
|
|
|
// METHODS
|
2023-03-18 17:05:29 +01:00
|
|
|
void addSymsDependency() { m_dependencies.insert(EmitCBase::symClassName()); }
|
2021-07-14 23:37:37 +02:00
|
|
|
void addModDependency(const AstNodeModule* modp) {
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
2023-03-18 17:05:29 +01:00
|
|
|
m_dependencies.insert(EmitCBase::prefixNameProtect(classp->classOrPackagep()));
|
2021-07-14 23:37:37 +02:00
|
|
|
} else {
|
2023-03-18 17:05:29 +01:00
|
|
|
m_dependencies.insert(EmitCBase::prefixNameProtect(modp));
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void addDTypeDependency(const AstNodeDType* nodep) {
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstClassRefDType* const dtypep = VN_CAST(nodep, ClassRefDType)) {
|
2021-07-14 23:37:37 +02:00
|
|
|
m_dependencies.insert(
|
2023-03-18 17:05:29 +01:00
|
|
|
EmitCBase::prefixNameProtect(dtypep->classp()->classOrPackagep()));
|
2023-01-28 04:41:12 +01:00
|
|
|
} else if (const AstNodeUOrStructDType* const dtypep
|
|
|
|
|
= VN_CAST(nodep, NodeUOrStructDType)) {
|
2022-12-21 01:22:42 +01:00
|
|
|
if (!dtypep->packed()) {
|
2023-05-07 03:41:17 +02:00
|
|
|
UASSERT_OBJ(dtypep->classOrPackagep(), nodep, "Unlinked struct package");
|
2023-03-18 17:05:29 +01:00
|
|
|
m_dependencies.insert(EmitCBase::prefixNameProtect(dtypep->classOrPackagep()));
|
2022-12-21 01:22:42 +01:00
|
|
|
}
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-09-08 13:34:35 +02:00
|
|
|
void addSelfDependency(VSelfPointerText selfPointer, AstNode* nodep) {
|
|
|
|
|
if (selfPointer.isEmpty()) {
|
2021-07-14 23:37:37 +02:00
|
|
|
// No self pointer (e.g.: function locals, const pool values, loose static methods),
|
|
|
|
|
// so no dependency
|
2023-09-08 13:34:35 +02:00
|
|
|
} else if (selfPointer.hasThis()) {
|
2021-07-14 23:37:37 +02:00
|
|
|
// Dereferencing 'this', we need the definition of this module, which is also the
|
|
|
|
|
// module that contains the variable.
|
|
|
|
|
addModDependency(EmitCParentModule::get(nodep));
|
|
|
|
|
} else {
|
|
|
|
|
// Must be an absolute reference
|
2023-09-08 13:34:35 +02:00
|
|
|
UASSERT_OBJ(selfPointer.isVlSym(), nodep,
|
|
|
|
|
"Unknown self pointer: '" << selfPointer.asString() << "'");
|
2021-07-14 23:37:37 +02:00
|
|
|
// Dereferencing vlSymsp, so we need it's definition...
|
2022-03-25 20:46:50 +01:00
|
|
|
addSymsDependency();
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCCall* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSelfDependency(nodep->selfPointer(), nodep->funcp());
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCNew* nodep) override {
|
2022-09-29 00:54:18 +02:00
|
|
|
addSymsDependency();
|
2021-07-14 23:37:37 +02:00
|
|
|
addDTypeDependency(nodep->dtypep());
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCMethodCall* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addDTypeDependency(nodep->fromp()->dtypep());
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNewCopy* nodep) override {
|
2022-09-29 00:54:18 +02:00
|
|
|
addSymsDependency();
|
2021-07-14 23:37:37 +02:00
|
|
|
addDTypeDependency(nodep->dtypep());
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
2022-12-21 01:22:42 +01:00
|
|
|
addDTypeDependency(nodep->fromp()->dtypep());
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstStructSel* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addDTypeDependency(nodep->fromp()->dtypep());
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSelfDependency(nodep->selfPointer(), nodep->varp());
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverDecl* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSymsDependency();
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverInc* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSymsDependency();
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstDumpCtl* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSymsDependency();
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScopeName* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSymsDependency();
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstPrintTimeScale* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSymsDependency();
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTimeFormat* nodep) override {
|
2021-07-14 23:37:37 +02:00
|
|
|
addSymsDependency();
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeSimpleText* nodep) override {
|
2022-03-25 20:46:50 +01:00
|
|
|
if (nodep->text().find("vlSymsp") != string::npos) addSymsDependency();
|
2021-07-14 23:37:37 +02:00
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2021-07-14 23:37:37 +02:00
|
|
|
|
|
|
|
|
// CONSTRUCTOR
|
|
|
|
|
explicit EmitCGatherDependencies(AstCFunc* cfuncp) {
|
|
|
|
|
// Strictly speaking, for loose methods, we could get away with just a forward
|
|
|
|
|
// declaration of the receiver class, but their body very likely includes at least one
|
|
|
|
|
// relative reference, so we are probably not loosing much.
|
|
|
|
|
addModDependency(EmitCParentModule::get(cfuncp));
|
2023-03-18 00:58:53 +01:00
|
|
|
iterateConst(cfuncp);
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2023-08-03 08:52:52 +02:00
|
|
|
static const std::set<std::string> gather(AstCFunc* cfuncp) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const EmitCGatherDependencies visitor{cfuncp};
|
2021-07-14 23:37:37 +02:00
|
|
|
return std::move(visitor.m_dependencies);
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Internal EmitC implementation
|
|
|
|
|
|
|
|
|
|
class EmitCImp final : EmitCFunc {
|
|
|
|
|
// MEMBERS
|
|
|
|
|
const AstNodeModule* const m_fileModp; // Files names/headers constructed using this module
|
|
|
|
|
const bool m_slow; // Creating __Slow file
|
2021-07-14 23:37:37 +02:00
|
|
|
const std::set<string>* m_requiredHeadersp; // Header files required by output file
|
|
|
|
|
std::string m_subFileName; // substring added to output filenames
|
|
|
|
|
V3UniqueNames m_uniqueNames; // For generating unique file names
|
2022-09-13 18:15:34 +02:00
|
|
|
std::deque<AstCFile*>& m_cfilesr; // cfiles generated by this emit
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2021-07-14 23:37:37 +02:00
|
|
|
void openNextOutputFile(const std::set<string>& headers, const string& subFileName) {
|
2021-07-07 20:16:40 +02:00
|
|
|
UASSERT(!m_ofp, "Output file already open");
|
|
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
splitSizeReset(); // Reset file size tracking
|
2021-07-07 20:16:40 +02:00
|
|
|
m_lazyDecls.reset(); // Need to emit new lazy declarations
|
|
|
|
|
|
|
|
|
|
if (v3Global.opt.lintOnly()) {
|
|
|
|
|
// Unfortunately we have some lint checks here, so we can't just skip processing.
|
|
|
|
|
// We should move them to a different stage.
|
|
|
|
|
const string filename = VL_DEV_NULL;
|
2023-04-11 13:25:10 +02:00
|
|
|
m_cfilesr.push_back(createCFile(filename, /* slow: */ m_slow, /* source: */ true));
|
2022-11-20 19:11:01 +01:00
|
|
|
m_ofp = new V3OutCFile{filename};
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
|
|
|
|
string filename = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp);
|
2021-07-14 23:37:37 +02:00
|
|
|
if (!subFileName.empty()) {
|
|
|
|
|
filename += "__" + subFileName;
|
|
|
|
|
filename = m_uniqueNames.get(filename);
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
if (m_slow) filename += "__Slow";
|
|
|
|
|
filename += ".cpp";
|
2023-04-11 13:25:10 +02:00
|
|
|
m_cfilesr.push_back(createCFile(filename, /* slow: */ m_slow, /* source: */ true));
|
2022-11-20 19:11:01 +01:00
|
|
|
m_ofp = v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename};
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-11 13:25:10 +02:00
|
|
|
putsHeader();
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("// DESCRIPTION: Verilator output: Design implementation internals\n");
|
|
|
|
|
puts("// See " + topClassName() + ".h for the primary calling header\n");
|
|
|
|
|
|
|
|
|
|
// Include files
|
2021-07-24 16:00:33 +02:00
|
|
|
puts("\n#include \"verilated.h\"\n");
|
2021-07-14 23:37:37 +02:00
|
|
|
if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("\n");
|
2023-04-14 17:53:27 +02:00
|
|
|
puts("#include \"" + symClassName() + ".h\"\n");
|
2021-07-14 23:37:37 +02:00
|
|
|
for (const string& name : headers) puts("#include \"" + name + ".h\"\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
emitTextSection(m_modp, VNType::atScImpHdr);
|
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
|
|
|
|
|
const string modName = prefixNameProtect(modp);
|
|
|
|
|
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()) {
|
|
|
|
|
puts(varp->vlArgType(true, false, false, modName));
|
|
|
|
|
puts(";\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
void emitParamDefns(const AstNodeModule* modp) {
|
2021-07-22 19:59:03 +02:00
|
|
|
const string modName = prefixNameProtect(modp);
|
|
|
|
|
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");
|
|
|
|
|
putsDecoration("// Parameter definitions for " + modName + "\n");
|
|
|
|
|
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();
|
|
|
|
|
puts(canBeConstexpr ? "constexpr " : "const ");
|
|
|
|
|
const string scopedName = modName + "::" + varp->nameProtect();
|
|
|
|
|
puts(varp->dtypep()->cType(scopedName, false, false));
|
|
|
|
|
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) {
|
|
|
|
|
const string modName = prefixNameProtect(modp);
|
|
|
|
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"),
|
|
|
|
|
"(" + modName + "* vlSelf);");
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
2023-01-21 00:38:59 +01:00
|
|
|
puts(modName + "::" + modName + "(" + symClassName() + "* symsp, const char* v__name)\n");
|
|
|
|
|
puts(" : VerilatedModule{v__name}\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
ofp()->indentInc();
|
|
|
|
|
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-07 20:16:40 +02:00
|
|
|
if (const AstBasicDType* const dtypep
|
|
|
|
|
= VN_CAST(varp->dtypeSkipRefp(), BasicDType)) {
|
|
|
|
|
if (dtypep->keyword().isMTaskState()) {
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(varp->nameProtect());
|
|
|
|
|
puts("(");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(varp->valuep());
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(")\n");
|
|
|
|
|
} else if (varp->isIO() && varp->isSc()) {
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(varp->nameProtect());
|
|
|
|
|
puts("(");
|
|
|
|
|
putsQuoted(varp->nameProtect());
|
|
|
|
|
puts(")\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
|
|
|
} else if (dtypep->isDelayScheduler()) {
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(varp->nameProtect());
|
|
|
|
|
puts("{*symsp->_vm_contextp__}\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-27 16:05:24 +02:00
|
|
|
puts(", vlSymsp{symsp}\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
ofp()->indentDec();
|
|
|
|
|
|
|
|
|
|
puts(" {\n");
|
|
|
|
|
|
|
|
|
|
putsDecoration("// Reset structure values\n");
|
|
|
|
|
puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n");
|
2022-01-02 19:56:40 +01:00
|
|
|
emitTextSection(modp, VNType::atScCtor);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
void emitConfigureImp(const AstNodeModule* modp) {
|
|
|
|
|
const string modName = prefixNameProtect(modp);
|
|
|
|
|
|
|
|
|
|
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");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("if (false && first) {} // Prevent unused\n");
|
|
|
|
|
if (v3Global.opt.coverage()) {
|
|
|
|
|
puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n");
|
|
|
|
|
}
|
|
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
|
|
|
|
}
|
|
|
|
|
void emitCoverageImp() {
|
|
|
|
|
if (v3Global.opt.coverage()) {
|
|
|
|
|
puts("\n// Coverage\n");
|
2021-07-14 23:37:37 +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
|
|
|
puts("void " + prefixNameProtect(m_modp) + "::__vlCoverInsert(");
|
|
|
|
|
puts(v3Global.opt.threads() ? "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.threads()) {
|
|
|
|
|
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");
|
|
|
|
|
// Used for second++ instantiation of identical bin
|
|
|
|
|
puts("if (!enable) count32p = &fake_zero_count;\n");
|
|
|
|
|
puts("*count32p = 0;\n");
|
|
|
|
|
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), count32p,");
|
|
|
|
|
puts(" \"filename\",filenamep,");
|
|
|
|
|
puts(" \"lineno\",lineno,");
|
|
|
|
|
puts(" \"column\",column,\n");
|
|
|
|
|
// Need to move hier into scopes and back out if do this
|
2022-08-30 05:50:32 +02:00
|
|
|
// puts( "\"hier\",std::string{vlSymsp->name()} + hierp,");
|
|
|
|
|
puts("\"hier\",std::string{name()} + hierp,");
|
2021-07-07 20:16:40 +02:00
|
|
|
puts(" \"page\",pagep,");
|
|
|
|
|
puts(" \"comment\",commentp,");
|
|
|
|
|
puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n");
|
|
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void emitDestructorImp(const AstNodeModule* modp) {
|
|
|
|
|
puts("\n");
|
|
|
|
|
puts(prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n");
|
2022-01-02 19:56:40 +01:00
|
|
|
emitTextSection(modp, VNType::atScDtor);
|
2021-07-07 20:16:40 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
puts("void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" + classname
|
|
|
|
|
+ "& os) {\n");
|
|
|
|
|
// 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()) {
|
2022-05-15 17:03:32 +02:00
|
|
|
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
|
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.");
|
2023-04-07 03:00:10 +02:00
|
|
|
const string ivar = string{"__Vi"} + 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++;
|
2023-04-07 03:00:10 +02:00
|
|
|
const string ivar = string{"__Vi"} + 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");
|
|
|
|
|
}
|
|
|
|
|
puts("os" + op + varp->nameProtect());
|
|
|
|
|
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;
|
|
|
|
|
// The fast file is only required when we have ScImp nodes
|
|
|
|
|
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (VN_IS(nodep, ScImp)) return true;
|
|
|
|
|
}
|
|
|
|
|
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
|
2022-01-02 19:56:40 +01:00
|
|
|
emitTextSection(modp, VNType::atScImp);
|
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)) {
|
|
|
|
|
std::set<string> headers;
|
|
|
|
|
headers.insert(prefixNameProtect(m_fileModp));
|
|
|
|
|
headers.insert(symClassName());
|
|
|
|
|
|
|
|
|
|
openNextOutputFile(headers, "");
|
|
|
|
|
|
|
|
|
|
doCommonImp(modp);
|
|
|
|
|
if (classp) {
|
|
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
m_modp = classp;
|
|
|
|
|
doCommonImp(classp);
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
|
2021-07-14 23:37:37 +02:00
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void emitCFuncImp(const AstNodeModule* modp) {
|
|
|
|
|
// Partition functions based on which module definitions they require, by building a
|
|
|
|
|
// map from "AstNodeModules whose definitions are required" -> "functions that need
|
|
|
|
|
// them"
|
|
|
|
|
std::map<const std::set<string>, std::vector<AstCFunc*>> depSet2funcps;
|
|
|
|
|
|
|
|
|
|
const auto gather = [this, &depSet2funcps](const AstNodeModule* modp) {
|
|
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) {
|
|
|
|
|
// TRACE_* and DPI handled elsewhere
|
|
|
|
|
if (funcp->isTrace()) continue;
|
|
|
|
|
if (funcp->dpiImportPrototype()) continue;
|
|
|
|
|
if (funcp->dpiExportDispatcher()) continue;
|
|
|
|
|
if (funcp->slow() != m_slow) continue;
|
|
|
|
|
const auto& depSet = EmitCGatherDependencies::gather(funcp);
|
|
|
|
|
depSet2funcps[depSet].push_back(funcp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
gather(modp);
|
2021-10-22 16:15:42 +02:00
|
|
|
if (const AstClassPackage* const packagep = VN_CAST(modp, ClassPackage)) {
|
2021-07-14 23:37:37 +02:00
|
|
|
gather(packagep->classp());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit all functions in each dependency set into separate files
|
|
|
|
|
for (const auto& pair : depSet2funcps) {
|
|
|
|
|
m_requiredHeadersp = &pair.first;
|
|
|
|
|
// Compute the hash of the dependencies, so we can add it to the filenames to
|
|
|
|
|
// disambiguate them
|
|
|
|
|
V3Hash hash;
|
2023-04-11 01:40:17 +02:00
|
|
|
for (const string& name : *m_requiredHeadersp) hash += name;
|
2021-08-11 17:22:52 +02:00
|
|
|
m_subFileName = "DepSet_" + hash.toString();
|
2021-07-14 23:37:37 +02:00
|
|
|
// Open output file
|
|
|
|
|
openNextOutputFile(*m_requiredHeadersp, m_subFileName);
|
|
|
|
|
// Emit functions in this dependency set
|
|
|
|
|
for (AstCFunc* const funcp : pair.second) {
|
|
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
m_modp = EmitCParentModule::get(funcp);
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(funcp);
|
2021-07-14 23:37:37 +02:00
|
|
|
}
|
|
|
|
|
// Close output file
|
|
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
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
|
|
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
|
|
|
|
// Open a new file
|
2021-07-14 23:37:37 +02:00
|
|
|
openNextOutputFile(*m_requiredHeadersp, m_subFileName);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmitCFunc::visit(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 18:15:34 +02:00
|
|
|
explicit EmitCImp(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr)
|
2021-07-07 20:16:40 +02:00
|
|
|
: m_fileModp{modp}
|
2022-09-13 18:15:34 +02:00
|
|
|
, m_slow{slow}
|
|
|
|
|
, m_cfilesr{cfilesr} {
|
2021-07-07 20:16:40 +02:00
|
|
|
UINFO(5, " Emitting implementation of " << prefixNameProtect(modp) << endl);
|
|
|
|
|
|
|
|
|
|
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:
|
2023-08-03 08:52:52 +02:00
|
|
|
static void main(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) {
|
2022-09-13 18:15:34 +02:00
|
|
|
EmitCImp{modp, slow, cfilesr};
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Tracing routines
|
|
|
|
|
|
|
|
|
|
class EmitCTrace final : EmitCFunc {
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// MEMBERS
|
|
|
|
|
const bool m_slow; // Making slow file
|
|
|
|
|
int m_enumNum = 0; // Enumeration number (whole netlist)
|
2021-07-14 23:37:37 +02:00
|
|
|
V3UniqueNames m_uniqueNames; // For generating unique file names
|
2022-09-12 18:00:41 +02:00
|
|
|
std::unordered_map<AstNode*, int> m_enumNumMap; // EnumDType to enumeration number
|
2022-09-13 18:15:34 +02:00
|
|
|
std::deque<AstCFile*>& m_cfilesr; // cfiles generated by this emit
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
2021-07-14 23:37:37 +02:00
|
|
|
void openNextOutputFile() {
|
|
|
|
|
UASSERT(!m_ofp, "Output file already open");
|
|
|
|
|
|
|
|
|
|
splitSizeReset(); // Reset file size tracking
|
2021-07-07 20:16:40 +02:00
|
|
|
m_lazyDecls.reset(); // Need to emit new lazy declarations
|
|
|
|
|
|
|
|
|
|
string filename
|
|
|
|
|
= (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace"));
|
2021-07-14 23:37:37 +02:00
|
|
|
filename = m_uniqueNames.get(filename);
|
|
|
|
|
if (m_slow) filename += "__Slow";
|
2021-07-07 20:16:40 +02:00
|
|
|
filename += ".cpp";
|
|
|
|
|
|
2023-04-11 13:25:10 +02:00
|
|
|
AstCFile* const cfilep = createCFile(filename, m_slow, true /*source*/);
|
2021-07-07 20:16:40 +02:00
|
|
|
cfilep->support(true);
|
2022-09-13 18:15:34 +02:00
|
|
|
m_cfilesr.push_back(cfilep);
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
if (optSystemC()) {
|
2022-11-20 19:11:01 +01:00
|
|
|
m_ofp = new V3OutScFile{filename};
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
2022-11-20 19:11:01 +01:00
|
|
|
m_ofp = new V3OutCFile{filename};
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
2023-04-11 13:25:10 +02:00
|
|
|
putsHeader();
|
|
|
|
|
puts("// DESCR"
|
|
|
|
|
"IPTION: Verilator output: Tracing implementation internals\n");
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
// Includes
|
|
|
|
|
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
|
|
|
|
|
puts("#include \"" + symClassName() + ".h\"\n");
|
|
|
|
|
puts("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool emitTraceIsScBv(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;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const varp = varrefp->varp();
|
2021-07-07 20:16:40 +02:00
|
|
|
return varp->isSc() && varp->isScBv();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool emitTraceIsScBigUint(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;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const varp = varrefp->varp();
|
2021-07-07 20:16:40 +02:00
|
|
|
return varp->isSc() && varp->isScBigUint();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool emitTraceIsScUint(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;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const varp = varrefp->varp();
|
2021-07-07 20:16:40 +02:00
|
|
|
return varp->isSc() && varp->isScUint();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) {
|
|
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
|
|
|
|
puts("tracep->declDouble");
|
|
|
|
|
} else if (nodep->isWide()) {
|
|
|
|
|
puts("tracep->declArray");
|
|
|
|
|
} else if (nodep->isQuad()) {
|
|
|
|
|
puts("tracep->declQuad");
|
|
|
|
|
} else if (nodep->bitRange().ranged()) {
|
|
|
|
|
puts("tracep->declBus");
|
2022-11-23 10:07:14 +01:00
|
|
|
} else if (nodep->dtypep()->basicp()->isEvent()) {
|
|
|
|
|
puts("tracep->declEvent");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
|
|
|
|
puts("tracep->declBit");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
puts("(c+" + cvtToStr(nodep->code()));
|
|
|
|
|
if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords()));
|
|
|
|
|
puts(",");
|
|
|
|
|
putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect()));
|
|
|
|
|
// Direction
|
|
|
|
|
if (v3Global.opt.traceFormat().fst()) {
|
|
|
|
|
puts("," + cvtToStr(enumNum));
|
|
|
|
|
// fstVarDir
|
|
|
|
|
if (nodep->declDirection().isInoutish()) {
|
|
|
|
|
puts(",FST_VD_INOUT");
|
|
|
|
|
} else if (nodep->declDirection().isWritable()) {
|
|
|
|
|
puts(",FST_VD_OUTPUT");
|
|
|
|
|
} else if (nodep->declDirection().isNonOutput()) {
|
|
|
|
|
puts(",FST_VD_INPUT");
|
|
|
|
|
} else {
|
|
|
|
|
puts(", FST_VD_IMPLICIT");
|
|
|
|
|
}
|
|
|
|
|
//
|
|
|
|
|
// fstVarType
|
2022-01-02 19:56:40 +01:00
|
|
|
const VVarType vartype = nodep->varType();
|
|
|
|
|
const VBasicDTypeKwd kwd = nodep->declKwd();
|
2021-07-07 20:16:40 +02:00
|
|
|
string fstvt;
|
|
|
|
|
// Doubles have special decoding properties, so must indicate if a double
|
|
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2023-08-20 14:55:16 +02:00
|
|
|
if (vartype.isParam()) {
|
2021-07-07 20:16:40 +02:00
|
|
|
fstvt = "FST_VT_VCD_REAL_PARAMETER";
|
|
|
|
|
} else {
|
|
|
|
|
fstvt = "FST_VT_VCD_REAL";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// clang-format off
|
2022-01-02 19:56:40 +01:00
|
|
|
else if (vartype == VVarType::GPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; }
|
|
|
|
|
else if (vartype == VVarType::LPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; }
|
|
|
|
|
else if (vartype == VVarType::SUPPLY0) { fstvt = "FST_VT_VCD_SUPPLY0"; }
|
|
|
|
|
else if (vartype == VVarType::SUPPLY1) { fstvt = "FST_VT_VCD_SUPPLY1"; }
|
|
|
|
|
else if (vartype == VVarType::TRI0) { fstvt = "FST_VT_VCD_TRI0"; }
|
|
|
|
|
else if (vartype == VVarType::TRI1) { fstvt = "FST_VT_VCD_TRI1"; }
|
|
|
|
|
else if (vartype == VVarType::TRIWIRE) { fstvt = "FST_VT_VCD_TRI"; }
|
|
|
|
|
else if (vartype == VVarType::WIRE) { fstvt = "FST_VT_VCD_WIRE"; }
|
|
|
|
|
else if (vartype == VVarType::PORT) { fstvt = "FST_VT_VCD_WIRE"; }
|
2021-07-07 20:16:40 +02:00
|
|
|
//
|
2022-01-02 19:56:40 +01:00
|
|
|
else if (kwd == VBasicDTypeKwd::INTEGER) { fstvt = "FST_VT_VCD_INTEGER"; }
|
|
|
|
|
else if (kwd == VBasicDTypeKwd::BIT) { fstvt = "FST_VT_SV_BIT"; }
|
|
|
|
|
else if (kwd == VBasicDTypeKwd::LOGIC) { fstvt = "FST_VT_SV_LOGIC"; }
|
|
|
|
|
else if (kwd == VBasicDTypeKwd::INT) { fstvt = "FST_VT_SV_INT"; }
|
|
|
|
|
else if (kwd == VBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; }
|
|
|
|
|
else if (kwd == VBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; }
|
|
|
|
|
else if (kwd == VBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; }
|
2022-11-23 10:07:14 +01:00
|
|
|
else if (kwd == VBasicDTypeKwd::EVENT) { fstvt = "FST_VT_VCD_EVENT"; }
|
2021-07-07 20:16:40 +02:00
|
|
|
else { fstvt = "FST_VT_SV_BIT"; }
|
|
|
|
|
// clang-format on
|
|
|
|
|
//
|
|
|
|
|
// Not currently supported
|
|
|
|
|
// FST_VT_VCD_PORT
|
|
|
|
|
// FST_VT_VCD_SHORTREAL
|
|
|
|
|
// FST_VT_VCD_REALTIME
|
|
|
|
|
// FST_VT_VCD_SPARRAY
|
|
|
|
|
// FST_VT_VCD_TRIAND
|
|
|
|
|
// FST_VT_VCD_TRIOR
|
|
|
|
|
// FST_VT_VCD_TRIREG
|
|
|
|
|
// FST_VT_VCD_WAND
|
|
|
|
|
// FST_VT_VCD_WOR
|
|
|
|
|
// FST_VT_SV_ENUM
|
|
|
|
|
// FST_VT_GEN_STRING
|
|
|
|
|
puts("," + fstvt);
|
|
|
|
|
}
|
|
|
|
|
// Range
|
|
|
|
|
if (nodep->arrayRange().ranged()) {
|
|
|
|
|
puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")");
|
|
|
|
|
} else {
|
|
|
|
|
puts(", false,-1");
|
|
|
|
|
}
|
|
|
|
|
if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) {
|
|
|
|
|
puts(", " + cvtToStr(nodep->bitRange().left()) + ","
|
|
|
|
|
+ cvtToStr(nodep->bitRange().right()));
|
|
|
|
|
}
|
|
|
|
|
puts(");");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int emitTraceDeclDType(AstNodeDType* nodep) {
|
|
|
|
|
// Return enum number or -1 for none
|
|
|
|
|
if (v3Global.opt.traceFormat().fst()) {
|
|
|
|
|
// 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)) {
|
2022-09-12 18:00:41 +02:00
|
|
|
int enumNum = m_enumNumMap[enump];
|
2021-07-07 20:16:40 +02:00
|
|
|
if (!enumNum) {
|
|
|
|
|
enumNum = ++m_enumNum;
|
2022-09-12 18:00:41 +02:00
|
|
|
m_enumNumMap[enump] = enumNum;
|
2021-07-07 20:16:40 +02:00
|
|
|
int nvals = 0;
|
|
|
|
|
puts("{\n");
|
|
|
|
|
puts("const char* " + protect("__VenumItemNames") + "[]\n");
|
|
|
|
|
puts("= {");
|
|
|
|
|
for (AstEnumItem* itemp = enump->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
2021-07-07 20:16:40 +02:00
|
|
|
if (++nvals > 1) puts(", ");
|
|
|
|
|
putbs("\"" + itemp->prettyName() + "\"");
|
|
|
|
|
}
|
|
|
|
|
puts("};\n");
|
|
|
|
|
nvals = 0;
|
|
|
|
|
puts("const char* " + protect("__VenumItemValues") + "[]\n");
|
|
|
|
|
puts("= {");
|
|
|
|
|
for (AstEnumItem* itemp = enump->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstConst* const constp = VN_AS(itemp->valuep(), Const);
|
2021-07-07 20:16:40 +02:00
|
|
|
if (++nvals > 1) puts(", ");
|
|
|
|
|
putbs("\"" + constp->num().displayed(nodep, "%0b") + "\"");
|
|
|
|
|
}
|
|
|
|
|
puts("};\n");
|
|
|
|
|
puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \""
|
|
|
|
|
+ enump->prettyName() + "\", " + cvtToStr(nvals) + ", "
|
|
|
|
|
+ cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", "
|
|
|
|
|
+ protect("__VenumItemValues") + ");\n");
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
return enumNum;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep->precondsp());
|
2021-07-07 20:16:40 +02:00
|
|
|
const string func = nodep->full() ? "full" : "chg";
|
|
|
|
|
bool emitWidth = true;
|
|
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2022-05-29 20:08:39 +02:00
|
|
|
puts("bufp->" + func + "Double");
|
2021-07-07 20:16:40 +02:00
|
|
|
emitWidth = false;
|
|
|
|
|
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
|
2022-05-29 20:08:39 +02:00
|
|
|
puts("bufp->" + func + "WData");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->isQuad()) {
|
2022-05-29 20:08:39 +02:00
|
|
|
puts("bufp->" + func + "QData");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 16) {
|
2022-05-29 20:08:39 +02:00
|
|
|
puts("bufp->" + func + "IData");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 8) {
|
2022-05-29 20:08:39 +02:00
|
|
|
puts("bufp->" + func + "SData");
|
2021-07-07 20:16:40 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 1) {
|
2022-05-29 20:08:39 +02:00
|
|
|
puts("bufp->" + func + "CData");
|
2022-11-23 10:07:14 +01:00
|
|
|
} else if (nodep->dtypep()->basicp()->isEvent()) {
|
|
|
|
|
puts("bufp->" + func + "Event");
|
|
|
|
|
emitWidth = false;
|
2021-07-07 20:16:40 +02:00
|
|
|
} else {
|
2022-05-29 20:08:39 +02:00
|
|
|
puts("bufp->" + func + "Bit");
|
2021-07-07 20:16:40 +02:00
|
|
|
emitWidth = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords());
|
|
|
|
|
const uint32_t code = nodep->declp()->code() + offset;
|
2022-05-29 20:08:39 +02:00
|
|
|
puts(v3Global.opt.useTraceOffload() && !nodep->full() ? "(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");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void emitTraceValue(AstTraceInc* nodep, int arrayindex) {
|
|
|
|
|
if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstVar* const varp = varrefp->varp();
|
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
|
|
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
|
|
|
|
// Open a new file
|
2021-07-14 23:37:37 +02:00
|
|
|
openNextOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EmitCFunc::visit(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTracePushNamePrefix* nodep) override {
|
2021-12-19 16:15:07 +01:00
|
|
|
puts("tracep->pushNamePrefix(");
|
|
|
|
|
putsQuoted(VIdProtect::protectWordsIf(nodep->prefix(), nodep->protect()));
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTracePopNamePrefix* nodep) override { //
|
2021-12-19 16:15:07 +01:00
|
|
|
puts("tracep->popNamePrefix(");
|
|
|
|
|
puts(cvtToStr(nodep->count()));
|
|
|
|
|
puts(");\n");
|
|
|
|
|
}
|
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());
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-13 18:15:34 +02:00
|
|
|
explicit EmitCTrace(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr)
|
|
|
|
|
: m_slow{slow}
|
|
|
|
|
, m_cfilesr{cfilesr} {
|
2021-07-07 20:16:40 +02:00
|
|
|
m_modp = modp;
|
|
|
|
|
// Open output file
|
2021-07-14 23:37:37 +02:00
|
|
|
openNextOutputFile();
|
2021-07-07 20:16:40 +02:00
|
|
|
// Emit functions
|
|
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2023-03-18 17:17:25 +01:00
|
|
|
if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) { iterateConst(funcp); }
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
// Close output file
|
|
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~EmitCTrace() override = default;
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
public:
|
2023-05-05 14:36:20 +02:00
|
|
|
static void main(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
|
2022-09-13 18:15:34 +02:00
|
|
|
EmitCTrace{modp, slow, cfilesr};
|
|
|
|
|
}
|
2021-07-07 20:16:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// EmitC class functions
|
|
|
|
|
|
|
|
|
|
void V3EmitC::emitcImp() {
|
|
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-07-22 16:53:42 +02:00
|
|
|
// Make parent module pointers available.
|
2021-11-26 23:55:36 +01:00
|
|
|
const EmitCParentModule emitCParentModule;
|
2022-09-13 18:15:34 +02:00
|
|
|
std::list<std::deque<AstCFile*>> cfiles;
|
2023-05-05 14:36:20 +02:00
|
|
|
std::list<std::future<void>> futures;
|
2021-07-07 20:16:40 +02:00
|
|
|
|
|
|
|
|
// 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
|
2021-10-22 16:15:42 +02:00
|
|
|
const AstNodeModule* const modp = VN_AS(nodep, NodeModule);
|
2022-09-13 18:15:34 +02:00
|
|
|
cfiles.emplace_back();
|
2023-05-05 14:36:20 +02:00
|
|
|
auto& slowCfilesr = cfiles.back();
|
2023-06-24 00:25:12 +02:00
|
|
|
futures.push_back(V3ThreadPool::s().enqueue(
|
2023-05-05 14:36:20 +02:00
|
|
|
[modp, &slowCfilesr]() { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); }));
|
2022-09-13 18:15:34 +02:00
|
|
|
cfiles.emplace_back();
|
2023-05-05 14:36:20 +02:00
|
|
|
auto& fastCfilesr = cfiles.back();
|
2023-06-24 00:25:12 +02:00
|
|
|
futures.push_back(V3ThreadPool::s().enqueue(
|
2023-05-05 14:36:20 +02:00
|
|
|
[modp, &fastCfilesr]() { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); }));
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit trace routines (currently they can only exist in the top module)
|
2021-07-19 18:00:23 +02:00
|
|
|
if (v3Global.opt.trace() && !v3Global.opt.lintOnly()) {
|
2022-09-13 18:15:34 +02:00
|
|
|
cfiles.emplace_back();
|
2023-05-05 14:36:20 +02:00
|
|
|
auto& slowCfilesr = cfiles.back();
|
2023-06-24 00:25:12 +02:00
|
|
|
futures.push_back(V3ThreadPool::s().enqueue([&slowCfilesr]() {
|
2023-05-05 14:36:20 +02:00
|
|
|
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr);
|
|
|
|
|
}));
|
2022-09-13 18:15:34 +02:00
|
|
|
cfiles.emplace_back();
|
2023-05-05 14:36:20 +02:00
|
|
|
auto& fastCfilesr = cfiles.back();
|
2023-06-24 00:25:12 +02:00
|
|
|
futures.push_back(V3ThreadPool::s().enqueue([&fastCfilesr]() {
|
2023-05-05 14:36:20 +02:00
|
|
|
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr);
|
|
|
|
|
}));
|
2022-09-13 18:15:34 +02:00
|
|
|
}
|
2023-05-05 14:36:20 +02:00
|
|
|
// Wait for futures
|
|
|
|
|
V3ThreadPool::waitForFutures(futures);
|
2022-09-13 18:15:34 +02:00
|
|
|
for (const auto& collr : cfiles) {
|
|
|
|
|
for (const auto cfilep : collr) v3Global.rootp()->addFilesp(cfilep);
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3EmitC::emitcFiles() {
|
|
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
|
for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep;
|
2021-10-22 14:56:48 +02:00
|
|
|
filep = VN_AS(filep->nextp(), NodeFile)) {
|
2021-11-26 23:55:36 +01:00
|
|
|
AstCFile* const cfilep = VN_CAST(filep, CFile);
|
2021-07-07 20:16:40 +02:00
|
|
|
if (cfilep && cfilep->tblockp()) {
|
2022-11-20 19:11:01 +01:00
|
|
|
V3OutCFile of{cfilep->name()};
|
2021-07-07 20:16:40 +02:00
|
|
|
of.puts("// DESCR"
|
|
|
|
|
"IPTION: Verilator generated C++\n");
|
2022-11-20 19:11:01 +01:00
|
|
|
const EmitCFunc visitor{cfilep->tblockp(), &of, true};
|
2021-07-07 20:16:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|