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: Break always into sensitivity active domains
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Active's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
// Note this can be called multiple times.
|
|
|
|
|
// Create a IACTIVE(initial), SACTIVE(combo)
|
2019-05-19 22:13:13 +02:00
|
|
|
// ALWAYS: Remove any-edges from sense list
|
|
|
|
|
// If no POS/NEG in senselist, Fold into SACTIVE(combo)
|
|
|
|
|
// Else fold into SACTIVE(sequent).
|
|
|
|
|
// OPTIMIZE: When support async clocks, fold into that active if possible
|
|
|
|
|
// INITIAL: Move into IACTIVE
|
|
|
|
|
// WIRE: Move into SACTIVE(combo)
|
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 "V3Active.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2009-01-07 15:37:59 +01:00
|
|
|
#include "V3Const.h"
|
2021-01-05 20:26:01 +01:00
|
|
|
#include "V3Graph.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2020-08-15 16:03:34 +02:00
|
|
|
#include <unordered_map>
|
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
|
|
|
//***** See below for main transformation engine
|
|
|
|
|
|
2021-01-05 20:26:01 +01:00
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
// Extend V3GraphVertex class for use in latch detection graph
|
|
|
|
|
|
|
|
|
|
class LatchDetectGraphVertex final : public V3GraphVertex {
|
2023-09-01 00:00:53 +02:00
|
|
|
VL_RTTI_IMPL(LatchDetectGraphVertex, V3GraphVertex)
|
2021-01-05 20:26:01 +01:00
|
|
|
public:
|
|
|
|
|
enum VertexType : uint8_t { VT_BLOCK, VT_BRANCH, VT_OUTPUT };
|
|
|
|
|
|
|
|
|
|
private:
|
2021-11-26 23:55:36 +01:00
|
|
|
const string m_name; // Only used for .dot file generation
|
|
|
|
|
const VertexType m_type; // Vertex type (BLOCK/BRANCH/OUTPUT)
|
2021-01-05 20:26:01 +01:00
|
|
|
|
2023-04-20 13:02:31 +02:00
|
|
|
string typestr() const VL_MT_SAFE { // "
|
2021-01-05 20:26:01 +01:00
|
|
|
switch (m_type) {
|
|
|
|
|
case VT_BLOCK: return "(||)"; // basic block node
|
|
|
|
|
case VT_BRANCH: return "(&&)"; // if/else branch mode
|
|
|
|
|
case VT_OUTPUT: return "(out)"; // var assignment
|
|
|
|
|
default: return "??"; // unknown
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LatchDetectGraphVertex(V3Graph* graphp, const string& name, VertexType type = VT_BLOCK)
|
2021-07-12 00:42:01 +02:00
|
|
|
: V3GraphVertex{graphp}
|
|
|
|
|
, m_name{name}
|
|
|
|
|
, m_type{type} {}
|
2023-03-18 01:24:15 +01:00
|
|
|
string name() const override VL_MT_STABLE { return m_name + " " + typestr(); }
|
2022-09-16 12:22:11 +02:00
|
|
|
string dotColor() const override { return user() ? "green" : "black"; }
|
2021-01-05 20:26:01 +01:00
|
|
|
virtual int type() const { return m_type; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Extend V3Graph class for use as a latch detection graph
|
|
|
|
|
|
|
|
|
|
class LatchDetectGraph final : public V3Graph {
|
|
|
|
|
protected:
|
2022-07-30 16:01:25 +02:00
|
|
|
LatchDetectGraphVertex* m_curVertexp = nullptr; // Current latch detection graph vertex
|
2021-03-12 23:26:53 +01:00
|
|
|
std::vector<AstVarRef*> m_outputs; // Vector of lvalues encountered on this pass
|
2021-01-05 20:26:01 +01:00
|
|
|
|
|
|
|
|
static LatchDetectGraphVertex* castVertexp(void* vertexp) {
|
|
|
|
|
return reinterpret_cast<LatchDetectGraphVertex*>(vertexp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Recursively traverse the graph to determine whether every control 'BLOCK' has an assignment
|
2022-12-23 17:32:38 +01:00
|
|
|
// to the output we are currently analyzing (the output whose 'user() is set), if so return
|
2021-01-05 20:26:01 +01:00
|
|
|
// true. Where a BLOCK contains a BRANCH, both the if and else sides of the branch must return
|
2022-12-03 00:46:38 +01:00
|
|
|
// true for the BRANCH to evaluate to true. A BLOCK however needs only a single one of its
|
2021-01-05 20:26:01 +01:00
|
|
|
// siblings to evaluate true in order to evaluate true itself. On output vertex only evaluates
|
|
|
|
|
// true if it is the vertex we are analyzing on this check
|
|
|
|
|
|
|
|
|
|
bool latchCheckInternal(LatchDetectGraphVertex* vertexp) {
|
|
|
|
|
bool result = false;
|
|
|
|
|
switch (vertexp->type()) {
|
|
|
|
|
case LatchDetectGraphVertex::VT_OUTPUT: // Base case
|
|
|
|
|
result = vertexp->user();
|
|
|
|
|
break;
|
|
|
|
|
case LatchDetectGraphVertex::VT_BLOCK: // (OR of potentially many siblings)
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress constVariableReference
|
2024-03-26 00:06:25 +01:00
|
|
|
for (V3GraphEdge& edge : vertexp->outEdges()) {
|
|
|
|
|
if (latchCheckInternal(castVertexp(edge.top()))) {
|
2021-01-05 20:26:01 +01:00
|
|
|
result = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case LatchDetectGraphVertex::VT_BRANCH: // (AND of both sibling)
|
2021-11-13 19:50:44 +01:00
|
|
|
// A BRANCH vertex always has exactly 2 siblings
|
2024-03-26 00:06:25 +01:00
|
|
|
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outEdges().frontp()->top());
|
|
|
|
|
LatchDetectGraphVertex* const elsp = castVertexp(vertexp->outEdges().backp()->top());
|
2021-01-05 20:26:01 +01:00
|
|
|
result = latchCheckInternal(ifp) && latchCheckInternal(elsp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
vertexp->user(result);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
LatchDetectGraph() { clear(); }
|
2022-09-16 12:22:11 +02:00
|
|
|
~LatchDetectGraph() override { clear(); }
|
2021-01-05 20:26:01 +01:00
|
|
|
// ACCESSORS
|
|
|
|
|
LatchDetectGraphVertex* currentp() { return m_curVertexp; }
|
|
|
|
|
void currentp(LatchDetectGraphVertex* vertex) { m_curVertexp = vertex; }
|
|
|
|
|
// METHODS
|
|
|
|
|
void begin() {
|
|
|
|
|
// Start a new if/else tracking graph
|
2022-10-22 01:03:40 +02:00
|
|
|
m_curVertexp = new LatchDetectGraphVertex{this, "ROOT"};
|
2021-01-05 20:26:01 +01:00
|
|
|
}
|
|
|
|
|
// Clear out userp field of referenced outputs on destruction
|
|
|
|
|
// (occurs at the end of each combinational always block)
|
2025-08-20 19:21:24 +02:00
|
|
|
// cppcheck-suppress duplInheritedMember
|
2021-01-05 20:26:01 +01:00
|
|
|
void clear() {
|
|
|
|
|
m_outputs.clear();
|
|
|
|
|
// Calling base class clear will unlink & delete all edges & vertices
|
|
|
|
|
V3Graph::clear();
|
|
|
|
|
m_curVertexp = nullptr;
|
|
|
|
|
}
|
|
|
|
|
// Add a new control path and connect it to its parent
|
|
|
|
|
LatchDetectGraphVertex* addPathVertex(LatchDetectGraphVertex* parent, const string& name,
|
|
|
|
|
bool branch = false) {
|
2021-07-12 00:42:01 +02:00
|
|
|
m_curVertexp = new LatchDetectGraphVertex{this, name,
|
2021-01-05 20:26:01 +01:00
|
|
|
branch ? LatchDetectGraphVertex::VT_BRANCH
|
2021-07-12 00:42:01 +02:00
|
|
|
: LatchDetectGraphVertex::VT_BLOCK};
|
|
|
|
|
new V3GraphEdge{this, parent, m_curVertexp, 1};
|
2021-01-05 20:26:01 +01:00
|
|
|
return m_curVertexp;
|
|
|
|
|
}
|
|
|
|
|
// Add a new output variable vertex and store a pointer to it in the user1 field of the
|
|
|
|
|
// variables AstNode
|
|
|
|
|
LatchDetectGraphVertex* addOutputVertex(AstVarRef* nodep) {
|
2021-11-13 19:50:44 +01:00
|
|
|
LatchDetectGraphVertex* const outVertexp
|
2021-07-12 00:42:01 +02:00
|
|
|
= new LatchDetectGraphVertex{this, nodep->name(), LatchDetectGraphVertex::VT_OUTPUT};
|
2021-01-05 20:26:01 +01:00
|
|
|
nodep->varp()->user1p(outVertexp);
|
|
|
|
|
m_outputs.push_back(nodep);
|
|
|
|
|
return outVertexp;
|
|
|
|
|
}
|
|
|
|
|
// Connect an output assignment to its parent control block
|
|
|
|
|
void addAssignment(AstVarRef* nodep) {
|
|
|
|
|
LatchDetectGraphVertex* outVertexp;
|
|
|
|
|
if (!nodep->varp()->user1p()) { // Not seen this output before
|
|
|
|
|
outVertexp = addOutputVertex(nodep);
|
2022-01-08 18:01:39 +01:00
|
|
|
} else {
|
2021-01-05 20:26:01 +01:00
|
|
|
outVertexp = castVertexp(nodep->varp()->user1p());
|
2022-01-08 18:01:39 +01:00
|
|
|
}
|
2022-10-22 01:03:40 +02:00
|
|
|
new V3GraphEdge{this, m_curVertexp, outVertexp, 1};
|
2021-01-05 20:26:01 +01:00
|
|
|
}
|
|
|
|
|
// Run latchCheckInternal on each variable assigned by the always block to see if all control
|
|
|
|
|
// paths make an assignment. Detected latches are flagged in the variables AstVar
|
|
|
|
|
void latchCheck(AstNode* nodep, bool latch_expected) {
|
|
|
|
|
bool latch_detected = false;
|
2025-08-19 23:02:10 +02:00
|
|
|
for (const AstVarRef* const vrp : m_outputs) {
|
2021-11-13 19:50:44 +01:00
|
|
|
LatchDetectGraphVertex* const vertp = castVertexp(vrp->varp()->user1p());
|
2021-01-05 20:26:01 +01:00
|
|
|
vertp->user(true); // Identify the output vertex we are checking paths _to_
|
2024-03-26 00:06:25 +01:00
|
|
|
if (!latchCheckInternal(castVertexp(vertices().frontp()))) latch_detected = true;
|
2021-01-05 20:26:01 +01:00
|
|
|
if (latch_detected && !latch_expected) {
|
|
|
|
|
nodep->v3warn(
|
|
|
|
|
LATCH,
|
|
|
|
|
"Latch inferred for signal "
|
|
|
|
|
<< vrp->prettyNameQ()
|
|
|
|
|
<< " (not all control paths of combinational always assign a value)\n"
|
|
|
|
|
<< nodep->warnMore()
|
|
|
|
|
<< "... Suggest use of always_latch for intentional latches");
|
2023-05-04 00:04:10 +02:00
|
|
|
if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("latch_" + vrp->name());
|
2021-01-05 20:26:01 +01:00
|
|
|
}
|
|
|
|
|
vertp->user(false); // Clear again (see above)
|
|
|
|
|
vrp->varp()->isLatched(latch_detected);
|
|
|
|
|
}
|
|
|
|
|
// Should _all_ variables assigned in always_latch be latches? Probably, but this only
|
|
|
|
|
// warns if none of them are
|
|
|
|
|
if (latch_expected && !latch_detected)
|
|
|
|
|
nodep->v3warn(NOLATCH, "No latches detected in always_latch block");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Collect existing active names
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class ActiveNamer final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// STATE
|
2020-08-15 19:11:27 +02:00
|
|
|
AstScope* m_scopep = nullptr; // Current scope to add statement to
|
2022-05-15 17:03:32 +02:00
|
|
|
AstActive* m_sActivep = nullptr; // For current scope, the Static active we're building
|
|
|
|
|
AstActive* m_iActivep = nullptr; // For current scope, the Initial active we're building
|
|
|
|
|
AstActive* m_fActivep = nullptr; // For current scope, the Final active we're building
|
|
|
|
|
AstActive* m_cActivep = nullptr; // For current scope, the Combo active we're building
|
2018-06-12 04:05:45 +02:00
|
|
|
|
2022-02-05 17:04:48 +01:00
|
|
|
// Map from AstSenTree (equivalence) to the corresponding AstActive created.
|
|
|
|
|
std::unordered_map<VNRef<AstSenTree>, AstActive*> m_activeMap;
|
2018-06-12 04:05:45 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
|
|
|
|
void addActive(AstActive* nodep) {
|
2020-08-15 16:12:55 +02:00
|
|
|
UASSERT_OBJ(m_scopep, nodep, "nullptr scope");
|
2022-09-15 20:43:56 +02:00
|
|
|
m_scopep->addBlocksp(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_scopep = nodep;
|
2022-05-15 17:03:32 +02:00
|
|
|
m_sActivep = nullptr;
|
2020-08-15 16:12:55 +02:00
|
|
|
m_iActivep = nullptr;
|
2022-05-15 17:03:32 +02:00
|
|
|
m_fActivep = nullptr;
|
2020-08-15 16:12:55 +02:00
|
|
|
m_cActivep = nullptr;
|
2018-06-12 04:05:45 +02:00
|
|
|
m_activeMap.clear();
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't clear scopep, the namer persists beyond this visit
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenTree* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Simplify sensitivity list
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(V3Const::constifyExpensiveEdit(nodep), nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeStmt*) override {} // Accelerate
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Specialized below for the special sensitivity classes
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_SenItemKind>
|
2022-08-05 13:15:59 +02:00
|
|
|
AstActive*& getSpecialActive();
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2020-04-04 14:31:14 +02:00
|
|
|
// METHODS
|
2006-08-26 13:35:28 +02:00
|
|
|
AstScope* scopep() { return m_scopep; }
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2022-12-23 13:34:49 +01:00
|
|
|
// Make a new AstActive sensitive to the given sentree and return it
|
|
|
|
|
AstActive* makeActive(FileLine* const fl, AstSenTree* const senTreep) {
|
2025-03-02 23:01:35 +01:00
|
|
|
AstActive* const activep = new AstActive{fl, "", senTreep};
|
2025-08-18 01:14:34 +02:00
|
|
|
activep->senTreeStorep(activep->sentreep());
|
2022-10-13 21:04:43 +02:00
|
|
|
addActive(activep);
|
|
|
|
|
return activep;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-23 13:34:49 +01:00
|
|
|
// Make a new AstActive sensitive to the given special sensitivity class and return it
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_SenItemKind>
|
2022-12-23 13:34:49 +01:00
|
|
|
AstActive* makeSpecialActive(FileLine* const fl) {
|
2024-11-30 02:20:38 +01:00
|
|
|
AstSenTree* const senTreep = new AstSenTree{fl, new AstSenItem{fl, T_SenItemKind{}}};
|
2022-12-23 13:34:49 +01:00
|
|
|
return makeActive(fl, senTreep);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-13 21:04:43 +02:00
|
|
|
// Return an AstActive sensitive to the given special sensitivity class (possibly pre-created)
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_SenItemKind>
|
2022-08-05 13:15:59 +02:00
|
|
|
AstActive* getSpecialActive(FileLine* fl) {
|
2024-11-30 02:20:38 +01:00
|
|
|
AstActive*& cachep = getSpecialActive<T_SenItemKind>();
|
|
|
|
|
if (!cachep) cachep = makeSpecialActive<T_SenItemKind>(fl);
|
2022-05-15 17:03:32 +02:00
|
|
|
return cachep;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-02-05 17:04:48 +01:00
|
|
|
|
|
|
|
|
// Return an AstActive that is sensitive to a SenTree equivalent to the given sentreep.
|
2025-08-18 01:14:34 +02:00
|
|
|
AstActive* getActive(FileLine* fl, AstSenTree* sentreep) {
|
|
|
|
|
UASSERT(sentreep, "Must be non-null");
|
2018-06-12 04:05:45 +02:00
|
|
|
|
2025-08-18 01:14:34 +02:00
|
|
|
auto it = m_activeMap.find(*sentreep);
|
2022-02-05 17:04:48 +01:00
|
|
|
// If found matching AstActive, return it
|
|
|
|
|
if (it != m_activeMap.end()) return it->second;
|
2018-06-12 04:05:45 +02:00
|
|
|
|
2022-02-05 17:04:48 +01:00
|
|
|
// No such AstActive yet, creat it, and add to map.
|
2025-08-18 01:14:34 +02:00
|
|
|
AstSenTree* const newsenp = sentreep->cloneTree(false);
|
2022-10-22 01:03:40 +02:00
|
|
|
AstActive* const activep = new AstActive{fl, "sequent", newsenp};
|
2025-08-18 01:14:34 +02:00
|
|
|
activep->senTreeStorep(activep->sentreep());
|
2022-02-05 17:04:48 +01:00
|
|
|
addActive(activep);
|
|
|
|
|
m_activeMap.emplace(*newsenp, activep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return activep;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-14 04:51:35 +02:00
|
|
|
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-11-17 01:56:16 +01:00
|
|
|
ActiveNamer() = default;
|
2022-09-16 12:22:11 +02:00
|
|
|
~ActiveNamer() override = default;
|
2020-04-14 04:51:35 +02:00
|
|
|
void main(AstScope* nodep) { iterate(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2022-08-05 13:15:59 +02:00
|
|
|
template <>
|
|
|
|
|
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Static>() {
|
|
|
|
|
return m_sActivep;
|
|
|
|
|
}
|
|
|
|
|
template <>
|
|
|
|
|
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Initial>() {
|
|
|
|
|
return m_iActivep;
|
|
|
|
|
}
|
|
|
|
|
template <>
|
|
|
|
|
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Final>() {
|
|
|
|
|
return m_fActivep;
|
|
|
|
|
}
|
|
|
|
|
template <>
|
|
|
|
|
AstActive*& ActiveNamer::getSpecialActive<AstSenItem::Combo>() {
|
|
|
|
|
return m_cActivep;
|
|
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2021-01-05 20:26:01 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// Latch checking visitor
|
|
|
|
|
|
2023-03-18 17:17:25 +01:00
|
|
|
class ActiveLatchCheckVisitor final : public VNVisitorConst {
|
2021-01-05 20:26:01 +01:00
|
|
|
// NODE STATE
|
|
|
|
|
// Input:
|
|
|
|
|
// AstVar::user1p // V2LatchGraphVertex* The vertex handling this node
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2021-01-05 20:26:01 +01:00
|
|
|
// STATE
|
|
|
|
|
LatchDetectGraph m_graph; // Graph used to detect latches in combo always
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstVar* const varp = nodep->varp();
|
2023-06-12 15:24:46 +02:00
|
|
|
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()
|
2025-04-05 12:21:34 +02:00
|
|
|
&& !varp->isFuncLocalSticky() && !varp->lifetime().isAutomatic()) {
|
2021-01-05 20:26:01 +01:00
|
|
|
m_graph.addAssignment(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNodeIf* nodep) override {
|
2021-06-01 15:01:18 +02:00
|
|
|
if (!nodep->isBoundsCheck()) {
|
2021-11-13 19:50:44 +01:00
|
|
|
LatchDetectGraphVertex* const parentp = m_graph.currentp();
|
|
|
|
|
LatchDetectGraphVertex* const branchp = m_graph.addPathVertex(parentp, "BRANCH", true);
|
2021-06-01 15:01:18 +02:00
|
|
|
m_graph.addPathVertex(branchp, "IF");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep->thensp());
|
2021-06-01 15:01:18 +02:00
|
|
|
m_graph.addPathVertex(branchp, "ELSE");
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateAndNextConstNull(nodep->elsesp());
|
2021-06-01 15:01:18 +02:00
|
|
|
m_graph.currentp(parentp);
|
2023-06-12 15:24:46 +02:00
|
|
|
} else {
|
|
|
|
|
iterateChildrenConst(nodep);
|
2021-06-01 15:01:18 +02:00
|
|
|
}
|
2021-01-05 20:26:01 +01:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2023-03-18 17:17:25 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2021-01-05 20:26:01 +01:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2022-05-15 17:03:32 +02:00
|
|
|
ActiveLatchCheckVisitor(AstNode* nodep, bool expectLatch) {
|
2021-01-05 20:26:01 +01:00
|
|
|
m_graph.begin();
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep);
|
2022-05-15 17:03:32 +02:00
|
|
|
m_graph.latchCheck(nodep, expectLatch);
|
2021-01-05 20:26:01 +01:00
|
|
|
}
|
2022-07-30 16:01:25 +02:00
|
|
|
~ActiveLatchCheckVisitor() override = default;
|
2021-01-05 20:26:01 +01:00
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2022-04-29 17:32:02 +02:00
|
|
|
// Replace unsupported non-blocking assignments with blocking assignments
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class ActiveDlyVisitor final : public VNVisitor {
|
2012-04-27 02:40:13 +02:00
|
|
|
public:
|
2025-01-09 14:40:32 +01:00
|
|
|
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL, CT_SUSPENDABLE };
|
2020-04-14 04:51:35 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
2022-04-29 17:32:02 +02:00
|
|
|
// MEMBERS
|
|
|
|
|
const CheckType m_check; // Process type we are checking
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssignDly* nodep) override {
|
2022-04-29 17:32:02 +02:00
|
|
|
// Non-blocking assignments are OK in sequential processes
|
2025-01-09 14:40:32 +01:00
|
|
|
if (m_check == CT_SEQ || m_check == CT_SUSPENDABLE) return;
|
2022-04-29 17:32:02 +02:00
|
|
|
|
|
|
|
|
// Issue appropriate warning
|
|
|
|
|
if (m_check == CT_INITIAL) {
|
|
|
|
|
nodep->v3warn(INITIALDLY,
|
|
|
|
|
"Non-blocking assignment '<=' in initial/final block\n"
|
|
|
|
|
<< nodep->warnMore()
|
|
|
|
|
<< "... This will be executed as a blocking assignment '='!");
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3warn(COMBDLY,
|
|
|
|
|
"Non-blocking assignment '<=' in combinational logic process\n"
|
|
|
|
|
<< nodep->warnMore()
|
|
|
|
|
<< "... This will be executed as a blocking assignment '='!");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-04-29 17:32:02 +02:00
|
|
|
|
|
|
|
|
// Convert to blocking 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
|
|
|
nodep->replaceWith(new AstAssign{
|
|
|
|
|
nodep->fileline(), //
|
|
|
|
|
nodep->lhsp()->unlinkFrBack(), //
|
|
|
|
|
nodep->rhsp()->unlinkFrBack(), //
|
|
|
|
|
nodep->timingControlp() ? nodep->timingControlp()->unlinkFrBack() : nullptr});
|
2022-04-29 17:32:02 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2011-01-01 00:36:29 +01:00
|
|
|
}
|
2022-04-29 17:32:02 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-15 19:11:27 +02:00
|
|
|
ActiveDlyVisitor(AstNode* nodep, CheckType check)
|
2022-04-29 17:32:02 +02:00
|
|
|
: m_check{check} {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2022-07-30 16:01:25 +02:00
|
|
|
~ActiveDlyVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Active class functions
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class ActiveVisitor final : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// NODE STATE
|
2009-01-07 15:37:59 +01:00
|
|
|
// Each call to V3Const::constify
|
2022-05-15 17:03:32 +02:00
|
|
|
// AstVarScope::user1() bool: This VarScope is referenced in the sensitivity list
|
|
|
|
|
// AstVarScope::user2() bool: This VarScope is written in the current process
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNode::user4() Used by V3Const::constify, called below
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-04-14 04:51:35 +02:00
|
|
|
ActiveNamer m_namer; // Tracking of active names
|
2022-05-15 17:03:32 +02:00
|
|
|
bool m_clockedProcess = false; // Whether current process is a clocked process
|
|
|
|
|
bool m_allChanged = false; // Whether all SenItem in the SenTree are ET_CHANGED
|
|
|
|
|
bool m_walkingBody = false; // Walking body of a process
|
|
|
|
|
bool m_canBeComb = false; // Whether current clocked process can be turned into a comb process
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// METHODS
|
2022-08-05 13:15:59 +02:00
|
|
|
template <typename T>
|
|
|
|
|
void moveUnderSpecial(AstNode* nodep) {
|
2022-05-15 17:03:32 +02:00
|
|
|
AstActive* const wantactivep = m_namer.getSpecialActive<T>(nodep->fileline());
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
wantactivep->addStmtsp(nodep);
|
2008-12-12 21:34:02 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2025-08-18 01:14:34 +02:00
|
|
|
void visitAlways(AstNode* nodep, AstSenTree* oldsentreep, VAlwaysKwd kwd) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Move always to appropriate ACTIVE based on its sense list
|
2025-08-18 01:14:34 +02:00
|
|
|
if (oldsentreep && oldsentreep->sensesp() && oldsentreep->sensesp()->isNever()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Never executing. Kill it.
|
2025-08-18 01:14:34 +02:00
|
|
|
UASSERT_OBJ(!oldsentreep->sensesp()->nextp(), nodep,
|
2019-07-06 18:57:50 +02:00
|
|
|
"Never senitem should be alone, else the never should be eliminated.");
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
{
|
|
|
|
|
const VNUser1InUse user1InUse;
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Walk sensitivity list
|
|
|
|
|
m_clockedProcess = false;
|
|
|
|
|
m_allChanged = true;
|
2025-08-18 01:14:34 +02:00
|
|
|
if (oldsentreep) {
|
|
|
|
|
oldsentreep->unlinkFrBack();
|
|
|
|
|
iterateChildrenConst(oldsentreep);
|
2022-05-15 17:03:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If all SenItems are ET_CHANGE, then walk the body to determine if this process
|
|
|
|
|
// could be turned into a combinational process instead.
|
|
|
|
|
if (m_allChanged) {
|
|
|
|
|
const VNUser2InUse user2InUse;
|
|
|
|
|
m_walkingBody = true;
|
|
|
|
|
m_canBeComb = true;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
m_walkingBody = false;
|
|
|
|
|
if (m_canBeComb) m_clockedProcess = false;
|
2020-04-14 04:51:35 +02:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
AstActive* const wantactivep
|
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_clockedProcess ? m_namer.getSpecialActive<AstSenItem::Combo>(nodep->fileline())
|
2025-08-18 01:14:34 +02:00
|
|
|
: oldsentreep ? m_namer.getActive(nodep->fileline(), oldsentreep)
|
|
|
|
|
// Clocked, no sensitivity lists, it's a suspendable, put it in initial
|
|
|
|
|
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Move node to new active
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
wantactivep->addStmtsp(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// Warn and convert any delayed assignments
|
2022-07-30 18:49:30 +02:00
|
|
|
{
|
2025-01-09 14:40:32 +01:00
|
|
|
ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB
|
2025-08-18 01:14:34 +02:00
|
|
|
: oldsentreep ? ActiveDlyVisitor::CT_SEQ
|
2025-01-09 14:40:32 +01:00
|
|
|
: ActiveDlyVisitor::CT_SUSPENDABLE};
|
2022-07-30 18:49:30 +02:00
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2025-01-09 14:40:32 +01:00
|
|
|
// Delete sensitivity list
|
2025-08-18 01:14:34 +02:00
|
|
|
if (oldsentreep) VL_DO_DANGLING(oldsentreep->deleteTree(), oldsentreep);
|
2025-01-09 14:40:32 +01:00
|
|
|
|
2022-05-15 17:03:32 +02:00
|
|
|
// check combinational processes for latches
|
|
|
|
|
if (!m_clockedProcess || kwd == VAlwaysKwd::ALWAYS_LATCH) {
|
|
|
|
|
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd == VAlwaysKwd::ALWAYS_LATCH};
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2010-04-06 02:01:17 +02:00
|
|
|
|
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
|
|
|
void visitSenItems(AstNode* nodep) {
|
|
|
|
|
if (v3Global.opt.timing().isSetTrue()) {
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->foreach([this](AstSenItem* senItemp) { visit(senItemp); });
|
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-05-15 17:03:32 +02:00
|
|
|
// VISITORS
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstScope* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
m_namer.main(nodep); // Clear last scope's names, and collect this scope's existing names
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstActive* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
// Actives are being formed, so we can ignore any already made
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstInitialStatic* nodep) override { moveUnderSpecial<AstSenItem::Static>(nodep); }
|
|
|
|
|
void visit(AstInitial* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
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
|
|
|
visitSenItems(nodep);
|
2022-05-15 17:03:32 +02:00
|
|
|
moveUnderSpecial<AstSenItem::Initial>(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstFinal* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
const ActiveDlyVisitor dlyvisitor{nodep, ActiveDlyVisitor::CT_INITIAL};
|
|
|
|
|
moveUnderSpecial<AstSenItem::Final>(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstCoverToggle* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
|
|
|
|
void visit(AstAlways* nodep) override {
|
2022-09-15 20:43:56 +02:00
|
|
|
if (!nodep->stmtsp()) { // Empty always. Remove it now.
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
Internals: Make AstAssignW a procedural statement (#6280) (#6556)
Initial idea was to remodel AssignW as Assign under Alway. Trying that
uncovered some issues, the most difficult of them was that a delay
attached to a continuous assignment behaves differently from a delay
attached to a blocking assignment statement, so we need to keep the
knowledge of which flavour an assignment was until V3Timing.
So instead of removing AstAssignW, we always wrap it in an AstAlways,
with a special `keyword()` type. This makes it into a proper procedural
statement, which is almost equivalent to AstAssign, except for the case
when they contain a delay. We still gain the benefits of #6280 and can
simplify some code. Every AstNodeStmt should now be under an
AstNodeProcedure - which we should rename to AstProcess, or an
AstNodeFTask). As a result, V3Table can now handle AssignW for free.
Also uncovered and fixed a bug in handling intra-assignment delays if
a function is present on the RHS of an AssignW.
There is more work to be done towards #6280, and potentially simplifying
AssignW handing, but this is the minimal change required to tick it off
the TODO list for #6280.
2025-10-14 10:05:19 +02:00
|
|
|
if (nodep->keyword() == VAlwaysKwd::CONT_ASSIGN) {
|
|
|
|
|
moveUnderSpecial<AstSenItem::Combo>(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
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
|
|
|
visitSenItems(nodep);
|
2025-08-18 01:14:34 +02:00
|
|
|
visitAlways(nodep, nodep->sentreep(), nodep->keyword());
|
2010-04-06 02:01:17 +02:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlwaysPostponed* nodep) override {
|
2022-05-16 21:02:49 +02:00
|
|
|
// Might be empty with later optimizations, so this assertion can be removed,
|
|
|
|
|
// but for now it is guaranteed to be not empty.
|
2022-09-15 20:43:56 +02:00
|
|
|
UASSERT_OBJ(nodep->stmtsp(), nodep, "Should not be empty");
|
2022-10-13 21:04:43 +02:00
|
|
|
// Make a new active for it, needs to be the only item under the active for V3Sched
|
|
|
|
|
AstActive* const activep = m_namer.makeSpecialActive<AstSenItem::Combo>(nodep->fileline());
|
|
|
|
|
activep->addStmtsp(nodep->unlinkFrBack());
|
2020-11-29 17:31:38 +01:00
|
|
|
}
|
2022-12-23 13:34:49 +01:00
|
|
|
void visit(AstAlwaysObserved* nodep) override {
|
2025-08-18 01:14:34 +02:00
|
|
|
UASSERT_OBJ(nodep->sentreep(), nodep, "Should have a sentree");
|
|
|
|
|
AstSenTree* const sentreep = nodep->sentreep();
|
|
|
|
|
sentreep->unlinkFrBack();
|
2022-12-23 13:34:49 +01:00
|
|
|
// Make a new active for it, needs to be the only item under the active for V3Sched
|
2025-08-18 01:14:34 +02:00
|
|
|
AstActive* const activep = m_namer.makeActive(nodep->fileline(), sentreep);
|
2022-12-23 13:34:49 +01:00
|
|
|
activep->addStmtsp(nodep->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
void visit(AstAlwaysReactive* nodep) override {
|
2025-08-18 01:14:34 +02:00
|
|
|
UASSERT_OBJ(nodep->sentreep(), nodep, "Should have a sentree");
|
|
|
|
|
AstSenTree* const sentreep = nodep->sentreep();
|
|
|
|
|
sentreep->unlinkFrBack();
|
2022-12-23 13:34:49 +01:00
|
|
|
// Make a new active for it, needs to be the only item under the active for V3Sched
|
2025-08-18 01:14:34 +02:00
|
|
|
AstActive* const activep = m_namer.makeActive(nodep->fileline(), sentreep);
|
2022-12-23 13:34:49 +01:00
|
|
|
activep->addStmtsp(nodep->unlinkFrBack());
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstCFunc* nodep) override { visitSenItems(nodep); }
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstSenItem* 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_OBJ(!m_walkingBody, nodep,
|
|
|
|
|
"Should not reach here when walking body without --timing");
|
2022-05-15 17:03:32 +02:00
|
|
|
if (!nodep->sensp()) return; // Ignore sequential items (e.g.: initial, comb, etc.)
|
|
|
|
|
|
|
|
|
|
m_clockedProcess = true;
|
|
|
|
|
if (nodep->edgeType() != VEdgeType::ET_CHANGED) m_allChanged = false;
|
|
|
|
|
|
2025-03-02 23:01:35 +01:00
|
|
|
if (const AstNodeDType* const dtypep = nodep->sensp()->dtypep()) {
|
|
|
|
|
if (const AstBasicDType* const basicp = dtypep->basicp()) {
|
2022-05-15 17:03:32 +02:00
|
|
|
if (basicp->isEvent()) nodep->edgeType(VEdgeType::ET_EVENT);
|
2020-04-25 21:37:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
|
Deprecate clocker attribute and --clk option (#6463)
The only use for the clocker attribute and the AstVar::isUsedClock that
is actually necessary today for correctness is to mark top level inputs
of --lib-create blocks as being (or driving) a clock signal. Correctness
of --lib-create (and hence hierarchical blocks) actually used to depend
on having the right optimizations eliminate intermediate clocks (e.g.:
V3Gate), when the top level port was not used directly in a sensitivity
list, or marking top level signals manually via --clk or the clocker
attribute. However V3Sched::partition already needs to trace through the
logic to figure out what signals might drive a sensitivity list, so it
can very easily mark all top level inputs as such.
In this patch we remove the AstVar::attrClocker and AstVar::isUsedClock
attributes, and replace them with AstVar::isPrimaryClock, automatically
set by V3Sched::partition. This eliminates all need for manual
annotation so we are deprecating the --clk/--no-clk options and the
clocker/no_clocker attributes.
This also eliminates the opportunity for any further mis-optimization
similar to #6453.
Regarding the other uses of the removed AstVar attributes:
- As of 5.000, initial edges are triggered via a separate mechanism
applied in V3Sched, so the use in V3EmitCFunc.cpp is redundant
- Also as of 5.000, we can handle arbitrary sensitivity expressions, so
the restriction on eliminating clock signals in V3Gate is unnecessary
- Since the recent change when Dfg is applied after V3Scope, it does
perform the equivalent of GateClkDecomp, so we can delete that pass.
2025-09-20 16:50:22 +02:00
|
|
|
nodep->sensp()->foreach([](const AstVarRef* refp) { refp->varScopep()->user1(true); });
|
2022-05-15 17:03:32 +02:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
AstVarScope* const vscp = nodep->varScopep();
|
|
|
|
|
if (nodep->access().isWriteOnly()) {
|
|
|
|
|
vscp->user2(true);
|
|
|
|
|
} else {
|
2022-10-27 19:32:10 +02:00
|
|
|
// If the variable is read before it is written (and is not a never-changing value),
|
|
|
|
|
// and is not in the sensitivity list, then this cannot be optimized into a
|
|
|
|
|
// combinational process
|
2022-05-15 17:03:32 +02:00
|
|
|
// TODO: live variable analysis would be more precise
|
2022-10-27 19:32:10 +02:00
|
|
|
if (!vscp->user2() && !vscp->varp()->valuep() && !vscp->user1()) m_canBeComb = false;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2009-01-07 15:37:59 +01:00
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstAssignDly* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
m_canBeComb = false;
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (nodep->isTimingControl()) m_clockedProcess = true;
|
2022-05-15 17:03:32 +02:00
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstFireEvent* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
m_canBeComb = false;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstAssignForce* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
m_canBeComb = false;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstRelease* nodep) override {
|
2022-05-15 17:03:32 +02:00
|
|
|
m_canBeComb = false;
|
|
|
|
|
iterateChildrenConst(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->isTimingControl()) {
|
|
|
|
|
m_canBeComb = false;
|
|
|
|
|
m_clockedProcess = true;
|
|
|
|
|
}
|
|
|
|
|
// Do not iterate children, technically not part of this process
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//--------------------
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstVar*) override {} // Accelerate
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstVarScope*) override {} // Accelerate
|
2022-09-16 17:15:10 +02:00
|
|
|
void visit(AstNode* 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 (!v3Global.opt.timing().isSetTrue() && m_walkingBody && !m_canBeComb) {
|
|
|
|
|
return; // Accelerate
|
|
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
if (!nodep->isPure()) m_canBeComb = false;
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
m_canBeComb = false;
|
|
|
|
|
m_clockedProcess = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-05-15 17:03:32 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2020-04-04 04:31:54 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-15 19:11:27 +02:00
|
|
|
explicit ActiveVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2022-07-30 16:01:25 +02:00
|
|
|
~ActiveVisitor() override = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Active class functions
|
|
|
|
|
|
|
|
|
|
void V3Active::activeAll(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2021-11-26 16:52:36 +01:00
|
|
|
{ ActiveVisitor{nodep}; } // Destruct before checking
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("active", 0, dumpTreeEitherLevel() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|