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
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Prepare AST for timing features
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Copyright 2003-2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
|
|
|
//
|
|
|
|
|
// TimingVisitor transformations:
|
|
|
|
|
// - for each intra-assignment timing control:
|
|
|
|
|
// - if it's a continuous assignment, transform it into an always
|
|
|
|
|
// - introduce an intermediate variable
|
|
|
|
|
// - write the original RHS to the intermediate variable before the timing control
|
|
|
|
|
// - write the intermediate variable to the original LHS after the timing control
|
|
|
|
|
// - for each delay:
|
|
|
|
|
// - scale it according to the module's timescale
|
|
|
|
|
// - replace it with a CAwait statement waiting on the global delay scheduler (with the
|
|
|
|
|
// specified delay value)
|
|
|
|
|
// - if there is no global delay scheduler (see verilated_timing.{h,cpp}), create it
|
|
|
|
|
// - for each event control:
|
|
|
|
|
// - if there is no corresponding trigger scheduler (see verilated_timing.{h,cpp}), create it
|
|
|
|
|
// - replace with a CAwait statement waiting on the corresponding trigger scheduler
|
|
|
|
|
// - for each wait(cond) statement:
|
|
|
|
|
// - replace it with a loop like: while (!cond) @(<vars from cond>)
|
|
|
|
|
// - for each fork:
|
|
|
|
|
// - put each statement in a begin if it isn't in one already
|
|
|
|
|
// - if it's not a fork..join_none:
|
|
|
|
|
// - create a join sync variable
|
|
|
|
|
// - create statements that sync the main process with its children
|
|
|
|
|
// - for each process or C++ function, if it has CAwait statements, mark it as suspendable
|
|
|
|
|
// - if we mark a virtual function as suspendable, mark all overriding and overridden functions
|
|
|
|
|
// as suspendable, as well as calling processes
|
|
|
|
|
//
|
|
|
|
|
// See the internals documentation docs/internals.rst for more details.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
|
|
|
|
#include "V3Timing.h"
|
|
|
|
|
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Const.h"
|
|
|
|
|
#include "V3EmitV.h"
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3SenTree.h"
|
|
|
|
|
#include "V3UniqueNames.h"
|
|
|
|
|
|
2022-09-22 18:28:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
|
|
|
|
// ######################################################################
|
|
|
|
|
// Transform nodes affected by 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
|
|
|
|
|
|
|
|
class TimingVisitor final : public VNVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// TYPES
|
|
|
|
|
// Vertex of a dependency graph of suspendable nodes, e.g. if a node (process or task) is
|
|
|
|
|
// suspendable, all its dependents should also be suspendable
|
|
|
|
|
class DependencyVertex final : public V3GraphVertex {
|
|
|
|
|
AstNode* const m_nodep; // AST node represented by this graph vertex
|
|
|
|
|
// ACCESSORS
|
2022-09-16 17:15:10 +02:00
|
|
|
string name() const override {
|
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
|
|
|
return cvtToHex(nodep()) + ' ' + nodep()->prettyTypeName();
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
FileLine* fileline() const override { return nodep()->fileline(); }
|
|
|
|
|
string dotColor() const override { return nodep()->user2() ? "red" : "black"; }
|
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
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
DependencyVertex(V3Graph* graphp, AstNode* nodep)
|
|
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_nodep{nodep} {}
|
2022-09-16 17:15:10 +02:00
|
|
|
~DependencyVertex() override = default;
|
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
|
|
|
|
|
|
|
|
// ACCESSORS
|
|
|
|
|
virtual AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// AstNode::user1() -> bool. Set true if the node has been
|
|
|
|
|
// processed.
|
|
|
|
|
// AstSenTree::user1() -> AstVarScope*. Trigger scheduler assigned
|
|
|
|
|
// to this sentree
|
|
|
|
|
// Ast{NodeProcedure,CFunc,Begin}::user2() -> bool. Set true if process/task is
|
|
|
|
|
// suspendable
|
|
|
|
|
// AstSenTree::user2() -> AstText*. Debug info passed to the
|
|
|
|
|
// timing schedulers
|
|
|
|
|
// Ast{NodeProcedure,CFunc,Begin}::user3() -> DependencyVertex*. Vertex in m_depGraph
|
|
|
|
|
const VNUser1InUse m_user1InUse;
|
|
|
|
|
const VNUser2InUse m_user2InUse;
|
|
|
|
|
const VNUser3InUse m_user3InUse;
|
|
|
|
|
|
|
|
|
|
// STATE
|
|
|
|
|
// Current context
|
|
|
|
|
AstNetlist* const m_netlistp; // Root node
|
|
|
|
|
AstScope* const m_scopeTopp = m_netlistp->topScopep()->scopep(); // Scope at the top
|
|
|
|
|
AstClass* m_classp = nullptr; // Current class
|
|
|
|
|
AstScope* m_scopep = nullptr; // Current scope
|
|
|
|
|
AstActive* m_activep = nullptr; // Current active
|
|
|
|
|
AstNode* m_procp = nullptr; // NodeProcedure/CFunc/Fork we're under
|
2022-10-03 05:04:55 +02:00
|
|
|
double m_timescaleFactor = 1.0; // Factor to scale delays by
|
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
|
|
|
|
|
|
|
|
// Unique names
|
|
|
|
|
V3UniqueNames m_contAssignVarNames{"__VassignWtmp__"}; // Names for temp AssignW vars
|
|
|
|
|
V3UniqueNames m_intraValueNames{"__Vintraval__"}; // Intra assign delay value var names
|
|
|
|
|
V3UniqueNames m_intraIndexNames{"__Vintraidx__"}; // Intra assign delay index var names
|
|
|
|
|
V3UniqueNames m_intraLsbNames{"__Vintralsb__"}; // Intra assign delay LSB var names
|
|
|
|
|
V3UniqueNames m_forkNames{"__Vfork__"}; // Fork name generator
|
|
|
|
|
V3UniqueNames m_trigSchedNames{"__VtrigSched"}; // Trigger scheduler name generator
|
|
|
|
|
|
|
|
|
|
// DTypes
|
|
|
|
|
AstBasicDType* m_forkDtp = nullptr; // Fork variable type
|
|
|
|
|
AstBasicDType* m_trigSchedDtp = nullptr; // Trigger scheduler type
|
|
|
|
|
|
|
|
|
|
// Timing-related globals
|
|
|
|
|
AstVarScope* m_delaySchedp = nullptr; // Global delay scheduler
|
|
|
|
|
AstSenTree* m_delaySensesp = nullptr; // Domain to trigger if a delayed coroutine is resumed
|
|
|
|
|
|
|
|
|
|
// Other
|
|
|
|
|
V3Graph m_depGraph; // Dependency graph where a node is a dependency of another if it being
|
|
|
|
|
// suspendable makes the other node suspendable
|
|
|
|
|
SenTreeFinder m_finder{m_netlistp}; // Sentree finder and uniquifier
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
// Get or create the dependency vertex for the given node
|
|
|
|
|
DependencyVertex* getDependencyVertex(AstNode* const nodep) {
|
|
|
|
|
if (!nodep->user3p()) nodep->user3p(new DependencyVertex{&m_depGraph, nodep});
|
|
|
|
|
return nodep->user3u().to<DependencyVertex*>();
|
|
|
|
|
}
|
|
|
|
|
// Find net delay on the LHS of an assignment
|
|
|
|
|
AstNode* getLhsNetDelay(AstNodeAssign* nodep) const {
|
|
|
|
|
bool foundWrite = false;
|
|
|
|
|
AstNode* delayp = nullptr;
|
|
|
|
|
nodep->lhsp()->foreach<AstNodeVarRef>([&](const AstNodeVarRef* const refp) {
|
|
|
|
|
if (!refp->access().isWriteOrRW()) return;
|
|
|
|
|
UASSERT_OBJ(!foundWrite, nodep, "Should only be one variable written to on the LHS");
|
|
|
|
|
foundWrite = true;
|
|
|
|
|
if (refp->varp()->delayp()) delayp = refp->varp()->delayp()->cloneTree(false);
|
|
|
|
|
});
|
|
|
|
|
return delayp;
|
|
|
|
|
}
|
|
|
|
|
// Transform an assignment with an intra timing control into a timing control with the
|
|
|
|
|
// assignment under it
|
|
|
|
|
AstNodeStmt* factorOutTimingControl(AstNodeAssign* nodep) const {
|
|
|
|
|
AstNodeStmt* stmtp = nodep;
|
|
|
|
|
AstNode* delayp = getLhsNetDelay(nodep);
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstNode* const controlp = nodep->timingControlp();
|
|
|
|
|
if (controlp) {
|
|
|
|
|
controlp->unlinkFrBack();
|
|
|
|
|
if (!VN_IS(controlp, SenTree)) {
|
|
|
|
|
delayp = delayp ? new AstAdd{flp, delayp, controlp} : controlp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (delayp) {
|
|
|
|
|
auto* const delayStmtp = new AstDelay{flp, delayp, nullptr};
|
|
|
|
|
stmtp->replaceWith(delayStmtp);
|
2022-09-15 20:43:56 +02:00
|
|
|
delayStmtp->addStmtsp(stmtp);
|
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
|
|
|
stmtp = delayStmtp;
|
|
|
|
|
}
|
|
|
|
|
if (auto* const sensesp = VN_CAST(controlp, SenTree)) {
|
|
|
|
|
auto* const eventControlp = new AstEventControl{flp, sensesp, nullptr};
|
|
|
|
|
stmtp->replaceWith(eventControlp);
|
2022-09-15 20:43:56 +02:00
|
|
|
eventControlp->addStmtsp(stmtp);
|
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
|
|
|
stmtp = eventControlp;
|
|
|
|
|
}
|
|
|
|
|
return stmtp == nodep ? nullptr : stmtp;
|
|
|
|
|
}
|
|
|
|
|
// Calculate the factor to scale delays by
|
|
|
|
|
double calculateTimescaleFactor(VTimescale timeunit) const {
|
|
|
|
|
int scalePowerOfTen = timeunit.powerOfTen() - m_netlistp->timeprecision().powerOfTen();
|
|
|
|
|
return std::pow(10.0, scalePowerOfTen);
|
|
|
|
|
}
|
|
|
|
|
// Construct SenItems from VarRefs in an expression
|
|
|
|
|
AstSenItem* varRefpsToSenItemsp(AstNode* const nodep) const {
|
|
|
|
|
AstNode* senItemsp = nullptr;
|
|
|
|
|
const VNUser4InUse user4InUse;
|
|
|
|
|
nodep->foreach<AstNodeVarRef>([&](AstNodeVarRef* refp) {
|
|
|
|
|
if (refp->access().isWriteOnly()) return;
|
|
|
|
|
AstVarScope* const vscp = refp->varScopep();
|
|
|
|
|
if (vscp->user4SetOnce()) return;
|
|
|
|
|
const bool isEvent = vscp->dtypep() && vscp->dtypep()->basicp()
|
|
|
|
|
&& vscp->dtypep()->basicp()->isEvent();
|
|
|
|
|
const auto edgeType = isEvent ? VEdgeType::ET_EVENT : VEdgeType::ET_CHANGED;
|
|
|
|
|
senItemsp = AstNode::addNext(
|
|
|
|
|
senItemsp, new AstSenItem{refp->fileline(), edgeType,
|
|
|
|
|
new AstVarRef{refp->fileline(), vscp, VAccess::READ}});
|
|
|
|
|
});
|
|
|
|
|
return VN_AS(senItemsp, SenItem);
|
|
|
|
|
}
|
|
|
|
|
// Creates the global delay scheduler variable
|
|
|
|
|
AstVarScope* getCreateDelayScheduler() {
|
|
|
|
|
if (m_delaySchedp) return m_delaySchedp;
|
|
|
|
|
auto* const dlySchedDtp = new AstBasicDType{
|
|
|
|
|
m_scopeTopp->fileline(), VBasicDTypeKwd::DELAY_SCHEDULER, VSigning::UNSIGNED};
|
|
|
|
|
m_netlistp->typeTablep()->addTypesp(dlySchedDtp);
|
|
|
|
|
m_delaySchedp = m_scopeTopp->createTemp("__VdlySched", dlySchedDtp);
|
|
|
|
|
// Delay scheduler has to be accessible from top
|
|
|
|
|
m_delaySchedp->varp()->sigPublic(true);
|
|
|
|
|
m_netlistp->delaySchedulerp(m_delaySchedp->varp());
|
|
|
|
|
return m_delaySchedp;
|
|
|
|
|
}
|
|
|
|
|
// Creates the delay sentree
|
|
|
|
|
AstSenTree* getCreateDelaySenTree() {
|
|
|
|
|
if (m_delaySensesp) return m_delaySensesp;
|
|
|
|
|
FileLine* const flp = m_scopeTopp->fileline();
|
|
|
|
|
auto* const awaitingCurrentTimep
|
|
|
|
|
= new AstCMethodHard{flp, new AstVarRef{flp, getCreateDelayScheduler(), VAccess::READ},
|
|
|
|
|
"awaitingCurrentTime"};
|
|
|
|
|
awaitingCurrentTimep->dtypeSetBit();
|
|
|
|
|
m_delaySensesp
|
|
|
|
|
= new AstSenTree{flp, new AstSenItem{flp, VEdgeType::ET_TRUE, awaitingCurrentTimep}};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_netlistp->topScopep()->addSenTreesp(m_delaySensesp);
|
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
|
|
|
return m_delaySensesp;
|
|
|
|
|
}
|
|
|
|
|
// Creates a trigger scheduler variable
|
|
|
|
|
AstVarScope* getCreateTriggerSchedulerp(AstSenTree* const sensesp) {
|
|
|
|
|
if (!sensesp->user1p()) {
|
|
|
|
|
if (!m_trigSchedDtp) {
|
|
|
|
|
m_trigSchedDtp
|
|
|
|
|
= new AstBasicDType{m_scopeTopp->fileline(), VBasicDTypeKwd::TRIGGER_SCHEDULER,
|
|
|
|
|
VSigning::UNSIGNED};
|
|
|
|
|
m_netlistp->typeTablep()->addTypesp(m_trigSchedDtp);
|
|
|
|
|
}
|
|
|
|
|
AstVarScope* const trigSchedp
|
|
|
|
|
= m_scopeTopp->createTemp(m_trigSchedNames.get(sensesp), m_trigSchedDtp);
|
|
|
|
|
sensesp->user1p(trigSchedp);
|
|
|
|
|
}
|
|
|
|
|
return VN_AS(sensesp->user1p(), VarScope);
|
|
|
|
|
}
|
|
|
|
|
// Creates a string describing the sentree
|
|
|
|
|
AstText* createEventDescription(AstSenTree* const sensesp) const {
|
|
|
|
|
if (!sensesp->user2p()) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << '"';
|
|
|
|
|
V3EmitV::verilogForTree(sensesp, ss);
|
|
|
|
|
ss << '"';
|
|
|
|
|
auto* const commentp = new AstText{sensesp->fileline(), ss.str()};
|
|
|
|
|
sensesp->user2p(commentp);
|
|
|
|
|
return commentp;
|
|
|
|
|
}
|
|
|
|
|
return VN_AS(sensesp->user2p(), Text)->cloneTree(false);
|
|
|
|
|
}
|
|
|
|
|
// Creates the fork handle type and returns it
|
|
|
|
|
AstBasicDType* getCreateForkSyncDTypep() {
|
|
|
|
|
if (m_forkDtp) return m_forkDtp;
|
|
|
|
|
m_forkDtp = new AstBasicDType{m_scopeTopp->fileline(), VBasicDTypeKwd::FORK_SYNC,
|
|
|
|
|
VSigning::UNSIGNED};
|
|
|
|
|
m_netlistp->typeTablep()->addTypesp(m_forkDtp);
|
|
|
|
|
return m_forkDtp;
|
|
|
|
|
}
|
|
|
|
|
// Create a temp variable and optionally put it before the specified node (mark local if so)
|
|
|
|
|
AstVarScope* createTemp(FileLine* const flp, const std::string& name,
|
|
|
|
|
AstNodeDType* const dtypep, AstNode* const insertBeforep = nullptr) {
|
|
|
|
|
AstVar* varp;
|
|
|
|
|
if (insertBeforep) {
|
|
|
|
|
varp = new AstVar{flp, VVarType::BLOCKTEMP, name, dtypep};
|
|
|
|
|
varp->funcLocal(true);
|
|
|
|
|
insertBeforep->addHereThisAsNext(varp);
|
|
|
|
|
} else {
|
|
|
|
|
varp = new AstVar{flp, VVarType::MODULETEMP, name, dtypep};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->modp()->addStmtsp(varp);
|
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
|
|
|
}
|
|
|
|
|
AstVarScope* vscp = new AstVarScope{flp, m_scopep, varp};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->addVarsp(vscp);
|
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
|
|
|
return vscp;
|
|
|
|
|
}
|
|
|
|
|
// Add a done() call on the fork sync
|
|
|
|
|
void addForkDone(AstBegin* const beginp, AstVarScope* const forkVscp) const {
|
|
|
|
|
FileLine* const flp = beginp->fileline();
|
|
|
|
|
auto* const donep = new AstCMethodHard{
|
|
|
|
|
beginp->fileline(), new AstVarRef{flp, forkVscp, VAccess::WRITE}, "done"};
|
|
|
|
|
donep->dtypeSetVoid();
|
|
|
|
|
donep->statement(true);
|
|
|
|
|
// Add debug info
|
|
|
|
|
donep->addPinsp(new AstText{flp, '"' + flp->filename() + '"'});
|
|
|
|
|
donep->addPinsp(new AstText{flp, cvtToStr(flp->lineno())});
|
|
|
|
|
beginp->addStmtsp(donep);
|
|
|
|
|
}
|
|
|
|
|
// Handle the 'join' part of a fork..join
|
|
|
|
|
void makeForkJoin(AstFork* const forkp) {
|
|
|
|
|
// Create a fork sync var
|
|
|
|
|
FileLine* const flp = forkp->fileline();
|
|
|
|
|
// If we're in a function, insert the sync var directly before the fork
|
|
|
|
|
AstNode* const insertBeforep = VN_IS(m_procp, CFunc) ? forkp : nullptr;
|
|
|
|
|
AstVarScope* forkVscp
|
|
|
|
|
= createTemp(flp, forkp->name() + "__sync", getCreateForkSyncDTypep(), insertBeforep);
|
|
|
|
|
unsigned joinCount = 0; // Needed for join counter
|
|
|
|
|
// Add a <fork sync>.done() to each begin
|
|
|
|
|
for (AstNode* beginp = forkp->stmtsp(); beginp; beginp = beginp->nextp()) {
|
|
|
|
|
addForkDone(VN_AS(beginp, Begin), forkVscp);
|
|
|
|
|
joinCount++;
|
|
|
|
|
}
|
|
|
|
|
if (forkp->joinType().joinAny()) joinCount = 1;
|
|
|
|
|
// Set the join counter
|
|
|
|
|
auto* const initp = new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE},
|
|
|
|
|
"init", new AstConst{flp, joinCount}};
|
|
|
|
|
initp->dtypeSetVoid();
|
|
|
|
|
initp->statement(true);
|
|
|
|
|
forkp->addHereThisAsNext(initp);
|
|
|
|
|
// Await the join at the end
|
|
|
|
|
auto* const joinp
|
|
|
|
|
= new AstCMethodHard{flp, new AstVarRef{flp, forkVscp, VAccess::WRITE}, "join"};
|
|
|
|
|
joinp->dtypeSetVoid();
|
|
|
|
|
// Add debug info
|
|
|
|
|
joinp->addPinsp(new AstText{flp, '"' + flp->filename() + '"'});
|
|
|
|
|
joinp->addPinsp(new AstText{flp, cvtToStr(flp->lineno())});
|
|
|
|
|
auto* const awaitp = new AstCAwait{flp, joinp};
|
|
|
|
|
awaitp->statement(true);
|
|
|
|
|
forkp->addNextHere(awaitp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
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
|
|
|
UASSERT(!m_classp, "Module or class under class");
|
|
|
|
|
VL_RESTORER(m_classp);
|
|
|
|
|
m_classp = VN_CAST(nodep, Class);
|
|
|
|
|
VL_RESTORER(m_timescaleFactor);
|
|
|
|
|
m_timescaleFactor = calculateTimescaleFactor(nodep->timeunit());
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
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
|
|
|
VL_RESTORER(m_scopep);
|
|
|
|
|
m_scopep = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstActive* nodep) override {
|
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
|
|
|
m_activep = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
m_activep = nullptr;
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNodeProcedure* nodep) override {
|
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
|
|
|
VL_RESTORER(m_procp);
|
|
|
|
|
m_procp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (nodep->user2()) nodep->setSuspendable();
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstAlways* nodep) override {
|
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
|
|
|
visit(static_cast<AstNodeProcedure*>(nodep));
|
|
|
|
|
if (nodep->isSuspendable() && !nodep->user1SetOnce()) {
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstSenTree* const sensesp = m_activep->sensesp();
|
|
|
|
|
if (sensesp->hasClocked()) {
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNode* bodysp = nodep->stmtsp()->unlinkFrBackWithNext();
|
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
|
|
|
auto* const controlp = new AstEventControl{flp, sensesp->cloneTree(false), bodysp};
|
2022-09-15 20:43:56 +02:00
|
|
|
nodep->addStmtsp(controlp);
|
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
|
|
|
iterate(controlp);
|
|
|
|
|
}
|
|
|
|
|
// Note: The 'while (true)' outer loop will be added in V3Sched
|
|
|
|
|
auto* const activep = new AstActive{
|
|
|
|
|
flp, "", new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Initial{}}}};
|
|
|
|
|
activep->sensesStorep(activep->sensesp());
|
|
|
|
|
activep->addStmtsp(nodep->unlinkFrBack());
|
|
|
|
|
m_activep->addNextHere(activep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstCFunc* nodep) override {
|
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
|
|
|
VL_RESTORER(m_procp);
|
|
|
|
|
m_procp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
DependencyVertex* const vxp = getDependencyVertex(nodep);
|
|
|
|
|
if (m_classp && nodep->isVirtual()
|
|
|
|
|
&& !nodep->user1SetOnce()) { // If virtual (only visit once)
|
|
|
|
|
// Go over overridden functions
|
|
|
|
|
m_classp->repairCache();
|
|
|
|
|
for (auto* cextp = m_classp->extendsp(); cextp;
|
|
|
|
|
cextp = VN_AS(cextp->nextp(), ClassExtends)) {
|
|
|
|
|
if (auto* const overriddenp
|
|
|
|
|
= VN_CAST(cextp->classp()->findMember(nodep->name()), CFunc)) {
|
|
|
|
|
if (overriddenp->user2()) { // If suspendable
|
|
|
|
|
if (!nodep->user2()) {
|
|
|
|
|
// It should be a coroutine but it has no awaits. Add a co_return at
|
|
|
|
|
// the end (either that or a co_await is required in a coroutine)
|
|
|
|
|
nodep->addStmtsp(new AstCStmt{nodep->fileline(), "co_return;\n"});
|
|
|
|
|
}
|
|
|
|
|
nodep->user2(true);
|
|
|
|
|
// If it's suspendable already, no need to add it as our dependency or
|
|
|
|
|
// self to its dependencies
|
|
|
|
|
} else {
|
|
|
|
|
DependencyVertex* const overriddenVxp = getDependencyVertex(overriddenp);
|
|
|
|
|
new V3GraphEdge{&m_depGraph, vxp, overriddenVxp, 1};
|
|
|
|
|
new V3GraphEdge{&m_depGraph, overriddenVxp, vxp, 1};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nodep->user2() && !nodep->isCoroutine()) { // If first marked as suspendable
|
|
|
|
|
nodep->rtnType("VlCoroutine");
|
2022-09-29 00:54:18 +02:00
|
|
|
// If in a class, create a shared pointer to 'this'
|
|
|
|
|
if (m_classp) nodep->addInitsp(new AstCStmt{nodep->fileline(), "VL_KEEP_THIS;\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
|
|
|
// Revisit dependent nodes if needed
|
|
|
|
|
for (V3GraphEdge* edgep = vxp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
|
|
|
|
auto* const depVxp = static_cast<DependencyVertex*>(edgep->fromp());
|
|
|
|
|
AstNode* const depp = depVxp->nodep();
|
|
|
|
|
if (!depp->user2()) { // If dependent not suspendable
|
|
|
|
|
depp->user2(true);
|
|
|
|
|
if (auto* const funcp = VN_CAST(depp, CFunc)) {
|
|
|
|
|
// It's a coroutine but has no awaits (a class method that overrides/is
|
|
|
|
|
// overridden by a suspendable, but doesn't have any awaits itself). Add a
|
|
|
|
|
// co_return at the end (either that or a co_await is required in a
|
|
|
|
|
// coroutine)
|
|
|
|
|
funcp->addStmtsp(new AstCStmt{funcp->fileline(), "co_return;\n"});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
iterate(depp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNodeCCall* nodep) override {
|
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
|
|
|
if (nodep->funcp()->user2()) { // If suspendable
|
2022-09-15 20:43:56 +02:00
|
|
|
VNRelinker relinker;
|
|
|
|
|
nodep->unlinkFrBack(&relinker);
|
|
|
|
|
relinker.relink(new AstCAwait{nodep->fileline(), nodep});
|
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 {
|
|
|
|
|
// Add our process/func as the CFunc's dependency as we might have to put an await here
|
|
|
|
|
DependencyVertex* const procVxp = getDependencyVertex(m_procp);
|
|
|
|
|
DependencyVertex* const funcVxp = getDependencyVertex(nodep->funcp());
|
|
|
|
|
new V3GraphEdge{&m_depGraph, procVxp, funcVxp, 1};
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstCAwait* nodep) override {
|
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
|
|
|
v3Global.setUsesTiming();
|
|
|
|
|
m_procp->user2(true);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstDelay* nodep) override {
|
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
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
AstNode* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
|
|
|
|
auto* const constp = VN_CAST(valuep, Const);
|
|
|
|
|
if (constp && constp->isZero()) {
|
|
|
|
|
nodep->v3warn(ZERODLY, "Unsupported: #0 delays do not schedule process resumption in "
|
|
|
|
|
"the Inactive region");
|
|
|
|
|
} else {
|
|
|
|
|
// Scale the delay
|
|
|
|
|
if (valuep->dtypep()->isDouble()) {
|
|
|
|
|
valuep = new AstRToIRoundS{
|
|
|
|
|
flp,
|
|
|
|
|
new AstMulD{flp, valuep,
|
|
|
|
|
new AstConst{flp, AstConst::RealDouble{}, m_timescaleFactor}}};
|
|
|
|
|
} else {
|
|
|
|
|
valuep = new AstMul{flp, valuep,
|
|
|
|
|
new AstConst{flp, AstConst::Unsized64(),
|
|
|
|
|
static_cast<uint64_t>(m_timescaleFactor)}};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Replace self with a 'co_await dlySched.delay(<valuep>)'
|
|
|
|
|
auto* const delayMethodp = new AstCMethodHard{
|
|
|
|
|
flp, new AstVarRef{flp, getCreateDelayScheduler(), VAccess::WRITE}, "delay", valuep};
|
|
|
|
|
delayMethodp->dtypeSetVoid();
|
|
|
|
|
// Add debug info
|
|
|
|
|
delayMethodp->addPinsp(new AstText{flp, '"' + flp->filename() + '"'});
|
|
|
|
|
delayMethodp->addPinsp(new AstText{flp, cvtToStr(flp->lineno())});
|
|
|
|
|
// Create the co_await
|
|
|
|
|
auto* const awaitp = new AstCAwait{flp, delayMethodp, getCreateDelaySenTree()};
|
|
|
|
|
awaitp->statement(true);
|
|
|
|
|
// Relink child statements after the co_await
|
2022-09-17 17:08:05 +02:00
|
|
|
if (nodep->stmtsp()) {
|
|
|
|
|
AstNode::addNext<AstNode, AstNode>(awaitp, nodep->stmtsp()->unlinkFrBackWithNext());
|
|
|
|
|
}
|
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
|
|
|
nodep->replaceWith(awaitp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstEventControl* nodep) override {
|
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
|
|
|
if (m_classp) nodep->v3warn(E_UNSUPPORTED, "Unsupported: event controls in methods");
|
|
|
|
|
auto* const sensesp = m_finder.getSenTree(nodep->sensesp());
|
|
|
|
|
nodep->sensesp()->unlinkFrBack()->deleteTree();
|
|
|
|
|
// Get this sentree's trigger scheduler
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
// Replace self with a 'co_await trigSched.trigger()'
|
|
|
|
|
auto* const triggerMethodp = new AstCMethodHard{
|
|
|
|
|
flp, new AstVarRef{flp, getCreateTriggerSchedulerp(sensesp), VAccess::WRITE},
|
|
|
|
|
"trigger"};
|
|
|
|
|
triggerMethodp->dtypeSetVoid();
|
|
|
|
|
// Add debug info
|
|
|
|
|
triggerMethodp->addPinsp(createEventDescription(sensesp));
|
|
|
|
|
triggerMethodp->addPinsp(new AstText{flp, '"' + flp->filename() + '"'});
|
|
|
|
|
triggerMethodp->addPinsp(new AstText{flp, cvtToStr(flp->lineno())});
|
|
|
|
|
// Create the co_await
|
|
|
|
|
auto* const awaitp = new AstCAwait{flp, triggerMethodp, sensesp};
|
|
|
|
|
awaitp->statement(true);
|
|
|
|
|
// Relink child statements after the co_await
|
2022-09-17 17:08:05 +02:00
|
|
|
if (nodep->stmtsp()) {
|
|
|
|
|
AstNode::addNext<AstNode, AstNode>(awaitp, nodep->stmtsp()->unlinkFrBackWithNext());
|
|
|
|
|
}
|
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
|
|
|
nodep->replaceWith(awaitp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
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);
|
|
|
|
|
// Only process once to avoid infinite loops (due to the net delay)
|
|
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
AstNode* const controlp = factorOutTimingControl(nodep);
|
|
|
|
|
if (!controlp) return;
|
|
|
|
|
// Handle the intra assignment timing control
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
if (VN_IS(nodep, AssignDly)) {
|
|
|
|
|
// If it's an NBA with an intra assignment delay, put it in a fork
|
|
|
|
|
auto* const forkp = new AstFork{flp, "", nullptr};
|
|
|
|
|
forkp->joinType(VJoinType::JOIN_NONE);
|
|
|
|
|
controlp->replaceWith(forkp);
|
|
|
|
|
forkp->addStmtsp(controlp);
|
|
|
|
|
}
|
|
|
|
|
// Insert new vars before the timing control if we're in a function; in a process we can't
|
|
|
|
|
// do that. These intra-assignment vars will later be passed to forked processes by value.
|
|
|
|
|
AstNode* const insertBeforep = VN_IS(m_procp, CFunc) ? controlp : nullptr;
|
|
|
|
|
// Function for replacing values with intermediate variables
|
|
|
|
|
const auto replaceWithIntermediate = [&](AstNode* const valuep, const std::string& name) {
|
|
|
|
|
AstVarScope* const newvscp = createTemp(flp, name, valuep->dtypep(), insertBeforep);
|
|
|
|
|
valuep->replaceWith(new AstVarRef{flp, newvscp, VAccess::READ});
|
|
|
|
|
controlp->addHereThisAsNext(
|
|
|
|
|
new AstAssign{flp, new AstVarRef{flp, newvscp, VAccess::WRITE}, valuep});
|
|
|
|
|
};
|
|
|
|
|
// Create the intermediate select vars. Note: because 'foreach' proceeds in
|
|
|
|
|
// pre-order, and we replace indices in selects with variables, we cannot
|
|
|
|
|
// reach another select under the index position. This is exactly what
|
|
|
|
|
// we want as only the top level selects are LValues. As an example,
|
|
|
|
|
// this transforms 'x[a[i]][b[j]] = y'
|
|
|
|
|
// into 't1 = a[i]; t0 = b[j]; x[t1][t0] = y'.
|
|
|
|
|
nodep->lhsp()->foreach<AstSel>([&](AstSel* selp) {
|
|
|
|
|
if (VN_IS(selp->lsbp(), Const)) return;
|
|
|
|
|
replaceWithIntermediate(selp->lsbp(), m_intraLsbNames.get(nodep));
|
|
|
|
|
// widthp should be const
|
|
|
|
|
});
|
|
|
|
|
nodep->lhsp()->foreach<AstNodeSel>([&](AstNodeSel* selp) {
|
|
|
|
|
if (VN_IS(selp->bitp(), Const)) return;
|
|
|
|
|
replaceWithIntermediate(selp->bitp(), m_intraIndexNames.get(nodep));
|
|
|
|
|
});
|
|
|
|
|
// Replace the RHS with an intermediate value var
|
|
|
|
|
replaceWithIntermediate(nodep->rhsp(), m_intraValueNames.get(nodep));
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstAssignW* nodep) override {
|
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);
|
|
|
|
|
auto* const netDelayp = getLhsNetDelay(nodep);
|
|
|
|
|
if (!netDelayp && !nodep->timingControlp()) return;
|
|
|
|
|
// This assignment will be converted to an always. In some cases this may generate an
|
|
|
|
|
// UNOPTFLAT, e.g.: assign #1 clk = ~clk. We create a temp var for the LHS of this assign,
|
|
|
|
|
// to disable the UNOPTFLAT warning for it.
|
|
|
|
|
// TODO: Find a way to do this without introducing this var. Perhaps make V3SchedAcyclic
|
|
|
|
|
// recognize awaits and prevent it from treating this kind of logic as cyclic
|
|
|
|
|
AstNode* const lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
std::string varname;
|
|
|
|
|
if (auto* const refp = VN_CAST(lhsp, VarRef)) {
|
|
|
|
|
varname = m_contAssignVarNames.get(refp->name());
|
|
|
|
|
} else {
|
|
|
|
|
varname = m_contAssignVarNames.get(lhsp);
|
|
|
|
|
}
|
|
|
|
|
auto* const tempvscp = m_scopep->createTemp(varname, lhsp->dtypep());
|
|
|
|
|
tempvscp->varp()->delayp(netDelayp);
|
|
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
flp->modifyWarnOff(V3ErrorCode::UNOPTFLAT, true);
|
|
|
|
|
tempvscp->fileline(flp);
|
|
|
|
|
tempvscp->varp()->fileline(flp);
|
|
|
|
|
// Remap the LHS to the new temp var
|
|
|
|
|
nodep->lhsp(new AstVarRef{flp, tempvscp, VAccess::WRITE});
|
|
|
|
|
// Convert it to an always; the new assign with intra delay will be handled by
|
|
|
|
|
// visit(AstNodeAssign*)
|
|
|
|
|
AstAlways* const alwaysp = nodep->convertToAlways();
|
|
|
|
|
visit(alwaysp);
|
|
|
|
|
// Put the LHS back in the AssignW; put the temp var on the RHS
|
|
|
|
|
nodep->lhsp(lhsp);
|
|
|
|
|
nodep->rhsp(new AstVarRef{flp, tempvscp, VAccess::READ});
|
|
|
|
|
// Put the AssignW right after the always. Different order can produce UNOPTFLAT on the LHS
|
|
|
|
|
// var
|
|
|
|
|
alwaysp->addNextHere(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstWait* nodep) override {
|
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
|
|
|
// Wait on changed events related to the vars in the wait statement
|
|
|
|
|
AstSenItem* const senItemsp = varRefpsToSenItemsp(nodep->condp());
|
|
|
|
|
AstNode* const condp = nodep->condp()->unlinkFrBack();
|
2022-09-15 20:43:56 +02:00
|
|
|
AstNode* const stmtsp = nodep->stmtsp();
|
|
|
|
|
if (stmtsp) stmtsp->unlinkFrBackWithNext();
|
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
|
|
|
FileLine* const flp = nodep->fileline();
|
|
|
|
|
if (senItemsp) {
|
|
|
|
|
// Put the event control in a while loop with the wait expression as condition
|
2022-09-17 17:08:05 +02:00
|
|
|
AstNode* const loopp
|
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
|
|
|
= new AstWhile{flp, new AstLogNot{flp, condp},
|
|
|
|
|
new AstEventControl{flp, new AstSenTree{flp, senItemsp}, nullptr}};
|
2022-09-15 20:43:56 +02:00
|
|
|
if (stmtsp) loopp->addNext(stmtsp);
|
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
|
|
|
nodep->replaceWith(loopp);
|
|
|
|
|
} else {
|
|
|
|
|
condp->v3warn(WAITCONST, "Wait statement condition is constant");
|
|
|
|
|
auto* constCondp = VN_AS(V3Const::constifyEdit(condp), Const);
|
|
|
|
|
if (constCondp->isZero()) {
|
|
|
|
|
// We have to await forever instead of simply returning in case we're deep in a
|
|
|
|
|
// callstack
|
|
|
|
|
auto* const awaitp = new AstCAwait{flp, new AstCStmt{flp, "VlForever{}"}};
|
|
|
|
|
awaitp->statement(true);
|
|
|
|
|
nodep->replaceWith(awaitp);
|
2022-09-15 20:43:56 +02:00
|
|
|
if (stmtsp) VL_DO_DANGLING(stmtsp->deleteTree(), stmtsp);
|
|
|
|
|
} else if (stmtsp) {
|
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
|
|
|
// Just put the body there
|
2022-09-15 20:43:56 +02:00
|
|
|
nodep->replaceWith(stmtsp);
|
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
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(constCondp->deleteTree(), condp);
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstFork* nodep) override {
|
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
|
|
|
if (nodep->user1SetOnce()) return;
|
|
|
|
|
// Create a unique name for this fork
|
|
|
|
|
nodep->name(m_forkNames.get(nodep));
|
|
|
|
|
unsigned idx = 0; // Index for naming begins
|
|
|
|
|
AstNode* stmtp = nodep->stmtsp();
|
|
|
|
|
// Put each statement in a begin
|
|
|
|
|
while (stmtp) {
|
|
|
|
|
if (!VN_IS(stmtp, Begin)) {
|
|
|
|
|
auto* const beginp = new AstBegin{stmtp->fileline(), "", nullptr};
|
|
|
|
|
stmtp->replaceWith(beginp);
|
|
|
|
|
beginp->addStmtsp(stmtp);
|
|
|
|
|
stmtp = beginp;
|
|
|
|
|
}
|
|
|
|
|
auto* const beginp = VN_AS(stmtp, Begin);
|
|
|
|
|
stmtp = beginp->nextp();
|
|
|
|
|
VL_RESTORER(m_procp);
|
|
|
|
|
m_procp = beginp;
|
|
|
|
|
iterate(beginp);
|
2022-09-05 16:19:19 +02:00
|
|
|
// Even if we do not find any awaits, we cannot simply inline the process here, as new
|
|
|
|
|
// awaits could be added later.
|
|
|
|
|
// Name the begin (later the name will be used for a new function)
|
|
|
|
|
beginp->name(nodep->name() + "__" + cvtToStr(idx++));
|
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
|
|
|
}
|
2022-09-05 16:19:19 +02:00
|
|
|
if (!nodep->joinType().joinNone()) makeForkJoin(nodep);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNodeMath*) override {} // Accelerate
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
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
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
explicit TimingVisitor(AstNetlist* nodep)
|
|
|
|
|
: m_netlistp{nodep} {
|
|
|
|
|
iterate(nodep);
|
2022-09-22 18:28:42 +02:00
|
|
|
if (dumpGraph() >= 6) m_depGraph.dumpDotFilePrefixed("timing_deps");
|
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
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
~TimingVisitor() override = default;
|
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
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Timing class functions
|
|
|
|
|
|
|
|
|
|
void V3Timing::timingAll(AstNetlist* nodep) {
|
|
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
|
TimingVisitor{nodep};
|
2022-09-22 18:28:42 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("timing", 0, dumpTree() >= 3);
|
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
|
|
|
}
|