verilator/src/V3EmitCMain.cpp

179 lines
6.7 KiB
C++
Raw Normal View History

2020-04-22 02:45:23 +02:00
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
2020-04-22 02:45:23 +02:00
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3EmitCMain.h"
2020-04-22 02:45:23 +02:00
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#include <map>
VL_DEFINE_DEBUG_FUNCTIONS;
2020-04-22 02:45:23 +02:00
//######################################################################
class EmitCMain final : EmitCBaseVisitorConst {
2020-04-22 02:45:23 +02:00
// METHODS
// VISITORS
2020-05-17 00:02:54 +02:00
// This visitor doesn't really iterate, but exist to appease base class
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); } // LCOV_EXCL_LINE
2020-04-22 02:45:23 +02:00
public:
// CONSTRUCTORS
EmitCMain() { emitInt(); }
2020-04-22 02:45:23 +02:00
private:
// MAIN METHOD
void emitInt() {
// Not defining main_time/vl_time_stamp, so
v3Global.opt.addCFlags("-DVL_TIME_CONTEXT"); // On MSVC++ anyways
// Optional main top name argument, with empty string replacement
string topName = v3Global.opt.mainTopName();
if (topName == "-") topName = "";
openNewOutputSourceFile(EmitCUtil::topClassName() + "__main", false, false,
"main() simulation loop, created with --main");
2020-04-22 02:45:23 +02:00
puts("\n");
// Heavily commented output, as users are likely to look at or copy this code
2020-04-22 02:45:23 +02:00
puts("#include \"verilated.h\"\n");
if (v3Global.opt.vpi()) puts("#include \"verilated_vpi.h\"\n");
puts("#include \"" + EmitCUtil::topClassName() + ".h\"\n");
if (v3Global.opt.debugRuntimeTimeout()) {
puts("\n");
puts("#include <csignal>\n");
}
puts("\n");
2020-04-22 02:45:23 +02:00
if (v3Global.opt.vpi()) {
puts("// User VPI code adds to this array to get startup main() callbacks\n");
puts("extern \"C\" void (*vlog_startup_routines[])() VL_ATTR_WEAK;\n");
puts("\n");
}
puts("//======================\n\n");
2020-04-22 02:45:23 +02:00
if (v3Global.opt.debugRuntimeTimeout()) {
puts("void alarmHandler(int signum) {\n");
puts(" VL_FATAL_MT(\"\", 0, \"\", \"Alarm signal received,"s
+ " '--debug-runtime-timeout "s
+ std::to_string(v3Global.opt.debugRuntimeTimeout()) + "' exceeded\\n\");\n");
puts("}\n");
puts("\n");
}
2020-04-22 02:45:23 +02:00
puts("int main(int argc, char** argv, char**) {\n");
puts("// Setup context, defaults, and parse command line\n");
2020-04-22 02:45:23 +02:00
puts("Verilated::debug(0);\n");
puts("const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};\n");
if (v3Global.opt.trace()) puts("contextp->traceEverOn(true);\n");
puts("contextp->threads(" + std::to_string(v3Global.opt.threads()) + ");\n");
puts("contextp->commandArgs(argc, argv);\n");
puts("\n");
if (v3Global.opt.debugRuntimeTimeout()) {
puts("signal(SIGALRM, alarmHandler);\n");
puts("alarm("s + std::to_string(v3Global.opt.debugRuntimeTimeout()) + ");\n");
puts("\n");
}
2020-04-22 02:45:23 +02:00
puts("// Construct the Verilated model, from Vtop.h generated from Verilating\n");
puts("const std::unique_ptr<" + EmitCUtil::topClassName() + "> topp{new "
+ EmitCUtil::topClassName() + "{contextp.get(), \"" + topName + "\"}};\n");
puts("\n");
if (v3Global.opt.vpi()) {
puts("// Hook VPI startup routines and invoke callback\n");
puts("if (vlog_startup_routines) {\n");
puts(/**/ "for (auto routinep = &vlog_startup_routines[0]; *routinep; routinep++)"
" (*routinep)();\n");
puts("}\n");
puts("VerilatedVpi::callCbs(cbStartOfSimulation);\n");
puts("\n");
}
2020-04-22 02:45:23 +02:00
puts("// Simulate until $finish\n");
puts("while (VL_LIKELY(!contextp->gotFinish())) {\n");
if (v3Global.opt.vpi()) {
puts(/**/ "// VPI callbacks\n");
puts(/**/ "VerilatedVpi::callTimedCbs();\n");
puts(/**/ "VerilatedVpi::callCbs(cbNextSimTime);\n"); // Before next event queue
puts(/**/ "VerilatedVpi::callCbs(cbAtStartOfSimTime);\n"); // Before time queue
}
2020-04-24 03:22:47 +02:00
puts(/**/ "// Evaluate model\n");
puts(/**/ "topp->eval();\n");
if (v3Global.opt.vpi()) {
puts(/**/ "// VPI callbacks\n");
puts(/**/ "VerilatedVpi::callValueCbs();\n");
puts(/**/ "VerilatedVpi::callCbs(cbAtEndOfSimTime);\n"); // After nonblocking events
puts(/**/ "VerilatedVpi::callCbs(cbReadWriteSynch);\n"); // After a specified time
puts(/**/ "VerilatedVpi::callCbs(cbReadOnlySynch);\n"); // After cbReadWriteSynch
}
2020-04-24 03:22:47 +02:00
puts(/**/ "// Advance time\n");
if (v3Global.rootp()->delaySchedulerp() || v3Global.opt.timing()) {
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
puts("if (!topp->eventsPending()) break;\n");
}
const std::string nextSlot = v3Global.rootp()->delaySchedulerp() ? "topp->nextTimeSlot()"
: "contextp->time() + 1";
if (v3Global.opt.vpi()) {
puts("contextp->time(std::min("s + nextSlot + ", VerilatedVpi::cbNextDeadline()));\n");
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
} else {
puts("contextp->time("s + nextSlot + ");\n");
Timing support (#3363) Adds timing support to Verilator. It makes it possible to use delays, event controls within processes (not just at the start), wait statements, and forks. Building a design with those constructs requires a compiler that supports C++20 coroutines (GCC 10, Clang 5). The basic idea is to have processes and tasks with delays/event controls implemented as C++20 coroutines. This allows us to suspend and resume them at any time. There are five main runtime classes responsible for managing suspended coroutines: * `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle` with move semantics and automatic cleanup. * `VlDelayScheduler`, for coroutines suspended by delays. It resumes them at a proper simulation time. * `VlTriggerScheduler`, for coroutines suspended by event controls. It resumes them if its corresponding trigger was set. * `VlForkSync`, used for syncing `fork..join` and `fork..join_any` blocks. * `VlCoroutine`, the return type of all verilated coroutines. It allows for suspending a stack of coroutines (normally, C++ coroutines are stackless). There is a new visitor in `V3Timing.cpp` which: * scales delays according to the timescale, * simplifies intra-assignment timing controls and net delays into regular timing controls and assignments, * simplifies wait statements into loops with event controls, * marks processes and tasks with timing controls in them as suspendable, * creates delay, trigger scheduler, and fork sync variables, * transforms timing controls and fork joins into C++ awaits There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`) that integrate static scheduling with timing. This involves providing external domains for variables, so that the necessary combinational logic gets triggered after coroutine resumption, as well as statements that need to be injected into the design eval function to perform this resumption at the correct time. There is also a function that transforms forked processes into separate functions. See the comments in `verilated_timing.h`, `verilated_timing.cpp`, `V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals documentation for more details. Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
}
2020-04-24 03:22:47 +02:00
puts("}\n");
puts("\n");
puts("if (VL_LIKELY(!contextp->gotFinish())) {\n");
2020-04-24 03:22:47 +02:00
puts(/**/ "VL_DEBUG_IF(VL_PRINTF(\"+ Exiting without $finish; no events left\\n\"););\n");
2020-04-22 02:45:23 +02:00
puts("}\n");
puts("\n");
puts("// Execute 'final' processes\n");
2020-04-22 02:45:23 +02:00
puts("topp->final();\n");
if (v3Global.opt.vpi()) puts("VerilatedVpi::callCbs(cbEndOfSimulation);\n");
puts("\n");
if (v3Global.opt.coverage()) {
puts("// Write coverage data (since Verilated with --coverage)\n");
puts("contextp->coveragep()->write();\n");
puts("\n");
}
puts("// Print statistical summary report\n");
2024-03-30 18:16:36 +01:00
puts("contextp->statsPrintSummary();\n");
puts("\n");
puts("return 0;\n");
2020-04-22 02:45:23 +02:00
puts("}\n");
closeOutputFile();
2020-04-22 02:45:23 +02:00
}
};
//######################################################################
// EmitC class functions
void V3EmitCMain::emit() {
UINFO(2, __FUNCTION__ << ":");
{ EmitCMain visitor; }
2020-04-22 02:45:23 +02:00
}