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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 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
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Active.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Ast.h"
|
2009-01-07 15:37:59 +01:00
|
|
|
#include "V3Const.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3Global.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 {
|
|
|
|
|
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
|
|
|
|
|
|
|
|
string typestr() const { // "
|
|
|
|
|
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} {}
|
2022-09-16 12:22:11 +02:00
|
|
|
string name() const override { return m_name + " " + typestr(); }
|
|
|
|
|
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
|
|
|
|
|
// to the output we are currently analysing (the output whose 'user() is set), if so return
|
|
|
|
|
// 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)
|
|
|
|
|
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
|
|
|
|
if (latchCheckInternal(castVertexp(edgep->top()))) {
|
|
|
|
|
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
|
|
|
|
|
LatchDetectGraphVertex* const ifp = castVertexp(vertexp->outBeginp()->top());
|
|
|
|
|
LatchDetectGraphVertex* const elsp
|
|
|
|
|
= castVertexp(vertexp->outBeginp()->outNextp()->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
|
|
|
|
|
// See NODE STATE comment in ActiveLatchCheckVisitor
|
|
|
|
|
AstNode::user1ClearTree();
|
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)
|
|
|
|
|
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;
|
|
|
|
|
for (const auto& 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_
|
2021-02-22 03:25:21 +01:00
|
|
|
if (!latchCheckInternal(castVertexp(verticesBeginp()))) 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");
|
2022-09-18 21:53:42 +02:00
|
|
|
if (dumpGraph() >= 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
|
|
|
private:
|
|
|
|
|
// 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
|
2022-08-05 13:15:59 +02:00
|
|
|
template <typename SenItemKind>
|
|
|
|
|
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) {
|
2022-10-13 21:04:43 +02:00
|
|
|
auto* const activep = new AstActive{fl, "", senTreep};
|
|
|
|
|
activep->sensesStorep(activep->sensesp());
|
|
|
|
|
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
|
|
|
|
|
template <typename SenItemKind>
|
|
|
|
|
AstActive* makeSpecialActive(FileLine* const fl) {
|
|
|
|
|
AstSenTree* const senTreep = new AstSenTree{fl, new AstSenItem{fl, SenItemKind{}}};
|
|
|
|
|
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)
|
2022-08-05 13:15:59 +02:00
|
|
|
template <typename SenItemKind>
|
|
|
|
|
AstActive* getSpecialActive(FileLine* fl) {
|
2022-05-15 17:03:32 +02:00
|
|
|
AstActive*& cachep = getSpecialActive<SenItemKind>();
|
2022-10-13 21:04:43 +02:00
|
|
|
if (!cachep) cachep = makeSpecialActive<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.
|
2006-08-26 13:35:28 +02:00
|
|
|
AstActive* getActive(FileLine* fl, AstSenTree* sensesp) {
|
2022-05-15 17:03:32 +02:00
|
|
|
UASSERT(sensesp, "Must be non-null");
|
2018-06-12 04:05:45 +02:00
|
|
|
|
2022-02-05 17:04:48 +01:00
|
|
|
auto it = m_activeMap.find(*sensesp);
|
|
|
|
|
// 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.
|
|
|
|
|
AstSenTree* const newsenp = sensesp->cloneTree(false);
|
2022-10-22 01:03:40 +02:00
|
|
|
AstActive* const activep = new AstActive{fl, "sequent", newsenp};
|
2022-02-05 17:04:48 +01:00
|
|
|
activep->sensesStorep(activep->sensesp());
|
|
|
|
|
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
|
|
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
class ActiveLatchCheckVisitor final : public VNVisitor {
|
2021-01-05 20:26:01 +01:00
|
|
|
private:
|
|
|
|
|
// 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();
|
2021-01-05 20:26:01 +01:00
|
|
|
if (nodep->access().isWriteOrRW() && varp->isSignal() && !varp->isUsedLoopIdx()) {
|
|
|
|
|
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");
|
2022-09-15 20:43:56 +02:00
|
|
|
iterateAndNextNull(nodep->thensp());
|
2021-06-01 15:01:18 +02:00
|
|
|
m_graph.addPathVertex(branchp, "ELSE");
|
|
|
|
|
iterateAndNextNull(nodep->elsesp());
|
|
|
|
|
m_graph.currentp(parentp);
|
|
|
|
|
}
|
2021-01-05 20:26:01 +01:00
|
|
|
}
|
|
|
|
|
//--------------------
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(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();
|
|
|
|
|
iterate(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:
|
2022-04-29 17:32:02 +02:00
|
|
|
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
|
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
|
|
|
|
|
if (m_check == CT_SEQ) return;
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAssign* nodep) override {
|
2022-04-29 17:32:02 +02:00
|
|
|
// Blocking assignments are always OK in combinational (and initial/final) processes
|
|
|
|
|
if (m_check != CT_SEQ) return;
|
|
|
|
|
|
2022-10-20 14:48:44 +02:00
|
|
|
const bool ignore = nodep->lhsp()->forall([&](const AstVarRef* refp) {
|
2022-04-29 17:32:02 +02:00
|
|
|
// Ignore reads (e.g.: index expressions)
|
|
|
|
|
if (refp->access().isReadOnly()) return true;
|
|
|
|
|
const AstVar* const varp = refp->varp();
|
|
|
|
|
// Ignore ...
|
|
|
|
|
return varp->isUsedLoopIdx() // ... loop indices
|
|
|
|
|
|| varp->isTemp() // ... temporaries
|
|
|
|
|
|| varp->fileline()->warnIsOff(V3ErrorCode::BLKSEQ); // ... user said so
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (ignore) return;
|
|
|
|
|
|
|
|
|
|
nodep->v3warn(BLKSEQ,
|
|
|
|
|
"Blocking assignment '=' in sequential logic process\n"
|
|
|
|
|
<< nodep->warnMore() //
|
|
|
|
|
<< "... Suggest using delayed assignment '<='");
|
2006-08-26 13:35:28 +02: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
|
|
|
private:
|
|
|
|
|
// 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
|
|
|
|
2014-12-24 03:42:33 +01:00
|
|
|
void visitAlways(AstNode* nodep, AstSenTree* oldsensesp, VAlwaysKwd kwd) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Move always to appropriate ACTIVE based on its sense list
|
2021-10-22 18:36:58 +02:00
|
|
|
if (oldsensesp && oldsensesp->sensesp() && oldsensesp->sensesp()->isNever()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Never executing. Kill it.
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!oldsensesp->sensesp()->nextp(), nodep,
|
|
|
|
|
"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;
|
|
|
|
|
if (oldsensesp) {
|
|
|
|
|
oldsensesp->unlinkFrBack();
|
|
|
|
|
iterateChildrenConst(oldsensesp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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())
|
|
|
|
|
: oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp)
|
|
|
|
|
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());
|
2022-05-15 17:03:32 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Delete sensitivity list
|
2022-05-15 17:03:32 +02:00
|
|
|
if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp);
|
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
|
|
|
{
|
|
|
|
|
ActiveDlyVisitor{nodep, m_clockedProcess ? ActiveDlyVisitor::CT_SEQ
|
|
|
|
|
: ActiveDlyVisitor::CT_COMB};
|
|
|
|
|
}
|
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(AstAssignAlias* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
|
|
|
|
void visit(AstCoverToggle* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
2022-10-14 09:35:26 +02:00
|
|
|
void visit(AstAssignW* nodep) override { moveUnderSpecial<AstSenItem::Combo>(nodep); }
|
2022-09-16 17:15:10 +02:00
|
|
|
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;
|
|
|
|
|
}
|
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);
|
2019-05-19 22:13:13 +02:00
|
|
|
visitAlways(nodep, nodep->sensesp(), 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 {
|
|
|
|
|
UASSERT_OBJ(nodep->sensesp(), nodep, "Should have a sentree");
|
|
|
|
|
AstSenTree* const sensesp = nodep->sensesp();
|
|
|
|
|
sensesp->unlinkFrBack();
|
|
|
|
|
// Make a new active for it, needs to be the only item under the active for V3Sched
|
|
|
|
|
AstActive* const activep = m_namer.makeActive(nodep->fileline(), sensesp);
|
|
|
|
|
activep->addStmtsp(nodep->unlinkFrBack());
|
|
|
|
|
}
|
|
|
|
|
void visit(AstAlwaysReactive* nodep) override {
|
|
|
|
|
UASSERT_OBJ(nodep->sensesp(), nodep, "Should have a sentree");
|
|
|
|
|
AstSenTree* const sensesp = nodep->sensesp();
|
|
|
|
|
sensesp->unlinkFrBack();
|
|
|
|
|
// Make a new active for it, needs to be the only item under the active for V3Sched
|
|
|
|
|
AstActive* const activep = m_namer.makeActive(nodep->fileline(), sensesp);
|
|
|
|
|
activep->addStmtsp(nodep->unlinkFrBack());
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstAlwaysPublic* nodep) override {
|
2019-05-19 22:13:13 +02:00
|
|
|
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
2010-04-06 02:01:17 +02:00
|
|
|
}
|
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;
|
|
|
|
|
|
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 (const auto* const dtypep = nodep->sensp()->dtypep()) {
|
|
|
|
|
if (const auto* 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
|
|
|
|
2022-10-20 14:48:44 +02:00
|
|
|
nodep->sensp()->foreach([](const AstVarRef* refp) {
|
2022-05-15 17:03:32 +02:00
|
|
|
refp->varp()->usedClock(true);
|
|
|
|
|
refp->varScopep()->user1(true);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
2020-04-14 04:51:35 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-11-26 16:52:36 +01:00
|
|
|
{ ActiveVisitor{nodep}; } // Destruct before checking
|
2022-09-18 21:53:42 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("active", 0, dumpTree() >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|