2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Add Unknown assigns
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2024-01-01 09:19:59 +01:00
|
|
|
// Copyright 2003-2024 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Unknown's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Each module:
|
2019-05-19 22:13:13 +02:00
|
|
|
// TBD: Eliminate tristates by adding __in, __inen, __en wires in parallel
|
|
|
|
|
// Need __en in changed list if a signal is on the LHS of a assign
|
|
|
|
|
// Constants:
|
|
|
|
|
// RHS, Replace 5'bx_1_x with a module global we init to a random value
|
|
|
|
|
// CONST(5'bx_1_x) -> VARREF(_{numberedtemp})
|
|
|
|
|
// -> VAR(_{numberedtemp})
|
|
|
|
|
// -> INITIAL(VARREF(_{numberedtemp}),
|
|
|
|
|
// OR(5'bx_1_x,AND(random,5'b0_1_x))
|
|
|
|
|
// OPTIMIZE: Must not collapse this initial back into the equation.
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Unknown.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Const.h"
|
|
|
|
|
#include "V3Stats.h"
|
2021-08-11 15:30:00 +02:00
|
|
|
#include "V3UniqueNames.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class UnknownVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared on Netlist
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstSel::user() -> bool. Set true if already processed
|
|
|
|
|
// AstArraySel::user() -> bool. Set true if already processed
|
|
|
|
|
// AstNode::user2p() -> AstIf* Inserted if assignment for conditional
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
|
|
|
|
const VNUser2InUse m_inuser2;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-08-16 15:55:36 +02:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
|
|
|
|
AstAssignW* m_assignwp = nullptr; // Current assignment
|
|
|
|
|
AstAssignDly* m_assigndlyp = nullptr; // Current assignment
|
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
|
|
|
AstNode* m_timingControlp = nullptr; // Current assignment's intra timing control
|
2020-08-16 15:55:36 +02:00
|
|
|
bool m_constXCvt = false; // Convert X's
|
2022-01-02 18:26:10 +01:00
|
|
|
bool m_allowXUnique = true; // Allow unique assignments
|
2020-04-15 13:58:34 +02:00
|
|
|
VDouble0 m_statUnkVars; // Statistic tracking
|
2021-08-11 15:30:00 +02:00
|
|
|
V3UniqueNames m_lvboundNames; // For generating unique temporary variable names
|
|
|
|
|
V3UniqueNames m_xrandNames; // For generating unique temporary variable names
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2009-01-21 22:56:50 +01:00
|
|
|
// METHODS
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
void replaceBoundLvalue(AstNodeExpr* nodep, AstNodeExpr* condp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Spec says a out-of-range LHS SEL results in a NOP.
|
|
|
|
|
// This is a PITA. We could:
|
|
|
|
|
// 1. IF(...) around an ASSIGN,
|
|
|
|
|
// but that would break a "foo[TOO_BIG]=$fopen(...)".
|
|
|
|
|
// 2. Hack to extend the size of the output structure
|
|
|
|
|
// by one bit, and write to that temporary, but never read it.
|
|
|
|
|
// That makes there be two widths() and is likely a bug farm.
|
|
|
|
|
// 3. Make a special SEL to choose between the real lvalue
|
|
|
|
|
// and a temporary NOP register.
|
|
|
|
|
// 4. Assign to a temp, then IF that assignment.
|
|
|
|
|
// This is suspected to be nicest to later optimizations.
|
|
|
|
|
// 4 seems best but breaks later optimizations. 3 was tried,
|
|
|
|
|
// but makes a mess in the emitter as lvalue switching is needed. So 4.
|
|
|
|
|
// SEL(...) -> temp
|
|
|
|
|
// if (COND(LTE(bit<=maxlsb))) ASSIGN(SEL(...)),temp)
|
|
|
|
|
if (m_assignwp) {
|
|
|
|
|
// Wire assigns must become always statements to deal with insertion
|
|
|
|
|
// of multiple statements. Perhaps someday make all wassigns into always's?
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " IM_WireRep " << m_assignwp << endl);
|
2020-01-18 16:29:49 +01:00
|
|
|
m_assignwp->convertToAlways();
|
2020-08-15 16:12:55 +02:00
|
|
|
VL_DO_CLEAR(pushDeletep(m_assignwp), m_assignwp = nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-06-21 00:32:57 +02:00
|
|
|
const bool needDly = (m_assigndlyp != nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_assigndlyp) {
|
|
|
|
|
// Delayed assignments become normal assignments,
|
|
|
|
|
// then the temp created becomes the delayed assignment
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNode* const newp = new AstAssign{m_assigndlyp->fileline(),
|
2021-11-13 19:50:44 +01:00
|
|
|
m_assigndlyp->lhsp()->unlinkFrBackWithNext(),
|
2022-11-20 23:40:38 +01:00
|
|
|
m_assigndlyp->rhsp()->unlinkFrBackWithNext()};
|
2020-01-18 16:29:49 +01:00
|
|
|
m_assigndlyp->replaceWith(newp);
|
2020-08-15 16:12:55 +02:00
|
|
|
VL_DO_CLEAR(pushDeletep(m_assigndlyp), m_assigndlyp = nullptr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* prep = nodep;
|
2009-10-09 02:42:45 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Scan back to put the condlvalue above all selects (IE top of the lvalue)
|
2020-04-15 13:58:34 +02:00
|
|
|
while (VN_IS(prep->backp(), NodeSel) || VN_IS(prep->backp(), Sel)) {
|
2022-11-13 21:33:11 +01:00
|
|
|
prep = VN_AS(prep->backp(), NodeExpr);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-11-13 19:50:44 +01:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2019-05-19 22:13:13 +02:00
|
|
|
VL_DANGLING(nodep); // Zap it so we don't use it by mistake - use prep
|
2009-10-09 02:42:45 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Already exists; rather than IF(a,... IF(b... optimize to IF(a&&b,
|
|
|
|
|
// Saves us teaching V3Const how to optimize, and it won't be needed again.
|
2021-11-13 19:50:44 +01:00
|
|
|
if (const AstIf* const ifp = VN_AS(prep->user2p(), If)) {
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!needDly, prep, "Should have already converted to non-delay");
|
2022-01-02 16:32:35 +01:00
|
|
|
VNRelinker replaceHandle;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const earliercondp = ifp->condp()->unlinkFrBack(&replaceHandle);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const newp = new AstLogAnd{condp->fileline(), condp, earliercondp};
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, "Edit BOUNDLVALUE " << newp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceHandle.relink(newp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2021-08-11 15:30:00 +02:00
|
|
|
AstVar* const varp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstVar{fl, VVarType::MODULETEMP, m_lvboundNames.get(prep), prep->dtypep()};
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(varp);
|
2021-08-11 15:30:00 +02:00
|
|
|
AstNode* const abovep = prep->backp(); // Grab above point before we replace 'prep'
|
2022-11-20 23:40:38 +01:00
|
|
|
prep->replaceWith(new AstVarRef{fl, varp, VAccess::WRITE});
|
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_timingControlp) m_timingControlp->unlinkFrBack();
|
2022-11-20 23:40:38 +01:00
|
|
|
AstIf* const newp = new AstIf{
|
2020-09-07 23:09:25 +02:00
|
|
|
fl, condp,
|
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
|
|
|
(needDly
|
|
|
|
|
? static_cast<AstNode*>(new AstAssignDly{
|
|
|
|
|
fl, prep, new AstVarRef{fl, varp, VAccess::READ}, m_timingControlp})
|
|
|
|
|
: static_cast<AstNode*>(new AstAssign{
|
2022-11-20 23:40:38 +01:00
|
|
|
fl, prep, new AstVarRef{fl, varp, VAccess::READ}, m_timingControlp}))};
|
2019-10-05 13:54:14 +02:00
|
|
|
newp->branchPred(VBranchPred::BP_LIKELY);
|
2021-06-01 15:01:18 +02:00
|
|
|
newp->isBoundsCheck(true);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newp->dumpTree("- _new: ");
|
2019-01-06 23:38:27 +01:00
|
|
|
abovep->addNextStmt(newp, abovep);
|
|
|
|
|
prep->user2p(newp); // Save so we may LogAnd it next time
|
|
|
|
|
}
|
2009-10-09 02:42:45 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " MOD " << nodep << endl);
|
2020-08-25 03:10:43 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2020-11-28 02:25:02 +01:00
|
|
|
VL_RESTORER(m_constXCvt);
|
2022-01-02 18:26:10 +01:00
|
|
|
VL_RESTORER(m_allowXUnique);
|
2020-01-20 19:27:27 +01:00
|
|
|
{
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_constXCvt = true;
|
2022-01-02 18:26:10 +01:00
|
|
|
// Class X randomization causes Vxrand in strange places, so disable
|
|
|
|
|
if (VN_IS(nodep, Class)) m_allowXUnique = false;
|
2021-08-11 15:30:00 +02:00
|
|
|
m_lvboundNames.reset();
|
|
|
|
|
m_xrandNames.reset();
|
2020-01-20 19:27:27 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignDly* nodep) override {
|
2020-11-28 02:25:02 +01:00
|
|
|
VL_RESTORER(m_assigndlyp);
|
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_timingControlp);
|
2020-11-28 02:25:02 +01:00
|
|
|
{
|
|
|
|
|
m_assigndlyp = 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
|
|
|
m_timingControlp = nodep->timingControlp();
|
2020-11-28 02:25:02 +01:00
|
|
|
VL_DO_DANGLING(iterateChildren(nodep), nodep); // May delete nodep.
|
|
|
|
|
}
|
2009-10-09 02:42:45 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignW* nodep) override {
|
2020-11-28 02:25:02 +01:00
|
|
|
VL_RESTORER(m_assignwp);
|
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_timingControlp);
|
2020-11-28 02:25:02 +01:00
|
|
|
{
|
|
|
|
|
m_assignwp = 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
|
|
|
m_timingControlp = nodep->timingControlp();
|
2020-11-28 02:25:02 +01:00
|
|
|
VL_DO_DANGLING(iterateChildren(nodep), nodep); // May delete nodep.
|
|
|
|
|
}
|
2009-10-09 02:42:45 +02:00
|
|
|
}
|
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
|
|
|
VL_RESTORER(m_timingControlp);
|
|
|
|
|
{
|
|
|
|
|
m_timingControlp = nodep->timingControlp();
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCaseItem* nodep) override {
|
2020-11-28 02:25:02 +01:00
|
|
|
VL_RESTORER(m_constXCvt);
|
|
|
|
|
{
|
|
|
|
|
m_constXCvt = false; // Avoid losing the X's in casex
|
|
|
|
|
iterateAndNextNull(nodep->condsp());
|
|
|
|
|
m_constXCvt = true;
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2020-11-28 02:25:02 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeDType* nodep) override {
|
2020-11-28 02:25:02 +01:00
|
|
|
VL_RESTORER(m_constXCvt);
|
|
|
|
|
{
|
|
|
|
|
m_constXCvt = false; // Avoid losing the X's in casex
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2013-02-02 18:43:28 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
void visitEqNeqCase(AstNodeBiop* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " N/EQCASE->EQ " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
|
|
|
|
|
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Both sides are constant, node can be constant
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(V3Const::constifyEdit(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
} else {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* newp;
|
2019-05-19 22:13:13 +02:00
|
|
|
// If we got ==1'bx it can never be true (but 1'bx==1'bx can be!)
|
2021-10-22 14:56:48 +02:00
|
|
|
if (((VN_IS(lhsp, Const) && VN_AS(lhsp, Const)->num().isFourState())
|
|
|
|
|
|| (VN_IS(rhsp, Const) && VN_AS(rhsp, Const)->num().isFourState()))) {
|
2022-11-20 23:40:38 +01:00
|
|
|
newp = new AstConst(nodep->fileline(), AstConst::WidthedValue{}, 1,
|
2020-04-15 13:58:34 +02:00
|
|
|
(VN_IS(nodep, EqCase) ? 0 : 1));
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
|
|
|
|
|
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2018-10-15 00:39:33 +02:00
|
|
|
if (VN_IS(nodep, EqCase)) {
|
2022-11-20 23:40:38 +01:00
|
|
|
newp = new AstEq{nodep->fileline(), lhsp, rhsp};
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2022-11-20 23:40:38 +01:00
|
|
|
newp = new AstNeq{nodep->fileline(), lhsp, rhsp};
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate tree now that we may have gotten rid of Xs
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2007-07-18 17:01:39 +02:00
|
|
|
void visitEqNeqWild(AstNodeBiop* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " N/EQWILD->EQ " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Const::constifyEdit(nodep->lhsp()); // lhsp may change
|
|
|
|
|
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
2018-02-02 03:32:58 +01:00
|
|
|
if (VN_IS(nodep->lhsp(), Const) && VN_IS(nodep->rhsp(), Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Both sides are constant, node can be constant
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(V3Const::constifyEdit(nodep), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
} else {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const lhsp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* const rhsp = nodep->rhsp()->unlinkFrBack();
|
|
|
|
|
AstNodeExpr* newp;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(rhsp, Const)) {
|
2020-06-10 01:20:16 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: RHS of ==? or !=? must be "
|
|
|
|
|
"constant to be synthesizable"); // Says spec.
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replace with anything that won't cause more errors
|
2022-11-20 23:40:38 +01:00
|
|
|
newp = new AstEq{nodep->fileline(), lhsp, rhsp};
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
// X or Z's become mask, ala case statements.
|
2022-11-20 23:40:38 +01:00
|
|
|
V3Number nummask{rhsp, rhsp->width()};
|
2021-10-22 14:56:48 +02:00
|
|
|
nummask.opBitsNonX(VN_AS(rhsp, Const)->num());
|
2022-11-20 23:40:38 +01:00
|
|
|
V3Number numval{rhsp, rhsp->width()};
|
2021-10-22 14:56:48 +02:00
|
|
|
numval.opBitsOne(VN_AS(rhsp, Const)->num());
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const and1p = new AstAnd{nodep->fileline(), lhsp,
|
|
|
|
|
new AstConst{nodep->fileline(), nummask}};
|
|
|
|
|
AstNodeExpr* const and2p = new AstConst{nodep->fileline(), numval};
|
2018-10-15 00:39:33 +02:00
|
|
|
if (VN_IS(nodep, EqWild)) {
|
2022-11-20 23:40:38 +01:00
|
|
|
newp = new AstEq{nodep->fileline(), and1p, and2p};
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
2022-11-20 23:40:38 +01:00
|
|
|
newp = new AstNeq{nodep->fileline(), and1p, and2p};
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate tree now that we may have gotten rid of the compare
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(newp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2007-07-18 17:01:39 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstEqCase* nodep) override { visitEqNeqCase(nodep); }
|
|
|
|
|
void visit(AstNeqCase* nodep) override { visitEqNeqCase(nodep); }
|
|
|
|
|
void visit(AstEqWild* nodep) override { visitEqNeqWild(nodep); }
|
|
|
|
|
void visit(AstNeqWild* nodep) override { visitEqNeqWild(nodep); }
|
|
|
|
|
void visit(AstIsUnknown* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Ahh, we're two state, so this is easy
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " ISUNKNOWN->0 " << nodep << endl);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstConst* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstCountBits* nodep) override {
|
2020-11-28 02:51:32 +01:00
|
|
|
// Ahh, we're two state, so this is easy
|
|
|
|
|
std::array<bool, 3> dropop;
|
2021-10-22 14:56:48 +02:00
|
|
|
dropop[0] = VN_IS(nodep->rhsp(), Const) && VN_AS(nodep->rhsp(), Const)->num().isAnyX();
|
|
|
|
|
dropop[1] = VN_IS(nodep->thsp(), Const) && VN_AS(nodep->thsp(), Const)->num().isAnyX();
|
|
|
|
|
dropop[2] = VN_IS(nodep->fhsp(), Const) && VN_AS(nodep->fhsp(), Const)->num().isAnyX();
|
2020-11-28 02:51:32 +01:00
|
|
|
UINFO(4, " COUNTBITS(" << dropop[0] << dropop[1] << dropop[2] << " " << nodep << endl);
|
|
|
|
|
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* nonXp = nullptr;
|
2022-01-08 18:01:39 +01:00
|
|
|
if (!dropop[0]) {
|
2020-11-28 02:51:32 +01:00
|
|
|
nonXp = nodep->rhsp();
|
2022-01-08 18:01:39 +01:00
|
|
|
} else if (!dropop[1]) {
|
2020-11-28 02:51:32 +01:00
|
|
|
nonXp = nodep->thsp();
|
2022-01-08 18:01:39 +01:00
|
|
|
} else if (!dropop[2]) {
|
2020-11-28 02:51:32 +01:00
|
|
|
nonXp = nodep->fhsp();
|
2022-01-08 18:01:39 +01:00
|
|
|
} else { // Was all X-s
|
2020-11-28 02:51:32 +01:00
|
|
|
UINFO(4, " COUNTBITS('x)->0 " << nodep << endl);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstConst* const newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}};
|
2020-11-28 02:51:32 +01:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (dropop[0]) {
|
|
|
|
|
nodep->rhsp()->unlinkFrBack()->deleteTree();
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->rhsp(nonXp->cloneTreePure(true));
|
2020-11-28 02:51:32 +01:00
|
|
|
}
|
|
|
|
|
if (dropop[1]) {
|
|
|
|
|
nodep->thsp()->unlinkFrBack()->deleteTree();
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->thsp(nonXp->cloneTreePure(true));
|
2020-11-28 02:51:32 +01:00
|
|
|
}
|
|
|
|
|
if (dropop[2]) {
|
|
|
|
|
nodep->fhsp()->unlinkFrBack()->deleteTree();
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->fhsp(nonXp->cloneTreePure(true));
|
2020-11-28 02:51:32 +01:00
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstConst* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_constXCvt && nodep->num().isFourState()) {
|
|
|
|
|
UINFO(4, " CONST4 " << nodep << endl);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- Const_old: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// CONST(num) -> VARREF(newvarp)
|
|
|
|
|
// -> VAR(newvarp)
|
|
|
|
|
// -> INITIAL(VARREF(newvarp, OR(num_No_Xs,AND(random,num_1s_Where_X))
|
2022-11-20 23:40:38 +01:00
|
|
|
V3Number numb1{nodep, nodep->width()};
|
2019-05-10 02:03:19 +02:00
|
|
|
numb1.opBitsOne(nodep->num());
|
2022-11-20 23:40:38 +01:00
|
|
|
V3Number numbx{nodep, nodep->width()};
|
2019-05-10 02:03:19 +02:00
|
|
|
numbx.opBitsXZ(nodep->num());
|
2022-01-02 18:26:10 +01:00
|
|
|
if (!m_allowXUnique || v3Global.opt.xAssign() != "unique") {
|
2019-05-10 02:03:19 +02:00
|
|
|
// All X bits just become 0; fastest simulation, but not nice
|
2022-11-20 23:40:38 +01:00
|
|
|
V3Number numnew{nodep, numb1.width()};
|
2020-04-15 13:58:34 +02:00
|
|
|
if (v3Global.opt.xAssign() == "1") {
|
2019-05-19 22:13:13 +02:00
|
|
|
numnew.opOr(numb1, numbx);
|
|
|
|
|
} else {
|
|
|
|
|
numnew.opAssign(numb1);
|
|
|
|
|
}
|
2022-11-20 23:40:38 +01:00
|
|
|
AstConst* const newp = new AstConst{nodep->fileline(), numnew};
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->replaceWith(newp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " -> " << newp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
// Make a Vxrand variable
|
|
|
|
|
// We use the special XTEMP type so it doesn't break pure functions
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(m_modp, nodep, "X number not under module");
|
2021-08-11 15:30:00 +02:00
|
|
|
AstVar* const newvarp
|
2022-09-16 01:58:01 +02:00
|
|
|
= new AstVar{nodep->fileline(), VVarType::XTEMP, m_xrandNames.get(nodep),
|
|
|
|
|
VFlagLogicPacked{}, nodep->width()};
|
2022-01-02 18:26:10 +01:00
|
|
|
newvarp->lifetime(VLifetime::STATIC);
|
2019-05-19 22:13:13 +02:00
|
|
|
++m_statUnkVars;
|
2022-01-02 16:32:35 +01:00
|
|
|
VNRelinker replaceHandle;
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack(&replaceHandle);
|
2021-08-11 15:30:00 +02:00
|
|
|
AstNodeVarRef* const newref1p
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstVarRef{nodep->fileline(), newvarp, VAccess::READ};
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceHandle.relink(newref1p); // Replace const with varref
|
2022-11-20 23:40:38 +01:00
|
|
|
AstInitial* const newinitp = new AstInitial{
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->fileline(),
|
2022-11-20 23:40:38 +01:00
|
|
|
new AstAssign{
|
2020-09-07 23:09:25 +02:00
|
|
|
nodep->fileline(),
|
2022-11-20 23:40:38 +01:00
|
|
|
new AstVarRef{nodep->fileline(), newvarp, VAccess::WRITE},
|
|
|
|
|
new AstOr{nodep->fileline(), new AstConst{nodep->fileline(), numb1},
|
|
|
|
|
new AstAnd{nodep->fileline(),
|
|
|
|
|
new AstConst{nodep->fileline(), numbx},
|
|
|
|
|
new AstRand{nodep->fileline(), AstRand::Reset{},
|
|
|
|
|
nodep->dtypep(), true}}}}};
|
2019-05-19 22:13:13 +02:00
|
|
|
// Add inits in front of other statement.
|
|
|
|
|
// In the future, we should stuff the initp into the module's constructor.
|
2021-08-11 15:30:00 +02:00
|
|
|
AstNode* const afterp = m_modp->stmtsp()->unlinkFrBackWithNext();
|
2022-09-15 20:43:56 +02:00
|
|
|
m_modp->addStmtsp(newvarp);
|
|
|
|
|
m_modp->addStmtsp(newinitp);
|
|
|
|
|
m_modp->addStmtsp(afterp);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newref1p->dumpTree("- _new: ");
|
|
|
|
|
if (debug() >= 9) newvarp->dumpTree("- _new: ");
|
|
|
|
|
if (debug() >= 9) newinitp->dumpTree("- _new: ");
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSel* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user1SetOnce()) {
|
|
|
|
|
// Guard against reading/writing past end of bit vector array
|
2021-11-26 23:55:36 +01:00
|
|
|
const AstNode* const basefromp = AstArraySel::baseFromp(nodep, true);
|
2019-05-19 22:13:13 +02:00
|
|
|
bool lvalue = false;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstNodeVarRef* const varrefp = VN_CAST(basefromp, NodeVarRef)) {
|
2020-11-07 16:37:55 +01:00
|
|
|
lvalue = varrefp->access().isWriteOrRW();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Find range of dtype we are selecting from
|
|
|
|
|
// Similar code in V3Const::warnSelect
|
2021-06-21 00:32:57 +02:00
|
|
|
const int maxmsb = nodep->fromp()->dtypep()->width() - 1;
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- sel_old: ");
|
2009-10-06 23:19:38 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// If (maxmsb >= selected), we're in bound
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* condp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstGte{nodep->fileline(),
|
2022-11-22 02:41:32 +01:00
|
|
|
new AstConst(nodep->fileline(), AstConst::WidthedValue{},
|
2022-11-13 21:33:11 +01:00
|
|
|
nodep->lsbp()->width(), maxmsb),
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->lsbp()->cloneTreePure(false)};
|
2019-05-19 22:13:13 +02:00
|
|
|
// See if the condition is constant true (e.g. always in bound due to constant select)
|
|
|
|
|
// Note below has null backp(); the Edit function knows how to deal with that.
|
|
|
|
|
condp = V3Const::constifyEdit(condp);
|
|
|
|
|
if (condp->isOne()) {
|
|
|
|
|
// We don't need to add a conditional; we know the existing expression is ok
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(condp->deleteTree(), condp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (!lvalue) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// SEL(...) -> COND(LTE(bit<=maxmsb), ARRAYSEL(...), {width{1'bx}})
|
2022-01-02 16:32:35 +01:00
|
|
|
VNRelinker replaceHandle;
|
2019-05-10 02:03:19 +02:00
|
|
|
nodep->unlinkFrBack(&replaceHandle);
|
2022-11-20 23:40:38 +01:00
|
|
|
V3Number xnum{nodep, nodep->width()};
|
2019-05-10 02:03:19 +02:00
|
|
|
xnum.setAllBitsX();
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNode* const newp = new AstCondBound{nodep->fileline(), condp, nodep,
|
|
|
|
|
new AstConst{nodep->fileline(), xnum}};
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newp->dumpTree("- _new: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Link in conditional
|
|
|
|
|
replaceHandle.relink(newp);
|
|
|
|
|
// Added X's, tristate them too
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(newp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else { // lvalue
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceBoundLvalue(nodep, condp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2017-11-23 20:55:32 +01:00
|
|
|
// visit(AstSliceSel) not needed as its bounds are constant and checked
|
|
|
|
|
// in V3Width.
|
|
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstArraySel* nodep) override {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (!nodep->user1SetOnce()) {
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() == 9) nodep->dumpTree("- in: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Guard against reading/writing past end of arrays
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNode* const basefromp = AstArraySel::baseFromp(nodep->fromp(), true);
|
2019-05-19 22:13:13 +02:00
|
|
|
bool lvalue = false;
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstNodeVarRef* const varrefp = VN_CAST(basefromp, NodeVarRef)) {
|
2020-11-07 16:37:55 +01:00
|
|
|
lvalue = varrefp->access().isWriteOrRW();
|
2018-02-02 03:32:58 +01:00
|
|
|
} else if (VN_IS(basefromp, Const)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// If it's a PARAMETER[bit], then basefromp may be a constant instead of a varrefp
|
|
|
|
|
} else {
|
2023-02-13 13:47:35 +01:00
|
|
|
// Normally one of above, but might have MEMBERSEL or otherwise
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Find range of dtype we are selecting from
|
|
|
|
|
int declElements = -1;
|
2021-11-26 23:55:36 +01:00
|
|
|
AstNodeDType* const dtypep = nodep->fromp()->dtypep()->skipRefp();
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(dtypep, nodep, "Select of non-selectable type");
|
2021-11-26 23:55:36 +01:00
|
|
|
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
declElements = adtypep->elementsConst();
|
|
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
nodep->v3error("Select from non-array " << dtypep->prettyTypeName());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) nodep->dumpTree("- arraysel_old: ");
|
2009-10-06 23:19:38 +02:00
|
|
|
|
2023-09-19 03:17:21 +02:00
|
|
|
// If value MODDIV constant, where constant <= declElements, known ok
|
|
|
|
|
// V3Random makes these to intentionally prevent exceeding enum array bounds.
|
|
|
|
|
if (const AstModDiv* const moddivp = VN_CAST(nodep->bitp(), ModDiv)) {
|
|
|
|
|
if (const AstConst* const modconstp = VN_CAST(moddivp->rhsp(), Const)) {
|
|
|
|
|
if (modconstp->width() <= 32
|
|
|
|
|
&& modconstp->toUInt() <= static_cast<uint32_t>(declElements)) {
|
|
|
|
|
UINFO(9, "arraysel mod const " << declElements
|
|
|
|
|
<< " >= " << modconstp->toUInt() << endl);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
// See if the condition is constant true
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* condp
|
2022-11-20 23:40:38 +01:00
|
|
|
= new AstGte{nodep->fileline(),
|
|
|
|
|
new AstConst(nodep->fileline(), AstConst::WidthedValue{},
|
2022-11-13 21:33:11 +01:00
|
|
|
nodep->bitp()->width(), declElements - 1),
|
2023-09-17 04:50:54 +02:00
|
|
|
nodep->bitp()->cloneTreePure(false)};
|
2019-05-19 22:13:13 +02:00
|
|
|
// Note below has null backp(); the Edit function knows how to deal with that.
|
|
|
|
|
condp = V3Const::constifyEdit(condp);
|
|
|
|
|
if (condp->isOne()) {
|
|
|
|
|
// We don't need to add a conditional; we know the existing expression is ok
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(condp->deleteTree(), condp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (!lvalue
|
|
|
|
|
// Making a scalar would break if we're making an array
|
2024-03-01 15:14:49 +01:00
|
|
|
&& !VN_IS(nodep->dtypep()->skipRefp(), NodeArrayDType)
|
|
|
|
|
&& !(VN_IS(nodep->dtypep()->skipRefp(), NodeUOrStructDType)
|
2024-03-01 15:15:34 +01:00
|
|
|
&& !VN_CAST(nodep->dtypep()->skipRefp(), NodeUOrStructDType)
|
|
|
|
|
->packed())) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// ARRAYSEL(...) -> COND(LT(bit<maxbit), ARRAYSEL(...), {width{1'bx}})
|
2022-01-02 16:32:35 +01:00
|
|
|
VNRelinker replaceHandle;
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack(&replaceHandle);
|
2022-11-20 23:40:38 +01:00
|
|
|
V3Number xnum{nodep, nodep->width()};
|
2019-05-10 02:03:19 +02:00
|
|
|
if (nodep->isString()) {
|
2022-09-16 01:58:01 +02:00
|
|
|
xnum = V3Number{V3Number::String{}, nodep, ""};
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
xnum.setAllBitsX();
|
|
|
|
|
}
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNode* const newp = new AstCondBound{nodep->fileline(), condp, nodep,
|
|
|
|
|
new AstConst{nodep->fileline(), xnum}};
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newp->dumpTree("- _new: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Link in conditional, can blow away temp xor
|
|
|
|
|
replaceHandle.relink(newp);
|
|
|
|
|
// Added X's, tristate them too
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(newp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (!lvalue) { // Mid-multidimension read, just use zero
|
2019-05-19 22:13:13 +02:00
|
|
|
// ARRAYSEL(...) -> ARRAYSEL(COND(LT(bit<maxbit), bit, 0))
|
2022-01-02 16:32:35 +01:00
|
|
|
VNRelinker replaceHandle;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const bitp = nodep->bitp()->unlinkFrBack(&replaceHandle);
|
2022-11-20 23:40:38 +01:00
|
|
|
AstNodeExpr* const newp = new AstCondBound{
|
2020-04-15 13:58:34 +02:00
|
|
|
bitp->fileline(), condp, bitp,
|
2022-11-20 23:40:38 +01:00
|
|
|
new AstConst{bitp->fileline(), AstConst::WidthedValue{}, bitp->width(), 0}};
|
2019-05-19 22:13:13 +02:00
|
|
|
// Added X's, tristate them too
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newp->dumpTree("- _new: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceHandle.relink(newp);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(newp);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else { // lvalue
|
2019-05-19 22:13:13 +02:00
|
|
|
replaceBoundLvalue(nodep, condp);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2021-08-11 15:30:00 +02:00
|
|
|
explicit UnknownVisitor(AstNetlist* nodep)
|
|
|
|
|
: m_lvboundNames{"__Vlvbound"}
|
|
|
|
|
, m_xrandNames{"__Vxrand"} {
|
|
|
|
|
iterate(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~UnknownVisitor() override { //
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Unknowns, variables created", m_statUnkVars);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Unknown class functions
|
|
|
|
|
|
|
|
|
|
void V3Unknown::unknownAll(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-11-26 16:52:36 +01:00
|
|
|
{ UnknownVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("unknown", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|