Internals: Refactor file handling in EmitC* (#6667)

Combined the 3 various APIs used in EmitC* passes to handle file
opening/splitting into a single one. This removes a lot of copy paste
and makes everything consistent.

All C++ file handling goes through `EmitCBaseVisitor` using the
`openNewOutputHeaderFile`, `openNewOutputSourceFile` and
`closOutputFile` methods.

To emit a new kind of file, always derive a new class from
`EmitCBaseVisitor`, and use the above APIs, they will take care of
everything else in a consistent matter.

Subsequently also removed V3OutSCFile, and instead included
verilated_sc.h (which included the systemc header itself) in the two
files that need it (the primary model header, and the root module
header).

Functional changes:
- The PCH header did not use to have a corresponding AstCFile. Now it
  does, though this makes no difference in the output
- All 'slow' sources now have '__Slow' in the name automatically (the
  only one missing was for the ConstPool files)

Rest of the output is identical except for the header line now being
present in all generated C++ files.
This commit is contained in:
Geza Lore 2025-11-09 17:41:13 +00:00 committed by GitHub
parent aaafa6e8df
commit f4086496cb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 414 additions and 511 deletions

View File

@ -92,21 +92,6 @@ string EmitCBaseVisitorConst::funcNameProtect(const AstCFunc* nodep, const AstNo
return name;
}
AstCFile* EmitCBaseVisitorConst::newCFile(const string& filename, bool slow, bool source) {
AstCFile* const cfilep = createCFile(filename, slow, source);
v3Global.rootp()->addFilesp(cfilep);
return cfilep;
}
AstCFile* EmitCBaseVisitorConst::createCFile(const string& filename, bool slow,
bool source) VL_MT_SAFE {
AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), filename};
cfilep->slow(slow);
cfilep->source(source);
if (source) V3Stats::addStatSum(V3Stats::STAT_CPP_FILES, 1);
return cfilep;
}
string EmitCBaseVisitorConst::cFuncArgs(const AstCFunc* nodep) {
// Return argument list for given C function
string args;

View File

@ -85,40 +85,89 @@ public:
// Base Visitor class -- holds output file pointer
class EmitCBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
public:
// STATE
V3OutCFile* m_ofp = nullptr;
AstCFile* m_outFileNodep = nullptr;
int m_splitSize = 0; // # of cfunc nodes placed into output file
V3OutCFile* m_ofp = nullptr; // File handle to emit to
AstCFile* m_cfilep = nullptr; // Current AstCFile being emitted
std::vector<AstCFile*> m_newCfileps; // AstCFiles created
size_t m_splitSize = 0; // Complexity of this file
const size_t m_splitLimit = v3Global.opt.outputSplit()
? static_cast<size_t>(v3Global.opt.outputSplit())
: std::numeric_limits<size_t>::max();
// METHODS
// Create new AstCFile and open it for writing
void openNewOutputFile(const std::string& baseName, bool slow, bool support, bool source,
const char* const descriptionp) {
std::string fileName = v3Global.opt.makeDir() + "/" + baseName;
if (source) {
if (slow) fileName += "__Slow";
fileName += ".cpp";
} else {
fileName += ".h";
}
AstCFile* const cfilep = new AstCFile{v3Global.rootp()->fileline(), fileName};
cfilep->slow(slow);
cfilep->source(source);
cfilep->support(support);
m_newCfileps.emplace_back(cfilep);
openOutputFile(cfilep, descriptionp);
}
public:
// Create new source AstCFile and open it for writing
void openNewOutputSourceFile(const std::string& baseName, bool slow, bool support,
const char* descriptionp) {
V3Stats::addStatSum(V3Stats::STAT_CPP_FILES, 1);
openNewOutputFile(baseName, slow, support, true, descriptionp);
}
// Create new header AstCFile and open it for writing
void openNewOutputHeaderFile(const std::string& baseName, const char* descriptionp) {
openNewOutputFile(baseName, false, false, false, descriptionp);
}
// Open exisitng AstCFile for writing
void openOutputFile(AstCFile* cfilep, const char* descriptionp) {
UASSERT(!m_ofp, "Output file is already open");
m_cfilep = cfilep;
m_splitSize = 0;
if (v3Global.opt.lintOnly()) {
// Unfortunately we have some lint checks in EmitCImp, so we can't
// just skip processing. TODO: Move them to an earlier stage.
m_ofp = new V3OutCFile{VL_DEV_NULL};
return;
}
m_ofp = new V3OutCFile{cfilep->name()};
putsHeader();
// Emit description
m_ofp->putsNoTracking("// DESCR" /* keep this comment */ "IPTION: Verilator output: ");
m_ofp->putsNoTracking(descriptionp);
m_ofp->putsNoTracking("\n");
}
// Close current output file. Sets ofp() and outFileNodep() to nullptr.
void closeOutputFile() {
UASSERT(m_ofp, "No currently open output file");
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
m_cfilep->complexityScore(m_splitSize);
m_cfilep = nullptr;
}
std::vector<AstCFile*> getAndClearCfileps() {
UASSERT(!m_ofp, "Output file is open");
return std::move(m_newCfileps);
}
void splitSizeInc(size_t count) { m_splitSize += count; }
void splitSizeInc(const AstNode* nodep) {
splitSizeInc(static_cast<size_t>(nodep->nodeCount()));
}
bool splitNeeded(size_t splitLimit) const { return m_splitSize >= splitLimit; }
bool splitNeeded() const { return splitNeeded(m_splitLimit); }
// Returns pointer to current output file object.
V3OutCFile* ofp() const VL_MT_SAFE { return m_ofp; }
// Returns pointer to the AST node that represents the output file (`ofp()`)
AstCFile* outFileNodep() const VL_MT_SAFE { return m_outFileNodep; }
// Sets ofp() and outFileNodep() to the given pointers, without closing a file these pointers
// currently point to.
void setOutputFile(V3OutCFile* ofp, AstCFile* nodep) {
// cppcheck-suppress danglingLifetime // ofp is often on stack in caller, it's fine.
m_ofp = ofp;
m_outFileNodep = nodep;
}
// Sets ofp() and outFileNodep() to null, without closing a file these pointers currently point
// to. NOTE: Dummy nullptr argument is taken to make function calls more explicit.
void setOutputFile(std::nullptr_t nullp) {
UASSERT(nullp == nullptr, "Expected nullptr as the argument");
m_ofp = nullp;
m_outFileNodep = nullp;
}
// Closes current output file. Sets ofp() and outFileNodep() to nullptr.
void closeOutputFile() {
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
m_outFileNodep->complexityScore(m_splitSize);
m_outFileNodep = nullptr;
}
void puts(const string& str) { ofp()->puts(str); }
void putns(const AstNode* nodep, const string& str) { ofp()->putns(nodep, str); }
@ -133,8 +182,6 @@ public:
bool optSystemC() { return v3Global.opt.systemC(); }
static string protect(const string& name) VL_MT_SAFE { return VIdProtect::protect(name); }
static string funcNameProtect(const AstCFunc* nodep, const AstNodeModule* modp = nullptr);
static AstCFile* newCFile(const string& filename, bool slow, bool source);
static AstCFile* createCFile(const string& filename, bool slow, bool source) VL_MT_SAFE;
string cFuncArgs(const AstCFunc* nodep);
void emitCFuncHeader(const AstCFunc* funcp, const AstNodeModule* modp, bool withScope);
void emitCFuncDecl(const AstCFunc* funcp, const AstNodeModule* modp, bool cLinkage = false);
@ -163,7 +210,11 @@ public:
// CONSTRUCTORS
EmitCBaseVisitorConst() = default;
~EmitCBaseVisitorConst() override = default;
~EmitCBaseVisitorConst() override {
UASSERT(!m_ofp, "Did not close output file");
// Add files cerated to the netlist (unless retrieved before destruction)
for (AstCFile* const cfilep : m_newCfileps) v3Global.rootp()->addFilesp(cfilep);
}
};
#endif // guard

View File

@ -20,6 +20,7 @@
#include "V3EmitCConstInit.h"
#include "V3File.h"
#include "V3Stats.h"
#include "V3UniqueNames.h"
#include <algorithm>
#include <cinttypes>
@ -34,40 +35,15 @@ class EmitCConstPool final : public EmitCConstInit {
using OutCFilePair = std::pair<V3OutCFile*, AstCFile*>;
// MEMBERS
uint32_t m_outFileCount = 0;
int m_outFileSize = 0;
VDouble0 m_tablesEmitted;
VDouble0 m_constsEmitted;
V3UniqueNames m_uniqueNames; // Generates unique file names
const std::string m_fileBaseName = EmitCUtil::topClassName() + "__ConstPool";
// METHODS
OutCFilePair newOutCFile() const {
const string fileName = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName()
+ "__ConstPool_" + cvtToStr(m_outFileCount) + ".cpp";
AstCFile* const cfilep = newCFile(fileName, /* slow: */ true, /* source: */ true);
V3OutCFile* const ofp = new V3OutCFile{fileName};
ofp->putsHeader();
ofp->puts("// DESCRIPTION: Verilator output: Constant pool\n");
ofp->puts("//\n");
ofp->puts("\n");
ofp->puts("#include \"verilated.h\"\n");
return {ofp, cfilep};
}
void maybeSplitCFile() {
if (v3Global.opt.outputSplit() && m_outFileSize < v3Global.opt.outputSplit()) return;
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close current file
closeOutputFile();
// Open next file
m_outFileSize = 0;
++m_outFileCount;
const OutCFilePair outFileAndNodePair = newOutCFile();
setOutputFile(outFileAndNodePair.first, outFileAndNodePair.second);
}
void emitVars(const AstConstPool* poolp) {
UASSERT(!ofp(), "Output file should not be open");
std::vector<const AstVar*> varps;
for (AstNode* nodep = poolp->modp()->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstVar* const varp = VN_CAST(nodep, Var)) varps.push_back(varp);
@ -79,12 +55,22 @@ class EmitCConstPool final : public EmitCConstInit {
return ap->name() < bp->name();
});
const OutCFilePair outFileAndNodePair = newOutCFile();
setOutputFile(outFileAndNodePair.first, outFileAndNodePair.second);
for (const AstVar* varp : varps) {
maybeSplitCFile();
const string nameProtect
if (splitNeeded()) {
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
// Close old file
closeOutputFile();
}
if (!ofp()) {
openNewOutputSourceFile(m_uniqueNames.get(m_fileBaseName), true, false,
"Constant pool");
puts("\n");
puts("#include \"verilated.h\"\n");
}
const std::string nameProtect
= EmitCUtil::topClassName() + "__ConstPool__" + varp->nameProtect();
puts("\n");
putns(varp, "extern const ");
@ -101,12 +87,18 @@ class EmitCConstPool final : public EmitCConstInit {
}
}
closeOutputFile();
if (ofp()) closeOutputFile();
}
// VISITORS
void visit(AstConst* nodep) override {
m_outFileSize += nodep->num().isString() ? 10 : nodep->isWide() ? nodep->widthWords() : 1;
if (nodep->num().isString()) {
splitSizeInc(AstNode::INSTR_COUNT_STR);
} else if (nodep->isWide()) {
splitSizeInc(nodep->widthWords());
} else {
splitSizeInc(1);
}
EmitCConstInit::visit(nodep);
}

View File

@ -144,7 +144,7 @@ class EmitCFunc VL_NOT_FINAL : public EmitCConstInit {
} m_emitDispState;
protected:
EmitCLazyDecls m_lazyDecls; // Visitor for emitting lazy declarations
EmitCLazyDecls m_lazyDecls{*this}; // Visitor for emitting lazy declarations
bool m_useSelfForThis = false; // Replace "this" with "vlSelf"
bool m_usevlSelfRef = false; // Use vlSelfRef reference instead of vlSelf pointer
const AstNodeModule* m_modp = nullptr; // Current module being emitted
@ -169,16 +169,6 @@ protected:
}
public:
// METHODS
// ACCESSORS
void splitSizeInc(int count) { m_splitSize += count; }
void splitSizeInc(const AstNode* nodep) { splitSizeInc(nodep->nodeCount()); }
void splitSizeReset() { m_splitSize = 0; }
bool splitNeeded() const {
return v3Global.opt.outputSplit() && m_splitSize >= v3Global.opt.outputSplit();
}
// METHODS
void displayNode(AstNode* nodep, AstScopeName* scopenamep, const string& vformat,
AstNode* exprsp, bool isScan);
@ -1730,13 +1720,8 @@ public:
}
} // LCOV_EXCL_STOP
EmitCFunc()
: m_lazyDecls{*this} {}
EmitCFunc(AstNode* nodep, V3OutCFile* ofp, AstCFile* cfilep)
: EmitCFunc{} {
setOutputFile(ofp, cfilep);
iterateConst(nodep);
}
protected:
EmitCFunc() = default;
~EmitCFunc() override = default;
};

View File

@ -635,16 +635,7 @@ class EmitCHeader final : public EmitCConstInit {
UINFO(5, " Emitting header for " << EmitCUtil::prefixNameProtect(modp));
// Open output file
const string filename
= v3Global.opt.makeDir() + "/" + EmitCUtil::prefixNameProtect(modp) + ".h";
AstCFile* const cfilep = newCFile(filename, /* slow: */ false, /* source: */ false);
V3OutCFile* const ofilep
= v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename};
setOutputFile(ofilep, cfilep);
ofp()->putsHeader();
puts("// DESCRIPTION: Verilator output: Design internal header\n");
openNewOutputHeaderFile(EmitCUtil::prefixNameProtect(modp), "Design internal header");
puts("// See " + EmitCUtil::topClassName() + ".h for the primary calling header\n");
ofp()->putsGuard();
@ -653,6 +644,7 @@ class EmitCHeader final : public EmitCConstInit {
puts("\n");
ofp()->putsIntTopInclude();
puts("#include \"verilated.h\"\n");
if (modp->isTop() && optSystemC()) puts("#include \"verilated_sc.h\"\n");
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");

View File

@ -30,47 +30,23 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Internal EmitC implementation
class EmitCImp final : EmitCFunc {
class EmitCImp final : public EmitCFunc {
// MEMBERS
const AstNodeModule* const m_fileModp; // Files names/headers constructed using this module
// Base module (For non-classes, same as m_modp. For classes, it's the ClassPackage.)
const AstNodeModule* const m_fileModp;
const bool m_slow; // Creating __Slow file
size_t m_nSplitFiles = 0; // Sequence number for file splitting
std::deque<AstCFile*>& m_cfilesr; // cfiles generated by this emit
V3UniqueNames m_uniqueNames; // Generates unique file names
const std::string m_fileBaseName = EmitCUtil::prefixNameProtect(m_fileModp);
// METHODS
void openNextOutputFile(bool canBeSplit) {
UASSERT(!ofp(), "Output file already open");
splitSizeReset(); // Reset file size tracking
m_lazyDecls.reset(); // Need to emit new lazy declarations
AstCFile* filep = nullptr;
V3OutCFile* ofilep = nullptr;
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 std::string filename = VL_DEV_NULL;
filep = createCFile(filename, /* slow: */ m_slow, /* source: */ true);
ofilep = new V3OutCFile{filename};
} else {
std::string filename = v3Global.opt.makeDir();
filename += "/" + EmitCUtil::prefixNameProtect(m_fileModp);
if (canBeSplit) filename += "__" + std::to_string(m_nSplitFiles++);
if (m_slow) filename += "__Slow";
filename += ".cpp";
filep = createCFile(filename, /* slow: */ m_slow, /* source: */ true);
ofilep = v3Global.opt.systemC() ? new V3OutScFile{filename} : new V3OutCFile{filename};
}
m_cfilesr.push_back(filep);
setOutputFile(ofilep, filep);
putsHeader();
puts("// DESCRIPTION: Verilator output: Design implementation internals\n");
void openNextOutputFile(const std::string& fileName) {
openNewOutputSourceFile(fileName, m_slow, false, "Design implementation internals");
puts("// See " + EmitCUtil::topClassName() + ".h for the primary calling header\n");
puts("\n");
puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n");
emitSystemCSection(m_modp, VSystemCSectionType::IMP_HDR);
emitSystemCSection(m_fileModp, VSystemCSectionType::IMP_HDR);
// Need to emit new lazy declarations
m_lazyDecls.reset();
}
void emitStaticVarDefns(const AstNodeModule* modp) {
@ -402,7 +378,7 @@ class EmitCImp final : EmitCFunc {
= VN_IS(modp, ClassPackage) ? VN_AS(modp, ClassPackage)->classp() : nullptr;
if (hasCommonImp(modp) || hasCommonImp(classp)) {
openNextOutputFile(/* canBeSplit: */ false);
openNextOutputFile(m_fileBaseName);
doCommonImp(modp);
if (classp) {
@ -442,7 +418,7 @@ class EmitCImp final : EmitCFunc {
if (funcps.empty()) return;
// Open output file
openNextOutputFile(/* canBeSplit: */ true);
openNextOutputFile(m_uniqueNames.get(m_fileBaseName));
// Emit all functions
for (AstCFunc* const funcp : funcps) {
VL_RESTORER(m_modp);
@ -461,16 +437,15 @@ class EmitCImp final : EmitCFunc {
// Close old file
closeOutputFile();
// Open a new file
openNextOutputFile(/* canBeSplit: */ true);
openNextOutputFile(m_uniqueNames.get(m_fileBaseName));
}
EmitCFunc::visit(nodep);
}
explicit EmitCImp(const AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr)
explicit EmitCImp(const AstNodeModule* modp, bool slow)
: m_fileModp{modp}
, m_slow{slow}
, m_cfilesr{cfilesr} {
, m_slow{slow} {
UINFO(5, " Emitting implementation of " << EmitCUtil::prefixNameProtect(modp));
m_modp = modp;
@ -489,121 +464,142 @@ class EmitCImp final : EmitCFunc {
~EmitCImp() override = default;
public:
static void main(const AstNodeModule* modp, bool slow,
std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
EmitCImp{modp, slow, cfilesr};
static std::vector<AstCFile*> main(const AstNodeModule* modp, bool slow) VL_MT_STABLE {
EmitCImp emitCImp{modp, slow};
return emitCImp.getAndClearCfileps();
}
};
//######################################################################
// Tracing routines
class EmitCTrace final : EmitCFunc {
// 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 {
// NODE STATE/TYPES
// None allowed to support threaded emitting
// STATE
int m_enumNum = 0; // Enumeration number (whole netlist)
std::unordered_map<AstNode*, int> m_enumNumMap; // EnumDType to enumeration number
int m_traceTypeSubs = 0; // Number of trace type declaration sub-functions
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();
void openNextOutputFile() {
openNewOutputSourceFile(m_uniqueNames.get(m_fileBaseName), true, true,
"Tracing declarations");
puts("\n");
for (const std::string& base : v3Global.opt.traceSourceLangs()) {
puts("#include \"" + base + ".h\"\n");
}
puts("\n");
puts("\nvoid " + EmitCUtil::prefixNameProtect(m_modp) + "__"
+ protect("traceDeclTypesSub" + std::to_string(m_traceTypeSubs++)) + "("
+ v3Global.opt.traceClassBase() + "* tracep) {\n");
}
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();
}
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);
}
return enumNumr;
}
// Close output file
void finalize() {
// Close function definition
puts("}\n");
const std::string modName = EmitCUtil::prefixNameProtect(m_modp);
const std::string args = v3Global.opt.traceClassBase() + "* tracep";
// Forward declarations for subs in other files
for (int i = 0; i < m_traceTypeSubs - 1; ++i) {
puts("void " + modName + "__" + protect("traceDeclTypesSub" + std::to_string(i)) + "("
+ args + ");\n");
}
// Create top level trace_decl_types function and call each sub-function
puts("\nvoid " + modName + "__" + protect("trace_decl_types") + "(" + args + ") {\n");
for (int i = 0; i < m_traceTypeSubs; ++i) {
puts(modName + "__" + protect("traceDeclTypesSub" + std::to_string(i))
+ "(tracep);\n");
}
puts("}\n");
closeOutputFile();
}
EmitCTraceTypes() {
m_modp = v3Global.rootp()->topModulep();
openNextOutputFile();
}
~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
int m_enumNum = 0; // Enumeration number (whole netlist)
V3UniqueNames m_uniqueNames; // For generating unique file names
std::unordered_map<AstNode*, int> m_enumNumMap; // EnumDType to enumeration number
std::deque<AstCFile*>& m_cfilesr; // cfiles generated by this emit
V3OutCFile* m_typesFp = nullptr; // File for type declarations
int m_traceTypeSubs = 0; // Number of trace type declaration sub-functions
int m_typeSplitSize = 0; // # of cfunc nodes placed into output 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");
// METHODS
void openNextOutputFile() {
UASSERT(!ofp(), "Output file already open");
splitSizeReset(); // Reset file size tracking
m_lazyDecls.reset(); // Need to emit new lazy declarations
string filename
= (v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "_" + protect("_Trace"));
filename = m_uniqueNames.get(filename);
if (m_slow) filename += "__Slow";
filename += ".cpp";
AstCFile* const cfilep = createCFile(filename, m_slow, true /*source*/);
cfilep->support(true);
m_cfilesr.push_back(cfilep);
V3OutCFile* const ofilep
= optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename};
setOutputFile(ofilep, cfilep);
putsHeader();
puts("// DESCR"
"IPTION: Verilator output: Tracing implementation internals\n");
// Includes
for (const string& base : v3Global.opt.traceSourceLangs())
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");
}
V3OutCFile* typesFp() const VL_MT_SAFE { return m_typesFp; }
void openNextTypesFile() {
UASSERT(!m_typesFp, "Declarations output file already open");
string filename = (v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "_"
+ protect("_TraceDecls"));
filename = m_uniqueNames.get(filename);
filename += "__Slow.cpp";
AstCFile* const cfilep = createCFile(filename, m_slow, true /*source*/);
cfilep->support(true);
m_cfilesr.push_back(cfilep);
if (optSystemC()) {
m_typesFp = new V3OutScFile{filename};
} else {
m_typesFp = new V3OutCFile{filename};
}
typesFp()->putsHeader();
typesFp()->puts("// DESCR"
"IPTION: Verilator output: Tracing declarations\n");
// Includes
for (const string& base : v3Global.opt.traceSourceLangs())
typesFp()->puts("#include \"" + base + ".h\"\n");
typesFp()->puts("\n");
typesFp()->puts("\nvoid " + EmitCUtil::prefixNameProtect(m_modp) + "__"
+ protect("traceDeclTypesSub" + cvtToStr(m_traceTypeSubs++)) + "("
+ v3Global.opt.traceClassBase() + "* tracep) {\n");
}
void closeTypesFile() {
typesFp()->puts("}\n");
VL_DO_CLEAR(delete m_typesFp, m_typesFp = nullptr);
}
void callTypeSubs() {
typesFp()->puts("}\n");
// Forward declarations for subs in other files
for (int i = 0; i < m_traceTypeSubs - 1; ++i) {
typesFp()->puts("void " + EmitCUtil::prefixNameProtect(m_modp) + "__"
+ protect("traceDeclTypesSub" + cvtToStr(i)) + "("
+ v3Global.opt.traceClassBase() + "* tracep);\n");
}
typesFp()->puts("\nvoid " + EmitCUtil::prefixNameProtect(m_modp) + "__"
+ protect("trace_decl_types") + "(" + v3Global.opt.traceClassBase()
+ "* tracep) {\n");
for (int i = 0; i < m_traceTypeSubs; ++i) {
typesFp()->puts(EmitCUtil::prefixNameProtect(m_modp) + "__"
+ protect("traceDeclTypesSub" + cvtToStr(i)) + "(tracep);\n");
}
}
bool typesSplitNeeded() {
return v3Global.opt.outputSplitCTrace()
&& m_typeSplitSize >= v3Global.opt.outputSplitCTrace();
// Need to emit new lazy declarations
m_lazyDecls.reset();
}
bool emitTraceIsScBv(const AstTraceInc* nodep) {
@ -693,54 +689,13 @@ class EmitCTrace final : EmitCFunc {
puts(");");
}
int getEnumMapNum(AstEnumDType* nodep) {
int enumNum = m_enumNumMap[nodep];
if (!enumNum) {
if (typesSplitNeeded()) {
// Splitting file, so using parallel build.
v3Global.useParallelBuild(true);
closeTypesFile();
openNextTypesFile();
}
enumNum = ++m_enumNum;
m_enumNumMap[nodep] = enumNum;
int nvals = 0;
typesFp()->puts("{\n");
typesFp()->putns(nodep, "const char* " + protect("__VenumItemNames") + "[]\n");
typesFp()->puts("= {");
for (AstEnumItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), EnumItem)) {
if (++nvals > 1) typesFp()->puts(", ");
typesFp()->putbs("\"" + itemp->prettyName() + "\"");
}
typesFp()->puts("};\n");
nvals = 0;
typesFp()->puts("const char* " + protect("__VenumItemValues") + "[]\n");
typesFp()->puts("= {");
for (AstEnumItem* itemp = nodep->itemsp(); itemp;
itemp = VN_AS(itemp->nextp(), EnumItem)) {
AstConst* const constp = VN_AS(itemp->valuep(), Const);
if (++nvals > 1) typesFp()->puts(", ");
typesFp()->putbs("\"" + constp->num().displayed(nodep, "%0b") + "\"");
}
typesFp()->puts("};\n");
typesFp()->puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \""
+ nodep->prettyName() + "\", " + cvtToStr(nvals) + ", "
+ cvtToStr(nodep->widthMin()) + ", " + protect("__VenumItemNames")
+ ", " + protect("__VenumItemValues") + ");\n");
typesFp()->puts("}\n");
m_typeSplitSize += 3;
}
return enumNum;
}
int emitTraceDeclDType(AstNodeDType* nodep) {
// Return enum number or -1 for none
if (v3Global.opt.traceEnabledFst()) {
// 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
if (AstEnumDType* const enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) {
return getEnumMapNum(enump);
return m_emitTypesp->getEnumMapNum(enump);
}
}
return -1;
@ -873,29 +828,49 @@ class EmitCTrace final : EmitCFunc {
}
}
explicit EmitCTrace(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr)
: m_slow{slow}
, m_cfilesr{cfilesr} {
m_modp = modp;
explicit EmitCTrace(bool slow)
: m_slow{slow} {
m_modp = v3Global.rootp()->topModulep();
// Open output file
openNextOutputFile();
if (m_slow) openNextTypesFile();
// Emit functions
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (AstCFunc* const funcp = VN_CAST(nodep, CFunc)) iterateConst(funcp);
}
// Close output file
closeOutputFile();
if (m_slow) {
callTypeSubs();
closeTypesFile();
}
if (m_slow) m_emitTypesp->finalize();
}
~EmitCTrace() override = default;
public:
static void main(AstNodeModule* modp, bool slow, std::deque<AstCFile*>& cfilesr) VL_MT_STABLE {
EmitCTrace{modp, slow, cfilesr};
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};
}
};
@ -904,42 +879,37 @@ public:
void V3EmitC::emitcImp() {
UINFO(2, __FUNCTION__ << ":");
// Make parent module pointers available.
const EmitCParentModule emitCParentModule;
std::list<std::deque<AstCFile*>> cfiles;
V3ThreadScope threadScope;
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();
auto& slowCfilesr = cfiles.back();
threadScope.enqueue(
[modp, &slowCfilesr] { EmitCImp::main(modp, /* slow: */ true, slowCfilesr); });
cfiles.emplace_back();
auto& fastCfilesr = cfiles.back();
threadScope.enqueue(
[modp, &fastCfilesr] { EmitCImp::main(modp, /* slow: */ false, fastCfilesr); });
}
// 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();
auto& slowCfilesr = cfiles.back();
threadScope.enqueue([&slowCfilesr] {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ true, slowCfilesr);
});
cfiles.emplace_back();
auto& fastCfilesr = cfiles.back();
threadScope.enqueue([&fastCfilesr] {
EmitCTrace::main(v3Global.rootp()->topModulep(), /* slow: */ false, fastCfilesr);
});
// 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); });
}
}
// Wait for futures
threadScope.wait();
for (const auto& collr : cfiles) {
for (const auto cfilep : collr) v3Global.rootp()->addFilesp(cfilep);
// Add files to netlist
for (const std::vector<AstCFile*>& cfileps : cfiles) {
for (AstCFile* const cfilep : cfileps) v3Global.rootp()->addFilesp(cfilep);
}
}
@ -947,12 +917,6 @@ void V3EmitC::emitcFiles() {
UINFO(2, __FUNCTION__ << ":");
for (AstNodeFile *filep = v3Global.rootp()->filesp(), *nextp; filep; filep = nextp) {
nextp = VN_AS(filep->nextp(), NodeFile);
AstCFile* const cfilep = VN_CAST(filep, CFile);
if (cfilep && cfilep->tblockp()) {
V3OutCFile of{cfilep->name()};
of.puts("// DESCR"
"IPTION: Verilator generated C++\n");
EmitCFunc{cfilep->tblockp(), &of, cfilep};
}
if (AstCFile* const cfilep = VN_CAST(filep, CFile)) EmitCFile::main(cfilep);
}
}

View File

@ -41,12 +41,6 @@ public:
private:
// MAIN METHOD
void emitInt() {
const string filename
= v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__main.cpp";
AstCFile* const cfilep = newCFile(filename, false /*slow*/, true /*source*/);
V3OutCFile cf{filename};
setOutputFile(&cf, cfilep);
// Not defining main_time/vl_time_stamp, so
v3Global.opt.addCFlags("-DVL_TIME_CONTEXT"); // On MSVC++ anyways
@ -54,11 +48,12 @@ private:
string topName = v3Global.opt.mainTopName();
if (topName == "-") topName = "";
// Heavily commented output, as users are likely to look at or copy this code
ofp()->putsHeader();
puts("// DESCRIPTION: main() calling loop, created with Verilator --main\n");
openNewOutputSourceFile(EmitCUtil::topClassName() + "__main", false, false,
"main() simulation loop, created with --main");
puts("\n");
// Heavily commented output, as users are likely to look at or copy this code
puts("#include \"verilated.h\"\n");
puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
@ -116,7 +111,7 @@ private:
puts("return 0;\n");
puts("}\n");
setOutputFile(nullptr);
closeOutputFile();
}
};

View File

@ -53,15 +53,7 @@ class EmitCModel final : public EmitCFunc {
}
void emitHeader(AstNodeModule* modp) {
UASSERT(!ofp(), "Output file should not be open");
const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + ".h";
setOutputFile(v3Global.opt.systemC() ? new V3OutScFile{filename}
: new V3OutCFile{filename},
newCFile(filename, /* slow: */ false, /* source: */ false));
ofp()->putsHeader();
puts("// DESCRIPTION: Verilator output: Primary model header\n");
openNewOutputHeaderFile(EmitCUtil::topClassName(), "Primary model header");
puts("//\n");
puts("// This header should be included by all source files instantiating the design.\n");
puts("// The class here is then constructed to instantiate the design.\n");
@ -72,7 +64,9 @@ class EmitCModel final : public EmitCFunc {
// Include files
puts("\n");
ofp()->putsIntTopInclude();
puts("#include \"verilated.h\"\n");
if (v3Global.opt.systemC()) puts("#include \"verilated_sc.h\"\n");
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
@ -614,17 +608,8 @@ class EmitCModel final : public EmitCFunc {
}
void emitImplementation(AstNodeModule* modp) {
UASSERT(!ofp(), "Output file should not be open");
const string filename = v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + ".cpp";
setOutputFile(v3Global.opt.systemC() ? new V3OutScFile{filename}
: new V3OutCFile{filename},
newCFile(filename, /* slow: */ false, /* source: */ true));
ofp()->putsHeader();
puts("// DESCRIPTION: Verilator output: "
"Model implementation (design independent parts)\n");
openNewOutputSourceFile(EmitCUtil::topClassName(), false, false,
"Model implementation (design independent parts)");
puts("\n");
puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n");
for (const string& base : v3Global.opt.traceSourceLangs())
@ -643,6 +628,10 @@ class EmitCModel final : public EmitCFunc {
void emitDpiExportDispatchers(AstNodeModule* modp) {
UASSERT(!ofp(), "Output file should not be open");
// File name utils
V3UniqueNames uniqueNames;
const std::string fileBaseName = EmitCUtil::topClassName() + "__Dpi_Export";
// Emit DPI Export dispatchers
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
AstCFunc* const funcp = VN_CAST(nodep, CFunc);
@ -656,23 +645,14 @@ class EmitCModel final : public EmitCFunc {
}
if (!ofp()) {
string filename
= v3Global.opt.makeDir() + "/" + EmitCUtil::topClassName() + "__Dpi_Export";
filename = m_uniqueNames.get(filename);
filename += ".cpp";
setOutputFile(v3Global.opt.systemC() ? new V3OutScFile{filename}
: new V3OutCFile{filename},
newCFile(filename, /* slow: */ false, /* source: */ true));
splitSizeReset(); // Reset file size tracking
m_lazyDecls.reset();
ofp()->putsHeader();
puts(
"// DESCRIPTION: Verilator output: Implementation of DPI export functions.\n");
puts("//\n");
openNewOutputSourceFile(uniqueNames.get(fileBaseName), false, false,
"Implementation of DPI export functions.");
puts("\n");
puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
puts("#include \"" + EmitCUtil::symClassName() + ".h\"\n");
puts("#include \"verilated_dpi.h\"\n");
puts("\n");
m_lazyDecls.reset();
}
iterateConst(funcp);

View File

@ -23,48 +23,49 @@ VL_DEFINE_DEBUG_FUNCTIONS;
//######################################################################
// Precompiled header emitter
class EmitCPch final {
class EmitCPch final : public EmitCBaseVisitorConst {
public:
// METHODS
void emitPch() {
// Generate the makefile
V3OutCFile of{v3Global.opt.makeDir() + "/" + EmitCUtil::pchClassName() + ".h"};
of.putsHeader();
of.puts("// DESCRIPTION: Verilator output: Precompiled header\n");
of.puts("//\n");
of.puts("// Internal details; most user sources do not need this header,\n");
of.puts("// unless using verilator public meta comments.\n");
of.puts("// Suggest use " + EmitCUtil::topClassName() + ".h instead.\n");
of.puts("\n");
openNewOutputHeaderFile(EmitCUtil::pchClassName(), "Precompiled header");
puts("//\n");
puts("// Internal details; most user sources do not need this header,\n");
puts("// unless using verilator public meta comments.\n");
puts("// Suggest use " + EmitCUtil::topClassName() + ".h instead.\n");
of.putsGuard();
ofp()->putsGuard();
of.puts("\n");
of.puts("// GCC and Clang only will precompile headers (PCH) for the first header.\n");
of.puts("// So, make sure this is the one and only PCH.\n");
of.puts("// If multiple module's includes are needed, use individual includes.\n");
of.puts("#ifdef VL_PCH_INCLUDED\n");
of.puts("# error \"Including multiple precompiled header files\"\n");
of.puts("#endif\n");
of.puts("#define VL_PCH_INCLUDED\n");
puts("\n");
puts("// GCC and Clang only will precompile headers (PCH) for the first header.\n");
puts("// So, make sure this is the one and only PCH.\n");
puts("// If multiple module's includes are needed, use individual includes.\n");
puts("#ifdef VL_PCH_INCLUDED\n");
puts("# error \"Including multiple precompiled header files\"\n");
puts("#endif\n");
puts("#define VL_PCH_INCLUDED\n");
of.puts("\n");
of.puts("\n#include \"verilated.h\"\n");
if (v3Global.dpi()) of.puts("#include \"verilated_dpi.h\"\n");
puts("\n");
puts("\n#include \"verilated.h\"\n");
if (v3Global.dpi()) puts("#include \"verilated_dpi.h\"\n");
of.puts("\n");
of.puts("#include \"" + EmitCUtil::symClassName() + ".h\"\n");
of.puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
puts("\n");
puts("#include \"" + EmitCUtil::symClassName() + ".h\"\n");
puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
of.puts("\n// Additional include files added using '--compiler-include'\n");
puts("\n// Additional include files added using '--compiler-include'\n");
for (const string& filename : v3Global.opt.compilerIncludes()) {
of.puts("#include \"" + filename + "\"\n");
puts("#include \"" + filename + "\"\n");
}
of.putsEndGuard();
ofp()->putsEndGuard();
closeOutputFile();
}
// VISITOR
void visit(AstNode* nodep) { nodep->v3fatalSrc("Unused"); }
public:
explicit EmitCPch() { emitPch(); }
};

View File

@ -103,16 +103,8 @@ class EmitCSyms final : EmitCBaseVisitorConst {
const bool m_dpiHdrOnly; // Only emit the DPI header
std::vector<std::string> m_splitFuncNames; // Split file names
VDouble0 m_statVarScopeBytes; // Statistic tracking
const std::string m_symsFileBase = v3Global.opt.makeDir() + "/" + symClassName();
// METHODS
void openOutputFile(const std::string fileName) {
V3OutCFile* const fp = optSystemC() ? new V3OutScFile{fileName} : new V3OutCFile{fileName};
AstCFile* const cfilep = newCFile(fileName, true /*slow*/, true /*source*/);
cfilep->support(true);
setOutputFile(fp, cfilep);
}
void emitSymHdr();
void emitSymImpPreamble();
void emitScopeHier(std::vector<std::string>& stmts, bool destroy);
@ -415,14 +407,7 @@ public:
void EmitCSyms::emitSymHdr() {
UINFO(6, __FUNCTION__ << ": ");
const std::string filename = m_symsFileBase + ".h";
AstCFile* const cfilep = newCFile(filename, true /*slow*/, false /*source*/);
V3OutCFile* const ofilep = optSystemC() ? new V3OutScFile{filename} : new V3OutCFile{filename};
setOutputFile(ofilep, cfilep);
ofp()->putsHeader();
puts("// DESCR"
"IPTION: Verilator output: Symbol table internal header\n");
openNewOutputHeaderFile(symClassName(), "Symbol table internal header");
puts("//\n");
puts("// Internal details; most calling programs do not need this header,\n");
puts("// unless using verilator public meta comments.\n");
@ -596,9 +581,6 @@ void EmitCSyms::emitSymHdr() {
}
void EmitCSyms::emitSymImpPreamble() {
ofp()->putsHeader();
puts("// DESCR"
"IPTION: Verilator output: Symbol table implementation internals\n");
puts("\n");
// Includes
@ -883,7 +865,6 @@ std::vector<std::string> EmitCSyms::getSymDtorStmts() {
void EmitCSyms::emitSplit(std::vector<std::string>& stmts, const std::string name,
size_t maxCost) {
const std::string baseName = m_symsFileBase + "__" + name;
size_t nSubFunctions = 0;
// Reduce into a balanced tree of sub-function calls until we end up with a single statement
while (stmts.size() > 1) {
@ -905,7 +886,7 @@ void EmitCSyms::emitSplit(std::vector<std::string>& stmts, const std::string nam
const std::string funcName = symClassName() + "__" + name + "__" + nStr;
m_splitFuncNames.emplace_back(funcName);
// Open split file
openOutputFile(baseName + "__" + nStr + "__Slow.cpp");
openNewOutputSourceFile(funcName, true, true, "Symbol table implementation internals");
// Emit header
emitSymImpPreamble();
// Open sub-function definition in the split file
@ -913,9 +894,9 @@ void EmitCSyms::emitSplit(std::vector<std::string>& stmts, const std::string nam
// Emit statements
for (size_t j = splitStart; j < splitEnd; ++j) {
m_ofp->putsNoTracking(" ");
m_ofp->putsNoTracking(stmts[j]);
m_ofp->putsNoTracking("\n");
ofp()->putsNoTracking(" ");
ofp()->putsNoTracking(stmts[j]);
ofp()->putsNoTracking("\n");
}
// Close sub-function
@ -961,7 +942,7 @@ void EmitCSyms::emitSymImp(AstNetlist* netlistp) {
}
}
openOutputFile(m_symsFileBase + "__Slow.cpp");
openNewOutputSourceFile(symClassName(), true, true, "Symbol table implementation internals");
emitSymImpPreamble();
// Constructor
@ -988,18 +969,18 @@ void EmitCSyms::emitSymImp(AstNetlist* netlistp) {
}
puts("{\n");
for (const std::string& stmt : ctorStmts) {
m_ofp->putsNoTracking(" ");
m_ofp->putsNoTracking(stmt);
m_ofp->putsNoTracking("\n");
ofp()->putsNoTracking(" ");
ofp()->putsNoTracking(stmt);
ofp()->putsNoTracking("\n");
}
puts("}\n");
// Destructor
puts("\n" + symClassName() + "::~" + symClassName() + "() {\n");
for (const std::string& stmt : dtorStmts) {
m_ofp->putsNoTracking(" ");
m_ofp->putsNoTracking(stmt);
m_ofp->putsNoTracking("\n");
ofp()->putsNoTracking(" ");
ofp()->putsNoTracking(stmt);
ofp()->putsNoTracking("\n");
}
puts("}\n");
@ -1058,15 +1039,9 @@ void EmitCSyms::emitSymImp(AstNetlist* netlistp) {
void EmitCSyms::emitDpiHdr() {
UINFO(6, __FUNCTION__ << ": ");
const std::string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.h";
AstCFile* const cfilep = newCFile(filename, false /*slow*/, false /*source*/);
cfilep->support(true);
V3OutCFile hf{filename};
setOutputFile(&hf, cfilep);
ofp()->putsHeader();
puts("// DESCR"
"IPTION: Verilator output: Prototypes for DPI import and export functions.\n");
openNewOutputHeaderFile(topClassName() + "__Dpi",
"Prototypes for DPI import and export functions.");
puts("//\n");
puts("// Verilator includes this file in all generated .cpp files that use DPI functions.\n");
puts("// Manually include this file where DPI .c import functions are declared to ensure\n");
@ -1105,22 +1080,16 @@ void EmitCSyms::emitDpiHdr() {
puts("#endif\n");
ofp()->putsEndGuard();
setOutputFile(nullptr);
closeOutputFile();
}
//######################################################################
void EmitCSyms::emitDpiImp() {
UINFO(6, __FUNCTION__ << ": ");
const std::string filename = v3Global.opt.makeDir() + "/" + topClassName() + "__Dpi.cpp";
AstCFile* const cfilep = newCFile(filename, false /*slow*/, true /*source*/);
cfilep->support(true);
V3OutCFile hf(filename);
setOutputFile(&hf, cfilep);
ofp()->putsHeader();
puts("// DESCR"
"IPTION: Verilator output: Implementation of DPI export functions.\n");
openNewOutputSourceFile(topClassName() + "__Dpi", false, true,
"Implementation of DPI export functions");
puts("//\n");
puts("// Verilator compiles this file in when DPI functions are used.\n");
puts("// If you have multiple Verilated designs with the same DPI exported\n");
@ -1164,7 +1133,7 @@ void EmitCSyms::emitDpiImp() {
puts("#endif\n");
puts("\n");
}
setOutputFile(nullptr);
closeOutputFile();
}
//######################################################################

View File

@ -400,19 +400,6 @@ public:
}
};
class V3OutScFile final : public V3OutCFile {
public:
explicit V3OutScFile(const string& filename)
: V3OutCFile{filename} {}
~V3OutScFile() override = default;
void putsHeader() override { puts("// Verilated -*- SystemC -*-\n"); }
void putsIntTopInclude() override {
putsForceIncs();
puts("#include \"systemc\"\n");
puts("#include \"verilated_sc.h\"\n");
}
};
class V3OutVFile final : public V3OutCFile {
public:
explicit V3OutVFile(const string& filename)

View File

@ -125,7 +125,7 @@ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_clas
test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2")
# Check combine count
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (271 if test.vltmt else 254))
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (272 if test.vltmt else 255))
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2)
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2)

View File

@ -2924,69 +2924,70 @@
],
"filesp": [
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck__Syms__Slow.cpp","addr":"(ZQB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck__Syms.h","addr":"(ARB)","loc":"a,0:0,0:0","source":false,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck__Syms.h","addr":"(ARB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck.h","addr":"(BRB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck.cpp","addr":"(CRB)","loc":"a,0:0,0:0","source":true,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root.h","addr":"(DRB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$unit.h","addr":"(ERB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root__Slow.cpp","addr":"(FRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root__0__Slow.cpp","addr":"(GRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root__0.cpp","addr":"(HRB)","loc":"a,0:0,0:0","source":true,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$unit__Slow.cpp","addr":"(IRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$unit__0__Slow.cpp","addr":"(JRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []}
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck__pch.h","addr":"(DRB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root.h","addr":"(ERB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$unit.h","addr":"(FRB)","loc":"a,0:0,0:0","source":false,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root__Slow.cpp","addr":"(GRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root__0__Slow.cpp","addr":"(HRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$root__0.cpp","addr":"(IRB)","loc":"a,0:0,0:0","source":true,"slow":false,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$unit__Slow.cpp","addr":"(JRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []},
{"type":"CFILE","name":"obj_vlt/t_json_only_debugcheck/Vt_json_only_debugcheck_$unit__0__Slow.cpp","addr":"(KRB)","loc":"a,0:0,0:0","source":true,"slow":true,"tblockp": []}
],
"miscsp": [
{"type":"TYPETABLE","name":"","addr":"(C)","loc":"a,0:0,0:0","constraintRefp":"UNLINKED","emptyQueuep":"UNLINKED","queueIndexp":"UNLINKED","streamp":"UNLINKED","voidp":"(AB)",
"typesp": [
{"type":"BASICDTYPE","name":"logic","addr":"(K)","loc":"d,33:24,33:27","dtypep":"(K)","keyword":"logic","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(EC)","loc":"d,53:16,53:17","dtypep":"(EC)","keyword":"logic","range":"31:0","generic":true,"rangep": []},
{"type":"BASICDTYPE","name":"logic","addr":"(KRB)","loc":"d,17:17,17:18","dtypep":"(KRB)","keyword":"logic","range":"3:0","generic":true,"rangep": []},
{"type":"ENUMDTYPE","name":"t.my_t","addr":"(LRB)","loc":"d,17:12,17:16","dtypep":"(LRB)","enum":true,"generic":false,"refDTypep":"(KRB)","childDTypep": [],
{"type":"BASICDTYPE","name":"logic","addr":"(LRB)","loc":"d,17:17,17:18","dtypep":"(LRB)","keyword":"logic","range":"3:0","generic":true,"rangep": []},
{"type":"ENUMDTYPE","name":"t.my_t","addr":"(MRB)","loc":"d,17:12,17:16","dtypep":"(MRB)","enum":true,"generic":false,"refDTypep":"(LRB)","childDTypep": [],
"itemsp": [
{"type":"ENUMITEM","name":"E01","addr":"(MRB)","loc":"d,18:24,18:27","dtypep":"(RB)","rangep": [],
{"type":"ENUMITEM","name":"E01","addr":"(NRB)","loc":"d,18:24,18:27","dtypep":"(RB)","rangep": [],
"valuep": [
{"type":"CONST","name":"4'h1","addr":"(NRB)","loc":"d,18:30,18:31","dtypep":"(RB)"}
{"type":"CONST","name":"4'h1","addr":"(ORB)","loc":"d,18:30,18:31","dtypep":"(RB)"}
]},
{"type":"ENUMITEM","name":"E03","addr":"(ORB)","loc":"d,19:24,19:27","dtypep":"(RB)","rangep": [],
{"type":"ENUMITEM","name":"E03","addr":"(PRB)","loc":"d,19:24,19:27","dtypep":"(RB)","rangep": [],
"valuep": [
{"type":"CONST","name":"4'h3","addr":"(PRB)","loc":"d,19:30,19:31","dtypep":"(RB)"}
{"type":"CONST","name":"4'h3","addr":"(QRB)","loc":"d,19:30,19:31","dtypep":"(RB)"}
]},
{"type":"ENUMITEM","name":"E04","addr":"(QRB)","loc":"d,20:24,20:27","dtypep":"(RB)","rangep": [],
{"type":"ENUMITEM","name":"E04","addr":"(RRB)","loc":"d,20:24,20:27","dtypep":"(RB)","rangep": [],
"valuep": [
{"type":"CONST","name":"4'h4","addr":"(RRB)","loc":"d,20:30,20:31","dtypep":"(RB)"}
{"type":"CONST","name":"4'h4","addr":"(SRB)","loc":"d,20:30,20:31","dtypep":"(RB)"}
]}
]},
{"type":"BASICDTYPE","name":"integer","addr":"(P)","loc":"d,23:4,23:11","dtypep":"(P)","keyword":"integer","range":"31:0","generic":true,"rangep": []},
{"type":"REFDTYPE","name":"my_t","addr":"(M)","loc":"d,24:4,24:8","dtypep":"(LRB)","generic":false,"typedefp":"UNLINKED","refDTypep":"(LRB)","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []},
{"type":"REFDTYPE","name":"my_t","addr":"(M)","loc":"d,24:4,24:8","dtypep":"(MRB)","generic":false,"typedefp":"UNLINKED","refDTypep":"(MRB)","classOrPackagep":"UNLINKED","typeofp": [],"classOrPackageOpp": [],"paramsp": []},
{"type":"BASICDTYPE","name":"string","addr":"(PB)","loc":"d,28:4,28:10","dtypep":"(PB)","keyword":"string","generic":true,"rangep": []},
{"type":"UNPACKARRAYDTYPE","name":"","addr":"(ZB)","loc":"d,17:12,17:16","dtypep":"(ZB)","isCompound":false,"declRange":"[7:0]","generic":false,"refDTypep":"(LRB)","childDTypep": [],
{"type":"UNPACKARRAYDTYPE","name":"","addr":"(ZB)","loc":"d,17:12,17:16","dtypep":"(ZB)","isCompound":false,"declRange":"[7:0]","generic":false,"refDTypep":"(MRB)","childDTypep": [],
"rangep": [
{"type":"RANGE","name":"","addr":"(SRB)","loc":"d,17:12,17:16","ascending":false,"fromBracket":false,
{"type":"RANGE","name":"","addr":"(TRB)","loc":"d,17:12,17:16","ascending":false,"fromBracket":false,
"leftp": [
{"type":"CONST","name":"32'h7","addr":"(TRB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
{"type":"CONST","name":"32'h7","addr":"(URB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
],
"rightp": [
{"type":"CONST","name":"32'h0","addr":"(URB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
{"type":"CONST","name":"32'h0","addr":"(VRB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
]}
]},
{"type":"UNPACKARRAYDTYPE","name":"","addr":"(TI)","loc":"d,17:12,17:16","dtypep":"(TI)","isCompound":false,"declRange":"[7:0]","generic":false,"refDTypep":"(LRB)","childDTypep": [],
{"type":"UNPACKARRAYDTYPE","name":"","addr":"(TI)","loc":"d,17:12,17:16","dtypep":"(TI)","isCompound":false,"declRange":"[7:0]","generic":false,"refDTypep":"(MRB)","childDTypep": [],
"rangep": [
{"type":"RANGE","name":"","addr":"(VRB)","loc":"d,17:12,17:16","ascending":false,"fromBracket":false,
{"type":"RANGE","name":"","addr":"(WRB)","loc":"d,17:12,17:16","ascending":false,"fromBracket":false,
"leftp": [
{"type":"CONST","name":"32'h7","addr":"(WRB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
{"type":"CONST","name":"32'h7","addr":"(XRB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
],
"rightp": [
{"type":"CONST","name":"32'h0","addr":"(XRB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
{"type":"CONST","name":"32'h0","addr":"(YRB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
]}
]},
{"type":"UNPACKARRAYDTYPE","name":"","addr":"(FM)","loc":"d,17:12,17:16","dtypep":"(FM)","isCompound":true,"declRange":"[7:0]","generic":false,"refDTypep":"(PB)","childDTypep": [],
"rangep": [
{"type":"RANGE","name":"","addr":"(YRB)","loc":"d,17:12,17:16","ascending":false,"fromBracket":false,
{"type":"RANGE","name":"","addr":"(ZRB)","loc":"d,17:12,17:16","ascending":false,"fromBracket":false,
"leftp": [
{"type":"CONST","name":"32'h7","addr":"(ZRB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
{"type":"CONST","name":"32'h7","addr":"(ASB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
],
"rightp": [
{"type":"CONST","name":"32'h0","addr":"(ASB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
{"type":"CONST","name":"32'h0","addr":"(BSB)","loc":"d,17:12,17:16","dtypep":"(EC)"}
]}
]},
{"type":"BASICDTYPE","name":"logic","addr":"(IB)","loc":"d,23:23,23:24","dtypep":"(IB)","keyword":"logic","range":"31:0","generic":true,"rangep": []},
@ -2994,12 +2995,12 @@
{"type":"BASICDTYPE","name":"bit","addr":"(EN)","loc":"a,0:0,0:0","dtypep":"(EN)","keyword":"bit","range":"63:0","generic":true,"rangep": []},
{"type":"UNPACKARRAYDTYPE","name":"","addr":"(T)","loc":"d,11:8,11:9","dtypep":"(T)","isCompound":false,"declRange":"[0:0]","generic":false,"refDTypep":"(EN)","childDTypep": [],
"rangep": [
{"type":"RANGE","name":"","addr":"(BSB)","loc":"d,11:8,11:9","ascending":false,"fromBracket":false,
{"type":"RANGE","name":"","addr":"(CSB)","loc":"d,11:8,11:9","ascending":false,"fromBracket":false,
"leftp": [
{"type":"CONST","name":"32'h0","addr":"(CSB)","loc":"d,11:8,11:9","dtypep":"(EC)"}
{"type":"CONST","name":"32'h0","addr":"(DSB)","loc":"d,11:8,11:9","dtypep":"(EC)"}
],
"rightp": [
{"type":"CONST","name":"32'h0","addr":"(DSB)","loc":"d,11:8,11:9","dtypep":"(EC)"}
{"type":"CONST","name":"32'h0","addr":"(ESB)","loc":"d,11:8,11:9","dtypep":"(EC)"}
]}
]},
{"type":"BASICDTYPE","name":"IData","addr":"(LP)","loc":"a,0:0,0:0","dtypep":"(LP)","keyword":"IData","range":"31:0","generic":true,"rangep": []},
@ -3013,9 +3014,9 @@
]},
{"type":"CONSTPOOL","name":"","addr":"(D)","loc":"a,0:0,0:0",
"modulep": [
{"type":"MODULE","name":"@CONST-POOL@","addr":"(ESB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [],
{"type":"MODULE","name":"@CONST-POOL@","addr":"(FSB)","loc":"a,0:0,0:0","isChecker":false,"isProgram":false,"hasGenericIface":false,"origName":"@CONST-POOL@","level":0,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","inlinesp": [],
"stmtsp": [
{"type":"SCOPE","name":"TOP","addr":"(FSB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(ESB)","varsp": [],"blocksp": [],"inlinesp": []}
{"type":"SCOPE","name":"TOP","addr":"(GSB)","loc":"a,0:0,0:0","aboveScopep":"UNLINKED","aboveCellp":"UNLINKED","modp":"(FSB)","varsp": [],"blocksp": [],"inlinesp": []}
]}
]}
]}

View File

@ -1786,6 +1786,7 @@
<cfile loc="a,0,0,0,0" name="obj_vlt/t_xml_debugcheck/Vt_xml_debugcheck__Syms.h"/>
<cfile loc="a,0,0,0,0" name="obj_vlt/t_xml_debugcheck/Vt_xml_debugcheck.h"/>
<cfile loc="a,0,0,0,0" name="obj_vlt/t_xml_debugcheck/Vt_xml_debugcheck.cpp"/>
<cfile loc="a,0,0,0,0" name="obj_vlt/t_xml_debugcheck/Vt_xml_debugcheck__pch.h"/>
<cfile loc="a,0,0,0,0" name="obj_vlt/t_xml_debugcheck/Vt_xml_debugcheck_$root.h"/>
<cfile loc="a,0,0,0,0" name="obj_vlt/t_xml_debugcheck/Vt_xml_debugcheck_$unit.h"/>
<cfile loc="a,0,0,0,0" name="obj_vlt/t_xml_debugcheck/Vt_xml_debugcheck_$root__Slow.cpp"/>