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: Removal of named begin blocks
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2024-01-01 09:19:59 +01:00
|
|
|
// Copyright 2003-2024 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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Begin's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Each module:
|
2019-05-19 22:13:13 +02:00
|
|
|
// Look for BEGINs
|
|
|
|
|
// BEGIN(VAR...) -> VAR ... {renamed}
|
|
|
|
|
// FOR -> WHILEs
|
2023-01-23 14:35:10 +01:00
|
|
|
// Move static function variables and their AstInitialStatic blocks before a function
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
2009-10-12 02:50:31 +02:00
|
|
|
// There are two scopes; named BEGINs change %m and variable scopes.
|
|
|
|
|
// Unnamed BEGINs change only variable, not $display("%m") scope.
|
|
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 04:50:27 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3Begin.h"
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class BeginState final {
|
2012-02-22 04:23:06 +01:00
|
|
|
// NODE STATE
|
2020-04-15 13:58:34 +02:00
|
|
|
// Entire netlist:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNodeFTask::user1 -> bool, 1=processed
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2020-08-15 19:11:27 +02:00
|
|
|
bool m_anyFuncInBegin = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2012-02-22 04:23:06 +01:00
|
|
|
public:
|
2020-11-17 01:56:16 +01:00
|
|
|
BeginState() = default;
|
|
|
|
|
~BeginState() = default;
|
2015-11-14 15:06:09 +01:00
|
|
|
void userMarkChanged(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user1(true);
|
|
|
|
|
m_anyFuncInBegin = true;
|
2012-02-22 04:23:06 +01:00
|
|
|
}
|
|
|
|
|
bool anyFuncInBegin() const { return m_anyFuncInBegin; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class BeginVisitor final : public VNVisitor {
|
2024-11-10 16:51:48 +01:00
|
|
|
// STATE - across all visitors
|
2021-11-13 19:50:44 +01:00
|
|
|
BeginState* const m_statep; // Current global state
|
2024-11-10 16:51:48 +01:00
|
|
|
|
|
|
|
|
// STATE - for current visit position (use VL_RESTORER)
|
2020-08-15 19:11:27 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2023-01-23 14:35:10 +01:00
|
|
|
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
2021-07-08 15:58:57 +02:00
|
|
|
AstNode* m_liftedp = nullptr; // Local nodes we are lifting into m_ftaskp
|
2021-11-27 02:38:48 +01:00
|
|
|
string m_displayScope; // Name of %m in $display/AstScopeName
|
2020-04-15 13:58:34 +02:00
|
|
|
string m_namedScope; // Name of begin blocks above us
|
|
|
|
|
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
2020-08-15 19:11:27 +02:00
|
|
|
int m_ifDepth = 0; // Current if depth
|
2022-12-23 13:34:49 +01:00
|
|
|
bool m_keepBegins = false; // True if begins should not be inlined
|
2009-01-21 22:56:50 +01:00
|
|
|
|
|
|
|
|
// METHODS
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-10-28 03:35:29 +01:00
|
|
|
string dot(const string& a, const string& b) {
|
|
|
|
|
if (a == "") return b;
|
2023-05-03 02:24:44 +02:00
|
|
|
if (b == "") return a;
|
2020-10-28 03:35:29 +01:00
|
|
|
return a + "__DOT__" + b;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 21:45:33 +02:00
|
|
|
void dotNames(const AstNodeBlock* const nodep, const char* const blockName) {
|
|
|
|
|
UINFO(8, "nname " << m_namedScope << endl);
|
|
|
|
|
if (nodep->name() != "") { // Else unneeded unnamed block
|
|
|
|
|
// Create data for dotted variable resolution
|
|
|
|
|
string dottedname = nodep->name() + "__DOT__"; // So always found
|
|
|
|
|
string::size_type pos;
|
|
|
|
|
while ((pos = dottedname.find("__DOT__")) != string::npos) {
|
|
|
|
|
const string ident = dottedname.substr(0, pos);
|
2022-09-15 03:10:19 +02:00
|
|
|
dottedname = dottedname.substr(pos + std::strlen("__DOT__"));
|
2022-05-16 21:45:33 +02:00
|
|
|
if (nodep->name() != "") {
|
|
|
|
|
m_displayScope = dot(m_displayScope, ident);
|
|
|
|
|
m_namedScope = dot(m_namedScope, ident);
|
|
|
|
|
}
|
|
|
|
|
m_unnamedScope = dot(m_unnamedScope, ident);
|
|
|
|
|
// Create CellInline for dotted var resolution
|
|
|
|
|
if (!m_ftaskp) {
|
2022-11-19 20:45:33 +01:00
|
|
|
AstCellInline* const inlinep = new AstCellInline{
|
|
|
|
|
nodep->fileline(), m_unnamedScope, blockName, m_modp->timeunit()};
|
2022-05-16 21:45:33 +02:00
|
|
|
m_modp->addInlinesp(inlinep); // Must be parsed before any AstCells
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remap var names and replace lower Begins
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-08 15:58:57 +02:00
|
|
|
void liftNode(AstNode* nodep) {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
if (m_ftaskp) {
|
|
|
|
|
// AstBegin under ftask, just move into the ftask
|
|
|
|
|
if (!m_liftedp) {
|
|
|
|
|
m_liftedp = nodep;
|
|
|
|
|
} else {
|
|
|
|
|
m_liftedp->addNext(nodep);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Move to module
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(nodep);
|
2021-07-08 15:58:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// VISITORS
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstFork* nodep) override {
|
2023-03-21 13:50:53 +01:00
|
|
|
// Keep begins in forks to group their statements together
|
2022-12-23 13:34:49 +01:00
|
|
|
VL_RESTORER(m_keepBegins);
|
|
|
|
|
m_keepBegins = true;
|
2023-03-21 13:50:53 +01:00
|
|
|
// If a statement is not a begin, wrap it in a begin. This fixes an issue when the
|
|
|
|
|
// statement is a task call that gets inlined later (or any other statement that gets
|
|
|
|
|
// replaced with multiple statements)
|
|
|
|
|
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
|
|
|
|
|
if (!VN_IS(stmtp, Begin)) {
|
|
|
|
|
AstBegin* const beginp = new AstBegin{stmtp->fileline(), "", nullptr};
|
|
|
|
|
stmtp->replaceWith(beginp);
|
|
|
|
|
beginp->addStmtsp(stmtp);
|
|
|
|
|
stmtp = beginp;
|
|
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
dotNames(nodep, "__FORK__");
|
|
|
|
|
nodep->name("");
|
|
|
|
|
}
|
2024-08-02 14:29:05 +02:00
|
|
|
void visit(AstForeach* nodep) override {
|
|
|
|
|
VL_DO_DANGLING(V3Begin::convertToWhile(nodep), nodep);
|
|
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
|
|
|
|
// Keep begin under assignment (in nodep->timingControlp())
|
|
|
|
|
VL_RESTORER(m_keepBegins);
|
|
|
|
|
m_keepBegins = true;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_modp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Rename it
|
|
|
|
|
if (m_unnamedScope != "") {
|
2020-10-28 03:35:29 +01:00
|
|
|
nodep->name(dot(m_unnamedScope, nodep->name()));
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " rename to " << nodep->name() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_statep->userMarkChanged(nodep);
|
|
|
|
|
}
|
|
|
|
|
// BEGIN wrapping a function rename that function, but don't affect
|
|
|
|
|
// the inside function's variables. We then restart with empty
|
|
|
|
|
// naming; so that any begin's inside the function will rename
|
|
|
|
|
// inside the function.
|
|
|
|
|
// Process children
|
2021-11-27 02:38:48 +01:00
|
|
|
VL_RESTORER(m_displayScope);
|
2024-11-10 16:51:48 +01:00
|
|
|
VL_RESTORER(m_ftaskp);
|
|
|
|
|
VL_RESTORER(m_liftedp);
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_namedScope);
|
|
|
|
|
VL_RESTORER(m_unnamedScope);
|
2024-11-10 16:51:48 +01:00
|
|
|
m_displayScope = dot(m_displayScope, nodep->name());
|
|
|
|
|
m_namedScope = "";
|
|
|
|
|
m_unnamedScope = "";
|
|
|
|
|
m_ftaskp = nodep;
|
|
|
|
|
m_liftedp = nullptr;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->foreach([&](AstInitialStatic* const initp) {
|
|
|
|
|
initp->unlinkFrBack();
|
|
|
|
|
m_ftaskp->addHereThisAsNext(initp);
|
|
|
|
|
});
|
|
|
|
|
if (m_liftedp) {
|
|
|
|
|
// Place lifted nodes at beginning of stmtsp, so Var nodes appear before referenced
|
|
|
|
|
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
|
|
|
|
stmtsp->unlinkFrBackWithNext();
|
|
|
|
|
m_liftedp->addNext(stmtsp);
|
2021-07-08 15:58:57 +02:00
|
|
|
}
|
2024-11-10 16:51:48 +01:00
|
|
|
nodep->addStmtsp(m_liftedp);
|
|
|
|
|
m_liftedp = nullptr;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstBegin* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Begin blocks were only useful in variable creation, change names and delete
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " " << nodep << endl);
|
2021-11-27 02:38:48 +01:00
|
|
|
VL_RESTORER(m_displayScope);
|
2020-10-28 03:35:29 +01:00
|
|
|
VL_RESTORER(m_namedScope);
|
|
|
|
|
VL_RESTORER(m_unnamedScope);
|
2019-05-19 22:13:13 +02:00
|
|
|
{
|
2024-11-10 16:51:48 +01:00
|
|
|
VL_RESTORER(m_keepBegins);
|
|
|
|
|
m_keepBegins = false;
|
|
|
|
|
dotNames(nodep, "__BEGIN__");
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
2011-11-30 00:23:18 +01:00
|
|
|
|
2024-11-10 16:51:48 +01:00
|
|
|
// Cleanup
|
|
|
|
|
if (m_keepBegins) {
|
|
|
|
|
nodep->name("");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AstNode* addsp = nullptr;
|
|
|
|
|
if (AstNode* const stmtsp = nodep->stmtsp()) {
|
|
|
|
|
stmtsp->unlinkFrBackWithNext();
|
|
|
|
|
addsp = AstNode::addNext(addsp, stmtsp);
|
|
|
|
|
}
|
|
|
|
|
if (addsp) {
|
|
|
|
|
nodep->replaceWith(addsp);
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2024-11-10 16:51:48 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2023-01-23 14:35:10 +01:00
|
|
|
// If static variable, move it outside a function.
|
|
|
|
|
if (nodep->lifetime().isStatic() && m_ftaskp) {
|
2023-05-03 02:24:44 +02:00
|
|
|
const std::string newName
|
|
|
|
|
= m_ftaskp->name() + "__Vstatic__" + dot(m_unnamedScope, nodep->name());
|
2023-01-23 14:35:10 +01:00
|
|
|
nodep->name(newName);
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
m_ftaskp->addHereThisAsNext(nodep);
|
|
|
|
|
nodep->funcLocal(false);
|
|
|
|
|
} else if (m_unnamedScope != "") {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Rename it
|
2020-10-28 03:35:29 +01:00
|
|
|
nodep->name(dot(m_unnamedScope, nodep->name()));
|
2019-05-19 22:13:13 +02:00
|
|
|
m_statep->userMarkChanged(nodep);
|
2021-07-08 15:58:57 +02:00
|
|
|
// Move it under enclosing tree
|
|
|
|
|
liftNode(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstTypedef* nodep) override {
|
2020-03-26 23:10:20 +01:00
|
|
|
if (m_unnamedScope != "") {
|
|
|
|
|
// Rename it
|
2020-10-28 03:35:29 +01:00
|
|
|
nodep->name(dot(m_unnamedScope, nodep->name()));
|
2020-03-26 23:10:20 +01:00
|
|
|
m_statep->userMarkChanged(nodep);
|
2021-07-08 15:58:57 +02:00
|
|
|
// Move it under enclosing tree
|
|
|
|
|
liftNode(nodep);
|
2020-03-26 23:10:20 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCell* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " CELL " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_namedScope != "") {
|
|
|
|
|
m_statep->userMarkChanged(nodep);
|
|
|
|
|
// Rename it
|
2020-10-28 03:35:29 +01:00
|
|
|
nodep->name(dot(m_namedScope, nodep->name()));
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " rename to " << nodep->name() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Move to module
|
|
|
|
|
nodep->unlinkFrBack();
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarXRef* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " VARXREF " << nodep << endl);
|
2020-09-27 16:10:44 +02:00
|
|
|
if (m_namedScope != "" && nodep->inlinedDots() == "" && !m_ftaskp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->inlinedDots(m_namedScope);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " rescope to " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2015-12-06 01:39:40 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScopeName* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// If there's a %m in the display text, we add a special node that will contain the name()
|
|
|
|
|
// Similar code in V3Inline
|
|
|
|
|
if (nodep->user1SetOnce()) return; // Don't double-add text's
|
2021-11-27 02:38:48 +01:00
|
|
|
// DPI svGetScope doesn't include function name, but %m does
|
|
|
|
|
const string scname = nodep->forFormat() ? m_displayScope : m_namedScope;
|
|
|
|
|
if (!scname.empty()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// To keep correct visual order, must add before other Text's
|
2022-09-15 20:43:56 +02:00
|
|
|
AstText* const afterp = nodep->scopeAttrp();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (afterp) afterp->unlinkFrBackWithNext();
|
2024-07-14 17:39:45 +02:00
|
|
|
nodep->addScopeAttrp(new AstText{nodep->fileline(), "__DOT__"s + scname});
|
2022-09-15 20:43:56 +02:00
|
|
|
if (afterp) nodep->addScopeAttrp(afterp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2007-03-06 19:53:24 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCoverDecl* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't need to fix path in coverage statements, they're not under
|
|
|
|
|
// any BEGINs, but V3Coverage adds them all under the module itself.
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2008-11-05 16:23:03 +01:00
|
|
|
}
|
2010-12-26 15:31:09 +01:00
|
|
|
// VISITORS - LINT CHECK
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
2022-12-23 13:34:49 +01:00
|
|
|
VL_RESTORER(m_keepBegins);
|
|
|
|
|
m_keepBegins = false;
|
2019-05-19 22:13:13 +02:00
|
|
|
// Check IFDEPTH warning - could be in other transform files if desire
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_ifDepth);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
|
2019-05-19 22:13:13 +02:00
|
|
|
} else if (nodep->uniquePragma() || nodep->unique0Pragma() || nodep->priorityPragma()) {
|
|
|
|
|
m_ifDepth = -1;
|
|
|
|
|
} else if (++m_ifDepth > v3Global.opt.ifDepth()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3warn(IFDEPTH,
|
|
|
|
|
"Deep 'if' statement; suggest unique/priority to avoid slow logic");
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->fileline()->modifyWarnOff(V3ErrorCode::IFDEPTH, true); // Warn only once
|
|
|
|
|
m_ifDepth = -1;
|
|
|
|
|
}
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2015-12-06 01:39:40 +01:00
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNode* nodep) override {
|
2022-12-23 13:34:49 +01:00
|
|
|
VL_RESTORER(m_keepBegins);
|
|
|
|
|
m_keepBegins = false;
|
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
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-15 19:11:27 +02:00
|
|
|
BeginVisitor(AstNetlist* nodep, BeginState* statep)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_statep{statep} {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~BeginVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2012-02-22 04:23:06 +01:00
|
|
|
//######################################################################
|
|
|
|
|
|
2023-11-13 00:26:29 +01:00
|
|
|
class BeginRelinkVisitor final : public VNVisitorConst {
|
2012-02-22 04:23:06 +01:00
|
|
|
// Replace tasks with new pointer
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Input:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNodeFTask::user1p // Node replaced, rename it
|
2012-02-22 04:23:06 +01:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2024-07-26 11:50:29 +02:00
|
|
|
UASSERT_OBJ(nodep->taskp(), nodep, "unlinked");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->taskp()->user1()) { // It was converted
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " relinkFTask " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->name(nodep->taskp()->name());
|
|
|
|
|
}
|
2023-11-13 00:26:29 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2012-02-22 04:23:06 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->varp()->user1()) { // It was converted
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, " relinVarRef " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2023-11-13 00:26:29 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2015-11-14 15:06:09 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstIfaceRefDType* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// May have changed cell names
|
|
|
|
|
// TypeTable is always after all modules, so names are stable
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " IFACEREFDTYPE " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (nodep->cellp()) nodep->cellName(nodep->cellp()->name());
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(8, " rename to " << nodep << endl);
|
2023-11-13 00:26:29 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2015-11-14 15:06:09 +01:00
|
|
|
}
|
2012-02-22 04:23:06 +01:00
|
|
|
//--------------------
|
2023-11-13 00:26:29 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2012-02-22 04:23:06 +01:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2023-11-13 00:26:29 +01:00
|
|
|
BeginRelinkVisitor(AstNetlist* nodep, BeginState*) { iterateConst(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~BeginRelinkVisitor() override = default;
|
2012-02-22 04:23:06 +01:00
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Task class functions
|
|
|
|
|
|
|
|
|
|
void V3Begin::debeginAll(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2018-03-10 18:57:50 +01:00
|
|
|
{
|
|
|
|
|
BeginState state;
|
2021-11-26 16:52:36 +01:00
|
|
|
{ BeginVisitor{nodep, &state}; }
|
|
|
|
|
if (state.anyFuncInBegin()) { BeginRelinkVisitor{nodep, &state}; }
|
2018-03-10 18:57:50 +01:00
|
|
|
} // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("begin", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2024-08-02 14:29:05 +02:00
|
|
|
|
|
|
|
|
static AstNode* createForeachLoop(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
|
|
|
|
|
AstNodeExpr* leftp, AstNodeExpr* rightp, VNType nodeType) {
|
|
|
|
|
FileLine* const fl = varp->fileline();
|
|
|
|
|
AstNodeExpr* varRefp = new AstVarRef{fl, varp, VAccess::READ};
|
|
|
|
|
AstNodeExpr* condp;
|
|
|
|
|
bool inc = true;
|
|
|
|
|
switch (nodeType) {
|
|
|
|
|
case VNType::atLteS: condp = new AstLteS{fl, varRefp, rightp}; break;
|
|
|
|
|
case VNType::atLt: condp = new AstLt{fl, varRefp, rightp}; break;
|
|
|
|
|
case VNType::atGteS:
|
|
|
|
|
condp = new AstGteS{fl, varRefp, rightp};
|
|
|
|
|
inc = false;
|
|
|
|
|
break;
|
|
|
|
|
default: UASSERT_OBJ(0, varp, "Missing comparison handling"); break;
|
|
|
|
|
}
|
|
|
|
|
AstNodeExpr* incp;
|
|
|
|
|
if (inc)
|
|
|
|
|
incp = new AstAdd{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
|
|
|
|
|
else
|
|
|
|
|
incp = new AstSub{fl, varRefp->cloneTree(false), new AstConst{fl, 1}};
|
|
|
|
|
|
|
|
|
|
AstWhile* const whilep = new AstWhile{
|
|
|
|
|
fl, condp, bodysp, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, incp}};
|
|
|
|
|
AstNode* const stmtsp = varp; // New statements for outer loop
|
|
|
|
|
stmtsp->addNext(new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, leftp});
|
|
|
|
|
stmtsp->addNext(whilep);
|
|
|
|
|
return stmtsp;
|
|
|
|
|
}
|
|
|
|
|
static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp, AstVar* varp,
|
|
|
|
|
const VNumRange& declRange) {
|
|
|
|
|
FileLine* const fl = varp->fileline();
|
|
|
|
|
V3Number left{nodep, 32}, right{nodep, 32};
|
|
|
|
|
left.isSigned(true);
|
|
|
|
|
right.isSigned(true);
|
|
|
|
|
left.setLongS(declRange.left());
|
|
|
|
|
right.setLongS(declRange.right());
|
|
|
|
|
AstNodeExpr* const leftp = new AstConst{fl, left};
|
|
|
|
|
AstNodeExpr* const rightp = new AstConst{fl, right};
|
|
|
|
|
return createForeachLoop(nodep, bodysp, varp, leftp, rightp,
|
|
|
|
|
declRange.left() <= declRange.right() ? VNType::atLteS
|
|
|
|
|
: VNType::atGteS);
|
|
|
|
|
}
|
|
|
|
|
AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
|
|
|
|
|
// if (debug()) dumpTree(cout, "- foreach-old: ");
|
|
|
|
|
const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars);
|
|
|
|
|
UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach");
|
|
|
|
|
AstNodeExpr* const fromp = loopsp->fromp();
|
|
|
|
|
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
|
|
|
|
|
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
|
|
|
|
|
// Split into for loop
|
|
|
|
|
// We record where the body needs to eventually go with bodyPointp
|
|
|
|
|
AstNode* bodyPointp = new AstBegin{nodep->fileline(), "[EditWrapper]", nullptr};
|
|
|
|
|
AstNode* newp = nullptr;
|
|
|
|
|
AstNode* lastp = nodep;
|
2024-12-03 13:57:50 +01:00
|
|
|
AstVar* nestedIndexp = nullptr;
|
2024-10-10 18:50:43 +02:00
|
|
|
// subfromp used to traverse each dimension of multi-d variable-sized unpacked array (queue,
|
|
|
|
|
// dyn-arr and associative-arr)
|
2024-10-10 16:50:37 +02:00
|
|
|
AstNodeExpr* subfromp = fromp->cloneTreePure(false);
|
2024-08-02 14:29:05 +02:00
|
|
|
// Major dimension first
|
|
|
|
|
for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
|
|
|
|
|
next_argsp = argsp->nextp();
|
|
|
|
|
const bool empty = VN_IS(argsp, Empty);
|
|
|
|
|
AstVar* const varp = VN_CAST(argsp, Var);
|
|
|
|
|
UASSERT_OBJ(varp || empty, argsp, "Missing foreach loop variable");
|
|
|
|
|
if (varp) varp->unlinkFrBack()->usedLoopIdx(true);
|
|
|
|
|
UASSERT_OBJ(fromDtp, argsp, "more loop vars than dimensions");
|
|
|
|
|
fromDtp = fromDtp->skipRefp();
|
|
|
|
|
|
|
|
|
|
FileLine* const fl = argsp->fileline();
|
|
|
|
|
if (varp) {
|
|
|
|
|
AstNode* loopp = nullptr;
|
|
|
|
|
VNRelinker handle;
|
|
|
|
|
lastp->unlinkFrBack(&handle);
|
|
|
|
|
if (const AstNodeArrayDType* const adtypep = VN_CAST(fromDtp, NodeArrayDType)) {
|
|
|
|
|
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
|
|
|
|
|
} else if (AstBasicDType* const adtypep = VN_CAST(fromDtp, BasicDType)) {
|
|
|
|
|
if (adtypep->isString()) {
|
|
|
|
|
AstConst* const leftp = new AstConst{fl, 0};
|
|
|
|
|
AstNodeExpr* const rightp = new AstLenN{fl, fromp->cloneTreePure(false)};
|
|
|
|
|
loopp
|
|
|
|
|
= createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
|
|
|
|
|
} else {
|
|
|
|
|
UASSERT_OBJ(adtypep->isRanged(), varp, "foreach on basic " << adtypep);
|
|
|
|
|
loopp = createForeachLoopRanged(nodep, bodyPointp, varp, adtypep->declRange());
|
|
|
|
|
}
|
|
|
|
|
} else if (VN_IS(fromDtp, DynArrayDType) || VN_IS(fromDtp, QueueDType)) {
|
|
|
|
|
AstConst* const leftp = new AstConst{fl, 0};
|
2024-12-03 13:57:50 +01:00
|
|
|
AstNodeExpr* const rightp = new AstCMethodHard{
|
|
|
|
|
fl,
|
|
|
|
|
VN_IS(subfromp->dtypep(), NodeArrayDType)
|
|
|
|
|
? new AstArraySel{fl, subfromp->cloneTreePure(false),
|
|
|
|
|
new AstVarRef{fl, nestedIndexp, VAccess::READ}}
|
|
|
|
|
: subfromp->cloneTreePure(false),
|
|
|
|
|
"size"};
|
2024-10-10 16:50:37 +02:00
|
|
|
AstVarRef* varRefp = new AstVarRef{fl, varp, VAccess::READ};
|
|
|
|
|
subfromp = new AstCMethodHard{fl, subfromp, "at", varRefp};
|
|
|
|
|
subfromp->dtypep(fromDtp);
|
2024-08-02 14:29:05 +02:00
|
|
|
rightp->dtypeSetSigned32();
|
|
|
|
|
rightp->protect(false);
|
|
|
|
|
loopp = createForeachLoop(nodep, bodyPointp, varp, leftp, rightp, VNType::atLt);
|
2024-08-06 14:48:12 +02:00
|
|
|
} else if (VN_IS(fromDtp, AssocArrayDType)) {
|
2024-08-02 14:29:05 +02:00
|
|
|
// Make this: var KEY_TYPE index;
|
|
|
|
|
// bit index__Vfirst;
|
|
|
|
|
// index__Vfirst = 0;
|
|
|
|
|
// if (0 != array.first(index))
|
|
|
|
|
// do body while (index__Vfirst || 0 != array.next(index))
|
|
|
|
|
AstVar* const first_varp = new AstVar{
|
|
|
|
|
fl, VVarType::BLOCKTEMP, varp->name() + "__Vfirst", VFlagBitPacked{}, 1};
|
|
|
|
|
first_varp->usedLoopIdx(true);
|
|
|
|
|
first_varp->lifetime(VLifetime::AUTOMATIC);
|
|
|
|
|
AstNodeExpr* const firstp
|
2024-10-10 18:50:43 +02:00
|
|
|
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), "first",
|
2024-08-02 14:29:05 +02:00
|
|
|
new AstVarRef{fl, varp, VAccess::READWRITE}};
|
|
|
|
|
firstp->dtypeSetSigned32();
|
|
|
|
|
AstNodeExpr* const nextp
|
2024-10-10 18:50:43 +02:00
|
|
|
= new AstCMethodHard{fl, subfromp->cloneTreePure(false), "next",
|
2024-08-02 14:29:05 +02:00
|
|
|
new AstVarRef{fl, varp, VAccess::READWRITE}};
|
|
|
|
|
nextp->dtypeSetSigned32();
|
2024-10-10 18:50:43 +02:00
|
|
|
AstVarRef* varRefp = new AstVarRef{fl, varp, VAccess::READ};
|
|
|
|
|
subfromp = new AstCMethodHard{fl, subfromp, "at", varRefp};
|
|
|
|
|
subfromp->dtypep(fromDtp);
|
2024-08-02 14:29:05 +02:00
|
|
|
AstNode* const first_clearp
|
|
|
|
|
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
|
|
|
|
new AstConst{fl, AstConst::BitFalse{}}};
|
|
|
|
|
auto* const orp = new AstLogOr{fl, new AstVarRef{fl, first_varp, VAccess::READ},
|
|
|
|
|
new AstNeq{fl, new AstConst{fl, 0}, nextp}};
|
|
|
|
|
AstNode* const whilep = new AstWhile{fl, orp, first_clearp};
|
|
|
|
|
first_clearp->addNext(bodyPointp);
|
|
|
|
|
AstNode* const ifbodyp
|
|
|
|
|
= new AstAssign{fl, new AstVarRef{fl, first_varp, VAccess::WRITE},
|
|
|
|
|
new AstConst{fl, AstConst::BitTrue{}}};
|
|
|
|
|
ifbodyp->addNext(whilep);
|
|
|
|
|
loopp = varp;
|
|
|
|
|
loopp->addNext(first_varp);
|
|
|
|
|
loopp->addNext(
|
|
|
|
|
new AstIf{fl, new AstNeq{fl, new AstConst{fl, 0}, firstp}, ifbodyp});
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(loopp, argsp, "unable to foreach " << fromDtp);
|
|
|
|
|
// New loop goes UNDER previous loop
|
|
|
|
|
handle.relink(loopp);
|
|
|
|
|
lastp = bodyPointp;
|
|
|
|
|
if (!newp) newp = loopp;
|
|
|
|
|
}
|
|
|
|
|
// Prep for next
|
2024-12-03 13:57:50 +01:00
|
|
|
nestedIndexp = varp;
|
2024-08-02 14:29:05 +02:00
|
|
|
fromDtp = fromDtp->subDTypep();
|
|
|
|
|
}
|
|
|
|
|
// The parser validates we don't have "foreach (array[,,,])"
|
|
|
|
|
AstNode* const bodyp = nodep->stmtsp();
|
|
|
|
|
UASSERT_OBJ(newp, nodep, "foreach has no non-empty loop variable");
|
|
|
|
|
if (bodyp) {
|
|
|
|
|
bodyPointp->replaceWith(bodyp->unlinkFrBackWithNext());
|
|
|
|
|
} else {
|
|
|
|
|
bodyPointp->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(bodyPointp->deleteTree(), bodyPointp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
// if (debug()) newp->dumpTreeAndNext(cout, "- foreach-new: ");
|
|
|
|
|
return newp;
|
|
|
|
|
}
|