2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Emit C++ for tree
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2021-01-01 16:29:54 +01:00
|
|
|
// Copyright 2003-2021 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3EmitC.h"
|
2021-06-24 18:35:12 +02:00
|
|
|
#include "V3EmitCFunc.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <vector>
|
2020-08-15 16:03:34 +02:00
|
|
|
#include <unordered_set>
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
//###################################################################### >
|
2006-08-26 13:35:28 +02:00
|
|
|
// Internal EmitC implementation
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
class EmitCImp final : EmitCFunc {
|
2006-08-26 13:35:28 +02:00
|
|
|
// MEMBERS
|
2021-05-15 17:05:24 +02:00
|
|
|
AstNodeModule* m_fileModp = nullptr; // Files (names, headers) constructed using this module
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_slow = false; // Creating __Slow file
|
|
|
|
|
bool m_fast = false; // Creating non __Slow file (or both)
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//---------------------------------------
|
|
|
|
|
// METHODS
|
|
|
|
|
|
2021-05-15 17:05:24 +02:00
|
|
|
V3OutCFile* newOutCFile(bool slow, bool source, int filenum = 0) {
|
2021-06-24 18:35:12 +02:00
|
|
|
m_lazyDecls.reset(); // Need to emit new lazy declarations
|
|
|
|
|
|
|
|
|
|
string filenameNoExt = v3Global.opt.makeDir() + "/" + prefixNameProtect(m_fileModp);
|
|
|
|
|
if (filenum) filenameNoExt += "__" + cvtToStr(filenum);
|
|
|
|
|
filenameNoExt += (slow ? "__Slow" : "");
|
|
|
|
|
V3OutCFile* ofp = 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 string filename = VL_DEV_NULL;
|
|
|
|
|
newCFile(filename, slow, source);
|
|
|
|
|
ofp = new V3OutCFile(filename);
|
|
|
|
|
} else if (optSystemC()) {
|
|
|
|
|
const string filename = filenameNoExt + (source ? ".cpp" : ".h");
|
|
|
|
|
newCFile(filename, slow, source);
|
|
|
|
|
ofp = new V3OutScFile(filename);
|
2020-11-01 16:18:32 +01:00
|
|
|
} else {
|
2021-06-24 18:35:12 +02:00
|
|
|
const string filename = filenameNoExt + (source ? ".cpp" : ".h");
|
|
|
|
|
newCFile(filename, slow, source);
|
|
|
|
|
ofp = new V3OutCFile(filename);
|
2020-11-01 16:18:32 +01:00
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
|
|
|
|
|
ofp->putsHeader();
|
|
|
|
|
if (m_fileModp->isTop() && !source) {
|
|
|
|
|
ofp->puts("// DESCR"
|
|
|
|
|
"IPTION: Verilator output: Primary design header\n");
|
|
|
|
|
ofp->puts("//\n");
|
|
|
|
|
ofp->puts("// This header should be included by all source files instantiating the "
|
|
|
|
|
"design.\n");
|
|
|
|
|
ofp->puts("// The class here is then constructed to instantiate the design.\n");
|
|
|
|
|
ofp->puts("// See the Verilator manual for examples.\n");
|
2020-10-18 19:23:02 +02:00
|
|
|
} else {
|
2021-06-24 18:35:12 +02:00
|
|
|
if (source) {
|
|
|
|
|
ofp->puts("// DESCR"
|
|
|
|
|
"IPTION: Verilator output: Design implementation internals\n");
|
|
|
|
|
} else {
|
|
|
|
|
ofp->puts("// DESCR"
|
|
|
|
|
"IPTION: Verilator output: Design internal header\n");
|
2020-10-18 19:23:02 +02:00
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
ofp->puts("// See " + v3Global.opt.prefix() + ".h for the primary calling header\n");
|
2020-10-18 19:23:02 +02:00
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
return ofp;
|
2020-10-18 19:23:02 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
//---------------------------------------
|
|
|
|
|
// VISITORS
|
|
|
|
|
using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning
|
|
|
|
|
virtual void visit(AstCFunc* nodep) override {
|
|
|
|
|
// TRACE_* and DPI handled elsewhere
|
|
|
|
|
if (nodep->funcType().isTrace()) return;
|
|
|
|
|
if (nodep->dpiImportPrototype()) return;
|
|
|
|
|
if (!(nodep->slow() ? m_slow : m_fast)) return;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
maybeSplit();
|
2016-05-12 13:19:02 +02:00
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
EmitCFunc::visit(nodep);
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//---------------------------------------
|
|
|
|
|
// ACCESSORS
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
// Low level
|
2021-06-24 18:35:12 +02:00
|
|
|
void emitTypedefs(AstNode* firstp) {
|
|
|
|
|
bool first = true;
|
|
|
|
|
for (AstNode* loopp = firstp; loopp; loopp = loopp->nextp()) {
|
|
|
|
|
if (const AstTypedef* nodep = VN_CAST(loopp, Typedef)) {
|
|
|
|
|
if (nodep->attrPublic()) {
|
|
|
|
|
if (first) {
|
|
|
|
|
first = false;
|
|
|
|
|
puts("\n// TYPEDEFS\n");
|
|
|
|
|
puts("// That were declared public\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("\n");
|
|
|
|
|
}
|
|
|
|
|
if (const AstEnumDType* adtypep
|
|
|
|
|
= VN_CAST(nodep->dtypep()->skipRefToEnump(), EnumDType)) {
|
|
|
|
|
if (adtypep->width() > 64) {
|
|
|
|
|
putsDecoration("// enum " + nodep->nameProtect()
|
|
|
|
|
+ " // Ignored: Too wide for C++\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts("enum " + nodep->name() + " {\n");
|
|
|
|
|
for (AstEnumItem* itemp = adtypep->itemsp(); itemp;
|
|
|
|
|
itemp = VN_CAST(itemp->nextp(), EnumItem)) {
|
|
|
|
|
puts(itemp->nameProtect());
|
|
|
|
|
puts(" = ");
|
|
|
|
|
iterateAndNextNull(itemp->valuep());
|
|
|
|
|
if (VN_IS(itemp->nextp(), EnumItem)) puts(",");
|
|
|
|
|
puts("\n");
|
|
|
|
|
}
|
|
|
|
|
puts("};\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-01 12:09:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
void emitParams(AstNodeModule* modp, bool init, bool* firstp, string& sectionr) {
|
|
|
|
|
bool anyi = false;
|
|
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (const AstVar* varp = VN_CAST(nodep, Var)) {
|
|
|
|
|
if (varp->isParam() && (varp->isUsedParam() || varp->isSigPublic())) {
|
|
|
|
|
if (!init && sectionr != "") {
|
|
|
|
|
puts(sectionr);
|
|
|
|
|
sectionr = "";
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(varp->valuep(), nodep, "No init for a param?");
|
|
|
|
|
// These should be static const values, however older MSVC++ did't
|
|
|
|
|
// support them; should be ok now under C++11, need to refactor.
|
|
|
|
|
if (varp->isWide()) { // Unsupported for output
|
|
|
|
|
if (!init) {
|
|
|
|
|
putsDecoration("// enum WData " + varp->nameProtect() + " //wide");
|
|
|
|
|
}
|
|
|
|
|
} else if (varp->isString()) {
|
|
|
|
|
if (init) {
|
|
|
|
|
puts("const std::string ");
|
|
|
|
|
puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name())
|
|
|
|
|
+ "(");
|
|
|
|
|
iterateAndNextNull(varp->valuep());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
anyi = true;
|
|
|
|
|
} else {
|
|
|
|
|
puts("static const std::string " + protect("var_" + varp->name())
|
|
|
|
|
+ ";\n");
|
|
|
|
|
}
|
|
|
|
|
} else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output
|
|
|
|
|
// putsDecoration("// enum ..... "+varp->nameProtect()
|
|
|
|
|
// +"not simple value, see variable above instead");
|
|
|
|
|
} else if (VN_IS(varp->dtypep(), BasicDType)
|
|
|
|
|
&& VN_CAST(varp->dtypep(), BasicDType)
|
|
|
|
|
->isOpaque()) { // Can't put out e.g. doubles
|
|
|
|
|
} else {
|
|
|
|
|
if (init) {
|
|
|
|
|
puts(varp->isQuad() ? "const QData " : "const IData ");
|
|
|
|
|
puts(prefixNameProtect(modp) + "::" + protect("var_" + varp->name())
|
|
|
|
|
+ "(");
|
|
|
|
|
iterateAndNextNull(varp->valuep());
|
|
|
|
|
puts(");\n");
|
|
|
|
|
anyi = true;
|
|
|
|
|
} else {
|
|
|
|
|
// enum
|
|
|
|
|
puts(varp->isQuad() ? "enum _QData" : "enum _IData");
|
|
|
|
|
puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = ");
|
|
|
|
|
iterateAndNextNull(varp->valuep());
|
|
|
|
|
puts("};\n");
|
|
|
|
|
// var
|
|
|
|
|
puts(varp->isQuad() ? "static const QData " : "static const IData ");
|
|
|
|
|
puts(protect("var_" + varp->name()) + ";\n");
|
|
|
|
|
}
|
2021-05-08 21:04:56 +02:00
|
|
|
}
|
2019-12-01 12:09:58 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-24 18:35:12 +02:00
|
|
|
if (anyi) puts("\n");
|
2019-12-01 12:09:58 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
void emitSensitives();
|
|
|
|
|
// Medium level
|
2009-11-07 12:20:20 +01:00
|
|
|
void emitCtorImp(AstNodeModule* modp);
|
|
|
|
|
void emitConfigureImp(AstNodeModule* modp);
|
|
|
|
|
void emitCoverageDecl(AstNodeModule* modp);
|
|
|
|
|
void emitCoverageImp(AstNodeModule* modp);
|
|
|
|
|
void emitDestructorImp(AstNodeModule* modp);
|
2012-08-27 03:13:47 +02:00
|
|
|
void emitSavableImp(AstNodeModule* modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
void emitTextSection(AstType type);
|
|
|
|
|
// High level
|
2021-05-15 17:05:24 +02:00
|
|
|
void emitImpTop();
|
|
|
|
|
void emitImp(AstNodeModule* modp);
|
2021-06-13 15:33:11 +02:00
|
|
|
void emitSettleLoop(bool initial);
|
|
|
|
|
void emitWrapEval();
|
|
|
|
|
void emitWrapFast();
|
2021-06-16 13:18:56 +02:00
|
|
|
void emitThreadingState();
|
|
|
|
|
void emitThreadingCtors(bool* firstp);
|
2021-06-10 23:41:33 +02:00
|
|
|
void emitIntTop(const AstNodeModule* modp);
|
2009-11-07 12:20:20 +01:00
|
|
|
void emitInt(AstNodeModule* modp);
|
2021-05-15 17:05:24 +02:00
|
|
|
void maybeSplit();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2021-06-24 18:35:12 +02:00
|
|
|
EmitCImp() {}
|
2020-11-17 01:56:16 +01:00
|
|
|
virtual ~EmitCImp() override = default;
|
2020-05-25 12:35:06 +02:00
|
|
|
void mainImp(AstNodeModule* modp, bool slow);
|
2020-02-02 01:32:04 +01:00
|
|
|
void mainInt(AstNodeModule* modp);
|
2020-04-15 13:58:34 +02:00
|
|
|
void mainDoFunc(AstCFunc* nodep) { iterate(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Internal EmitC
|
|
|
|
|
|
2020-11-11 03:40:14 +01:00
|
|
|
void EmitCImp::emitCoverageDecl(AstNodeModule*) {
|
2008-12-05 16:54:14 +01:00
|
|
|
if (v3Global.opt.coverage()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
ofp()->putsPrivate(false); // Accessed from loose methods
|
2019-05-19 22:13:13 +02:00
|
|
|
putsDecoration("// Coverage\n");
|
2020-02-04 00:43:41 +01:00
|
|
|
puts("void __vlCoverInsert(");
|
|
|
|
|
puts(v3Global.opt.threads() ? "std::atomic<uint32_t>" : "uint32_t");
|
|
|
|
|
puts("* countp, bool enable, const char* filenamep, int lineno, int column,\n");
|
2020-05-31 21:52:17 +02:00
|
|
|
puts("const char* hierp, const char* pagep, const char* commentp, const char* "
|
|
|
|
|
"linescovp);\n");
|
2006-08-30 03:14:29 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-16 13:18:56 +02:00
|
|
|
void EmitCImp::emitThreadingCtors(bool* firstp) {
|
|
|
|
|
ofp()->indentInc();
|
2020-04-15 13:58:34 +02:00
|
|
|
emitCtorSep(firstp);
|
2020-08-15 16:12:55 +02:00
|
|
|
puts("__Vm_threadPoolp(nullptr)");
|
2021-06-16 13:18:56 +02:00
|
|
|
emitCtorSep(firstp);
|
|
|
|
|
puts("__Vm_even_cycle(false)");
|
2018-07-23 02:54:28 +02:00
|
|
|
if (v3Global.opt.profThreads()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
emitCtorSep(firstp);
|
|
|
|
|
puts("__Vm_profile_cycle_start(0)");
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2021-06-16 13:18:56 +02:00
|
|
|
ofp()->indentDec();
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
void EmitCImp::emitCtorImp(AstNodeModule* modp) {
|
2006-08-30 19:27:53 +02:00
|
|
|
puts("\n");
|
2018-05-30 01:49:27 +02:00
|
|
|
bool first = true;
|
2020-11-11 03:40:14 +01:00
|
|
|
string section;
|
2020-10-17 01:05:28 +02:00
|
|
|
emitParams(modp, true, &first, section /*ref*/);
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
const string modName = prefixNameProtect(modp);
|
|
|
|
|
|
|
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit("void " + modName + "__", protect("_ctor_var_reset"),
|
|
|
|
|
"(" + modName + "* vlSelf);");
|
|
|
|
|
puts("\n");
|
|
|
|
|
|
2020-02-03 03:15:07 +01:00
|
|
|
if (VN_IS(modp, Class)) {
|
|
|
|
|
modp->v3fatalSrc("constructors should be AstCFuncs instead");
|
|
|
|
|
} else if (optSystemC() && modp->isTop()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(modName + "::" + modName + "(sc_module_name)");
|
2021-03-07 17:01:54 +01:00
|
|
|
} else if (modp->isTop()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(modName + "::" + modName
|
2021-03-07 17:01:54 +01:00
|
|
|
+ "(VerilatedContext* _vcontextp__, const char* _vcname__)\n");
|
|
|
|
|
puts(" : VerilatedModule{_vcname__}\n");
|
2021-02-21 18:12:11 +01:00
|
|
|
first = false; // printed the first ':'
|
2021-03-07 17:01:54 +01:00
|
|
|
} else {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(modName + "::" + modName + "(const char* _vcname__)\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
puts(" : VerilatedModule(_vcname__)\n");
|
2021-06-16 13:18:56 +02:00
|
|
|
first = false; // printed the first ':'
|
2006-08-30 19:27:53 +02:00
|
|
|
}
|
2018-05-30 01:49:27 +02:00
|
|
|
emitVarCtors(&first);
|
2021-06-16 13:18:56 +02:00
|
|
|
if (modp->isTop() && v3Global.opt.mtasks()) emitThreadingCtors(&first);
|
2020-10-17 01:05:28 +02:00
|
|
|
|
2006-08-30 19:27:53 +02:00
|
|
|
puts(" {\n");
|
2021-06-19 17:48:20 +02:00
|
|
|
|
|
|
|
|
if (modp->isTop()) {
|
|
|
|
|
putsDecoration("// Create Sym instance\n");
|
|
|
|
|
// Must be before other constructors, as __vlCoverInsert calls it.
|
|
|
|
|
// Note _vcontextp__ may be nullptr, VerilatedSyms::VerilatedSyms cleans it up
|
|
|
|
|
puts(EmitCBaseVisitor::symClassVar() + " = new " + symClassName() + "("
|
|
|
|
|
+ (optSystemC() ? "nullptr" : "_vcontextp__") + ", this, name());\n");
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-30 19:27:53 +02:00
|
|
|
emitSensitives();
|
2016-05-12 13:19:02 +02:00
|
|
|
|
2016-09-14 04:28:07 +02:00
|
|
|
putsDecoration("// Reset structure values\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n");
|
2016-11-05 14:47:56 +01:00
|
|
|
emitTextSection(AstType::atScCtor);
|
2018-07-23 02:54:28 +02:00
|
|
|
|
|
|
|
|
if (modp->isTop() && v3Global.opt.mtasks()) {
|
|
|
|
|
// TODO-- For now each top module creates its own ThreadPool here,
|
|
|
|
|
// and deletes it in the destructor. If A and B are each top level
|
|
|
|
|
// modules, each creates a separate thread pool. This allows
|
|
|
|
|
// A.eval() and B.eval() to run concurrently without any
|
|
|
|
|
// interference -- so long as the physical machine has enough cores
|
|
|
|
|
// to support both pools and all testbench threads.
|
|
|
|
|
//
|
|
|
|
|
// In the future, we might want to let the client provide a
|
|
|
|
|
// threadpool to the constructor. This would allow two or more
|
|
|
|
|
// models to share a single threadpool.
|
|
|
|
|
//
|
|
|
|
|
// For example: suppose models A and B are each compiled to run on
|
|
|
|
|
// 4 threads. The client might create a single thread pool with 3
|
2019-09-09 13:50:21 +02:00
|
|
|
// threads and pass it to both models. If the client can ensure that
|
2018-07-23 02:54:28 +02:00
|
|
|
// A.eval() and B.eval() do NOT run concurrently, there will be no
|
|
|
|
|
// contention for the threads. This mode is missing for now. (Is
|
|
|
|
|
// there demand for such a setup?)
|
|
|
|
|
puts("__Vm_threadPoolp = new VlThreadPool("
|
|
|
|
|
// Note we create N-1 threads in the thread pool. The thread
|
|
|
|
|
// that calls eval() becomes the final Nth thread for the
|
|
|
|
|
// duration of the eval call.
|
2021-03-07 17:01:54 +01:00
|
|
|
+ string("vlSymsp->_vm_contextp__, ") + cvtToStr(v3Global.opt.threads() - 1) + ", "
|
|
|
|
|
+ cvtToStr(v3Global.opt.profThreads()) + ");\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
|
|
|
|
|
if (v3Global.opt.profThreads()) {
|
|
|
|
|
puts("__Vm_profile_cycle_start = 0;\n");
|
|
|
|
|
puts("__Vm_profile_time_finished = 0;\n");
|
|
|
|
|
puts("__Vm_profile_window_ct = 0;");
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-30 19:27:53 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
void EmitCImp::emitConfigureImp(AstNodeModule* modp) {
|
2021-06-13 15:33:11 +02:00
|
|
|
const string modName = prefixNameProtect(modp);
|
|
|
|
|
|
|
|
|
|
if (v3Global.opt.coverage()) {
|
|
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit("void " + modName + "__", protect("_configure_coverage"),
|
|
|
|
|
"(" + modName + "* vlSelf, bool first);");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
puts("\nvoid " + modName + "::" + protect("__Vconfigure") + "(" + symClassName()
|
|
|
|
|
+ "* vlSymsp, bool first) {\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("if (false && first) {} // Prevent unused\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("this->vlSymsp = vlSymsp;\n"); // First, as later stuff needs it.
|
2021-05-22 19:50:55 +02:00
|
|
|
if (v3Global.opt.coverage()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(modName + "__" + protect("_configure_coverage") + "(this, first);\n");
|
2021-05-22 19:50:55 +02:00
|
|
|
}
|
2006-08-30 19:27:53 +02:00
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
splitSizeInc(10);
|
2006-08-30 19:27:53 +02:00
|
|
|
}
|
|
|
|
|
|
2020-11-11 03:40:14 +01:00
|
|
|
void EmitCImp::emitCoverageImp(AstNodeModule*) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (v3Global.opt.coverage()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("\n// Coverage\n");
|
|
|
|
|
// 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.
|
2020-02-04 00:43:41 +01: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");
|
2020-05-31 21:52:17 +02:00
|
|
|
puts("const char* hierp, const char* pagep, const char* commentp, const char* linescovp) "
|
|
|
|
|
"{\n");
|
2020-02-04 00:43:41 +01:00
|
|
|
if (v3Global.opt.threads()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("assert(sizeof(uint32_t) == sizeof(std::atomic<uint32_t>));\n");
|
|
|
|
|
puts("uint32_t* count32p = reinterpret_cast<uint32_t*>(countp);\n");
|
2020-02-04 00:43:41 +01:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("uint32_t* count32p = countp;\n");
|
2020-02-04 00:43:41 +01:00
|
|
|
}
|
|
|
|
|
// static doesn't need save-restore as is constant
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("static uint32_t fake_zero_count = 0;\n");
|
2020-02-04 00:43:41 +01:00
|
|
|
// Used for second++ instantiation of identical bin
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("if (!enable) count32p = &fake_zero_count;\n");
|
|
|
|
|
puts("*count32p = 0;\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("VL_COVER_INSERT(vlSymsp->_vm_contextp__->coveragep(), count32p,");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts(" \"filename\",filenamep,");
|
|
|
|
|
puts(" \"lineno\",lineno,");
|
|
|
|
|
puts(" \"column\",column,\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Need to move hier into scopes and back out if do this
|
2021-06-13 15:33:11 +02:00
|
|
|
// puts( "\"hier\",std::string(vlSymsp->name())+hierp,");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("\"hier\",std::string(name())+hierp,");
|
|
|
|
|
puts(" \"page\",pagep,");
|
2020-05-31 21:52:17 +02:00
|
|
|
puts(" \"comment\",commentp,");
|
|
|
|
|
puts(" (linescovp[0] ? \"linescov\" : \"\"), linescovp);\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-07 12:20:20 +01:00
|
|
|
void EmitCImp::emitDestructorImp(AstNodeModule* modp) {
|
2006-08-30 19:27:53 +02:00
|
|
|
puts("\n");
|
2020-01-25 15:16:00 +01:00
|
|
|
puts(prefixNameProtect(modp) + "::~" + prefixNameProtect(modp) + "() {\n");
|
2020-03-02 03:39:23 +01:00
|
|
|
if (modp->isTop()) {
|
2020-04-24 03:22:47 +02:00
|
|
|
if (v3Global.opt.mtasks()) {
|
2020-08-15 16:12:55 +02:00
|
|
|
puts("VL_DO_CLEAR(delete __Vm_threadPoolp, __Vm_threadPoolp = nullptr);\n");
|
2020-04-24 03:22:47 +02:00
|
|
|
}
|
2020-03-02 03:39:23 +01:00
|
|
|
// Call via function in __Trace.cpp as this .cpp file does not have trace header
|
|
|
|
|
if (v3Global.needTraceDumper()) {
|
|
|
|
|
puts("#ifdef VM_TRACE\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("if (VL_UNLIKELY(vlSymsp->__Vm_dumping)) _traceDumpClose();\n");
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("#endif // VM_TRACE\n");
|
|
|
|
|
}
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2016-11-05 14:47:56 +01:00
|
|
|
emitTextSection(AstType::atScDtor);
|
2021-06-13 15:33:11 +02:00
|
|
|
if (modp->isTop()) puts("VL_DO_CLEAR(delete vlSymsp, vlSymsp = nullptr);\n");
|
2006-08-30 19:27:53 +02:00
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
splitSizeInc(10);
|
2006-08-30 19:27:53 +02:00
|
|
|
}
|
|
|
|
|
|
2012-08-27 03:13:47 +02:00
|
|
|
void EmitCImp::emitSavableImp(AstNodeModule* modp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (v3Global.opt.savable()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("\n// Savable\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
for (int de = 0; de < 2; ++de) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const string classname = de ? "VerilatedDeserialize" : "VerilatedSerialize";
|
|
|
|
|
const string funcname = de ? "__Vdeserialize" : "__Vserialize";
|
|
|
|
|
const string op = de ? ">>" : "<<";
|
2018-10-14 17:10:11 +02:00
|
|
|
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
2020-01-25 15:16:00 +01:00
|
|
|
puts("void " + prefixNameProtect(modp) + "::" + protect(funcname) + "(" + classname
|
|
|
|
|
+ "& os) {\n");
|
2020-01-25 02:10:44 +01:00
|
|
|
// Place a computed checksum to ensure proper structure save/restore formatting
|
2019-05-19 22:13:13 +02:00
|
|
|
// OK if this hash includes some things we won't dump, since
|
|
|
|
|
// just looking for loading the wrong model
|
2019-09-28 19:32:28 +02:00
|
|
|
VHashSha256 hash;
|
2019-05-19 22:13:13 +02:00
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (const AstVar* varp = VN_CAST(nodep, Var)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
hash.insert(varp->name());
|
|
|
|
|
hash.insert(varp->dtypep()->width());
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-29 02:32:07 +02:00
|
|
|
ofp()->printf("vluint64_t __Vcheckval = 0x%" VL_PRI64 "xULL;\n",
|
2020-04-15 13:58:34 +02:00
|
|
|
static_cast<vluint64_t>(hash.digestUInt64()));
|
2019-05-19 22:13:13 +02:00
|
|
|
if (de) {
|
|
|
|
|
puts("os.readAssert(__Vcheckval);\n");
|
|
|
|
|
} else {
|
2021-03-07 17:01:54 +01:00
|
|
|
puts("os << __Vcheckval;\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-07 17:01:54 +01:00
|
|
|
// 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.
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("os " + op + " vlSymsp->_vm_contextp__;\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Save all members
|
|
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (const AstVar* varp = VN_CAST(nodep, Var)) {
|
2019-05-19 22:13:13 +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.
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (varp->isParam()) {
|
|
|
|
|
} else if (varp->isStatic() && varp->isConst()) {
|
|
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
int vects = 0;
|
2019-06-15 00:42:27 +02:00
|
|
|
AstNodeDType* elementp = varp->dtypeSkipRefp();
|
|
|
|
|
for (AstUnpackArrayDType* arrayp = VN_CAST(elementp, UnpackArrayDType);
|
2020-04-15 13:58:34 +02:00
|
|
|
arrayp; arrayp = VN_CAST(elementp, UnpackArrayDType)) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int vecnum = vects++;
|
2020-12-07 03:13:56 +01:00
|
|
|
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
|
2019-07-06 18:57:50 +02:00
|
|
|
"Should have swapped msb & lsb earlier.");
|
2021-06-21 00:32:57 +02:00
|
|
|
const string ivar = string("__Vi") + cvtToStr(vecnum);
|
2020-10-17 20:21:27 +02:00
|
|
|
puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0));
|
|
|
|
|
puts("; " + ivar + "<" + cvtToStr(arrayp->elementsConst()));
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("; ++" + ivar + ") {\n");
|
2019-06-15 00:42:27 +02:00
|
|
|
elementp = arrayp->subDTypep()->skipRefp();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-06-16 13:18:56 +02:00
|
|
|
const AstBasicDType* const basicp = elementp->basicp();
|
|
|
|
|
// Do not save MTask state, only matters within an evaluation
|
|
|
|
|
if (basicp && basicp->keyword().isMTaskState()) continue;
|
2019-06-15 00:42:27 +02:00
|
|
|
// Want to detect types that are represented as arrays
|
|
|
|
|
// (i.e. packed types of more than 64 bits).
|
|
|
|
|
if (elementp->isWide()
|
|
|
|
|
&& !(basicp && basicp->keyword() == AstBasicDTypeKwd::STRING)) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int vecnum = vects++;
|
|
|
|
|
const string ivar = string("__Vi") + cvtToStr(vecnum);
|
2020-10-17 20:21:27 +02:00
|
|
|
puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(0));
|
|
|
|
|
puts("; " + ivar + "<" + cvtToStr(elementp->widthWords()));
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("; ++" + ivar + ") {\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("os" + op + varp->nameProtect());
|
|
|
|
|
for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]");
|
2019-06-15 00:42:27 +02:00
|
|
|
puts(";\n");
|
2020-10-17 20:21:27 +02:00
|
|
|
for (int v = 0; v < vects; ++v) puts("}\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (modp->isTop()) { // Save the children
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("vlSymsp->" + protect(funcname) + "(os);\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
2012-08-27 03:13:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void EmitCImp::emitTextSection(AstType type) {
|
|
|
|
|
int last_line = -999;
|
2019-11-07 01:47:34 +01:00
|
|
|
for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (const AstNodeText* textp = VN_CAST(nodep, NodeText)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->type() == type) {
|
|
|
|
|
if (last_line != nodep->fileline()->lineno()) {
|
|
|
|
|
if (last_line < 0) {
|
|
|
|
|
puts("\n//*** Below code from `systemc in Verilog file\n");
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
putsDecoration(
|
|
|
|
|
ifNoProtect("// From `systemc at " + nodep->fileline()->ascii() + "\n"));
|
2019-05-19 22:13:13 +02:00
|
|
|
last_line = nodep->fileline()->lineno();
|
|
|
|
|
}
|
|
|
|
|
ofp()->putsNoTracking(textp->text());
|
|
|
|
|
last_line++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void EmitCImp::emitSensitives() {
|
|
|
|
|
// Create sensitivity list for when to evaluate the model.
|
|
|
|
|
// If C++ code, the user must call this routine themself.
|
|
|
|
|
if (m_modp->isTop() && optSystemC()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
putsDecoration("// Sensitivities on all clocks and combo inputs\n");
|
|
|
|
|
puts("SC_METHOD(eval);\n");
|
|
|
|
|
for (AstNode* nodep = m_modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (const AstVar* varp = VN_CAST(nodep, Var)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (varp->isNonOutput() && (varp->isScSensitive() || varp->isUsedClock())) {
|
2018-10-27 23:29:00 +02:00
|
|
|
int vects = 0;
|
|
|
|
|
// This isn't very robust and may need cleanup for other data types
|
2019-05-19 22:13:13 +02:00
|
|
|
for (AstUnpackArrayDType* arrayp
|
2020-04-15 13:58:34 +02:00
|
|
|
= VN_CAST(varp->dtypeSkipRefp(), UnpackArrayDType);
|
2018-02-02 03:32:58 +01:00
|
|
|
arrayp;
|
|
|
|
|
arrayp = VN_CAST(arrayp->subDTypep()->skipRefp(), UnpackArrayDType)) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int vecnum = vects++;
|
2020-12-07 03:13:56 +01:00
|
|
|
UASSERT_OBJ(arrayp->hi() >= arrayp->lo(), varp,
|
2019-07-06 18:57:50 +02:00
|
|
|
"Should have swapped msb & lsb earlier.");
|
2021-06-21 00:32:57 +02:00
|
|
|
const string ivar = string("__Vi") + cvtToStr(vecnum);
|
2020-12-07 03:13:56 +01:00
|
|
|
puts("for (int __Vi" + cvtToStr(vecnum) + "=" + cvtToStr(arrayp->lo()));
|
|
|
|
|
puts("; " + ivar + "<=" + cvtToStr(arrayp->hi()));
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("; ++" + ivar + ") {\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("sensitive << " + varp->nameProtect());
|
|
|
|
|
for (int v = 0; v < vects; ++v) puts("[__Vi" + cvtToStr(v) + "]");
|
2019-05-19 22:13:13 +02:00
|
|
|
puts(";\n");
|
2020-10-17 20:21:27 +02:00
|
|
|
for (int v = 0; v < vects; ++v) puts("}\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
puts("\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
void EmitCImp::emitSettleLoop(bool initial) {
|
|
|
|
|
const string self = initial ? "vlSelf" : "this";
|
2018-03-10 18:52:11 +01:00
|
|
|
putsDecoration("// Evaluate till stable\n");
|
|
|
|
|
puts("int __VclockLoop = 0;\n");
|
|
|
|
|
puts("QData __Vchange = 1;\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
if (v3Global.opt.trace()) puts("vlSymsp->__Vm_activity = true;\n");
|
2018-03-10 18:52:11 +01:00
|
|
|
puts("do {\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ ");
|
|
|
|
|
puts(initial ? "Initial" : "Clock");
|
|
|
|
|
puts(" loop\\n\"););\n");
|
|
|
|
|
if (initial) puts(topClassName() + "__" + protect("_eval_settle") + "(" + self + ");\n");
|
|
|
|
|
puts(topClassName() + "__" + protect("_eval") + "(" + self + ");\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("if (VL_UNLIKELY(++__VclockLoop > " + cvtToStr(v3Global.opt.convergeLimit()) + ")) {\n");
|
|
|
|
|
puts("// About to fail, so enable debug to see what's not settling.\n");
|
|
|
|
|
puts("// Note you must run make with OPT=-DVL_DEBUG for debug prints.\n");
|
|
|
|
|
puts("int __Vsaved_debug = Verilated::debug();\n");
|
|
|
|
|
puts("Verilated::debug(1);\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("__Vchange = " + topClassName() + "__" + protect("_change_request") + "(" + self
|
|
|
|
|
+ ");\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("Verilated::debug(__Vsaved_debug);\n");
|
|
|
|
|
puts("VL_FATAL_MT(");
|
2019-11-07 01:47:34 +01:00
|
|
|
putsQuoted(protect(m_modp->fileline()->filename()));
|
|
|
|
|
puts(", ");
|
|
|
|
|
puts(cvtToStr(m_modp->fileline()->lineno()));
|
|
|
|
|
puts(", \"\",\n");
|
|
|
|
|
puts("\"Verilated model didn't ");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (initial) puts("DC ");
|
2019-11-07 01:47:34 +01:00
|
|
|
puts("converge\\n\"\n");
|
2021-04-18 17:52:29 +02:00
|
|
|
puts("\"- See https://verilator.org/warn/DIDNOTCONVERGE\");\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("} else {\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("__Vchange = " + topClassName() + "__" + protect("_change_request") + "(" + self
|
|
|
|
|
+ ");\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("}\n");
|
2018-03-10 18:52:11 +01:00
|
|
|
puts("} while (VL_UNLIKELY(__Vchange));\n");
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
void EmitCImp::emitWrapFast() {
|
|
|
|
|
UASSERT_OBJ(m_modp->isTop(), m_modp, "Attempting to emitWrapFast for non-top class");
|
|
|
|
|
puts("\nVerilatedContext* " + topClassName() + "::contextp() const {\n");
|
|
|
|
|
puts(/**/ "return vlSymsp->_vm_contextp__;\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
void EmitCImp::emitWrapEval() {
|
|
|
|
|
UASSERT_OBJ(m_modp->isTop(), m_modp, "Attempting to emitWrapEval for non-top class");
|
|
|
|
|
|
|
|
|
|
const string selfDecl = "(" + topClassName() + "* vlSelf)";
|
|
|
|
|
|
|
|
|
|
// Forward declarations
|
|
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit("void " + topClassName() + "__", protect("_eval_initial"), selfDecl + ";");
|
|
|
|
|
m_lazyDecls.emit("void " + topClassName() + "__", protect("_eval_settle"), selfDecl + ";");
|
|
|
|
|
m_lazyDecls.emit("void " + topClassName() + "__", protect("_eval"), selfDecl + ";");
|
|
|
|
|
m_lazyDecls.emit("QData " + topClassName() + "__", protect("_change_request"), selfDecl + ";");
|
|
|
|
|
puts("#ifdef VL_DEBUG\n");
|
|
|
|
|
m_lazyDecls.emit("void " + topClassName() + "__", protect("_eval_debug_assertions"),
|
|
|
|
|
selfDecl + ";");
|
|
|
|
|
puts("#endif // VL_DEBUG\n");
|
|
|
|
|
m_lazyDecls.emit("void " + topClassName() + "__", protect("_final"), selfDecl + ";");
|
|
|
|
|
|
|
|
|
|
// _eval_initial_loop
|
|
|
|
|
puts("\nstatic void " + protect("_eval_initial_loop") + selfDecl + " {\n");
|
|
|
|
|
puts(symClassAssign());
|
|
|
|
|
puts("vlSymsp->__Vm_didInit = true;\n");
|
|
|
|
|
puts(topClassName() + "__" + protect("_eval_initial") + "(vlSelf);\n");
|
|
|
|
|
emitSettleLoop(/* initial: */ true);
|
|
|
|
|
ensureNewLine();
|
|
|
|
|
puts("}\n");
|
|
|
|
|
|
|
|
|
|
// ::eval_step
|
|
|
|
|
puts("\nvoid " + topClassName() + "::eval_step() {\n");
|
|
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+++++TOP Evaluate " + topClassName()
|
|
|
|
|
+ "::eval_step\\n\"); );\n");
|
2017-11-06 03:47:55 +01:00
|
|
|
puts("#ifdef VL_DEBUG\n");
|
|
|
|
|
putsDecoration("// Debug assertions\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(topClassName() + "__" + protect("_eval_debug_assertions") + "(this);\n");
|
2019-07-30 03:07:37 +02:00
|
|
|
puts("#endif // VL_DEBUG\n");
|
2016-09-14 04:28:07 +02:00
|
|
|
putsDecoration("// Initialize\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) " + protect("_eval_initial_loop")
|
2021-06-13 15:33:11 +02:00
|
|
|
+ "(this);\n");
|
2017-10-27 03:51:51 +02:00
|
|
|
|
2018-05-30 01:49:27 +02:00
|
|
|
if (v3Global.opt.threads() == 1) {
|
2019-05-19 22:13:13 +02:00
|
|
|
uint32_t mtaskId = 0;
|
2020-04-15 13:58:34 +02:00
|
|
|
putsDecoration("// MTask " + cvtToStr(mtaskId) + " start\n");
|
|
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"MTask" + cvtToStr(mtaskId) + " starting\\n\"););\n");
|
|
|
|
|
puts("Verilated::mtaskId(" + cvtToStr(mtaskId) + ");\n");
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2018-07-23 02:54:28 +02:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
if (v3Global.opt.mtasks() && v3Global.opt.profThreads()) {
|
2021-03-07 17:01:54 +01:00
|
|
|
puts("if (VL_UNLIKELY((vlSymsp->_vm_contextp__->profThreadsStart() != "
|
|
|
|
|
"__Vm_profile_time_finished)\n");
|
|
|
|
|
puts(" && (VL_TIME_Q() > vlSymsp->_vm_contextp__->profThreadsStart())\n");
|
|
|
|
|
puts(" && (vlSymsp->_vm_contextp__->profThreadsWindow() >= 1))) {\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
// Within a profile (either starting, middle, or end)
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("if (__Vm_profile_window_ct == 0) {\n"); // Opening file?
|
2018-07-23 02:54:28 +02:00
|
|
|
// Start profile on this cycle. We'll capture a window worth, then
|
|
|
|
|
// only analyze the next window worth. The idea is that the first window
|
|
|
|
|
// capture will hit some cache-cold stuff (eg printf) but it'll be warm
|
|
|
|
|
// by the time we hit the second window, we hope.
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
// "* 2" as first half is warmup, second half is collection
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("__Vm_profile_window_ct = vlSymsp->_vm_contextp__->profThreadsWindow() * 2 "
|
|
|
|
|
"+ "
|
2021-03-07 17:01:54 +01:00
|
|
|
"1;\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("}\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("--__Vm_profile_window_ct;\n");
|
|
|
|
|
puts("if (__Vm_profile_window_ct == vlSymsp->_vm_contextp__->profThreadsWindow()) "
|
|
|
|
|
"{\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
// This barrier record in every threads' profile demarcates the
|
|
|
|
|
// cache-warm-up cycles before the barrier from the actual profile
|
|
|
|
|
// cycles afterward.
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("__Vm_threadPoolp->profileAppendAll(");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("VlProfileRec(VlProfileRec::Barrier()));\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("__Vm_profile_cycle_start = VL_RDTSC_Q();\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("}\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("else if (__Vm_profile_window_ct == 0) {\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
// Ending file.
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("vluint64_t elapsed = VL_RDTSC_Q() - __Vm_profile_cycle_start;\n");
|
|
|
|
|
puts("__Vm_threadPoolp->profileDump(vlSymsp->_vm_contextp__->profThreadsFilename()."
|
|
|
|
|
"c_str(), elapsed);\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
// This turns off the test to enter the profiling code, but still
|
|
|
|
|
// allows the user to collect another profile by changing
|
|
|
|
|
// profThreadsStart
|
2021-03-07 17:01:54 +01:00
|
|
|
puts("__Vm_profile_time_finished = vlSymsp->_vm_contextp__->profThreadsStart();\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("__Vm_profile_cycle_start = 0;\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("}\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
emitSettleLoop(/* initial: */ false);
|
2018-05-30 01:49:27 +02:00
|
|
|
if (v3Global.opt.threads() == 1) {
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("Verilated::endOfThreadMTask(vlSymsp->__Vm_evalMsgQp);\n");
|
2017-10-27 03:51:51 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (v3Global.opt.threads()) puts("Verilated::endOfEval(vlSymsp->__Vm_evalMsgQp);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
// ::eval_end_step
|
2020-03-02 03:39:23 +01:00
|
|
|
if (v3Global.needTraceDumper() && !optSystemC()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("\nvoid " + topClassName() + "::eval_end_step() {\n");
|
|
|
|
|
puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+eval_end_step " + topClassName()
|
2020-03-02 03:39:23 +01:00
|
|
|
+ "::eval_end_step\\n\"); );\n");
|
|
|
|
|
puts("#ifdef VM_TRACE\n");
|
|
|
|
|
putsDecoration("// Tracing\n");
|
|
|
|
|
// SystemC's eval loop deals with calling trace, not us
|
2020-03-04 01:19:50 +01:00
|
|
|
puts("if (VL_UNLIKELY(vlSymsp->__Vm_dumping)) _traceDump();\n");
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("#endif // VM_TRACE\n");
|
|
|
|
|
puts("}\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
splitSizeInc(10);
|
2020-03-02 03:39:23 +01:00
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
// ::final
|
|
|
|
|
puts("\nvoid " + topClassName() + "::final() {\n");
|
|
|
|
|
puts(topClassName() + "__" + protect("_final") + "(this);\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
puts("}\n");
|
2013-09-04 01:35:32 +02:00
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-16 13:18:56 +02:00
|
|
|
void EmitCImp::emitThreadingState() {
|
2021-06-13 15:33:11 +02:00
|
|
|
ofp()->putsPrivate(false); // Accessed from loose function
|
2018-07-23 02:54:28 +02:00
|
|
|
AstExecGraph* execGraphp = v3Global.rootp()->execGraphp();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(execGraphp, v3Global.rootp(), "Root should have an execGraphp");
|
2018-07-23 02:54:28 +02:00
|
|
|
|
|
|
|
|
puts("VlThreadPool* __Vm_threadPoolp;\n");
|
2021-06-16 13:18:56 +02:00
|
|
|
puts("bool __Vm_even_cycle;\n");
|
2018-07-23 02:54:28 +02:00
|
|
|
|
|
|
|
|
if (v3Global.opt.profThreads()) {
|
|
|
|
|
// rdtsc() at current cycle start
|
|
|
|
|
puts("vluint64_t __Vm_profile_cycle_start;\n");
|
|
|
|
|
// Time we finished analysis
|
|
|
|
|
puts("vluint64_t __Vm_profile_time_finished;\n");
|
|
|
|
|
// Track our position in the cache warmup and actual profile window
|
|
|
|
|
puts("vluint32_t __Vm_profile_window_ct;\n");
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-10 23:41:33 +02:00
|
|
|
void EmitCImp::emitIntTop(const AstNodeModule* modp) {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Always have this first; gcc has short circuiting if #ifdef is first in a file
|
2019-12-24 01:00:17 +01:00
|
|
|
ofp()->putsGuard();
|
2017-09-08 03:08:49 +02:00
|
|
|
puts("\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
ofp()->putsIntTopInclude();
|
2010-01-17 21:10:37 +01:00
|
|
|
if (v3Global.needHeavy()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("#include \"verilated_heavy.h\"\n");
|
2010-01-17 21:10:37 +01:00
|
|
|
} else {
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("#include \"verilated.h\"\n");
|
2010-01-17 21:10:37 +01:00
|
|
|
}
|
2020-02-02 01:32:04 +01:00
|
|
|
if (v3Global.opt.mtasks()) puts("#include \"verilated_threads.h\"\n");
|
|
|
|
|
if (v3Global.opt.savable()) puts("#include \"verilated_save.h\"\n");
|
2021-05-22 13:27:32 +02:00
|
|
|
if (v3Global.opt.coverage()) puts("#include \"verilated_cov.h\"\n");
|
2021-06-10 23:41:33 +02:00
|
|
|
if (v3Global.dpi() && modp->isTop()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// do this before including our main .h file so that any references to
|
|
|
|
|
// types defined in svdpi.h are available
|
2021-06-10 23:41:33 +02:00
|
|
|
puts("#include \"svdpi.h\"\n");
|
2011-05-12 12:59:13 +02:00
|
|
|
}
|
2020-02-02 01:32:04 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-02-02 01:32:04 +01:00
|
|
|
void EmitCImp::emitInt(AstNodeModule* modp) {
|
2020-02-03 03:15:07 +01:00
|
|
|
puts("\n//==========\n\n");
|
2021-02-13 23:29:34 +01:00
|
|
|
|
|
|
|
|
if (AstClass* classp = VN_CAST(modp, Class)) {
|
|
|
|
|
if (classp->extendsp())
|
|
|
|
|
puts("#include \"" + prefixNameProtect(classp->extendsp()->classp()->classOrPackagep())
|
|
|
|
|
+ ".h\"\n");
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-01 22:45:11 +01:00
|
|
|
emitModCUse(modp, VUseType::INT_INCLUDE);
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// Declare foreign instances up front to make C++ happy
|
2020-02-01 22:45:11 +01:00
|
|
|
puts("class " + symClassName() + ";\n");
|
|
|
|
|
emitModCUse(modp, VUseType::INT_FWD_CLASS);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
puts("\n//----------\n\n");
|
2016-11-05 14:47:56 +01:00
|
|
|
emitTextSection(AstType::atScHdr);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-04-05 15:30:23 +02:00
|
|
|
if (AstClass* classp = VN_CAST(modp, Class)) {
|
|
|
|
|
puts("class " + prefixNameProtect(modp));
|
2020-08-24 01:37:56 +02:00
|
|
|
if (classp->extendsp())
|
|
|
|
|
puts(" : public " + prefixNameProtect(classp->extendsp()->classp()));
|
2020-04-05 15:30:23 +02:00
|
|
|
puts(" {\n");
|
|
|
|
|
} else if (optSystemC() && modp->isTop()) {
|
2020-01-25 15:16:00 +01:00
|
|
|
puts("SC_MODULE(" + prefixNameProtect(modp) + ") {\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
} else {
|
2020-01-25 15:16:00 +01:00
|
|
|
puts("VL_MODULE(" + prefixNameProtect(modp) + ") {\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
ofp()->resetPrivate();
|
|
|
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
|
|
2017-09-23 04:27:03 +02:00
|
|
|
{ // Instantiated cells
|
2019-05-19 22:13:13 +02:00
|
|
|
bool did = false;
|
|
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (AstCell* cellp = VN_CAST(nodep, Cell)) {
|
|
|
|
|
if (!did) {
|
|
|
|
|
did = true;
|
|
|
|
|
putsDecoration("// CELLS\n");
|
2020-02-04 05:21:56 +01:00
|
|
|
if (modp->isTop()) {
|
|
|
|
|
puts("// Public to allow access to /*verilator_public*/ items;\n");
|
|
|
|
|
puts("// otherwise the application code can consider these internals.\n");
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-01-25 15:16:00 +01:00
|
|
|
puts(prefixNameProtect(cellp->modp()) + "* " + cellp->nameProtect() + ";\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2014-11-07 13:50:11 +01:00
|
|
|
emitTypedefs(modp->stmtsp());
|
|
|
|
|
|
2020-02-02 02:11:21 +01:00
|
|
|
string section;
|
|
|
|
|
section = "\n// PORTS\n";
|
2020-04-16 03:47:37 +02:00
|
|
|
if (modp->isTop()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
section += ("// The application code writes and reads these signals to\n"
|
|
|
|
|
"// propagate new values into/out from the Verilated model.\n");
|
2020-04-16 03:47:37 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_IO, "", section /*ref*/);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-02-02 02:11:21 +01:00
|
|
|
section = "\n// LOCAL SIGNALS\n";
|
|
|
|
|
if (modp->isTop()) section += "// Internals; generally not touched by application code\n";
|
2020-04-15 13:58:34 +02:00
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_SIG, "", section /*ref*/);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-02-02 02:11:21 +01:00
|
|
|
section = "\n// LOCAL VARIABLES\n";
|
|
|
|
|
if (modp->isTop()) section += "// Internals; generally not touched by application code\n";
|
2020-04-15 13:58:34 +02:00
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_TEMP, "", section /*ref*/);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
puts("\n// INTERNAL VARIABLES\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
if (modp->isTop()) puts("// Internals; generally not touched by application code\n");
|
2020-04-07 18:17:48 +02:00
|
|
|
if (!VN_IS(modp, Class)) { // Avoid clang unused error (& don't want in every object)
|
2021-06-13 15:33:11 +02:00
|
|
|
ofp()->putsPrivate(false); // public: so loose methods can pick it up
|
|
|
|
|
puts(symClassName() + "* vlSymsp; // Symbol table\n");
|
2020-04-07 18:17:48 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
ofp()->putsPrivate(false); // public:
|
2020-03-02 03:39:23 +01:00
|
|
|
if (modp->isTop()) {
|
2021-06-16 13:18:56 +02:00
|
|
|
if (v3Global.opt.mtasks()) emitThreadingState();
|
2018-07-23 02:54:28 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
emitCoverageDecl(modp); // may flip public/private
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-02-02 02:11:21 +01:00
|
|
|
section = "\n// PARAMETERS\n";
|
2020-04-15 13:58:34 +02:00
|
|
|
if (modp->isTop())
|
|
|
|
|
section += "// Parameters marked /*verilator public*/ for use by application code\n";
|
2006-08-26 13:35:28 +02:00
|
|
|
ofp()->putsPrivate(false); // public:
|
2020-04-15 13:58:34 +02:00
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_PAR, "",
|
|
|
|
|
section /*ref*/); // Only those that are non-CONST
|
2020-06-16 13:07:59 +02:00
|
|
|
bool first = true;
|
|
|
|
|
emitParams(modp, false, &first, section /*ref*/);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-02-03 03:15:07 +01:00
|
|
|
if (!VN_IS(modp, Class)) {
|
|
|
|
|
puts("\n// CONSTRUCTORS\n");
|
|
|
|
|
ofp()->resetPrivate();
|
|
|
|
|
// We don't need a private copy constructor, as VerilatedModule has one for us.
|
|
|
|
|
ofp()->putsPrivate(true);
|
|
|
|
|
puts("VL_UNCOPYABLE(" + prefixNameProtect(modp) + "); ///< Copying not allowed\n");
|
|
|
|
|
}
|
2006-08-30 23:07:55 +02:00
|
|
|
|
2020-02-03 03:15:07 +01:00
|
|
|
if (VN_IS(modp, Class)) {
|
|
|
|
|
// CFuncs with isConstructor/isDestructor used instead
|
|
|
|
|
} else if (optSystemC() && modp->isTop()) {
|
|
|
|
|
ofp()->putsPrivate(false); // public:
|
2020-01-25 15:16:00 +01:00
|
|
|
puts("SC_CTOR(" + prefixNameProtect(modp) + ");\n");
|
|
|
|
|
puts("virtual ~" + prefixNameProtect(modp) + "();\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
} else if (optSystemC()) {
|
2020-02-03 03:15:07 +01:00
|
|
|
ofp()->putsPrivate(false); // public:
|
2021-02-21 18:12:11 +01:00
|
|
|
puts(prefixNameProtect(modp) + "(const char* __VCname = \"\");\n");
|
2020-01-25 15:16:00 +01:00
|
|
|
puts("~" + prefixNameProtect(modp) + "();\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
} else {
|
2020-02-03 03:15:07 +01:00
|
|
|
ofp()->putsPrivate(false); // public:
|
2019-05-19 22:13:13 +02:00
|
|
|
if (modp->isTop()) {
|
|
|
|
|
puts("/// Construct the model; called by application code\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
puts("/// If contextp is null, then the model will use the default global context\n");
|
|
|
|
|
puts("/// If name is \"\", then makes a wrapper with a\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("/// single model invisible with respect to DPI scope names.\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
puts(prefixNameProtect(modp) + "(VerilatedContext* contextp,"
|
|
|
|
|
+ " const char* name = \"TOP\");\n");
|
|
|
|
|
puts(prefixNameProtect(modp) + "(const char* name = \"TOP\")\n");
|
|
|
|
|
puts(" : " + prefixNameProtect(modp) + "(nullptr, name) {}\n");
|
2020-04-05 15:30:23 +02:00
|
|
|
} else {
|
2021-03-07 17:01:54 +01:00
|
|
|
if (VN_IS(modp, Class)) {
|
|
|
|
|
// TODO move all constructor definition to e.g. V3CUse
|
|
|
|
|
puts(prefixNameProtect(modp) + "();\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts(prefixNameProtect(modp) + "(const char* name = \"TOP\");\n");
|
|
|
|
|
}
|
2020-04-05 15:30:23 +02:00
|
|
|
}
|
2020-01-25 15:16:00 +01:00
|
|
|
if (modp->isTop()) {
|
|
|
|
|
puts("/// Destroy the model; called (often implicitly) by application code\n");
|
|
|
|
|
}
|
|
|
|
|
puts("~" + prefixNameProtect(modp) + "();\n");
|
2007-12-13 14:54:04 +01:00
|
|
|
}
|
2021-03-07 17:01:54 +01:00
|
|
|
|
2019-10-06 02:16:33 +02:00
|
|
|
if (v3Global.opt.trace() && modp->isTop()) {
|
|
|
|
|
puts("/// Trace signals in the model; called by application code\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("void trace(" + v3Global.opt.traceClassBase()
|
|
|
|
|
+ "C* tfp, int levels, int options = 0);\n");
|
2019-10-06 02:16:33 +02:00
|
|
|
if (optSystemC()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("/// SC tracing; avoid overloaded virtual function lint warning\n");
|
2020-08-15 17:44:10 +02:00
|
|
|
puts("virtual void trace(sc_trace_file* tfp) const override { "
|
2020-04-15 13:58:34 +02:00
|
|
|
"::sc_core::sc_module::trace(tfp); }\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2016-11-05 14:47:56 +01:00
|
|
|
emitTextSection(AstType::atScInt);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
if (modp->isTop()) {
|
2020-02-02 01:32:04 +01:00
|
|
|
puts("\n// API METHODS\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
puts("/// Return current simulation context for this model.\n");
|
|
|
|
|
puts("/// Used to get to e.g. simulation time via contextp()->time()\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("VerilatedContext* contextp() const;\n");
|
2021-03-07 17:01:54 +01:00
|
|
|
|
2020-03-02 03:39:23 +01:00
|
|
|
string callEvalEndStep
|
|
|
|
|
= (v3Global.needTraceDumper() && !optSystemC()) ? "eval_end_step(); " : "";
|
2020-04-15 13:58:34 +02:00
|
|
|
if (optSystemC()) {
|
|
|
|
|
ofp()->putsPrivate(true); ///< eval() is invoked by our sensitive() calls.
|
|
|
|
|
}
|
|
|
|
|
if (!optSystemC()) {
|
|
|
|
|
puts("/// Evaluate the model. Application must call when inputs change.\n");
|
|
|
|
|
}
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("void eval() { eval_step(); " + callEvalEndStep + "}\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!optSystemC()) {
|
|
|
|
|
puts("/// Evaluate when calling multiple units/models per time step.\n");
|
|
|
|
|
}
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("void eval_step();\n");
|
|
|
|
|
if (!optSystemC()) {
|
|
|
|
|
puts("/// Evaluate at end of a timestep for tracing, when using eval_step().\n");
|
|
|
|
|
puts("/// Application must call after all eval() and before time changes.\n");
|
|
|
|
|
puts("void eval_end_step()");
|
2020-04-15 13:58:34 +02:00
|
|
|
if (callEvalEndStep == "") {
|
|
|
|
|
puts(" {}\n");
|
|
|
|
|
} else {
|
|
|
|
|
puts(";\n");
|
|
|
|
|
}
|
2020-03-02 03:39:23 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
ofp()->putsPrivate(false); // public:
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!optSystemC()) {
|
|
|
|
|
puts("/// Simulation complete, run final blocks. Application "
|
|
|
|
|
"must call on completion.\n");
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("void final();\n");
|
2009-12-03 03:15:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
puts("\n// INTERNAL METHODS\n");
|
|
|
|
|
if (modp->isTop()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
ofp()->putsPrivate(false); // public: as accessed by loose functions
|
2020-04-06 00:30:46 +02:00
|
|
|
if (v3Global.needTraceDumper()) {
|
2020-11-10 00:29:09 +01:00
|
|
|
if (!optSystemC()) puts("void _traceDump();\n");
|
|
|
|
|
puts("void _traceDumpOpen();\n");
|
|
|
|
|
puts("void _traceDumpClose();\n");
|
2020-04-06 00:30:46 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-03 03:15:07 +01:00
|
|
|
if (!VN_IS(modp, Class)) {
|
|
|
|
|
ofp()->putsPrivate(false); // public:
|
|
|
|
|
puts("void " + protect("__Vconfigure") + "(" + symClassName() + "* symsp, bool first);\n");
|
|
|
|
|
}
|
2009-12-03 03:15:56 +01:00
|
|
|
|
2020-04-05 15:30:23 +02:00
|
|
|
ofp()->putsPrivate(false); // public:
|
2020-02-01 16:57:55 +01:00
|
|
|
emitIntFuncDecls(modp, true);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2012-08-27 03:13:47 +02:00
|
|
|
if (v3Global.opt.savable()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
ofp()->putsPrivate(false); // public:
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("void " + protect("__Vserialize") + "(VerilatedSerialize& os);\n");
|
|
|
|
|
puts("void " + protect("__Vdeserialize") + "(VerilatedDeserialize& os);\n");
|
2012-08-27 03:13:47 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-02-02 02:11:21 +01:00
|
|
|
puts("}");
|
2020-02-02 02:28:03 +01:00
|
|
|
if (!VN_IS(modp, Class)) puts(" VL_ATTR_ALIGNED(VL_CACHE_LINE_BYTES)");
|
2020-02-02 02:11:21 +01:00
|
|
|
puts(";\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-02-03 03:15:07 +01:00
|
|
|
puts("\n//----------\n\n");
|
2020-02-01 16:57:55 +01:00
|
|
|
emitIntFuncDecls(modp, false);
|
|
|
|
|
|
2012-08-27 03:13:47 +02:00
|
|
|
// Save/restore
|
|
|
|
|
if (v3Global.opt.savable() && modp->isTop()) {
|
2019-12-24 18:47:27 +01:00
|
|
|
puts("\n");
|
|
|
|
|
puts("inline VerilatedSerialize& operator<<(VerilatedSerialize& os, "
|
2020-04-15 13:58:34 +02:00
|
|
|
+ prefixNameProtect(modp) + "& rhs) {\n" //
|
|
|
|
|
+ "Verilated::quiesce(); rhs." + protect("__Vserialize") + "(os); return os; }\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, "
|
2020-04-15 13:58:34 +02:00
|
|
|
+ prefixNameProtect(modp) + "& rhs) {\n" //
|
|
|
|
|
+ "Verilated::quiesce(); rhs." + protect("__Vdeserialize") + "(os); return os; }\n");
|
2012-08-27 03:13:47 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
2021-05-15 17:05:24 +02:00
|
|
|
void EmitCImp::emitImpTop() {
|
2019-12-24 01:00:17 +01:00
|
|
|
puts("\n");
|
2021-05-15 17:05:24 +02:00
|
|
|
puts("#include \"" + prefixNameProtect(m_fileModp) + ".h\"\n");
|
2020-01-25 15:16:00 +01:00
|
|
|
puts("#include \"" + symClassName() + ".h\"\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2011-05-21 03:33:31 +02:00
|
|
|
if (v3Global.dpi()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("\n");
|
|
|
|
|
puts("#include \"verilated_dpi.h\"\n");
|
2011-05-21 03:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
2021-05-15 17:05:24 +02:00
|
|
|
emitModCUse(m_fileModp, VUseType::IMP_INCLUDE);
|
|
|
|
|
emitModCUse(m_fileModp, VUseType::IMP_FWD_CLASS);
|
2020-02-01 22:45:11 +01:00
|
|
|
|
2016-11-05 14:47:56 +01:00
|
|
|
emitTextSection(AstType::atScImpHdr);
|
2020-01-25 15:33:43 +01:00
|
|
|
}
|
2006-08-29 02:58:48 +02:00
|
|
|
|
2021-05-15 17:05:24 +02:00
|
|
|
void EmitCImp::emitImp(AstNodeModule* modp) {
|
2020-02-03 03:15:07 +01:00
|
|
|
puts("\n//==========\n");
|
2020-01-25 15:33:43 +01:00
|
|
|
if (m_slow) {
|
2020-02-02 02:11:21 +01:00
|
|
|
string section;
|
2020-04-15 13:58:34 +02:00
|
|
|
emitVarList(modp->stmtsp(), EVL_CLASS_ALL, prefixNameProtect(modp), section /*ref*/);
|
2020-02-03 03:15:07 +01:00
|
|
|
if (!VN_IS(modp, Class)) emitCtorImp(modp);
|
|
|
|
|
if (!VN_IS(modp, Class)) emitConfigureImp(modp);
|
|
|
|
|
if (!VN_IS(modp, Class)) emitDestructorImp(modp);
|
2019-05-19 22:13:13 +02:00
|
|
|
emitSavableImp(modp);
|
|
|
|
|
emitCoverageImp(modp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2006-08-30 19:27:53 +02:00
|
|
|
|
2020-01-25 15:33:43 +01:00
|
|
|
if (m_fast) {
|
|
|
|
|
emitTextSection(AstType::atScImp);
|
2021-03-07 17:01:54 +01:00
|
|
|
if (modp->isTop()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
emitWrapFast();
|
|
|
|
|
emitWrapEval();
|
2021-03-07 17:01:54 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Blocks
|
2020-01-25 15:33:43 +01:00
|
|
|
for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
|
2021-05-15 17:05:24 +02:00
|
|
|
if (AstCFunc* funcp = VN_CAST(nodep, CFunc)) { mainDoFunc(funcp); }
|
2020-01-25 15:33:43 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2021-05-15 17:05:24 +02:00
|
|
|
void EmitCImp::maybeSplit() {
|
|
|
|
|
if (!splitNeeded()) return;
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
m_ofp = newOutCFile(!m_fast, true /*source*/, splitFilenumInc());
|
|
|
|
|
emitImpTop();
|
2018-05-30 01:49:27 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-02 01:32:04 +01:00
|
|
|
void EmitCImp::mainInt(AstNodeModule* modp) {
|
|
|
|
|
m_modp = modp;
|
2021-05-15 17:05:24 +02:00
|
|
|
m_fileModp = modp;
|
2020-02-02 01:32:04 +01:00
|
|
|
m_slow = true;
|
|
|
|
|
m_fast = true;
|
|
|
|
|
|
|
|
|
|
UINFO(5, " Emitting " << prefixNameProtect(modp) << endl);
|
|
|
|
|
|
2021-05-15 17:05:24 +02:00
|
|
|
m_ofp = newOutCFile(false /*slow*/, false /*source*/);
|
2020-02-02 01:32:04 +01:00
|
|
|
emitIntTop(modp);
|
|
|
|
|
emitInt(modp);
|
2020-04-05 15:30:23 +02:00
|
|
|
if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) {
|
|
|
|
|
// Put the non-static class implementation in same h file for speed
|
|
|
|
|
m_modp = packagep->classp();
|
|
|
|
|
emitInt(packagep->classp());
|
|
|
|
|
m_modp = modp;
|
|
|
|
|
}
|
2020-02-02 01:32:04 +01:00
|
|
|
ofp()->putsEndGuard();
|
2020-08-15 16:12:55 +02:00
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
2020-02-02 01:32:04 +01:00
|
|
|
}
|
|
|
|
|
|
2020-05-25 12:35:06 +02:00
|
|
|
void EmitCImp::mainImp(AstNodeModule* modp, bool slow) {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Output a module
|
|
|
|
|
m_modp = modp;
|
2021-05-15 17:05:24 +02:00
|
|
|
m_fileModp = modp;
|
2006-08-26 13:35:28 +02:00
|
|
|
m_slow = slow;
|
2020-05-25 12:35:06 +02:00
|
|
|
m_fast = !slow;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-01-25 15:16:00 +01:00
|
|
|
UINFO(5, " Emitting " << prefixNameProtect(modp) << endl);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-05-15 17:05:24 +02:00
|
|
|
m_ofp = newOutCFile(!m_fast, true /*source*/);
|
|
|
|
|
emitImpTop();
|
|
|
|
|
emitImp(modp);
|
2008-06-10 03:25:10 +02:00
|
|
|
|
2020-04-05 15:30:23 +02:00
|
|
|
if (AstClassPackage* packagep = VN_CAST(modp, ClassPackage)) {
|
|
|
|
|
// Put the non-static class implementation in same C++ files as
|
|
|
|
|
// often optimizations are possible when both are seen by the
|
|
|
|
|
// compiler together
|
|
|
|
|
m_modp = packagep->classp();
|
2021-05-15 17:05:24 +02:00
|
|
|
emitImp(packagep->classp());
|
2020-04-05 15:30:23 +02:00
|
|
|
m_modp = modp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-15 16:12:55 +02:00
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Tracing routines
|
|
|
|
|
|
2021-06-24 18:35:12 +02:00
|
|
|
class EmitCTrace final : EmitCFunc {
|
2018-10-08 13:21:22 +02:00
|
|
|
// NODE STATE/TYPES
|
|
|
|
|
// Cleared on netlist
|
2021-06-13 15:33:11 +02:00
|
|
|
// AstNode::user1() -> int. Enum number
|
2018-10-08 13:21:22 +02:00
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
|
|
|
|
|
|
// MEMBERS
|
2020-10-31 13:59:35 +01:00
|
|
|
AstCFunc* m_cfuncp = nullptr; // Function we're in now
|
2020-04-15 13:58:34 +02:00
|
|
|
bool m_slow; // Making slow file
|
2020-08-16 15:55:36 +02:00
|
|
|
int m_enumNum = 0; // Enumeration number (whole netlist)
|
|
|
|
|
int m_baseCode = -1; // Code of first AstTraceInc in this function
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2008-11-17 23:13:57 +01:00
|
|
|
void newOutCFile(int filenum) {
|
2021-06-13 15:33:11 +02:00
|
|
|
m_lazyDecls.reset(); // Need to emit new lazy declarations
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
string filename
|
|
|
|
|
= (v3Global.opt.makeDir() + "/" + topClassName() + "_" + protect("_Trace"));
|
|
|
|
|
if (filenum) filename += "__" + cvtToStr(filenum);
|
|
|
|
|
filename += (m_slow ? "__Slow" : "");
|
2018-11-30 02:35:21 +01:00
|
|
|
filename += ".cpp";
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
AstCFile* cfilep = newCFile(filename, m_slow, true /*source*/);
|
2019-05-19 22:13:13 +02:00
|
|
|
cfilep->support(true);
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_ofp) v3fatalSrc("Previous file not closed");
|
2020-04-10 05:03:28 +02:00
|
|
|
if (optSystemC()) {
|
|
|
|
|
m_ofp = new V3OutScFile(filename);
|
|
|
|
|
} else {
|
|
|
|
|
m_ofp = new V3OutCFile(filename);
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
m_ofp->putsHeader();
|
2020-04-15 13:58:34 +02:00
|
|
|
m_ofp->puts("// DESCR"
|
|
|
|
|
"IPTION: Verilator output: Tracing implementation internals\n");
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
emitTraceHeader();
|
2008-11-17 23:13:57 +01:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void emitTraceHeader() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Includes
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("#include \"" + v3Global.opt.traceSourceLang() + ".h\"\n");
|
|
|
|
|
puts("#include \"" + symClassName() + ".h\"\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void emitTraceSlow() {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("\n//======================\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-03-02 03:39:23 +01:00
|
|
|
if (v3Global.needTraceDumper() && !optSystemC()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("\nvoid " + topClassName() + "::_traceDump() {\n");
|
2020-08-15 16:12:55 +02:00
|
|
|
// Caller checked for __Vm_dumperp non-nullptr
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("const VerilatedLockGuard lock(vlSymsp->__Vm_dumperMutex);\n");
|
|
|
|
|
puts("vlSymsp->__Vm_dumperp->dump(VL_TIME_Q());\n");
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (v3Global.needTraceDumper()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("\nvoid " + topClassName() + "::_traceDumpOpen() {\n");
|
|
|
|
|
puts("const VerilatedLockGuard lock(vlSymsp->__Vm_dumperMutex);\n");
|
|
|
|
|
puts("if (VL_UNLIKELY(!vlSymsp->__Vm_dumperp)) {\n");
|
|
|
|
|
puts("vlSymsp->__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
|
|
|
|
|
puts("trace(vlSymsp->__Vm_dumperp, 0, 0);\n");
|
|
|
|
|
puts("std::string dumpfile = vlSymsp->_vm_contextp__->dumpfileCheck();\n");
|
|
|
|
|
puts("vlSymsp->__Vm_dumperp->open(dumpfile.c_str());\n");
|
|
|
|
|
puts("vlSymsp->__Vm_dumping = true;\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("}\n");
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("\nvoid " + topClassName() + "::_traceDumpClose() {\n");
|
|
|
|
|
puts("const VerilatedLockGuard lock(vlSymsp->__Vm_dumperMutex);\n");
|
|
|
|
|
puts("vlSymsp->__Vm_dumping = false;\n");
|
|
|
|
|
puts("VL_DO_CLEAR(delete vlSymsp->__Vm_dumperp, vlSymsp->__Vm_dumperp = "
|
2020-08-15 16:12:55 +02:00
|
|
|
"nullptr);\n");
|
2020-03-02 03:39:23 +01:00
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
m_lazyDecls.emit("void " + topClassName() + "__", protect("traceInitTop"),
|
|
|
|
|
"(" + topClassName() + "* vlSelf, " + v3Global.opt.traceClassBase()
|
|
|
|
|
+ "* tracep);");
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("\nstatic void " + protect("traceInit") + "(void* voidSelf, "
|
2020-05-09 00:42:34 +02:00
|
|
|
+ v3Global.opt.traceClassBase() + "* tracep, uint32_t code) {\n");
|
|
|
|
|
putsDecoration("// Callback from tracep->open()\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(topClassName() + "*const __restrict vlSelf = static_cast<" + topClassName()
|
|
|
|
|
+ "*>(voidSelf);\n");
|
|
|
|
|
puts("if (!vlSelf->vlSymsp->_vm_contextp__->calcUnusedSigs()) {\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("VL_FATAL_MT(__FILE__, __LINE__, __FILE__,\n");
|
|
|
|
|
puts(" \"Turning on wave traces requires Verilated::traceEverOn(true) call "
|
|
|
|
|
"before time 0.\");\n");
|
2018-03-10 22:32:04 +01:00
|
|
|
puts("}\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("vlSelf->vlSymsp->__Vm_baseCode = code;\n");
|
|
|
|
|
puts("tracep->module(vlSelf->vlSymsp->name());\n");
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->scopeEscape(' ');\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
puts(topClassName() + "__" + protect("traceInitTop") + "(vlSelf, tracep);\n");
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->scopeEscape('.');\n"); // Restore so later traced files won't break
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("\n");
|
|
|
|
|
m_lazyDecls.emit("void " + topClassName() + "__", protect("traceRegister"),
|
|
|
|
|
"(" + topClassName() + "* vlSelf, " + v3Global.opt.traceClassBase()
|
|
|
|
|
+ "* tracep);");
|
|
|
|
|
|
|
|
|
|
puts("\nvoid " + topClassName() + "::trace(");
|
|
|
|
|
puts(v3Global.opt.traceClassBase() + "C* tfp, int, int) {\n");
|
|
|
|
|
puts("tfp->spTrace()->addInitCb(&" + protect("traceInit") + ", this);\n");
|
|
|
|
|
puts(topClassName() + "__" + protect("traceRegister") + "(this, tfp->spTrace());\n");
|
|
|
|
|
puts("}\n");
|
|
|
|
|
splitSizeInc(10);
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("\n//======================\n\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2009-03-13 19:17:30 +01:00
|
|
|
bool emitTraceIsScBv(AstTraceInc* nodep) {
|
2020-05-09 00:42:34 +02:00
|
|
|
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!varrefp) return false;
|
|
|
|
|
AstVar* varp = varrefp->varp();
|
|
|
|
|
return varp->isSc() && varp->isScBv();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2013-04-27 03:02:32 +02:00
|
|
|
|
|
|
|
|
bool emitTraceIsScBigUint(AstTraceInc* nodep) {
|
2020-05-09 00:42:34 +02:00
|
|
|
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!varrefp) return false;
|
|
|
|
|
AstVar* varp = varrefp->varp();
|
|
|
|
|
return varp->isSc() && varp->isScBigUint();
|
2013-04-27 03:02:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool emitTraceIsScUint(AstTraceInc* nodep) {
|
2020-05-09 00:42:34 +02:00
|
|
|
const AstVarRef* varrefp = VN_CAST(nodep->declp()->valuep(), VarRef);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!varrefp) return false;
|
|
|
|
|
AstVar* varp = varrefp->varp();
|
|
|
|
|
return varp->isSc() && varp->isScUint();
|
2013-04-27 03:02:32 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-08 13:21:22 +02:00
|
|
|
void emitTraceInitOne(AstTraceDecl* nodep, int enumNum) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->declDouble");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (nodep->isWide()) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->declArray");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (nodep->isQuad()) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->declQuad");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (nodep->bitRange().ranged()) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->declBus");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->declBit");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-10-04 01:51:05 +02:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("(c+" + cvtToStr(nodep->code()));
|
|
|
|
|
if (nodep->arrayRange().ranged()) puts("+i*" + cvtToStr(nodep->widthWords()));
|
2019-05-19 22:13:13 +02:00
|
|
|
puts(",");
|
2020-04-15 13:58:34 +02:00
|
|
|
if (nodep->isScoped()) puts("Verilated::catName(scopep,");
|
2019-10-06 19:24:21 +02:00
|
|
|
putsQuoted(VIdProtect::protectWordsIf(nodep->showname(), nodep->protect()));
|
2021-04-07 15:55:11 +02:00
|
|
|
if (nodep->isScoped()) puts(",(int)scopet,\" \")");
|
2018-10-04 01:51:05 +02:00
|
|
|
// Direction
|
2020-04-22 00:49:07 +02:00
|
|
|
if (v3Global.opt.traceFormat().fst()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("," + cvtToStr(enumNum));
|
2018-10-05 02:24:41 +02:00
|
|
|
// fstVarDir
|
2020-04-15 13:58:34 +02:00
|
|
|
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");
|
|
|
|
|
}
|
2018-10-05 02:24:41 +02:00
|
|
|
//
|
|
|
|
|
// fstVarType
|
2021-06-21 00:32:57 +02:00
|
|
|
const AstVarType vartype = nodep->varType();
|
|
|
|
|
const AstBasicDTypeKwd kwd = nodep->declKwd();
|
2018-10-05 02:24:41 +02:00
|
|
|
string fstvt;
|
|
|
|
|
// Doubles have special decoding properties, so must indicate if a double
|
|
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
|
|
|
|
if (vartype == AstVarType::GPARAM || vartype == AstVarType::LPARAM) {
|
|
|
|
|
fstvt = "FST_VT_VCD_REAL_PARAMETER";
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
|
|
|
|
fstvt = "FST_VT_VCD_REAL";
|
|
|
|
|
}
|
2018-10-05 02:24:41 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format off
|
2020-04-16 03:47:37 +02:00
|
|
|
else if (vartype == AstVarType::GPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; }
|
|
|
|
|
else if (vartype == AstVarType::LPARAM) { fstvt = "FST_VT_VCD_PARAMETER"; }
|
|
|
|
|
else if (vartype == AstVarType::SUPPLY0) { fstvt = "FST_VT_VCD_SUPPLY0"; }
|
|
|
|
|
else if (vartype == AstVarType::SUPPLY1) { fstvt = "FST_VT_VCD_SUPPLY1"; }
|
|
|
|
|
else if (vartype == AstVarType::TRI0) { fstvt = "FST_VT_VCD_TRI0"; }
|
|
|
|
|
else if (vartype == AstVarType::TRI1) { fstvt = "FST_VT_VCD_TRI1"; }
|
|
|
|
|
else if (vartype == AstVarType::TRIWIRE) { fstvt = "FST_VT_VCD_TRI"; }
|
|
|
|
|
else if (vartype == AstVarType::WIRE) { fstvt = "FST_VT_VCD_WIRE"; }
|
|
|
|
|
else if (vartype == AstVarType::PORT) { fstvt = "FST_VT_VCD_WIRE"; }
|
2018-10-05 02:24:41 +02:00
|
|
|
//
|
2020-04-16 03:47:37 +02:00
|
|
|
else if (kwd == AstBasicDTypeKwd::INTEGER) { fstvt = "FST_VT_VCD_INTEGER"; }
|
|
|
|
|
else if (kwd == AstBasicDTypeKwd::BIT) { fstvt = "FST_VT_SV_BIT"; }
|
|
|
|
|
else if (kwd == AstBasicDTypeKwd::LOGIC) { fstvt = "FST_VT_SV_LOGIC"; }
|
|
|
|
|
else if (kwd == AstBasicDTypeKwd::INT) { fstvt = "FST_VT_SV_INT"; }
|
|
|
|
|
else if (kwd == AstBasicDTypeKwd::SHORTINT) { fstvt = "FST_VT_SV_SHORTINT"; }
|
|
|
|
|
else if (kwd == AstBasicDTypeKwd::LONGINT) { fstvt = "FST_VT_SV_LONGINT"; }
|
|
|
|
|
else if (kwd == AstBasicDTypeKwd::BYTE) { fstvt = "FST_VT_SV_BYTE"; }
|
|
|
|
|
else { fstvt = "FST_VT_SV_BIT"; }
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format on
|
2018-10-05 02:24:41 +02:00
|
|
|
//
|
|
|
|
|
// Not currently supported
|
|
|
|
|
// FST_VT_VCD_EVENT
|
|
|
|
|
// 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
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("," + fstvt);
|
2018-10-04 01:51:05 +02:00
|
|
|
}
|
|
|
|
|
// Range
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->arrayRange().ranged()) {
|
2020-01-08 13:32:31 +01:00
|
|
|
puts(", true,(i+" + cvtToStr(nodep->arrayRange().lo()) + ")");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-01-08 13:32:31 +01:00
|
|
|
puts(", false,-1");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-01-08 13:32:31 +01:00
|
|
|
if (!nodep->dtypep()->basicp()->isDouble() && nodep->bitRange().ranged()) {
|
|
|
|
|
puts(", " + cvtToStr(nodep->bitRange().left()) + ","
|
|
|
|
|
+ cvtToStr(nodep->bitRange().right()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
puts(");");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2018-10-08 13:21:22 +02:00
|
|
|
int emitTraceDeclDType(AstNodeDType* nodep) {
|
|
|
|
|
// Return enum number or -1 for none
|
2020-04-22 00:49:07 +02:00
|
|
|
if (v3Global.opt.traceFormat().fst()) {
|
2018-10-08 13:21:22 +02:00
|
|
|
// Skip over refs-to-refs, but stop before final ref so can get data type name
|
2019-05-02 01:18:45 +02:00
|
|
|
// Alternatively back in V3Width we could push enum names from upper typedefs
|
2018-10-08 13:21:22 +02:00
|
|
|
if (AstEnumDType* enump = VN_CAST(nodep->skipRefToEnump(), EnumDType)) {
|
|
|
|
|
int enumNum = enump->user1();
|
|
|
|
|
if (!enumNum) {
|
|
|
|
|
enumNum = ++m_enumNum;
|
|
|
|
|
enump->user1(enumNum);
|
|
|
|
|
int nvals = 0;
|
|
|
|
|
puts("{\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("const char* " + protect("__VenumItemNames") + "[]\n");
|
2018-10-08 13:21:22 +02:00
|
|
|
puts("= {");
|
|
|
|
|
for (AstEnumItem* itemp = enump->itemsp(); itemp;
|
2019-05-19 22:13:13 +02:00
|
|
|
itemp = VN_CAST(itemp->nextp(), EnumItem)) {
|
2018-10-08 13:21:22 +02:00
|
|
|
if (++nvals > 1) puts(", ");
|
2020-04-15 13:58:34 +02:00
|
|
|
putbs("\"" + itemp->prettyName() + "\"");
|
2018-10-08 13:21:22 +02:00
|
|
|
}
|
|
|
|
|
puts("};\n");
|
|
|
|
|
nvals = 0;
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("const char* " + protect("__VenumItemValues") + "[]\n");
|
2018-10-08 13:21:22 +02:00
|
|
|
puts("= {");
|
|
|
|
|
for (AstEnumItem* itemp = enump->itemsp(); itemp;
|
2019-05-19 22:13:13 +02:00
|
|
|
itemp = VN_CAST(itemp->nextp(), EnumItem)) {
|
2018-10-08 13:21:22 +02:00
|
|
|
AstConst* constp = VN_CAST(itemp->valuep(), Const);
|
|
|
|
|
if (++nvals > 1) puts(", ");
|
2020-04-15 13:58:34 +02:00
|
|
|
putbs("\"" + constp->num().displayed(nodep, "%0b") + "\"");
|
2018-10-08 13:21:22 +02:00
|
|
|
}
|
|
|
|
|
puts("};\n");
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->declDTypeEnum(" + cvtToStr(enumNum) + ", \""
|
|
|
|
|
+ enump->prettyName() + "\", " + cvtToStr(nvals) + ", "
|
|
|
|
|
+ cvtToStr(enump->widthMin()) + ", " + protect("__VenumItemNames") + ", "
|
|
|
|
|
+ protect("__VenumItemValues") + ");\n");
|
2018-10-08 13:21:22 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
return enumNum;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void emitTraceChangeOne(AstTraceInc* nodep, int arrayindex) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateAndNextNull(nodep->precondsp());
|
2020-05-09 00:42:34 +02:00
|
|
|
const string func = nodep->full() ? "full" : "chg";
|
2020-04-30 01:09:09 +02:00
|
|
|
bool emitWidth = true;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->dtypep()->basicp()->isDouble()) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->" + func + "Double");
|
2020-04-30 01:09:09 +02:00
|
|
|
emitWidth = false;
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (nodep->isWide() || emitTraceIsScBv(nodep) || emitTraceIsScBigUint(nodep)) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->" + func + "WData");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (nodep->isQuad()) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->" + func + "QData");
|
2020-04-30 01:09:09 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 16) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->" + func + "IData");
|
2020-04-30 01:09:09 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 8) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->" + func + "SData");
|
2020-04-14 01:13:10 +02:00
|
|
|
} else if (nodep->declp()->widthMin() > 1) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->" + func + "CData");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("tracep->" + func + "Bit");
|
2020-04-30 01:09:09 +02:00
|
|
|
emitWidth = false;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-14 01:13:10 +02:00
|
|
|
|
|
|
|
|
const uint32_t offset = (arrayindex < 0) ? 0 : (arrayindex * nodep->declp()->widthWords());
|
|
|
|
|
const uint32_t code = nodep->declp()->code() + offset;
|
2020-05-09 00:42:34 +02:00
|
|
|
puts(v3Global.opt.trueTraceThreads() && !nodep->full() ? "(base+" : "(oldp+");
|
2020-04-25 23:38:25 +02:00
|
|
|
puts(cvtToStr(code - m_baseCode));
|
|
|
|
|
puts(",");
|
2019-05-19 22:13:13 +02:00
|
|
|
emitTraceValue(nodep, arrayindex);
|
2020-04-14 01:13:10 +02:00
|
|
|
if (emitWidth) puts("," + cvtToStr(nodep->declp()->widthMin()));
|
2019-05-19 22:13:13 +02:00
|
|
|
puts(");\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void emitTraceValue(AstTraceInc* nodep, int arrayindex) {
|
2020-05-09 00:42:34 +02:00
|
|
|
if (AstVarRef* const varrefp = VN_CAST(nodep->valuep(), VarRef)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
AstVar* varp = varrefp->varp();
|
|
|
|
|
puts("(");
|
2020-04-15 13:58:34 +02:00
|
|
|
if (emitTraceIsScBigUint(nodep)) {
|
|
|
|
|
puts("(vluint32_t*)");
|
|
|
|
|
} else if (emitTraceIsScBv(nodep)) {
|
|
|
|
|
puts("VL_SC_BV_DATAP(");
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(varrefp); // Put var name out
|
2019-05-19 22:13:13 +02:00
|
|
|
// Tracing only supports 1D arrays
|
|
|
|
|
if (nodep->declp()->arrayRange().ranged()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (arrayindex == -2) {
|
|
|
|
|
puts("[i]");
|
|
|
|
|
} else if (arrayindex == -1) {
|
|
|
|
|
puts("[0]");
|
|
|
|
|
} else {
|
|
|
|
|
puts("[" + cvtToStr(arrayindex) + "]");
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
if (varp->isSc()) puts(".read()");
|
2020-04-15 13:58:34 +02:00
|
|
|
if (emitTraceIsScUint(nodep)) {
|
|
|
|
|
puts(nodep->isQuad() ? ".to_uint64()" : ".to_uint()");
|
|
|
|
|
} else if (emitTraceIsScBigUint(nodep)) {
|
|
|
|
|
puts(".get_raw()");
|
|
|
|
|
} else if (emitTraceIsScBv(nodep)) {
|
|
|
|
|
puts(")");
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
puts(")");
|
|
|
|
|
} else {
|
|
|
|
|
puts("(");
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep->valuep());
|
2019-05-19 22:13:13 +02:00
|
|
|
puts(")");
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2021-06-24 18:35:12 +02:00
|
|
|
using EmitCFunc::visit; // Suppress hidden overloaded virtual function warning
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNetlist* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Top module only
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep->topModulep());
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-06-13 15:33:11 +02:00
|
|
|
virtual void visit(AstNodeModule* nodep) override {
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
m_modp = nullptr;
|
|
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstCFunc* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->slow() != m_slow) return;
|
2020-10-31 13:59:35 +01:00
|
|
|
VL_RESTORER(m_cfuncp);
|
2021-06-13 15:33:11 +02:00
|
|
|
VL_RESTORER(m_useSelfForThis);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->funcType().isTrace()) { // TRACE_*
|
2020-10-31 13:59:35 +01:00
|
|
|
m_cfuncp = nodep;
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
if (splitNeeded()) {
|
2020-05-25 22:12:34 +02:00
|
|
|
// Splitting file, so using parallel build.
|
|
|
|
|
v3Global.useParallelBuild(true);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Close old file
|
2020-08-15 16:12:55 +02:00
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Open a new file
|
2018-08-25 15:52:45 +02:00
|
|
|
newOutCFile(splitFilenumInc());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
splitSizeInc(nodep);
|
2008-11-17 23:13:57 +01:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("\n");
|
2021-06-13 15:33:11 +02:00
|
|
|
m_lazyDecls.emit(nodep);
|
|
|
|
|
emitCFuncHeader(nodep, m_modp, /* withScope: */ true);
|
|
|
|
|
puts(" {\n");
|
|
|
|
|
|
|
|
|
|
if (nodep->isLoose()) {
|
|
|
|
|
m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration
|
2021-06-16 14:52:37 +02:00
|
|
|
if (!nodep->isStatic()) { // Standard prologue
|
2021-06-13 15:33:11 +02:00
|
|
|
puts("if (false && vlSelf) {} // Prevent unused\n");
|
|
|
|
|
m_useSelfForThis = true;
|
|
|
|
|
puts(symClassAssign());
|
|
|
|
|
}
|
2020-05-09 00:42:34 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
if (nodep->initsp()) {
|
|
|
|
|
string section;
|
|
|
|
|
emitVarList(nodep->initsp(), EVL_FUNC_ALL, "", section /*ref*/);
|
|
|
|
|
iterateAndNextNull(nodep->initsp());
|
|
|
|
|
}
|
2006-08-31 00:00:55 +02:00
|
|
|
|
2020-04-14 01:13:10 +02:00
|
|
|
m_baseCode = -1;
|
|
|
|
|
|
2020-05-09 00:42:34 +02:00
|
|
|
if (nodep->funcType() == AstCFuncType::TRACE_CHANGE_SUB) {
|
|
|
|
|
const AstNode* const stmtp = nodep->stmtsp();
|
|
|
|
|
const AstIf* const ifp = VN_CAST_CONST(stmtp, If);
|
|
|
|
|
const AstTraceInc* const tracep
|
|
|
|
|
= VN_CAST_CONST(ifp ? ifp->ifsp() : stmtp, TraceInc);
|
|
|
|
|
// On rare occasions we can end up with an empty sub function
|
|
|
|
|
m_baseCode = tracep ? tracep->declp()->code() : 0;
|
|
|
|
|
if (v3Global.opt.trueTraceThreads()) {
|
|
|
|
|
puts("const vluint32_t base = vlSymsp->__Vm_baseCode + " + cvtToStr(m_baseCode)
|
|
|
|
|
+ ";\n");
|
|
|
|
|
puts("if (false && tracep && base) {} // Prevent unused\n");
|
2020-04-25 23:38:25 +02:00
|
|
|
} else {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode + "
|
|
|
|
|
+ cvtToStr(m_baseCode) + ");\n");
|
|
|
|
|
puts("if (false && oldp) {} // Prevent unused\n");
|
2020-04-25 23:38:25 +02:00
|
|
|
}
|
2020-05-09 00:42:34 +02:00
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_FULL_SUB) {
|
|
|
|
|
m_baseCode = 0;
|
|
|
|
|
puts("vluint32_t* const oldp = tracep->oldp(vlSymsp->__Vm_baseCode);\n");
|
|
|
|
|
puts("if (false && oldp) {} // Prevent unused\n");
|
2020-04-14 01:13:10 +02:00
|
|
|
} else if (nodep->funcType() == AstCFuncType::TRACE_INIT_SUB) {
|
2020-05-09 00:42:34 +02:00
|
|
|
puts("const int c = vlSymsp->__Vm_baseCode;\n");
|
|
|
|
|
puts("if (false && tracep && c) {} // Prevent unused\n");
|
2020-04-14 01:13:10 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-02 02:11:21 +01:00
|
|
|
if (nodep->stmtsp()) {
|
|
|
|
|
putsDecoration("// Body\n");
|
|
|
|
|
puts("{\n");
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
|
|
|
|
puts("}\n");
|
|
|
|
|
}
|
|
|
|
|
if (nodep->finalsp()) {
|
|
|
|
|
putsDecoration("// Final\n");
|
|
|
|
|
iterateAndNextNull(nodep->finalsp());
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("}\n");
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstTraceDecl* nodep) override {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int enumNum = emitTraceDeclDType(nodep->dtypep());
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->arrayRange().ranged()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
puts("{int i; for (i=0; i<" + cvtToStr(nodep->arrayRange().elements()) + "; i++) {\n");
|
2018-10-08 13:21:22 +02:00
|
|
|
emitTraceInitOne(nodep, enumNum);
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("}}\n");
|
|
|
|
|
} else {
|
2018-10-08 13:21:22 +02:00
|
|
|
emitTraceInitOne(nodep, enumNum);
|
2019-05-19 22:13:13 +02:00
|
|
|
puts("\n");
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstTraceInc* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->declp()->arrayRange().ranged()) {
|
|
|
|
|
// It traces faster if we unroll the loop
|
2020-04-15 13:58:34 +02:00
|
|
|
for (int i = 0; i < nodep->declp()->arrayRange().elements(); i++) {
|
2019-05-19 22:13:13 +02:00
|
|
|
emitTraceChangeOne(nodep, i);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
emitTraceChangeOne(nodep, -1);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstCoverDecl* nodep) override {}
|
|
|
|
|
virtual void visit(AstCoverInc* nodep) override {}
|
2008-06-10 03:25:10 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit EmitCTrace(bool slow)
|
2021-06-24 18:35:12 +02:00
|
|
|
: m_slow{slow} {}
|
2020-11-17 01:56:16 +01:00
|
|
|
virtual ~EmitCTrace() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
void main() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Put out the file
|
|
|
|
|
newOutCFile(0);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-02-22 03:25:21 +01:00
|
|
|
if (m_slow) emitTraceSlow();
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(v3Global.rootp());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-08-15 16:12:55 +02:00
|
|
|
VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// EmitC class functions
|
|
|
|
|
|
2021-06-13 15:33:11 +02:00
|
|
|
static void setParentClassPointers() {
|
|
|
|
|
// Set user4p in all CFunc and Var to point to the containing AstNodeModule
|
2021-06-21 00:32:57 +02:00
|
|
|
const auto setAll = [](AstNodeModule* modp) -> void {
|
2021-06-13 15:33:11 +02:00
|
|
|
for (AstNode* nodep = VN_CAST(modp, NodeModule)->stmtsp(); nodep; nodep = nodep->nextp()) {
|
|
|
|
|
if (VN_IS(nodep, CFunc) || VN_IS(nodep, Var)) nodep->user4p(modp);
|
|
|
|
|
}
|
2021-06-13 16:05:55 +02:00
|
|
|
};
|
|
|
|
|
for (AstNode* modp = v3Global.rootp()->modulesp(); modp; modp = modp->nextp()) {
|
|
|
|
|
setAll(VN_CAST(modp, NodeModule));
|
2021-06-13 15:33:11 +02:00
|
|
|
}
|
2021-06-13 16:05:55 +02:00
|
|
|
setAll(v3Global.rootp()->constPoolp()->modp());
|
2021-06-13 15:33:11 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void V3EmitC::emitc() {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-06-13 15:33:11 +02:00
|
|
|
// Set user4 to parent module
|
|
|
|
|
AstUser4InUse user4InUse;
|
|
|
|
|
setParentClassPointers();
|
2006-08-26 13:35:28 +02:00
|
|
|
// Process each module in turn
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNodeModule* nodep = v3Global.rootp()->modulesp(); nodep;
|
|
|
|
|
nodep = VN_CAST(nodep->nextp(), NodeModule)) {
|
2020-04-05 15:30:23 +02:00
|
|
|
if (VN_IS(nodep, Class)) continue; // Imped with ClassPackage
|
2021-05-15 17:05:24 +02:00
|
|
|
{
|
|
|
|
|
EmitCImp cint;
|
|
|
|
|
cint.mainInt(nodep);
|
|
|
|
|
cint.mainImp(nodep, true);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
EmitCImp fast;
|
|
|
|
|
fast.mainImp(nodep, false);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void V3EmitC::emitcTrace() {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2006-08-26 13:35:28 +02:00
|
|
|
if (v3Global.opt.trace()) {
|
2021-06-13 15:33:11 +02:00
|
|
|
// Set user4 to parent module
|
|
|
|
|
AstUser4InUse user4InUse;
|
|
|
|
|
setParentClassPointers();
|
2021-05-15 17:05:24 +02:00
|
|
|
{
|
|
|
|
|
EmitCTrace slow(true);
|
|
|
|
|
slow.main();
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
EmitCTrace fast(false);
|
|
|
|
|
fast.main();
|
|
|
|
|
}
|
2009-12-03 12:55:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2019-09-27 09:44:23 +02:00
|
|
|
|
|
|
|
|
void V3EmitC::emitcFiles() {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2020-01-22 01:54:14 +01:00
|
|
|
for (AstNodeFile* filep = v3Global.rootp()->filesp(); filep;
|
|
|
|
|
filep = VN_CAST(filep->nextp(), NodeFile)) {
|
2019-09-27 09:44:23 +02:00
|
|
|
AstCFile* cfilep = VN_CAST(filep, CFile);
|
|
|
|
|
if (cfilep && cfilep->tblockp()) {
|
|
|
|
|
V3OutCFile of(cfilep->name());
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("// DESCR"
|
|
|
|
|
"IPTION: Verilator generated C++\n");
|
2021-06-24 18:35:12 +02:00
|
|
|
EmitCFunc visitor(cfilep->tblockp(), &of, true);
|
2019-09-27 09:44:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|