Internals: Replace VlTriggerVec with unpacked array (#6616)
Removed the VlTriggerVec type, and refactored to use an unpacked array of 64-bit words instead. This means the trigger vector and its operations are now the same as for any other unpacked array. The few special functions required for operating on a trigger vector are now generated in V3SchedTrigger as regular AstCFunc if needed. No functional change intended, performance should be the same.
This commit is contained in:
parent
08330f5fe2
commit
922223a9c3
|
|
@ -1,5 +1,5 @@
|
|||
.. comment: generated by t_lint_didnotconverge_bad
|
||||
.. code-block::
|
||||
|
||||
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] a)
|
||||
-V{t#,#} 'stl' region trigger index 64 is active: @([hybrid] a)
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after 100 tries
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
|||
/// Print a debug message from internals with standard prefix, with printf style format
|
||||
extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||
|
||||
/// Print a debug message from string via VL_DBG_MSGF
|
||||
inline void VL_DBG_MSGS(const std::string& str) VL_MT_SAFE { VL_DBG_MSGF("%s", str.c_str()); }
|
||||
|
||||
// EMIT_RULE: VL_RANDOM: oclean=dirty
|
||||
inline IData VL_RANDOM_I() VL_MT_SAFE { return vl_rand64(); }
|
||||
inline QData VL_RANDOM_Q() VL_MT_SAFE { return vl_rand64(); }
|
||||
|
|
|
|||
|
|
@ -164,58 +164,6 @@ public:
|
|||
|
||||
inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
|
||||
|
||||
//===================================================================
|
||||
// Activity trigger vector
|
||||
|
||||
template <std::size_t N_Size> //
|
||||
class VlTriggerVec final {
|
||||
// TODO: static assert N_Size > 0, and don't generate when empty
|
||||
|
||||
// MEMBERS
|
||||
alignas(16) std::array<uint64_t, vlstd::roundUpToMultipleOf<64>(N_Size) / 64> m_flags;
|
||||
|
||||
public:
|
||||
// CONSTRUCTOR
|
||||
VlTriggerVec() { clear(); }
|
||||
~VlTriggerVec() = default;
|
||||
|
||||
// METHODS
|
||||
|
||||
// Set all elements to false
|
||||
void clear() { m_flags.fill(0); }
|
||||
|
||||
// Word at given 'wordIndex'
|
||||
uint64_t word(size_t wordIndex) const { return m_flags[wordIndex]; }
|
||||
|
||||
// Set specified word to given value
|
||||
void setWord(size_t wordIndex, uint64_t value) { m_flags[wordIndex] = value; }
|
||||
|
||||
// Set specified bit to given value
|
||||
void setBit(size_t index, bool value) {
|
||||
uint64_t& w = m_flags[index / 64];
|
||||
const size_t bitIndex = index % 64;
|
||||
w &= ~(1ULL << bitIndex);
|
||||
w |= (static_cast<uint64_t>(value) << bitIndex);
|
||||
}
|
||||
|
||||
// Return true iff at least one element is set
|
||||
bool any() const {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i)
|
||||
if (m_flags[i]) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set all elements true in 'this' that are set in 'other'
|
||||
void thisOr(const VlTriggerVec<N_Size>& other) {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] |= other.m_flags[i];
|
||||
}
|
||||
|
||||
// Set elements of 'this' to 'a & !b' element-wise
|
||||
void andNot(const VlTriggerVec<N_Size>& a, const VlTriggerVec<N_Size>& b) {
|
||||
for (size_t i = 0; i < m_flags.size(); ++i) m_flags[i] = a.m_flags[i] & ~b.m_flags[i];
|
||||
}
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// SystemVerilog event type
|
||||
|
||||
|
|
|
|||
|
|
@ -804,13 +804,14 @@ public:
|
|||
void dumpGdbHeader() const;
|
||||
|
||||
// METHODS - Tree modifications
|
||||
// Returns nodep, adds newp to end of nodep's list
|
||||
// Returns nodep. If newp is not nullptr, then add it to end of nodep's list.
|
||||
template <typename T_NodeResult, typename T_NodeNext>
|
||||
static T_NodeResult* addNext(T_NodeResult* nodep, T_NodeNext* newp) {
|
||||
static_assert(std::is_base_of<AstNode, T_NodeResult>::value,
|
||||
"'T_NodeResult' must be a subtype of AstNode");
|
||||
static_assert(std::is_base_of<T_NodeResult, T_NodeNext>::value,
|
||||
"'T_NodeNext' must be a subtype of 'T_NodeResult'");
|
||||
if (!newp) return nodep;
|
||||
return static_cast<T_NodeResult*>(addNext<AstNode, AstNode>(nodep, newp));
|
||||
}
|
||||
inline AstNode* addNext(AstNode* newp);
|
||||
|
|
|
|||
|
|
@ -427,7 +427,6 @@ public:
|
|||
SCOPEPTR,
|
||||
CHARPTR,
|
||||
MTASKSTATE,
|
||||
TRIGGERVEC,
|
||||
DELAY_SCHEDULER,
|
||||
TRIGGER_SCHEDULER,
|
||||
DYNAMIC_TRIGGER_SCHEDULER,
|
||||
|
|
@ -462,7 +461,6 @@ public:
|
|||
"VerilatedScope*",
|
||||
"char*",
|
||||
"VlMTaskState",
|
||||
"VlTriggerVec",
|
||||
"VlDelayScheduler",
|
||||
"VlTriggerScheduler",
|
||||
"VlDynamicTriggerScheduler",
|
||||
|
|
@ -477,16 +475,13 @@ public:
|
|||
return names[m_e];
|
||||
}
|
||||
const char* dpiType() const {
|
||||
static const char* const names[] = {"%E-unk", "svBit", "char",
|
||||
"void*", "char", "int",
|
||||
"%E-integer", "svLogic", "long long",
|
||||
"double", "short", "%E-time",
|
||||
"const char*", "%E-untyped", "dpiScope",
|
||||
"const char*", "%E-mtaskstate", "%E-triggervec",
|
||||
"%E-dly-sched", "%E-trig-sched", "%E-dyn-sched",
|
||||
"%E-fork", "%E-proc-ref", "%E-rand-gen",
|
||||
"%E-stdrand-gen", "IData", "QData",
|
||||
"%E-logic-implct", " MAX"};
|
||||
static const char* const names[]
|
||||
= {"%E-unk", "svBit", "char", "void*", "char",
|
||||
"int", "%E-integer", "svLogic", "long long", "double",
|
||||
"short", "%E-time", "const char*", "%E-untyped", "dpiScope",
|
||||
"const char*", "%E-mtaskstate", "%E-dly-sched", "%E-trig-sched", "%E-dyn-sched",
|
||||
"%E-fork", "%E-proc-ref", "%E-rand-gen", "%E-stdrand-gen", "IData",
|
||||
"QData", "%E-logic-implct", " MAX"};
|
||||
return names[m_e];
|
||||
}
|
||||
static void selfTest() {
|
||||
|
|
@ -520,7 +515,6 @@ public:
|
|||
case SCOPEPTR: return 0; // opaque
|
||||
case CHARPTR: return 0; // opaque
|
||||
case MTASKSTATE: return 0; // opaque
|
||||
case TRIGGERVEC: return 0; // opaque
|
||||
case DELAY_SCHEDULER: return 0; // opaque
|
||||
case TRIGGER_SCHEDULER: return 0; // opaque
|
||||
case DYNAMIC_TRIGGER_SCHEDULER: return 0; // opaque
|
||||
|
|
@ -565,10 +559,10 @@ public:
|
|||
}
|
||||
bool isOpaque() const VL_MT_SAFE { // IE not a simple number we can bit optimize
|
||||
return (m_e == EVENT || m_e == STRING || m_e == SCOPEPTR || m_e == CHARPTR
|
||||
|| m_e == MTASKSTATE || m_e == TRIGGERVEC || m_e == DELAY_SCHEDULER
|
||||
|| m_e == TRIGGER_SCHEDULER || m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC
|
||||
|| m_e == PROCESS_REFERENCE || m_e == RANDOM_GENERATOR
|
||||
|| m_e == RANDOM_STDGENERATOR || m_e == DOUBLE || m_e == UNTYPED);
|
||||
|| m_e == MTASKSTATE || m_e == DELAY_SCHEDULER || m_e == TRIGGER_SCHEDULER
|
||||
|| m_e == DYNAMIC_TRIGGER_SCHEDULER || m_e == FORK_SYNC || m_e == PROCESS_REFERENCE
|
||||
|| m_e == RANDOM_GENERATOR || m_e == RANDOM_STDGENERATOR || m_e == DOUBLE
|
||||
|| m_e == UNTYPED);
|
||||
}
|
||||
bool isDouble() const VL_MT_SAFE { return m_e == DOUBLE; }
|
||||
bool isEvent() const { return m_e == EVENT; }
|
||||
|
|
@ -614,7 +608,6 @@ public:
|
|||
/* SCOPEPTR: */ "", // Should not be traced
|
||||
/* CHARPTR: */ "", // Should not be traced
|
||||
/* MTASKSTATE: */ "", // Should not be traced
|
||||
/* TRIGGERVEC: */ "", // Should not be traced
|
||||
/* DELAY_SCHEDULER: */ "", // Should not be traced
|
||||
/* TRIGGER_SCHEDULER: */ "", // Should not be traced
|
||||
/* DYNAMIC_TRIGGER_SCHEDULER: */ "", // Should not be traced
|
||||
|
|
@ -807,13 +800,6 @@ public:
|
|||
SCHED_RESUME,
|
||||
SCHED_RESUMPTION,
|
||||
SCHED_TRIGGER,
|
||||
TRIGGER_AND_NOT,
|
||||
TRIGGER_ANY,
|
||||
TRIGGER_CLEAR,
|
||||
TRIGGER_SET_BIT,
|
||||
TRIGGER_SET_WORD,
|
||||
TRIGGER_THIS_OR,
|
||||
TRIGGER_WORD,
|
||||
UNPACKED_ASSIGN,
|
||||
UNPACKED_FILL,
|
||||
UNPACKED_NEQ,
|
||||
|
|
@ -942,13 +928,6 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
{SCHED_RESUME, "resume", false}, \
|
||||
{SCHED_RESUMPTION, "resumption", false}, \
|
||||
{SCHED_TRIGGER, "trigger", false}, \
|
||||
{TRIGGER_AND_NOT, "andNot", false}, \
|
||||
{TRIGGER_ANY, "any", true}, \
|
||||
{TRIGGER_CLEAR, "clear", false}, \
|
||||
{TRIGGER_SET_BIT, "setBit", false}, \
|
||||
{TRIGGER_SET_WORD, "setWord", false}, \
|
||||
{TRIGGER_THIS_OR, "thisOr", false}, \
|
||||
{TRIGGER_WORD, "word", true}, \
|
||||
{UNPACKED_ASSIGN, "assign", false}, \
|
||||
{UNPACKED_FILL, "fill", false}, \
|
||||
{UNPACKED_NEQ, "neq", true}, \
|
||||
|
|
|
|||
|
|
@ -460,7 +460,6 @@ public:
|
|||
bool isBitLogic() const { return keyword().isBitLogic(); }
|
||||
bool isDouble() const VL_MT_STABLE { return keyword().isDouble(); }
|
||||
bool isEvent() const VL_MT_STABLE { return keyword() == VBasicDTypeKwd::EVENT; }
|
||||
bool isTriggerVec() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::TRIGGERVEC; }
|
||||
bool isForkSync() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::FORK_SYNC; }
|
||||
bool isProcessRef() const VL_MT_SAFE { return keyword() == VBasicDTypeKwd::PROCESS_REFERENCE; }
|
||||
bool isDelayScheduler() const VL_MT_SAFE {
|
||||
|
|
|
|||
|
|
@ -1615,7 +1615,7 @@ class AstText final : public AstNode {
|
|||
// Avoid using this directly, internally usually want
|
||||
// AstCStmt::add("text") or AstCExpr::add("text") instead
|
||||
//
|
||||
std::string m_text; // The text to emit
|
||||
const std::string m_text; // The text to emit
|
||||
public:
|
||||
AstText(FileLine* fl, const std::string& text)
|
||||
: ASTGEN_SUPER_Text(fl)
|
||||
|
|
@ -1627,7 +1627,6 @@ public:
|
|||
return text() == VN_DBG_AS(samep, Text)->text();
|
||||
}
|
||||
const std::string& text() const VL_MT_SAFE { return m_text; }
|
||||
void text(const string& value) { m_text = value; }
|
||||
};
|
||||
class AstTextBlock final : public AstNode {
|
||||
// Text block emitted into output, with some arbitrary nodes interspersed
|
||||
|
|
@ -2215,7 +2214,7 @@ public:
|
|||
}
|
||||
bool needsCReset() const {
|
||||
return !isIfaceParent() && !isIfaceRef() && !noReset() && !isParam() && !isStatementTemp()
|
||||
&& !(basicp() && (basicp()->isEvent() || basicp()->isTriggerVec()));
|
||||
&& !(basicp() && basicp()->isEvent());
|
||||
}
|
||||
static AstVar* scVarRecurse(AstNode* nodep);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1027,8 +1027,6 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
|
|||
info.m_type = "std::string";
|
||||
} else if (bdtypep->keyword().isMTaskState()) {
|
||||
info.m_type = "VlMTaskVertex";
|
||||
} else if (bdtypep->isTriggerVec()) {
|
||||
info.m_type = "VlTriggerVec<" + cvtToStr(dtypep->width()) + ">";
|
||||
} else if (bdtypep->isDelayScheduler()) {
|
||||
info.m_type = "VlDelayScheduler";
|
||||
} else if (bdtypep->isTriggerScheduler()) {
|
||||
|
|
@ -3149,13 +3147,6 @@ void AstCAwait::dump(std::ostream& str) const {
|
|||
}
|
||||
void AstCAwait::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||
int AstCMethodHard::instrCount() const {
|
||||
if (const AstBasicDType* const basicp = fromp()->dtypep()->basicp()) {
|
||||
if (basicp->isTriggerVec() && m_method == VCMethod::TRIGGER_WORD) {
|
||||
// This is an important special case for scheduling so we compute it precisely,
|
||||
// it is simply a load.
|
||||
return INSTR_COUNT_LD;
|
||||
}
|
||||
}
|
||||
return 0; // TODO
|
||||
}
|
||||
void AstCMethodHard::setPurity() {
|
||||
|
|
|
|||
|
|
@ -188,8 +188,7 @@ class CastVisitor final : public VNVisitor {
|
|||
if (nodep->access().isReadOnly() && VN_IS(backp, NodeExpr) && !VN_IS(backp, CCast)
|
||||
&& !VN_IS(backp, NodeCCall) && !VN_IS(backp, CMethodHard) && !VN_IS(backp, SFormatF)
|
||||
&& !VN_IS(backp, ArraySel) && !VN_IS(backp, StructSel) && !VN_IS(backp, RedXor)
|
||||
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isTriggerVec()
|
||||
&& !nodep->varp()->basicp()->isForkSync()
|
||||
&& (nodep->varp()->basicp() && !nodep->varp()->basicp()->isForkSync()
|
||||
&& !nodep->varp()->basicp()->isProcessRef() && !nodep->varp()->basicp()->isEvent())
|
||||
&& backp->width() && castSize(nodep) != castSize(nodep->varp())) {
|
||||
// Cast vars to IData first, else below has upper bits wrongly set
|
||||
|
|
|
|||
|
|
@ -305,7 +305,6 @@ class EmitCImp final : EmitCFunc {
|
|||
// lower level subinst code does it.
|
||||
} else if (varp->isParam()) {
|
||||
} else if (varp->isStatic() && varp->isConst()) {
|
||||
} else if (varp->basicp() && varp->basicp()->isTriggerVec()) {
|
||||
} else if (VN_IS(varp->dtypep(), NBACommitQueueDType)) {
|
||||
} else {
|
||||
int vects = 0;
|
||||
|
|
|
|||
|
|
@ -106,9 +106,41 @@ AstCFunc* V3Order::order(AstNetlist* netlistp, //
|
|||
bool parallel, //
|
||||
bool slow, //
|
||||
const ExternalDomainsProvider& externalDomains) {
|
||||
FileLine* const flp = netlistp->fileline();
|
||||
// Build the OrderGraph
|
||||
const std::unique_ptr<OrderGraph> graph = buildOrderGraph(netlistp, logic, trigToSen);
|
||||
// Order it
|
||||
orderOrderGraph(*graph, tag);
|
||||
// Assign sensitivity domains to combinational logic
|
||||
processDomains(netlistp, *graph, tag, externalDomains);
|
||||
// Build the move graph
|
||||
OrderMoveDomScope::clear();
|
||||
const std::unique_ptr<OrderMoveGraph> moveGraphp = OrderMoveGraph::build(*graph, trigToSen);
|
||||
if (dumpGraphLevel() >= 9) moveGraphp->dumpDotFilePrefixed(tag + "_ordermv");
|
||||
|
||||
// The ordered statements, if there are any
|
||||
AstNodeStmt* stmtsp = nullptr;
|
||||
if (!moveGraphp->empty()) {
|
||||
if (parallel) {
|
||||
stmtsp = createParallel(*graph, *moveGraphp, tag, slow);
|
||||
} else {
|
||||
stmtsp = createSerial(*moveGraphp, tag, slow);
|
||||
}
|
||||
// Should have consumed all vertices
|
||||
UASSERT(moveGraphp->empty(), "Unconsumed vertices remain in OrderMoveGraph");
|
||||
}
|
||||
OrderMoveDomScope::clear();
|
||||
|
||||
// Dump data
|
||||
if (dumpGraphLevel()) graph->dumpDotFilePrefixed(tag + "_orderg_done");
|
||||
|
||||
// Dispose of the remnants of the inputs
|
||||
for (auto* const lbsp : logic) lbsp->deleteActives();
|
||||
|
||||
// If there is no resulting logic, then don't create an empty function
|
||||
if (!stmtsp) return nullptr;
|
||||
|
||||
// Create the result function
|
||||
FileLine* const flp = netlistp->fileline();
|
||||
AstCFunc* const funcp = [&]() {
|
||||
AstScope* const scopeTopp = netlistp->topScopep()->scopep();
|
||||
AstCFunc* const resp = new AstCFunc{flp, "_eval_" + tag, scopeTopp, ""};
|
||||
|
|
@ -122,36 +154,14 @@ AstCFunc* V3Order::order(AstNetlist* netlistp, //
|
|||
return resp;
|
||||
}();
|
||||
|
||||
// Assemble the body
|
||||
if (v3Global.opt.profExec()) {
|
||||
const string name
|
||||
const std::string name
|
||||
= (v3Global.opt.hierChild() ? (v3Global.opt.topModule() + " ") : "") + "func " + tag;
|
||||
funcp->addStmtsp(
|
||||
new AstCStmt{flp, "VL_EXEC_TRACE_ADD_RECORD(vlSymsp).sectionPush(\"" + name + "\");"});
|
||||
}
|
||||
|
||||
// Build the OrderGraph
|
||||
const std::unique_ptr<OrderGraph> graph = buildOrderGraph(netlistp, logic, trigToSen);
|
||||
// Order it
|
||||
orderOrderGraph(*graph, tag);
|
||||
// Assign sensitivity domains to combinational logic
|
||||
processDomains(netlistp, *graph, tag, externalDomains);
|
||||
|
||||
if (parallel) {
|
||||
// Construct the parallel code
|
||||
AstNodeStmt* const stmtsp = createParallel(*graph, tag, trigToSen, slow);
|
||||
funcp->addStmtsp(stmtsp);
|
||||
} else {
|
||||
// Construct the serial code
|
||||
AstNodeStmt* const stmtsp = createSerial(*graph, tag, trigToSen, slow);
|
||||
funcp->addStmtsp(stmtsp);
|
||||
}
|
||||
|
||||
// Dump data
|
||||
if (dumpGraphLevel()) graph->dumpDotFilePrefixed(tag + "_orderg_done");
|
||||
|
||||
// Dispose of the remnants of the inputs
|
||||
for (auto* const lbsp : logic) lbsp->deleteActives();
|
||||
|
||||
funcp->addStmtsp(stmtsp);
|
||||
if (v3Global.opt.profExec()) {
|
||||
funcp->addStmtsp(new AstCStmt{flp, "VL_EXEC_TRACE_ADD_RECORD(vlSymsp).sectionPop();"});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ class OrderGraphBuilder final : public VNVisitor {
|
|||
VL_RESTORER(m_hybridp);
|
||||
VL_RESTORER(m_inClocked);
|
||||
|
||||
// This is the original sensitivity of the block (i.e.: not the ref into the TRIGGERVEC)
|
||||
// This is the original sensitivity of the block (i.e.: not the ref into the trigger vec)
|
||||
|
||||
const AstSenTree* const senTreep = nodep->sentreep()->hasCombo()
|
||||
? nodep->sentreep()
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "V3Order.h"
|
||||
#include "V3OrderGraph.h"
|
||||
#include "V3OrderMoveGraph.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
|
@ -50,14 +51,13 @@ void processDomains(AstNetlist* netlistp, //
|
|||
const std::string& tag, //
|
||||
const ExternalDomainsProvider& externalDomains);
|
||||
|
||||
AstNodeStmt* createSerial(OrderGraph& orderGraph, //
|
||||
AstNodeStmt* createSerial(OrderMoveGraph& moveGraph, //
|
||||
const std::string& tag, //
|
||||
const TrigToSenMap& trigToSenMap, //
|
||||
bool slow);
|
||||
|
||||
AstNodeStmt* createParallel(OrderGraph& orderGraph, //
|
||||
OrderMoveGraph& moveGraph, //
|
||||
const std::string& tag, //
|
||||
const TrigToSenMap& trigToSenMap, //
|
||||
bool slow);
|
||||
|
||||
}; // namespace V3Order
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
#include "V3List.h"
|
||||
#include "V3OrderCFuncEmitter.h"
|
||||
#include "V3OrderInternal.h"
|
||||
#include "V3OrderMoveGraph.h"
|
||||
#include "V3Os.h"
|
||||
#include "V3PairingHeap.h"
|
||||
#include "V3Scoreboard.h"
|
||||
|
|
@ -2395,36 +2394,30 @@ struct MTaskVxIdLessThan final {
|
|||
}
|
||||
};
|
||||
|
||||
AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, const std::string& tag,
|
||||
const TrigToSenMap& trigToSen, bool slow) {
|
||||
AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, OrderMoveGraph& moveGraph,
|
||||
const std::string& tag, bool slow) {
|
||||
UINFO(2, " Constructing parallel code for '" + tag + "'");
|
||||
|
||||
// For nondeterminism debug:
|
||||
hashGraphDebug(orderGraph, "V3OrderParallel's input OrderGraph");
|
||||
|
||||
// Build the move graph
|
||||
OrderMoveDomScope::clear();
|
||||
const std::unique_ptr<OrderMoveGraph> moveGraphp
|
||||
= OrderMoveGraph::build(orderGraph, trigToSen);
|
||||
if (dumpGraphLevel() >= 9) moveGraphp->dumpDotFilePrefixed(tag + "_ordermv");
|
||||
|
||||
// Partition moveGraphp into LogicMTask's. The partitioner will set userp() on each logic
|
||||
// vertex in the moveGraphp to the MTask it belongs to.
|
||||
const std::unique_ptr<V3Graph> mTaskGraphp = Partitioner::apply(orderGraph, *moveGraphp);
|
||||
if (dumpGraphLevel() >= 9) moveGraphp->dumpDotFilePrefixed(tag + "_ordermv_mtasks");
|
||||
// Partition moveGraph into LogicMTask's. The partitioner will set userp() on each logic
|
||||
// vertex in the moveGraph to the MTask it belongs to.
|
||||
const std::unique_ptr<V3Graph> mTaskGraphp = Partitioner::apply(orderGraph, moveGraph);
|
||||
if (dumpGraphLevel() >= 9) moveGraph.dumpDotFilePrefixed(tag + "_ordermv_mtasks");
|
||||
|
||||
// Some variable OrderMoveVertices are not assigned to an MTask. Reroute and delete these.
|
||||
for (V3GraphVertex* const vtxp : moveGraphp->vertices().unlinkable()) {
|
||||
for (V3GraphVertex* const vtxp : moveGraph.vertices().unlinkable()) {
|
||||
OrderMoveVertex* const mVtxp = vtxp->as<OrderMoveVertex>();
|
||||
if (!mVtxp->userp()) {
|
||||
UASSERT_OBJ(!mVtxp->logicp(), mVtxp, "Logic OrderMoveVertex not assigned to mtask");
|
||||
mVtxp->rerouteEdges(moveGraphp.get());
|
||||
VL_DO_DANGLING(mVtxp->unlinkDelete(moveGraphp.get()), mVtxp);
|
||||
mVtxp->rerouteEdges(&moveGraph);
|
||||
VL_DO_DANGLING(mVtxp->unlinkDelete(&moveGraph), mVtxp);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all edges from the move graph that cross between MTasks. Add logic to MTask lists.
|
||||
for (V3GraphVertex& vtx : moveGraphp->vertices()) {
|
||||
for (V3GraphVertex& vtx : moveGraph.vertices()) {
|
||||
OrderMoveVertex* const mVtxp = vtx.as<OrderMoveVertex>();
|
||||
LogicMTask* const mtaskp = static_cast<LogicMTask*>(mVtxp->userp());
|
||||
// Add to list in MTask, in MoveGraph order. This should not be necessary, but see #4993.
|
||||
|
|
@ -2435,7 +2428,7 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, const std::string&
|
|||
if (mtaskp != toMVtxp->userp()) VL_DO_DANGLING(edgep->unlinkDelete(), edgep);
|
||||
}
|
||||
}
|
||||
if (dumpGraphLevel() >= 9) moveGraphp->dumpDotFilePrefixed(tag + "_ordermv_pruned");
|
||||
if (dumpGraphLevel() >= 9) moveGraph.dumpDotFilePrefixed(tag + "_ordermv_pruned");
|
||||
|
||||
// Create the AstExecGraph node which represents the execution of the MTask graph.
|
||||
FileLine* const rootFlp = v3Global.rootp()->fileline();
|
||||
|
|
@ -2445,7 +2438,7 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, const std::string&
|
|||
// Translate the LogicMTask graph into the corresponding ExecMTask graph,
|
||||
// which will outlive ordering.
|
||||
std::unordered_map<const LogicMTask*, ExecMTask*> logicMTaskToExecMTask;
|
||||
OrderMoveGraphSerializer serializer{*moveGraphp};
|
||||
OrderMoveGraphSerializer serializer{moveGraph};
|
||||
V3OrderCFuncEmitter emitter{tag, slow};
|
||||
GraphStream<MTaskVxIdLessThan> mtaskStream{mTaskGraphp.get()};
|
||||
while (const V3GraphVertex* const vtxp = mtaskStream.nextp()) {
|
||||
|
|
@ -2472,7 +2465,7 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, const std::string&
|
|||
emitter.emitLogic(logicp);
|
||||
}
|
||||
// Can delete the vertex now
|
||||
VL_DO_DANGLING(mVtxp->unlinkDelete(moveGraphp.get()), mVtxp);
|
||||
VL_DO_DANGLING(mVtxp->unlinkDelete(&moveGraph), mVtxp);
|
||||
}
|
||||
|
||||
// We have 2 objects, because AstMTaskBody is an AstNode, and ExecMTask is a GraphVertex.
|
||||
|
|
@ -2502,15 +2495,12 @@ AstNodeStmt* V3Order::createParallel(OrderGraph& orderGraph, const std::string&
|
|||
}
|
||||
|
||||
// Delete the remaining variable vertices
|
||||
for (V3GraphVertex* const vtxp : moveGraphp->vertices().unlinkable()) {
|
||||
for (V3GraphVertex* const vtxp : moveGraph.vertices().unlinkable()) {
|
||||
if (!vtxp->as<OrderMoveVertex>()->logicp()) {
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(moveGraphp.get()), vtxp);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(&moveGraph), vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
UASSERT(moveGraphp->empty(), "Waiting vertices remain, but none are ready");
|
||||
OrderMoveDomScope::clear();
|
||||
|
||||
return execGraphp;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include "V3OrderCFuncEmitter.h"
|
||||
#include "V3OrderInternal.h"
|
||||
#include "V3OrderMoveGraph.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -31,21 +30,15 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
//######################################################################
|
||||
// OrderSerial class
|
||||
|
||||
AstNodeStmt* V3Order::createSerial(OrderGraph& graph, const std::string& tag,
|
||||
const TrigToSenMap& trigToSen, bool slow) {
|
||||
AstNodeStmt* V3Order::createSerial(OrderMoveGraph& moveGraph, const std::string& tag, bool slow) {
|
||||
|
||||
UINFO(2, " Constructing serial code for '" + tag + "'");
|
||||
|
||||
// Build the move graph
|
||||
OrderMoveDomScope::clear();
|
||||
const std::unique_ptr<OrderMoveGraph> moveGraphp = OrderMoveGraph::build(graph, trigToSen);
|
||||
if (dumpGraphLevel() >= 9) moveGraphp->dumpDotFilePrefixed(tag + "_ordermv");
|
||||
|
||||
// Serializer
|
||||
OrderMoveGraphSerializer serializer{*moveGraphp};
|
||||
OrderMoveGraphSerializer serializer{moveGraph};
|
||||
|
||||
// Add initially ready vertices (those with no dependencies) to the serializer as seeds
|
||||
for (V3GraphVertex& vtx : moveGraphp->vertices()) {
|
||||
for (V3GraphVertex& vtx : moveGraph.vertices()) {
|
||||
if (vtx.inEmpty()) serializer.addSeed(vtx.as<OrderMoveVertex>());
|
||||
}
|
||||
|
||||
|
|
@ -63,18 +56,15 @@ AstNodeStmt* V3Order::createSerial(OrderGraph& graph, const std::string& tag,
|
|||
emitter.emitLogic(logicp);
|
||||
}
|
||||
// Can delete the vertex now
|
||||
VL_DO_DANGLING(mVtxp->unlinkDelete(moveGraphp.get()), mVtxp);
|
||||
VL_DO_DANGLING(mVtxp->unlinkDelete(&moveGraph), mVtxp);
|
||||
}
|
||||
|
||||
// Delete the remaining variable vertices
|
||||
for (V3GraphVertex* const vtxp : moveGraphp->vertices().unlinkable()) {
|
||||
for (V3GraphVertex* const vtxp : moveGraph.vertices().unlinkable()) {
|
||||
if (!vtxp->as<OrderMoveVertex>()->logicp()) {
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(moveGraphp.get()), vtxp);
|
||||
VL_DO_DANGLING(vtxp->unlinkDelete(&moveGraph), vtxp);
|
||||
}
|
||||
}
|
||||
|
||||
UASSERT(moveGraphp->empty(), "Waiting vertices remain, but none are ready");
|
||||
OrderMoveDomScope::clear();
|
||||
|
||||
return emitter.getStmts();
|
||||
}
|
||||
|
|
|
|||
351
src/V3Sched.cpp
351
src/V3Sched.cpp
|
|
@ -105,10 +105,10 @@ findTriggeredIface(const AstVarScope* vscp, const VirtIfaceTriggers::IfaceSensMa
|
|||
// Eval loop builder
|
||||
|
||||
struct EvalLoop final {
|
||||
// Flag set to true during the first iteration of the loop
|
||||
// Flag set to true on entry to the first iteration of the loop
|
||||
AstVarScope* firstIterp;
|
||||
// The loop itself and statements around it
|
||||
AstNodeStmt* stmtsp = nullptr;
|
||||
AstNodeStmt* stmtsp;
|
||||
};
|
||||
|
||||
// Create an eval loop with all the trimmings.
|
||||
|
|
@ -117,8 +117,8 @@ EvalLoop createEvalLoop(
|
|||
const std::string& tag, // Tag for current phase
|
||||
const string& name, // Name of current phase
|
||||
bool slow, // Should create slow functions
|
||||
AstVarScope* trigp, // The trigger vector
|
||||
AstCFunc* dumpFuncp, // Trigger dump function for debugging only
|
||||
const TriggerKit& trigKit, // The trigger kit
|
||||
AstVarScope* trigp, // The trigger vector - may be nullptr if no triggers
|
||||
AstNodeStmt* innerp, // The inner loop, if any
|
||||
AstNodeStmt* phasePrepp, // Prep statements run before checking triggers
|
||||
AstNodeStmt* phaseWorkp, // The work to do if anything triggered
|
||||
|
|
@ -127,6 +127,10 @@ EvalLoop createEvalLoop(
|
|||
// and must be unmodified otherwise.
|
||||
std::function<AstNodeStmt*(AstVarScope*)> phaseExtra = [](AstVarScope*) { return nullptr; } //
|
||||
) {
|
||||
// All work is under a trigger, so if there are no triggers, there is
|
||||
// nothing to do besides executing the inner loop.
|
||||
if (!trigp) return {nullptr, innerp};
|
||||
|
||||
const std::string varPrefix = "__V" + tag;
|
||||
AstScope* const scopeTopp = netlistp->topScopep()->scopep();
|
||||
FileLine* const flp = netlistp->fileline();
|
||||
|
|
@ -134,32 +138,37 @@ EvalLoop createEvalLoop(
|
|||
// We wrap the prep/cond/work in a function for readability
|
||||
AstCFunc* const phaseFuncp = util::makeTopFunction(netlistp, "_eval_phase__" + tag, slow);
|
||||
{
|
||||
// Add the preparatory statements
|
||||
phaseFuncp->addStmtsp(phasePrepp);
|
||||
|
||||
// The execute flag
|
||||
AstVarScope* const executeFlagp = scopeTopp->createTemp(varPrefix + "Execute", 1);
|
||||
executeFlagp->varp()->noReset(true);
|
||||
|
||||
// Add the preparatory statements
|
||||
phaseFuncp->addStmtsp(phasePrepp);
|
||||
// If there is work in this phase, execute it if any triggers fired
|
||||
if (phaseWorkp) {
|
||||
// Check if any triggers are fired, save the result
|
||||
AstNodeExpr* const lhsp = new AstVarRef{flp, executeFlagp, VAccess::WRITE};
|
||||
AstNodeExpr* const rhsp = trigKit.newAnySetCall(trigp);
|
||||
phaseFuncp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
|
||||
|
||||
// Check if any triggers are fired, save the result
|
||||
AstCMethodHard* const callp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, trigp, VAccess::READ}, VCMethod::TRIGGER_ANY};
|
||||
callp->dtypeSetBit();
|
||||
phaseFuncp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, executeFlagp, VAccess::WRITE}, callp});
|
||||
|
||||
// Add the work
|
||||
AstIf* const ifp = new AstIf{flp, new AstVarRef{flp, executeFlagp, VAccess::READ}};
|
||||
ifp->addThensp(phaseWorkp);
|
||||
phaseFuncp->addStmtsp(ifp);
|
||||
// Add the work
|
||||
AstIf* const ifp = new AstIf{flp, new AstVarRef{flp, executeFlagp, VAccess::READ}};
|
||||
ifp->addThensp(phaseWorkp);
|
||||
phaseFuncp->addStmtsp(ifp);
|
||||
}
|
||||
|
||||
// Construct the extra statements
|
||||
if (AstNodeStmt* const extrap = phaseExtra(executeFlagp)) phaseFuncp->addStmtsp(extrap);
|
||||
AstNodeStmt* const extraWorkp = phaseExtra(executeFlagp);
|
||||
if (extraWorkp) phaseFuncp->addStmtsp(extraWorkp);
|
||||
|
||||
// The function returns ture iff it did run the work
|
||||
// The function returns ture iff it did run work
|
||||
phaseFuncp->rtnType("bool");
|
||||
phaseFuncp->addStmtsp(
|
||||
new AstCReturn{flp, new AstVarRef{flp, executeFlagp, VAccess::READ}});
|
||||
AstNodeExpr* const retp
|
||||
= phaseWorkp || extraWorkp
|
||||
? static_cast<AstNodeExpr*>(new AstVarRef{flp, executeFlagp, VAccess::READ})
|
||||
: static_cast<AstNodeExpr*>(new AstConst{flp, AstConst::BitFalse{}});
|
||||
phaseFuncp->addStmtsp(new AstCReturn{flp, retp});
|
||||
}
|
||||
|
||||
// The result statements
|
||||
|
|
@ -171,46 +180,38 @@ EvalLoop createEvalLoop(
|
|||
const auto addVar = [&](const std::string& name, int width, uint32_t initVal) {
|
||||
AstVarScope* const vscp = scopeTopp->createTemp("__V" + tag + name, width);
|
||||
vscp->varp()->noReset(true);
|
||||
vscp->varp()->isInternal(true);
|
||||
stmtps = AstNode::addNext(stmtps, util::setVar(vscp, initVal));
|
||||
return vscp;
|
||||
};
|
||||
|
||||
// The iteration counter
|
||||
AstVarScope* const counterp = addVar("IterCount", 32, 0);
|
||||
// The first iteration flag
|
||||
// The first iteration flag - cleared in 'phasePrepp' if used
|
||||
AstVarScope* const firstIterFlagp = addVar("FirstIteration", 1, 1);
|
||||
// The continuation flag
|
||||
AstVarScope* const continueFlagp = addVar("Continue", 1, 1);
|
||||
|
||||
// The loop
|
||||
{
|
||||
AstNodeExpr* const condp = new AstVarRef{flp, continueFlagp, VAccess::READ};
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, condp});
|
||||
stmtps->addNext(loopp);
|
||||
|
||||
// Check the iteration limit (aborts if exceeded)
|
||||
loopp->addStmtsp(util::checkIterationLimit(netlistp, name, counterp, dumpFuncp));
|
||||
AstNodeStmt* const dumpCallp = trigKit.newDumpCall(trigp, tag, false);
|
||||
loopp->addStmtsp(util::checkIterationLimit(netlistp, name, counterp, dumpCallp));
|
||||
// Increment the iteration counter
|
||||
loopp->addStmtsp(util::incrementVar(counterp));
|
||||
|
||||
// Reset continuation flag
|
||||
loopp->addStmtsp(util::setVar(continueFlagp, 0));
|
||||
|
||||
// Execute the inner loop
|
||||
loopp->addStmtsp(innerp);
|
||||
|
||||
// Call the phase function to execute the current work. If we did
|
||||
// work, then need to loop again, so set the continuation flag
|
||||
// work, then need to loop again, so set the continuation flag.
|
||||
// If used, the first iteration flag is cleared when consumed, no
|
||||
// need to reset it
|
||||
AstCCall* const callp = new AstCCall{flp, phaseFuncp};
|
||||
callp->dtypeSetBit();
|
||||
AstIf* const ifp = new AstIf{flp, callp};
|
||||
ifp->addThensp(util::setVar(continueFlagp, 1));
|
||||
loopp->addStmtsp(ifp);
|
||||
|
||||
// Clear the first iteration flag
|
||||
loopp->addStmtsp(util::setVar(firstIterFlagp, 0));
|
||||
|
||||
stmtps->addNext(loopp);
|
||||
// Continues until the continuation flag is clear
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, callp});
|
||||
}
|
||||
|
||||
// Prof-exec section pop
|
||||
|
|
@ -356,53 +357,18 @@ void createFinal(AstNetlist* netlistp, const LogicClasses& logicClasses) {
|
|||
util::splitCheck(funcp);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// EvalKit groups items that have to be passed to createEval() for a given eval region
|
||||
|
||||
struct EvalKit final {
|
||||
// The TRIGGERVEC AstVarScope representing the region's trigger flags
|
||||
AstVarScope* const m_vscp = nullptr;
|
||||
// The AstCFunc that computes the region's active triggers
|
||||
AstCFunc* const m_triggerComputep = nullptr;
|
||||
// The AstCFunc that dumps the region's active triggers
|
||||
AstCFunc* const m_dumpp = nullptr;
|
||||
// The AstCFunc that evaluates the region's logic
|
||||
AstCFunc* const m_funcp = nullptr;
|
||||
// Is this kit used/required?
|
||||
bool empty() const { return !m_funcp; }
|
||||
};
|
||||
|
||||
// Create an AstSenTree that is sensitive to the given trigger index. Must not exist yet!
|
||||
AstSenTree* createTriggerSenTree(AstNetlist* netlistp, AstVarScope* const vscp, uint32_t index) {
|
||||
UASSERT_OBJ(index != std::numeric_limits<unsigned>::max(), netlistp, "Invalid trigger index");
|
||||
AstTopScope* const topScopep = netlistp->topScopep();
|
||||
FileLine* const flp = topScopep->fileline();
|
||||
AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
const uint32_t wordIndex = index / 64;
|
||||
const uint32_t bitIndex = index % 64;
|
||||
AstCMethodHard* const callp
|
||||
= new AstCMethodHard{flp, vrefp, VCMethod::TRIGGER_WORD, new AstConst{flp, wordIndex}};
|
||||
callp->dtypeSetUInt64();
|
||||
AstNodeExpr* const termp
|
||||
= new AstAnd{flp, new AstConst{flp, AstConst::Unsized64{}, 1ULL << bitIndex}, callp};
|
||||
AstSenItem* const senItemp = new AstSenItem{flp, VEdgeType::ET_TRUE, termp};
|
||||
AstSenTree* const resultp = new AstSenTree{flp, senItemp};
|
||||
topScopep->addSenTreesp(resultp);
|
||||
return resultp;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// Helper that creates virtual interface trigger resets
|
||||
|
||||
void addVirtIfaceTriggerAssignments(const VirtIfaceTriggers& virtIfaceTriggers,
|
||||
size_t vifTriggerIndex, size_t vifMemberTriggerIndex,
|
||||
const TriggerKit& actTrig) {
|
||||
uint32_t vifTriggerIndex, uint32_t vifMemberTriggerIndex,
|
||||
const TriggerKit& trigKit) {
|
||||
for (const auto& p : virtIfaceTriggers.m_ifaceTriggers) {
|
||||
actTrig.addExtraTriggerAssignment(p.second, vifTriggerIndex);
|
||||
trigKit.addExtraTriggerAssignment(p.second, vifTriggerIndex);
|
||||
++vifTriggerIndex;
|
||||
}
|
||||
for (const auto& p : virtIfaceTriggers.m_memberTriggers) {
|
||||
actTrig.addExtraTriggerAssignment(p.second, vifMemberTriggerIndex);
|
||||
trigKit.addExtraTriggerAssignment(p.second, vifMemberTriggerIndex);
|
||||
++vifMemberTriggerIndex;
|
||||
}
|
||||
}
|
||||
|
|
@ -421,24 +387,24 @@ void createSettle(AstNetlist* netlistp, AstCFunc* const initFuncp, SenExprBuilde
|
|||
if (comb.empty() && hybrid.empty()) return;
|
||||
|
||||
// We have an extra trigger denoting this is the first iteration of the settle loop
|
||||
ExtraTriggers extraTriggers;
|
||||
const size_t firstIterationTrigger = extraTriggers.allocate("first iteration");
|
||||
TriggerKit::ExtraTriggers extraTriggers;
|
||||
const uint32_t firstIterationTrigger = extraTriggers.allocate("first iteration");
|
||||
|
||||
// Gather the relevant sensitivity expressions and create the trigger kit
|
||||
const auto& senTreeps = getSenTreesUsedBy({&comb, &hybrid});
|
||||
const TriggerKit trig = TriggerKit::create(netlistp, initFuncp, senExprBulider, senTreeps,
|
||||
"stl", extraTriggers, true);
|
||||
const TriggerKit trigKit = TriggerKit::create(netlistp, initFuncp, senExprBulider, senTreeps,
|
||||
"stl", extraTriggers, true);
|
||||
|
||||
// Remap sensitivities (comb has none, so only do the hybrid)
|
||||
remapSensitivities(hybrid, trig.m_map);
|
||||
remapSensitivities(hybrid, trigKit.map());
|
||||
|
||||
// Create the inverse map from trigger ref AstSenTree to original AstSenTree
|
||||
V3Order::TrigToSenMap trigToSen;
|
||||
invertAndMergeSenTreeMap(trigToSen, trig.m_map);
|
||||
invertAndMergeSenTreeMap(trigToSen, trigKit.map());
|
||||
|
||||
// First trigger is for pure combinational triggers (first iteration)
|
||||
AstSenTree* const inputChanged
|
||||
= createTriggerSenTree(netlistp, trig.m_vscp, firstIterationTrigger);
|
||||
= trigKit.newTriggerSenTree(trigKit.vscp(), {firstIterationTrigger});
|
||||
|
||||
// Create and the body function
|
||||
AstCFunc* const stlFuncp = V3Order::order(
|
||||
|
|
@ -448,16 +414,16 @@ void createSettle(AstNetlist* netlistp, AstCFunc* const initFuncp, SenExprBuilde
|
|||
|
||||
// Create the eval loop
|
||||
const EvalLoop stlLoop = createEvalLoop( //
|
||||
netlistp, "stl", "Settle", /* slow: */ true, trig.m_vscp, trig.m_dumpp,
|
||||
netlistp, "stl", "Settle", /* slow: */ true, trigKit, trigKit.vscp(),
|
||||
// Inner loop statements
|
||||
nullptr,
|
||||
// Prep statements: Compute the current 'stl' triggers
|
||||
util::callVoidFunc(trig.m_funcp),
|
||||
trigKit.newCompCall(),
|
||||
// Work statements: Invoke the 'stl' function
|
||||
util::callVoidFunc(stlFuncp));
|
||||
|
||||
// Add the first iteration trigger to the trigger computation function
|
||||
trig.addFirstIterationTriggerAssignment(stlLoop.firstIterp, firstIterationTrigger);
|
||||
trigKit.addExtraTriggerAssignment(stlLoop.firstIterp, firstIterationTrigger);
|
||||
|
||||
// Add the eval loop to the top function
|
||||
funcp->addStmtsp(stlLoop.stmtsp);
|
||||
|
|
@ -489,11 +455,11 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
|||
// We have some extra trigger denoting external conditions
|
||||
AstVarScope* const dpiExportTriggerVscp = netlistp->dpiExportTriggerp();
|
||||
|
||||
ExtraTriggers extraTriggers;
|
||||
const size_t firstIterationTrigger = extraTriggers.allocate("first iteration");
|
||||
const size_t dpiExportTriggerIndex = dpiExportTriggerVscp
|
||||
? extraTriggers.allocate("DPI export trigger")
|
||||
: std::numeric_limits<unsigned>::max();
|
||||
TriggerKit::ExtraTriggers extraTriggers;
|
||||
const uint32_t firstIterationTrigger = extraTriggers.allocate("first iteration");
|
||||
const uint32_t dpiExportTriggerIndex = dpiExportTriggerVscp
|
||||
? extraTriggers.allocate("DPI export trigger")
|
||||
: std::numeric_limits<uint32_t>::max();
|
||||
const size_t firstVifTriggerIndex = extraTriggers.size();
|
||||
for (const auto& p : virtIfaceTriggers.m_ifaceTriggers) {
|
||||
extraTriggers.allocate("virtual interface: " + p.first->name());
|
||||
|
|
@ -507,34 +473,34 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
|||
|
||||
// Gather the relevant sensitivity expressions and create the trigger kit
|
||||
const auto& senTreeps = getSenTreesUsedBy({&logic});
|
||||
const TriggerKit trig = TriggerKit::create(netlistp, initFuncp, senExprBuilder, senTreeps,
|
||||
"ico", extraTriggers, false);
|
||||
const TriggerKit trigKit = TriggerKit::create(netlistp, initFuncp, senExprBuilder, senTreeps,
|
||||
"ico", extraTriggers, false);
|
||||
|
||||
if (dpiExportTriggerVscp) {
|
||||
trig.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
|
||||
trigKit.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
|
||||
}
|
||||
addVirtIfaceTriggerAssignments(virtIfaceTriggers, firstVifTriggerIndex,
|
||||
firstVifMemberTriggerIndex, trig);
|
||||
firstVifMemberTriggerIndex, trigKit);
|
||||
|
||||
// Remap sensitivities
|
||||
remapSensitivities(logic, trig.m_map);
|
||||
remapSensitivities(logic, trigKit.map());
|
||||
|
||||
// Create the inverse map from trigger ref AstSenTree to original AstSenTree
|
||||
V3Order::TrigToSenMap trigToSen;
|
||||
invertAndMergeSenTreeMap(trigToSen, trig.m_map);
|
||||
invertAndMergeSenTreeMap(trigToSen, trigKit.map());
|
||||
|
||||
// The trigger top level inputs (first iteration)
|
||||
AstSenTree* const inputChanged
|
||||
= createTriggerSenTree(netlistp, trig.m_vscp, firstIterationTrigger);
|
||||
= trigKit.newTriggerSenTree(trigKit.vscp(), {firstIterationTrigger});
|
||||
|
||||
// The DPI Export trigger
|
||||
AstSenTree* const dpiExportTriggered
|
||||
= dpiExportTriggerVscp ? createTriggerSenTree(netlistp, trig.m_vscp, dpiExportTriggerIndex)
|
||||
= dpiExportTriggerVscp ? trigKit.newTriggerSenTree(trigKit.vscp(), {dpiExportTriggerIndex})
|
||||
: nullptr;
|
||||
const auto& vifTriggeredIco
|
||||
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trig.m_vscp);
|
||||
const auto& vifMemberTriggeredIco
|
||||
= virtIfaceTriggers.makeMemberToSensMap(netlistp, firstVifMemberTriggerIndex, trig.m_vscp);
|
||||
= virtIfaceTriggers.makeIfaceToSensMap(trigKit, firstVifTriggerIndex, trigKit.vscp());
|
||||
const auto& vifMemberTriggeredIco = virtIfaceTriggers.makeMemberToSensMap(
|
||||
trigKit, firstVifMemberTriggerIndex, trigKit.vscp());
|
||||
|
||||
// Create and Order the body function
|
||||
AstCFunc* const icoFuncp = V3Order::order(
|
||||
|
|
@ -555,55 +521,38 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
|||
|
||||
// Create the eval loop
|
||||
const EvalLoop icoLoop = createEvalLoop( //
|
||||
netlistp, "ico", "Input combinational", /* slow: */ false, trig.m_vscp, trig.m_dumpp,
|
||||
netlistp, "ico", "Input combinational", /* slow: */ false, trigKit, trigKit.vscp(),
|
||||
// Inner loop statements
|
||||
nullptr,
|
||||
// Prep statements: Compute the current 'ico' triggers
|
||||
util::callVoidFunc(trig.m_funcp),
|
||||
trigKit.newCompCall(),
|
||||
// Work statements: Invoke the 'ico' function
|
||||
util::callVoidFunc(icoFuncp));
|
||||
|
||||
// Add the first iteration trigger to the trigger computation function
|
||||
trig.addFirstIterationTriggerAssignment(icoLoop.firstIterp, firstIterationTrigger);
|
||||
trigKit.addExtraTriggerAssignment(icoLoop.firstIterp, firstIterationTrigger);
|
||||
|
||||
return icoLoop.stmtsp;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// Helpers for 'createEval'
|
||||
// EvalKit groups items that have to be passed to createEval() for a given eval region
|
||||
|
||||
AstStmtExpr* createTriggerClearCall(FileLine* const flp, AstVarScope* const vscp) { // Trigger
|
||||
AstVarRef* const refp = new AstVarRef{flp, vscp, VAccess::WRITE};
|
||||
AstCMethodHard* const callp = new AstCMethodHard{flp, refp, VCMethod::TRIGGER_CLEAR};
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
}
|
||||
|
||||
AstStmtExpr* createTriggerSetCall(FileLine* const flp, AstVarScope* const toVscp,
|
||||
AstVarScope* const fromVscp) {
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, toVscp, VAccess::WRITE};
|
||||
AstVarRef* const argp = new AstVarRef{flp, fromVscp, VAccess::READ};
|
||||
AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, VCMethod::TRIGGER_THIS_OR, argp};
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
}
|
||||
|
||||
AstStmtExpr* createTriggerAndNotCall(FileLine* const flp, AstVarScope* const lhsVscp,
|
||||
AstVarScope* const aVscp, AstVarScope* const bVscp) {
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, lhsVscp, VAccess::WRITE};
|
||||
AstVarRef* const opap = new AstVarRef{flp, aVscp, VAccess::READ};
|
||||
AstVarRef* const opbp = new AstVarRef{flp, bVscp, VAccess::READ};
|
||||
opap->addNext(opbp);
|
||||
AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, VCMethod::TRIGGER_AND_NOT, opap};
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
}
|
||||
struct EvalKit final {
|
||||
// The AstVarScope representing the region's trigger vector
|
||||
AstVarScope* const m_vscp = nullptr;
|
||||
// The AstCFunc that evaluates the region's logic
|
||||
AstCFunc* const m_funcp = nullptr;
|
||||
// Is this kit used/required?
|
||||
bool empty() const { return !m_funcp; }
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
// Bolt together parts to create the top level _eval function
|
||||
|
||||
void createEval(AstNetlist* netlistp, //
|
||||
AstNode* icoLoop, //
|
||||
const TriggerKit& trigKit, //
|
||||
const EvalKit& actKit, //
|
||||
AstVarScope* preTrigsp, //
|
||||
const EvalKit& nbaKit, //
|
||||
|
|
@ -619,39 +568,40 @@ void createEval(AstNetlist* netlistp, //
|
|||
AstCCall* const timingResumep = timingKit.createResume(netlistp);
|
||||
|
||||
// Create the active eval loop
|
||||
const EvalLoop actLoop = createEvalLoop( //
|
||||
netlistp, "act", "Active", /* slow: */ false, actKit.m_vscp, actKit.m_dumpp,
|
||||
EvalLoop topLoop = createEvalLoop( //
|
||||
netlistp, "act", "Active", /* slow: */ false, trigKit, actKit.m_vscp,
|
||||
// Inner loop statements
|
||||
nullptr,
|
||||
// Prep statements
|
||||
[&]() {
|
||||
// Compute the current 'act' triggers
|
||||
AstNodeStmt* const stmtsp = util::callVoidFunc(actKit.m_triggerComputep);
|
||||
AstNodeStmt* stmtsp = trigKit.newCompCall();
|
||||
// Commit trigger awaits from the previous iteration
|
||||
if (timingCommitp) stmtsp->addNext(timingCommitp->makeStmt());
|
||||
if (timingCommitp) stmtsp = AstNode::addNext(stmtsp, timingCommitp->makeStmt());
|
||||
// Compute the 'pre' triggers
|
||||
stmtsp = AstNode::addNext(
|
||||
stmtsp, trigKit.newAndNotCall(preTrigsp, actKit.m_vscp, nbaKit.m_vscp));
|
||||
// Latch the 'act' triggers under the 'nba' triggers
|
||||
stmtsp = AstNode::addNext(stmtsp, trigKit.newOrIntoCall(nbaKit.m_vscp, actKit.m_vscp));
|
||||
//
|
||||
return stmtsp;
|
||||
}(),
|
||||
// Work statements
|
||||
[&]() {
|
||||
// Compute the 'pre' triggers
|
||||
AstNodeStmt* const workp
|
||||
= createTriggerAndNotCall(flp, preTrigsp, actKit.m_vscp, nbaKit.m_vscp);
|
||||
// Latch the 'act' triggers under the 'nba' triggers
|
||||
workp->addNext(createTriggerSetCall(flp, nbaKit.m_vscp, actKit.m_vscp));
|
||||
AstNodeStmt* workp = nullptr;
|
||||
// Resume triggered timing schedulers
|
||||
if (timingResumep) workp->addNext(timingResumep->makeStmt());
|
||||
if (timingResumep) workp = timingResumep->makeStmt();
|
||||
// Invoke the 'act' function
|
||||
workp->addNext(util::callVoidFunc(actKit.m_funcp));
|
||||
workp = AstNode::addNext(workp, util::callVoidFunc(actKit.m_funcp));
|
||||
//
|
||||
return workp;
|
||||
}());
|
||||
|
||||
// Create the NBA eval loop, which is the default top level loop.
|
||||
EvalLoop topLoop = createEvalLoop( //
|
||||
netlistp, "nba", "NBA", /* slow: */ false, nbaKit.m_vscp, nbaKit.m_dumpp,
|
||||
topLoop = createEvalLoop( //
|
||||
netlistp, "nba", "NBA", /* slow: */ false, trigKit, nbaKit.m_vscp,
|
||||
// Inner loop statements
|
||||
actLoop.stmtsp,
|
||||
topLoop.stmtsp,
|
||||
// Prep statements
|
||||
nullptr,
|
||||
// Work statements
|
||||
|
|
@ -659,14 +609,14 @@ void createEval(AstNetlist* netlistp, //
|
|||
AstNodeStmt* workp = nullptr;
|
||||
// Latch the 'nba' trigger flags under the following region's trigger flags
|
||||
if (!obsKit.empty()) {
|
||||
workp = createTriggerSetCall(flp, obsKit.m_vscp, nbaKit.m_vscp);
|
||||
workp = trigKit.newOrIntoCall(obsKit.m_vscp, nbaKit.m_vscp);
|
||||
} else if (!reactKit.empty()) {
|
||||
workp = createTriggerSetCall(flp, reactKit.m_vscp, nbaKit.m_vscp);
|
||||
workp = trigKit.newOrIntoCall(reactKit.m_vscp, nbaKit.m_vscp);
|
||||
}
|
||||
// Invoke the 'nba' function
|
||||
workp = AstNode::addNext(workp, util::callVoidFunc(nbaKit.m_funcp));
|
||||
// Clear the 'nba' triggers
|
||||
workp->addNext(createTriggerClearCall(flp, nbaKit.m_vscp));
|
||||
workp = AstNode::addNext(workp, trigKit.newClearCall(nbaKit.m_vscp));
|
||||
//
|
||||
return workp;
|
||||
}(),
|
||||
|
|
@ -694,7 +644,7 @@ void createEval(AstNetlist* netlistp, //
|
|||
if (!obsKit.empty()) {
|
||||
// Create the Observed eval loop, which becomes the top level loop.
|
||||
topLoop = createEvalLoop( //
|
||||
netlistp, "obs", "Observed", /* slow: */ false, obsKit.m_vscp, obsKit.m_dumpp,
|
||||
netlistp, "obs", "Observed", /* slow: */ false, trigKit, obsKit.m_vscp,
|
||||
// Inner loop statements
|
||||
topLoop.stmtsp,
|
||||
// Prep statements
|
||||
|
|
@ -704,12 +654,12 @@ void createEval(AstNetlist* netlistp, //
|
|||
AstNodeStmt* workp = nullptr;
|
||||
// Latch the Observed trigger flags under the Reactive trigger flags
|
||||
if (!reactKit.empty()) {
|
||||
workp = createTriggerSetCall(flp, reactKit.m_vscp, obsKit.m_vscp);
|
||||
workp = trigKit.newOrIntoCall(reactKit.m_vscp, obsKit.m_vscp);
|
||||
}
|
||||
// Invoke the 'obs' function
|
||||
workp = AstNode::addNext(workp, util::callVoidFunc(obsKit.m_funcp));
|
||||
// Clear the 'obs' triggers
|
||||
workp->addNext(createTriggerClearCall(flp, obsKit.m_vscp));
|
||||
workp = AstNode::addNext(workp, trigKit.newClearCall(obsKit.m_vscp));
|
||||
//
|
||||
return workp;
|
||||
}());
|
||||
|
|
@ -718,7 +668,7 @@ void createEval(AstNetlist* netlistp, //
|
|||
if (!reactKit.empty()) {
|
||||
// Create the Reactive eval loop, which becomes the top level loop.
|
||||
topLoop = createEvalLoop( //
|
||||
netlistp, "react", "Reactive", /* slow: */ false, reactKit.m_vscp, reactKit.m_dumpp,
|
||||
netlistp, "react", "Reactive", /* slow: */ false, trigKit, reactKit.m_vscp,
|
||||
// Inner loop statements
|
||||
topLoop.stmtsp,
|
||||
// Prep statements
|
||||
|
|
@ -726,9 +676,9 @@ void createEval(AstNetlist* netlistp, //
|
|||
// Work statements
|
||||
[&]() {
|
||||
// Invoke the 'react' function
|
||||
AstNodeStmt* const workp = util::callVoidFunc(reactKit.m_funcp);
|
||||
AstNodeStmt* workp = util::callVoidFunc(reactKit.m_funcp);
|
||||
// Clear the 'react' triggers
|
||||
workp->addNext(createTriggerClearCall(flp, reactKit.m_vscp));
|
||||
workp = AstNode::addNext(workp, trigKit.newClearCall(reactKit.m_vscp));
|
||||
return workp;
|
||||
}());
|
||||
}
|
||||
|
|
@ -757,22 +707,22 @@ void createEval(AstNetlist* netlistp, //
|
|||
// Helper that builds virtual interface trigger sentrees
|
||||
|
||||
VirtIfaceTriggers::IfaceSensMap
|
||||
VirtIfaceTriggers::makeIfaceToSensMap(AstNetlist* const netlistp, size_t vifTriggerIndex,
|
||||
VirtIfaceTriggers::makeIfaceToSensMap(const TriggerKit& trigKit, uint32_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const {
|
||||
std::map<const AstIface*, AstSenTree*> map;
|
||||
for (const auto& p : m_ifaceTriggers) {
|
||||
map.emplace(p.first, createTriggerSenTree(netlistp, trigVscp, vifTriggerIndex));
|
||||
map.emplace(p.first, trigKit.newTriggerSenTree(trigVscp, {vifTriggerIndex}));
|
||||
++vifTriggerIndex;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
VirtIfaceTriggers::IfaceMemberSensMap
|
||||
VirtIfaceTriggers::makeMemberToSensMap(AstNetlist* const netlistp, size_t vifTriggerIndex,
|
||||
VirtIfaceTriggers::makeMemberToSensMap(const TriggerKit& trigKit, uint32_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const {
|
||||
IfaceMemberSensMap map;
|
||||
for (const auto& p : m_memberTriggers) {
|
||||
map.emplace(p.first, createTriggerSenTree(netlistp, trigVscp, vifTriggerIndex));
|
||||
map.emplace(p.first, trigKit.newTriggerSenTree(trigVscp, {vifTriggerIndex}));
|
||||
++vifTriggerIndex;
|
||||
}
|
||||
return map;
|
||||
|
|
@ -871,15 +821,15 @@ void schedule(AstNetlist* netlistp) {
|
|||
netlistp->dpiExportTriggerp(nullptr); // Finished with this here
|
||||
|
||||
// We may have an extra trigger for variable updated in DPI exports
|
||||
ExtraTriggers extraTriggers;
|
||||
const size_t dpiExportTriggerIndex = dpiExportTriggerVscp
|
||||
? extraTriggers.allocate("DPI export trigger")
|
||||
: std::numeric_limits<unsigned>::max();
|
||||
const size_t firstVifTriggerIndex = extraTriggers.size();
|
||||
TriggerKit::ExtraTriggers extraTriggers;
|
||||
const uint32_t dpiExportTriggerIndex = dpiExportTriggerVscp
|
||||
? extraTriggers.allocate("DPI export trigger")
|
||||
: std::numeric_limits<uint32_t>::max();
|
||||
const uint32_t firstVifTriggerIndex = extraTriggers.size();
|
||||
for (const auto& p : virtIfaceTriggers.m_ifaceTriggers) {
|
||||
extraTriggers.allocate("virtual interface: " + p.first->name());
|
||||
}
|
||||
const size_t firstVifMemberTriggerIndex = extraTriggers.size();
|
||||
const uint32_t firstVifMemberTriggerIndex = extraTriggers.size();
|
||||
for (const auto& p : virtIfaceTriggers.m_memberTriggers) {
|
||||
const auto& item = p.first;
|
||||
extraTriggers.allocate("virtual interface member: " + item.m_ifacep->name() + "."
|
||||
|
|
@ -892,20 +842,20 @@ void schedule(AstNetlist* netlistp) {
|
|||
&logicRegions.m_obs, //
|
||||
&logicRegions.m_react, //
|
||||
&timingKit.m_lbs});
|
||||
const TriggerKit actTrig = TriggerKit::create(netlistp, staticp, senExprBuilder, senTreeps,
|
||||
const TriggerKit trigKit = TriggerKit::create(netlistp, staticp, senExprBuilder, senTreeps,
|
||||
"act", extraTriggers, false);
|
||||
|
||||
// Add post updates from the timing kit
|
||||
if (timingKit.m_postUpdates) actTrig.m_funcp->addStmtsp(timingKit.m_postUpdates);
|
||||
if (timingKit.m_postUpdates) trigKit.compp()->addStmtsp(timingKit.m_postUpdates);
|
||||
|
||||
if (dpiExportTriggerVscp) {
|
||||
actTrig.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
|
||||
trigKit.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
|
||||
}
|
||||
addVirtIfaceTriggerAssignments(virtIfaceTriggers, firstVifTriggerIndex,
|
||||
firstVifMemberTriggerIndex, actTrig);
|
||||
firstVifMemberTriggerIndex, trigKit);
|
||||
|
||||
AstVarScope* const actTrigVscp = actTrig.m_vscp;
|
||||
AstVarScope* const preTrigVscp = scopeTopp->createTempLike("__VpreTriggered", actTrigVscp);
|
||||
AstVarScope* const actTrigVscp = trigKit.vscp();
|
||||
AstVarScope* const preTrigVscp = trigKit.newTrigVec("pre");
|
||||
|
||||
const auto cloneMapWithNewTriggerReferences
|
||||
= [=](const std::unordered_map<const AstSenTree*, AstSenTree*>& map, AstVarScope* vscp) {
|
||||
|
|
@ -925,7 +875,7 @@ void schedule(AstNetlist* netlistp) {
|
|||
return newMap;
|
||||
};
|
||||
|
||||
const auto& actTrigMap = actTrig.m_map;
|
||||
const auto& actTrigMap = trigKit.map();
|
||||
const auto preTrigMap = cloneMapWithNewTriggerReferences(actTrigMap, preTrigVscp);
|
||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-triggers");
|
||||
|
||||
|
|
@ -950,14 +900,13 @@ void schedule(AstNetlist* netlistp) {
|
|||
|
||||
// The DPI Export trigger AstSenTree
|
||||
AstSenTree* const dpiExportTriggeredAct
|
||||
= dpiExportTriggerVscp
|
||||
? createTriggerSenTree(netlistp, actTrig.m_vscp, dpiExportTriggerIndex)
|
||||
: nullptr;
|
||||
= dpiExportTriggerVscp ? trigKit.newTriggerSenTree(trigKit.vscp(), {dpiExportTriggerIndex})
|
||||
: nullptr;
|
||||
|
||||
const auto& vifTriggeredAct
|
||||
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, actTrig.m_vscp);
|
||||
= virtIfaceTriggers.makeIfaceToSensMap(trigKit, firstVifTriggerIndex, trigKit.vscp());
|
||||
const auto& vifMemberTriggeredAct = virtIfaceTriggers.makeMemberToSensMap(
|
||||
netlistp, firstVifMemberTriggerIndex, actTrig.m_vscp);
|
||||
trigKit, firstVifMemberTriggerIndex, trigKit.vscp());
|
||||
|
||||
AstCFunc* const actFuncp = V3Order::order(
|
||||
netlistp, {&logicRegions.m_pre, &logicRegions.m_act, &logicReplicas.m_act}, trigToSenAct,
|
||||
|
|
@ -974,14 +923,13 @@ void schedule(AstNetlist* netlistp) {
|
|||
util::splitCheck(actFuncp);
|
||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-act");
|
||||
|
||||
const EvalKit& actKit = {actTrig.m_vscp, actTrig.m_funcp, actTrig.m_dumpp, actFuncp};
|
||||
const EvalKit actKit{trigKit.vscp(), actFuncp};
|
||||
|
||||
// Orders a region's logic and creates the region eval function
|
||||
const auto order = [&](const std::string& name,
|
||||
const std::vector<V3Sched::LogicByScope*>& logic) -> EvalKit {
|
||||
UINFO(2, "Scheduling " << name << " #logic = " << logic.size());
|
||||
AstVarScope* const trigVscp
|
||||
= scopeTopp->createTempLike("__V" + name + "Triggered", actTrigVscp);
|
||||
AstVarScope* const trigVscp = trigKit.newTrigVec(name);
|
||||
const auto trigMap = cloneMapWithNewTriggerReferences(actTrigMap, trigVscp);
|
||||
// Remap sensitivities of the input logic to the triggers
|
||||
for (LogicByScope* lbs : logic) remapSensitivities(*lbs, trigMap);
|
||||
|
|
@ -991,13 +939,12 @@ void schedule(AstNetlist* netlistp) {
|
|||
invertAndMergeSenTreeMap(trigToSen, trigMap);
|
||||
|
||||
AstSenTree* const dpiExportTriggered
|
||||
= dpiExportTriggerVscp
|
||||
? createTriggerSenTree(netlistp, trigVscp, dpiExportTriggerIndex)
|
||||
: nullptr;
|
||||
= dpiExportTriggerVscp ? trigKit.newTriggerSenTree(trigVscp, {dpiExportTriggerIndex})
|
||||
: nullptr;
|
||||
const auto& vifTriggered
|
||||
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trigVscp);
|
||||
const auto& vifMemberTriggered = virtIfaceTriggers.makeMemberToSensMap(
|
||||
netlistp, firstVifMemberTriggerIndex, trigVscp);
|
||||
= virtIfaceTriggers.makeIfaceToSensMap(trigKit, firstVifTriggerIndex, trigVscp);
|
||||
const auto& vifMemberTriggered
|
||||
= virtIfaceTriggers.makeMemberToSensMap(trigKit, firstVifMemberTriggerIndex, trigVscp);
|
||||
|
||||
const auto& timingDomains = timingKit.remapDomains(trigMap);
|
||||
AstCFunc* const funcp = V3Order::order(
|
||||
|
|
@ -1013,27 +960,11 @@ void schedule(AstNetlist* netlistp) {
|
|||
}
|
||||
});
|
||||
|
||||
// Create the trigger dumping function, which is the same as act trigger
|
||||
// dumping function, but referencing this region's trigger vector.
|
||||
AstCFunc* const dumpp = actTrig.m_dumpp->cloneTree(false);
|
||||
actTrig.m_dumpp->addNextHere(dumpp);
|
||||
dumpp->name("_dump_triggers__" + name);
|
||||
dumpp->foreach([&](AstVarRef* refp) {
|
||||
UASSERT_OBJ(refp->access().isReadOnly(), refp, "Should only read state");
|
||||
if (refp->varScopep() == actTrig.m_vscp) {
|
||||
refp->replaceWith(new AstVarRef{refp->fileline(), trigVscp, VAccess::READ});
|
||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||
}
|
||||
});
|
||||
dumpp->foreach([&](AstText* textp) { //
|
||||
textp->text(VString::replaceWord(textp->text(), "act", name));
|
||||
});
|
||||
|
||||
return {trigVscp, nullptr, dumpp, funcp};
|
||||
return {trigVscp, funcp};
|
||||
};
|
||||
|
||||
// Step 10: Create the 'nba' region evaluation function
|
||||
const EvalKit& nbaKit = order("nba", {&logicRegions.m_nba, &logicReplicas.m_nba});
|
||||
const EvalKit nbaKit = order("nba", {&logicRegions.m_nba, &logicReplicas.m_nba});
|
||||
util::splitCheck(nbaKit.m_funcp);
|
||||
netlistp->evalNbap(nbaKit.m_funcp); // Remember for V3LifePost
|
||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-nba");
|
||||
|
|
@ -1050,18 +981,18 @@ void schedule(AstNetlist* netlistp) {
|
|||
};
|
||||
|
||||
// Step 11: Create the 'obs' region evaluation function
|
||||
const EvalKit& obsKit = orderIfNonEmpty("obs", {&logicRegions.m_obs, &logicReplicas.m_obs});
|
||||
const EvalKit obsKit = orderIfNonEmpty("obs", {&logicRegions.m_obs, &logicReplicas.m_obs});
|
||||
|
||||
// Step 12: Create the 're' region evaluation function
|
||||
const EvalKit& reactKit
|
||||
const EvalKit reactKit
|
||||
= orderIfNonEmpty("react", {&logicRegions.m_react, &logicReplicas.m_react});
|
||||
|
||||
// Step 13: Create the 'postponed' region evaluation function
|
||||
auto* const postponedFuncp = createPostponed(netlistp, logicClasses);
|
||||
|
||||
// Step 14: Bolt it all together to create the '_eval' function
|
||||
createEval(netlistp, icoLoopp, actKit, preTrigVscp, nbaKit, obsKit, reactKit, postponedFuncp,
|
||||
timingKit);
|
||||
createEval(netlistp, icoLoopp, trigKit, actKit, preTrigVscp, nbaKit, obsKit, reactKit,
|
||||
postponedFuncp, timingKit);
|
||||
|
||||
// Haven't split static initializer yet
|
||||
util::splitCheck(staticp);
|
||||
|
|
|
|||
127
src/V3Sched.h
127
src/V3Sched.h
|
|
@ -128,48 +128,99 @@ struct LogicReplicas final {
|
|||
LogicReplicas& operator=(LogicReplicas&&) = default;
|
||||
};
|
||||
|
||||
// Utility for extra trigger allocation
|
||||
class ExtraTriggers final {
|
||||
std::vector<string> m_descriptions; // Human readable description of extra triggers
|
||||
// A TriggerKit holds all the components related to a trigger vector
|
||||
class TriggerKit final {
|
||||
// Triggers are storead as an UnpackedArray with a fixed word size
|
||||
static constexpr uint32_t WORD_SIZE_LOG2 = 6; // 64-bits / VL_QUADSIZE
|
||||
static constexpr uint32_t WORD_SIZE = 1 << WORD_SIZE_LOG2;
|
||||
|
||||
const std::string m_name; // TriggerKit name
|
||||
const bool m_slow; // TriggerKit is for schedulign 'slow' code
|
||||
const uint32_t m_nWords; // Number of word in trigger vector
|
||||
|
||||
// Data type of a single trigger word
|
||||
AstNodeDType* m_wordDTypep = nullptr;
|
||||
// Data type of a trigger vector
|
||||
AstNodeDType* m_trigDTypep = nullptr;
|
||||
// The AstVarScope representing the trigger vector
|
||||
AstVarScope* m_vscp = nullptr;
|
||||
// The AstCFunc that computes the current active triggers
|
||||
AstCFunc* m_compp = nullptr;
|
||||
// The AstCFunc that dumps the current active triggers
|
||||
AstCFunc* m_dumpp = nullptr;
|
||||
// The AstCFunc testing if a trigger vector has any bits set - create lazily
|
||||
mutable AstCFunc* m_anySetp = nullptr;
|
||||
// The AstCFunc setting a tigger vector to (_ & ~_) of 2 other trigger vectors - create lazily
|
||||
mutable AstCFunc* m_andNotp = nullptr;
|
||||
// The AstCFunc setting bits in a trigger vector that are set in another - create lazily
|
||||
mutable AstCFunc* m_orIntop = nullptr;
|
||||
// The AstCFunc setting a trigger vector to all zeroes - create lazily
|
||||
mutable AstCFunc* m_clearp = nullptr;
|
||||
|
||||
// The map from input sensitivity list to trigger sensitivity list
|
||||
std::unordered_map<const AstSenTree*, AstSenTree*> m_map;
|
||||
|
||||
// Methods to lazy construct functions processing trigger vectors
|
||||
AstCFunc* createAndNotFunc() const;
|
||||
AstCFunc* createAnySetFunc() const;
|
||||
AstCFunc* createClearFunc() const;
|
||||
AstCFunc* createOrIntoFunc() const;
|
||||
|
||||
TriggerKit(const std::string& name, bool slow, uint32_t nWords);
|
||||
VL_UNCOPYABLE(TriggerKit);
|
||||
TriggerKit& operator=(TriggerKit&&) = delete;
|
||||
|
||||
public:
|
||||
ExtraTriggers() = default;
|
||||
~ExtraTriggers() = default;
|
||||
// Move constructible
|
||||
TriggerKit(TriggerKit&&) = default;
|
||||
~TriggerKit() = default;
|
||||
|
||||
size_t allocate(const string& description) {
|
||||
m_descriptions.push_back(description);
|
||||
return m_descriptions.size() - 1;
|
||||
}
|
||||
size_t size() const { return m_descriptions.size(); }
|
||||
const string& description(size_t index) const { return m_descriptions[index]; }
|
||||
};
|
||||
// Utility for extra trigger allocation
|
||||
class ExtraTriggers final {
|
||||
friend class TriggerKit;
|
||||
std::vector<string> m_descriptions; // Human readable description of extra triggers
|
||||
|
||||
// A TriggerKit holds all the components related to a TRIGGERVEC variable
|
||||
struct TriggerKit final {
|
||||
// The TRIGGERVEC AstVarScope representing these trigger flags
|
||||
AstVarScope* const m_vscp;
|
||||
// The AstCFunc that computes the current active triggers
|
||||
AstCFunc* const m_funcp;
|
||||
// The AstCFunc that dumps the current active triggers
|
||||
AstCFunc* const m_dumpp;
|
||||
// The map from input sensitivity list to trigger sensitivity list
|
||||
const std::unordered_map<const AstSenTree*, AstSenTree*> m_map;
|
||||
public:
|
||||
ExtraTriggers() = default;
|
||||
~ExtraTriggers() = default;
|
||||
|
||||
// No VL_UNCOPYABLE(TriggerKit) as causes C++20 errors on MSVC
|
||||
|
||||
// Assigns the given index trigger to fire when the given variable is zero
|
||||
void addFirstIterationTriggerAssignment(AstVarScope* flagp, uint32_t index) const;
|
||||
// Set then clear an extra trigger
|
||||
void addExtraTriggerAssignment(AstVarScope* extraTriggerVscp, uint32_t index) const;
|
||||
uint32_t allocate(const string& description) {
|
||||
m_descriptions.push_back(description);
|
||||
return m_descriptions.size() - 1;
|
||||
}
|
||||
uint32_t size() const { return m_descriptions.size(); }
|
||||
};
|
||||
|
||||
// Create a TriggerKit for the given AstSenTree vector
|
||||
static const TriggerKit create(AstNetlist* netlistp, //
|
||||
AstCFunc* const initFuncp, //
|
||||
SenExprBuilder& senExprBuilder, //
|
||||
const std::vector<const AstSenTree*>& senTreeps, //
|
||||
const string& name, //
|
||||
const ExtraTriggers& extraTriggers, //
|
||||
bool slow);
|
||||
static TriggerKit create(AstNetlist* netlistp, //
|
||||
AstCFunc* const initFuncp, //
|
||||
SenExprBuilder& senExprBuilder, //
|
||||
const std::vector<const AstSenTree*>& senTreeps, //
|
||||
const string& name, //
|
||||
const ExtraTriggers& extraTriggers, //
|
||||
bool slow);
|
||||
|
||||
// ACCESSORS
|
||||
AstVarScope* vscp() const { return m_vscp; }
|
||||
AstCFunc* compp() const { return m_compp; }
|
||||
const std::unordered_map<const AstSenTree*, AstSenTree*>& map() const { return m_map; }
|
||||
|
||||
// Helpers for code generation - lazy construct relevant functions
|
||||
AstNodeStmt* newAndNotCall(AstVarScope* op, AstVarScope* ap, AstVarScope* bp) const;
|
||||
AstNodeExpr* newAnySetCall(AstVarScope* vscp) const;
|
||||
AstNodeStmt* newClearCall(AstVarScope* vscp) const;
|
||||
AstNodeStmt* newOrIntoCall(AstVarScope* op, AstVarScope* ip) const;
|
||||
// Helpers for code generation
|
||||
AstNodeStmt* newCompCall() const;
|
||||
AstNodeStmt* newDumpCall(AstVarScope* vscp, const std::string& tag, bool debugOnly) const;
|
||||
// Create a new trigger vector - might return nullptr if there are no triggers
|
||||
AstVarScope* newTrigVec(const std::string& name) const;
|
||||
|
||||
// Create an AstSenTree that is sensitive to the given trigger indices
|
||||
AstSenTree* newTriggerSenTree(AstVarScope* vscp, const std::vector<uint32_t>& indices) const;
|
||||
|
||||
// Set then extra trigger bit at 'index' to the value of 'vscp', then set 'vscp' to 0
|
||||
void addExtraTriggerAssignment(AstVarScope* vscp, uint32_t index) const;
|
||||
};
|
||||
|
||||
// Everything needed for combining timing with static scheduling.
|
||||
|
|
@ -238,10 +289,10 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
IfaceMemberSensMap makeMemberToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex,
|
||||
IfaceMemberSensMap makeMemberToSensMap(const TriggerKit& trigKit, uint32_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const;
|
||||
|
||||
IfaceSensMap makeIfaceToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex,
|
||||
IfaceSensMap makeIfaceToSensMap(const TriggerKit& trigKit, uint32_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const;
|
||||
|
||||
VL_UNCOPYABLE(VirtIfaceTriggers);
|
||||
|
|
@ -283,7 +334,7 @@ AstNodeStmt* incrementVar(AstVarScope* vscp);
|
|||
AstNodeStmt* callVoidFunc(AstCFunc* funcp);
|
||||
// Create statement that checks counterp' to see if the eval loop iteration limit is reached
|
||||
AstNodeStmt* checkIterationLimit(AstNetlist* netlistp, const string& name, AstVarScope* counterp,
|
||||
AstCFunc* trigDumpp);
|
||||
AstNodeStmt* dumpCallp);
|
||||
// Create statement that pushed a --prof-exec section
|
||||
AstNodeStmt* profExecSectionPush(FileLine* flp, const string& section);
|
||||
// Create statement that pops a --prof-exec section
|
||||
|
|
|
|||
|
|
@ -32,158 +32,454 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
|
||||
namespace V3Sched {
|
||||
|
||||
void TriggerKit::addFirstIterationTriggerAssignment(AstVarScope* flagp, uint32_t index) const {
|
||||
FileLine* const flp = flagp->fileline();
|
||||
AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
|
||||
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, VCMethod::TRIGGER_SET_BIT};
|
||||
callp->addPinsp(new AstConst{flp, index});
|
||||
callp->addPinsp(new AstVarRef{flp, flagp, VAccess::READ});
|
||||
callp->dtypeSetVoid();
|
||||
m_funcp->stmtsp()->addHereThisAsNext(callp->makeStmt());
|
||||
namespace {
|
||||
|
||||
AstVarScope* newArgument(AstCFunc* funcp, AstNodeDType* dtypep, const std::string& name,
|
||||
VDirection direction) {
|
||||
FileLine* const flp = funcp->fileline();
|
||||
AstScope* const scopep = funcp->scopep();
|
||||
AstVar* const varp = new AstVar{flp, VVarType::BLOCKTEMP, name, dtypep};
|
||||
varp->funcLocal(true);
|
||||
varp->direction(direction);
|
||||
funcp->addArgsp(varp);
|
||||
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
|
||||
scopep->addVarsp(vscp);
|
||||
return vscp;
|
||||
}
|
||||
|
||||
// Utility to set then clear an extra trigger
|
||||
void TriggerKit::addExtraTriggerAssignment(AstVarScope* extraTriggerVscp, uint32_t index) const {
|
||||
FileLine* const flp = extraTriggerVscp->fileline();
|
||||
AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
|
||||
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, VCMethod::TRIGGER_SET_BIT};
|
||||
callp->addPinsp(new AstConst{flp, index});
|
||||
callp->addPinsp(new AstVarRef{flp, extraTriggerVscp, VAccess::READ});
|
||||
callp->dtypeSetVoid();
|
||||
AstNode* const stmtp = callp->makeStmt();
|
||||
stmtp->addNext(new AstAssign{flp, new AstVarRef{flp, extraTriggerVscp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}});
|
||||
m_funcp->stmtsp()->addHereThisAsNext(stmtp);
|
||||
AstVarScope* newLocal(AstCFunc* funcp, AstNodeDType* dtypep, const std::string& name) {
|
||||
FileLine* const flp = funcp->fileline();
|
||||
AstScope* const scopep = funcp->scopep();
|
||||
AstVar* const varp = new AstVar{flp, VVarType::BLOCKTEMP, name, dtypep};
|
||||
varp->funcLocal(true);
|
||||
funcp->addVarsp(varp);
|
||||
AstVarScope* const vscp = new AstVarScope{flp, scopep, varp};
|
||||
scopep->addVarsp(vscp);
|
||||
return vscp;
|
||||
}
|
||||
|
||||
// Create a TRIGGERVEC and the related TriggerKit for the given AstSenTree vector
|
||||
const TriggerKit TriggerKit::create(AstNetlist* netlistp, //
|
||||
AstCFunc* const initFuncp, //
|
||||
SenExprBuilder& senExprBuilder, //
|
||||
const std::vector<const AstSenTree*>& senTreeps, //
|
||||
const string& name, //
|
||||
const ExtraTriggers& extraTriggers, //
|
||||
bool slow) {
|
||||
} // namespace
|
||||
|
||||
AstCFunc* TriggerKit::createAndNotFunc() const {
|
||||
AstNetlist* const netlistp = v3Global.rootp();
|
||||
FileLine* const flp = netlistp->topScopep()->fileline();
|
||||
|
||||
// Create the function
|
||||
AstCFunc* const funcp = util::makeSubFunction(netlistp, "_trigger_andNot__" + m_name, m_slow);
|
||||
funcp->isStatic(true);
|
||||
|
||||
// Add arguments
|
||||
AstVarScope* const oVscp = newArgument(funcp, m_trigDTypep, "out", VDirection::OUTPUT);
|
||||
AstVarScope* const aVscp = newArgument(funcp, m_trigDTypep, "inA", VDirection::CONSTREF);
|
||||
AstVarScope* const bVscp = newArgument(funcp, m_trigDTypep, "inB", VDirection::CONSTREF);
|
||||
|
||||
// Add loop counter variable
|
||||
AstVarScope* const nVscp
|
||||
= newLocal(funcp, netlistp->findBitDType(32, 32, VSigning::UNSIGNED), "n");
|
||||
nVscp->varp()->noReset(true);
|
||||
|
||||
// Creates read/write reference
|
||||
const auto rd = [flp](AstVarScope* vp) { return new AstVarRef{flp, vp, VAccess::READ}; };
|
||||
const auto wr = [flp](AstVarScope* vp) { return new AstVarRef{flp, vp, VAccess::WRITE}; };
|
||||
|
||||
// Function body
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
funcp->addStmtsp(util::setVar(nVscp, 0));
|
||||
funcp->addStmtsp(loopp);
|
||||
|
||||
// Loop body
|
||||
AstNodeExpr* const lhsp = new AstArraySel{flp, wr(oVscp), rd(nVscp)};
|
||||
AstNodeExpr* const aWordp = new AstArraySel{flp, rd(aVscp), rd(nVscp)};
|
||||
AstNodeExpr* const bWordp = new AstArraySel{flp, rd(bVscp), rd(nVscp)};
|
||||
AstNodeExpr* const rhsp = new AstAnd{flp, aWordp, new AstNot{flp, bWordp}};
|
||||
AstNodeExpr* const limp = new AstConst{flp, AstConst::WidthedValue{}, 32, m_nWords};
|
||||
loopp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
|
||||
loopp->addStmtsp(util::incrementVar(nVscp));
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLt{flp, rd(nVscp), limp}});
|
||||
|
||||
// Done
|
||||
return funcp;
|
||||
}
|
||||
AstCFunc* TriggerKit::createAnySetFunc() const {
|
||||
AstNetlist* const netlistp = v3Global.rootp();
|
||||
FileLine* const flp = netlistp->topScopep()->fileline();
|
||||
|
||||
// Create function
|
||||
AstCFunc* const funcp = util::makeSubFunction(netlistp, "_trigger_anySet__" + m_name, m_slow);
|
||||
funcp->isStatic(true);
|
||||
funcp->rtnType("bool");
|
||||
|
||||
// Add argument
|
||||
AstVarScope* const iVscp = newArgument(funcp, m_trigDTypep, "in", VDirection::CONSTREF);
|
||||
|
||||
// Add loop counter variable
|
||||
AstVarScope* const nVscp
|
||||
= newLocal(funcp, netlistp->findBitDType(32, 32, VSigning::UNSIGNED), "n");
|
||||
nVscp->varp()->noReset(true);
|
||||
|
||||
// Creates read reference
|
||||
const auto rd = [flp](AstVarScope* vp) { return new AstVarRef{flp, vp, VAccess::READ}; };
|
||||
|
||||
// Function body
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
funcp->addStmtsp(util::setVar(nVscp, 0));
|
||||
funcp->addStmtsp(loopp);
|
||||
funcp->addStmtsp(new AstCReturn{flp, new AstConst{flp, AstConst::BitFalse{}}});
|
||||
|
||||
// Loop body
|
||||
AstNodeExpr* const condp = new AstArraySel{flp, rd(iVscp), rd(nVscp)};
|
||||
AstNodeStmt* const thenp = new AstCReturn{flp, new AstConst{flp, AstConst::BitTrue{}}};
|
||||
AstNodeExpr* const limp = new AstConst{flp, AstConst::WidthedValue{}, 32, m_nWords};
|
||||
loopp->addStmtsp(new AstIf{flp, condp, thenp});
|
||||
loopp->addStmtsp(util::incrementVar(nVscp));
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLt{flp, rd(nVscp), limp}});
|
||||
|
||||
// Done
|
||||
return funcp;
|
||||
}
|
||||
AstCFunc* TriggerKit::createClearFunc() const {
|
||||
AstNetlist* const netlistp = v3Global.rootp();
|
||||
FileLine* const flp = netlistp->topScopep()->fileline();
|
||||
|
||||
// Create function
|
||||
AstCFunc* const funcp = util::makeSubFunction(netlistp, "_trigger_clear__" + m_name, m_slow);
|
||||
funcp->isStatic(true);
|
||||
|
||||
// Add arguments
|
||||
AstVarScope* const oVscp = newArgument(funcp, m_trigDTypep, "out", VDirection::OUTPUT);
|
||||
|
||||
// Add loop counter variable
|
||||
AstVarScope* const nVscp
|
||||
= newLocal(funcp, netlistp->findBitDType(32, 32, VSigning::UNSIGNED), "n");
|
||||
nVscp->varp()->noReset(true);
|
||||
|
||||
// Creates read/write reference
|
||||
const auto rd = [flp](AstVarScope* vp) { return new AstVarRef{flp, vp, VAccess::READ}; };
|
||||
const auto wr = [flp](AstVarScope* vp) { return new AstVarRef{flp, vp, VAccess::WRITE}; };
|
||||
|
||||
// Function body
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
funcp->addStmtsp(util::setVar(nVscp, 0));
|
||||
funcp->addStmtsp(loopp);
|
||||
|
||||
// Loop body
|
||||
AstNodeExpr* const lhsp = new AstArraySel{flp, wr(oVscp), rd(nVscp)};
|
||||
AstNodeExpr* const rhsp = new AstConst{flp, AstConst::DTyped{}, m_wordDTypep};
|
||||
AstNodeExpr* const limp = new AstConst{flp, AstConst::WidthedValue{}, 32, m_nWords};
|
||||
loopp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
|
||||
loopp->addStmtsp(util::incrementVar(nVscp));
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLt{flp, rd(nVscp), limp}});
|
||||
|
||||
// Done
|
||||
return funcp;
|
||||
}
|
||||
AstCFunc* TriggerKit::createOrIntoFunc() const {
|
||||
AstNetlist* const netlistp = v3Global.rootp();
|
||||
FileLine* const flp = netlistp->topScopep()->fileline();
|
||||
|
||||
// Create function
|
||||
AstCFunc* const funcp = util::makeSubFunction(netlistp, "_trigger_orInto__" + m_name, m_slow);
|
||||
funcp->isStatic(true);
|
||||
|
||||
// Add arguments
|
||||
AstVarScope* const oVscp = newArgument(funcp, m_trigDTypep, "out", VDirection::INOUT);
|
||||
AstVarScope* const iVscp = newArgument(funcp, m_trigDTypep, "in", VDirection::CONSTREF);
|
||||
|
||||
// Add loop counter variable
|
||||
AstVarScope* const nVscp
|
||||
= newLocal(funcp, netlistp->findBitDType(32, 32, VSigning::UNSIGNED), "n");
|
||||
nVscp->varp()->noReset(true);
|
||||
|
||||
// Creates read/write reference
|
||||
const auto rd = [flp](AstVarScope* vp) { return new AstVarRef{flp, vp, VAccess::READ}; };
|
||||
const auto wr = [flp](AstVarScope* vp) { return new AstVarRef{flp, vp, VAccess::WRITE}; };
|
||||
|
||||
// Function body
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
funcp->addStmtsp(util::setVar(nVscp, 0));
|
||||
funcp->addStmtsp(loopp);
|
||||
|
||||
// Loop body
|
||||
AstNodeExpr* const lhsp = new AstArraySel{flp, wr(oVscp), rd(nVscp)};
|
||||
AstNodeExpr* const oWordp = new AstArraySel{flp, rd(oVscp), rd(nVscp)};
|
||||
AstNodeExpr* const iWordp = new AstArraySel{flp, rd(iVscp), rd(nVscp)};
|
||||
AstNodeExpr* const rhsp = new AstOr{flp, oWordp, iWordp};
|
||||
AstNodeExpr* const limp = new AstConst{flp, AstConst::WidthedValue{}, 32, m_nWords};
|
||||
loopp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
|
||||
loopp->addStmtsp(util::incrementVar(nVscp));
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, new AstLt{flp, rd(nVscp), limp}});
|
||||
|
||||
// Done
|
||||
return funcp;
|
||||
}
|
||||
|
||||
AstNodeStmt* TriggerKit::newAndNotCall(AstVarScope* const oVscp, //
|
||||
AstVarScope* const aVscp, //
|
||||
AstVarScope* const bVscp) const {
|
||||
if (!m_nWords) return nullptr;
|
||||
|
||||
if (!m_andNotp) m_andNotp = createAndNotFunc();
|
||||
FileLine* const flp = v3Global.rootp()->topScopep()->fileline();
|
||||
AstCCall* const callp = new AstCCall{flp, m_andNotp};
|
||||
callp->addArgsp(new AstVarRef{flp, oVscp, VAccess::WRITE});
|
||||
callp->addArgsp(new AstVarRef{flp, aVscp, VAccess::READ});
|
||||
callp->addArgsp(new AstVarRef{flp, bVscp, VAccess::READ});
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
}
|
||||
AstNodeExpr* TriggerKit::newAnySetCall(AstVarScope* const vscp) const {
|
||||
FileLine* const flp = v3Global.rootp()->topScopep()->fileline();
|
||||
if (!m_nWords) return new AstConst{flp, AstConst::BitFalse{}};
|
||||
|
||||
if (!m_anySetp) m_anySetp = createAnySetFunc();
|
||||
AstCCall* const callp = new AstCCall{flp, m_anySetp};
|
||||
callp->addArgsp(new AstVarRef{flp, vscp, VAccess::WRITE});
|
||||
callp->dtypeSetBit();
|
||||
return callp;
|
||||
}
|
||||
AstNodeStmt* TriggerKit::newClearCall(AstVarScope* const vscp) const {
|
||||
if (!m_nWords) return nullptr;
|
||||
if (!m_clearp) m_clearp = createClearFunc();
|
||||
FileLine* const flp = v3Global.rootp()->topScopep()->fileline();
|
||||
AstCCall* const callp = new AstCCall{flp, m_clearp};
|
||||
callp->addArgsp(new AstVarRef{flp, vscp, VAccess::WRITE});
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
}
|
||||
AstNodeStmt* TriggerKit::newOrIntoCall(AstVarScope* const oVscp, AstVarScope* const iVscp) const {
|
||||
if (!m_nWords) return nullptr;
|
||||
if (!m_orIntop) m_orIntop = createOrIntoFunc();
|
||||
FileLine* const flp = v3Global.rootp()->topScopep()->fileline();
|
||||
AstCCall* const callp = new AstCCall{flp, m_orIntop};
|
||||
callp->addArgsp(new AstVarRef{flp, oVscp, VAccess::WRITE});
|
||||
callp->addArgsp(new AstVarRef{flp, iVscp, VAccess::READ});
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
}
|
||||
|
||||
AstNodeStmt* TriggerKit::newCompCall() const {
|
||||
if (!m_nWords) return nullptr;
|
||||
return util::callVoidFunc(m_compp);
|
||||
}
|
||||
|
||||
AstNodeStmt* TriggerKit::newDumpCall(AstVarScope* const vscp, const std::string& tag,
|
||||
bool debugOnly) const {
|
||||
FileLine* const flp = v3Global.rootp()->topScopep()->fileline();
|
||||
if (!m_nWords) return new AstComment{flp, "No triggers - dump"};
|
||||
|
||||
AstCCall* const callp = new AstCCall{flp, m_dumpp};
|
||||
callp->addArgsp(new AstVarRef{flp, vscp, VAccess::READ});
|
||||
callp->addArgsp(new AstConst{flp, AstConst::String{}, tag});
|
||||
callp->dtypeSetVoid();
|
||||
AstCStmt* const cstmtp = new AstCStmt{flp};
|
||||
cstmtp->add("#ifdef VL_DEBUG\n");
|
||||
if (debugOnly) {
|
||||
cstmtp->add("if (VL_UNLIKELY(vlSymsp->_vm_contextp__->debug())) {\n");
|
||||
cstmtp->add(callp->makeStmt());
|
||||
cstmtp->add("}\n");
|
||||
} else {
|
||||
cstmtp->add(callp->makeStmt());
|
||||
}
|
||||
cstmtp->add("#endif");
|
||||
return cstmtp;
|
||||
}
|
||||
|
||||
AstVarScope* TriggerKit::newTrigVec(const std::string& name) const {
|
||||
if (!m_trigDTypep) return nullptr;
|
||||
AstScope* const scopep = v3Global.rootp()->topScopep()->scopep();
|
||||
return scopep->createTemp("__V" + name + "Triggered", m_trigDTypep);
|
||||
}
|
||||
|
||||
AstSenTree* TriggerKit::newTriggerSenTree(AstVarScope* const vscp,
|
||||
const std::vector<uint32_t>& indices) const {
|
||||
AstNetlist* const netlistp = v3Global.rootp();
|
||||
AstTopScope* const topScopep = netlistp->topScopep();
|
||||
AstScope* const scopeTopp = topScopep->scopep();
|
||||
FileLine* const flp = scopeTopp->fileline();
|
||||
FileLine* const flp = topScopep->fileline();
|
||||
|
||||
AstSenTree* const senTreep = new AstSenTree{flp, nullptr};
|
||||
topScopep->addSenTreesp(senTreep);
|
||||
for (const uint32_t index : indices) {
|
||||
UASSERT(index <= m_nWords * WORD_SIZE, "Invalid trigger index");
|
||||
const uint32_t wordIndex = index / WORD_SIZE;
|
||||
const uint32_t bitIndex = index % WORD_SIZE;
|
||||
AstVarRef* const refp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
AstNodeExpr* const aselp = new AstArraySel{flp, refp, static_cast<int>(wordIndex)};
|
||||
// Use a mask & _ to extract the bit, V3Const can optimize this to combine terms
|
||||
AstConst* const maskp
|
||||
= new AstConst{flp, AstConst::WidthedValue{}, static_cast<int>(WORD_SIZE), 0};
|
||||
maskp->num().setBit(bitIndex, '1');
|
||||
AstNodeExpr* const termp = new AstAnd{flp, maskp, aselp};
|
||||
senTreep->addSensesp(new AstSenItem{flp, VEdgeType::ET_TRUE, termp});
|
||||
}
|
||||
return senTreep;
|
||||
}
|
||||
|
||||
void TriggerKit::addExtraTriggerAssignment(AstVarScope* vscp, uint32_t index) const {
|
||||
const uint32_t wordIndex = index / WORD_SIZE;
|
||||
const uint32_t bitIndex = index % WORD_SIZE;
|
||||
FileLine* const flp = vscp->fileline();
|
||||
// Set the trigger bit
|
||||
AstVarRef* const refp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
|
||||
AstNodeExpr* const wordp = new AstArraySel{flp, refp, static_cast<int>(wordIndex)};
|
||||
AstNodeExpr* const trigLhsp = new AstSel{flp, wordp, static_cast<int>(bitIndex), 1};
|
||||
AstNodeExpr* const trigRhsp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
AstNodeStmt* const setp = new AstAssign{flp, trigLhsp, trigRhsp};
|
||||
// Clear the input variable
|
||||
AstNodeExpr* const vscpLhsp = new AstVarRef{flp, vscp, VAccess::WRITE};
|
||||
AstNodeExpr* const vscpRhsp = new AstConst{flp, AstConst::BitFalse{}};
|
||||
AstNodeStmt* const clrp = new AstAssign{flp, vscpLhsp, vscpRhsp};
|
||||
// Note these are added in reverse order, so 'setp' executes before 'clrp'
|
||||
m_compp->stmtsp()->addHereThisAsNext(clrp);
|
||||
m_compp->stmtsp()->addHereThisAsNext(setp);
|
||||
}
|
||||
|
||||
TriggerKit::TriggerKit(const std::string& name, bool slow, uint32_t nWords)
|
||||
: m_name{name}
|
||||
, m_slow{slow}
|
||||
, m_nWords{nWords} {
|
||||
// If no triggers, we don't need to generate anything
|
||||
if (!nWords) return;
|
||||
// Othewise construc the parts of the kit
|
||||
AstNetlist* const netlistp = v3Global.rootp();
|
||||
AstScope* const scopep = netlistp->topScopep()->scopep();
|
||||
FileLine* const flp = scopep->fileline();
|
||||
// Data type of a single trigger word
|
||||
m_wordDTypep = netlistp->findBitDType(WORD_SIZE, WORD_SIZE, VSigning::UNSIGNED);
|
||||
// Data type of a trigger vector
|
||||
AstRange* const rangep = new AstRange{flp, static_cast<int>(m_nWords - 1), 0};
|
||||
m_trigDTypep = new AstUnpackArrayDType{flp, m_wordDTypep, rangep};
|
||||
netlistp->typeTablep()->addTypesp(m_trigDTypep);
|
||||
// The AstVarScope representing the trigger vector
|
||||
m_vscp = scopep->createTemp("__V" + m_name + "Triggered", m_trigDTypep);
|
||||
m_vscp->varp()->isInternal(true);
|
||||
// The trigger computation function
|
||||
m_compp = util::makeSubFunction(netlistp, "_eval_triggers__" + m_name, m_slow);
|
||||
// The debug dump function, always 'slow'
|
||||
m_dumpp = util::makeSubFunction(netlistp, "_dump_triggers__" + m_name, true);
|
||||
m_dumpp->isStatic(true);
|
||||
m_dumpp->ifdef("VL_DEBUG");
|
||||
}
|
||||
|
||||
TriggerKit TriggerKit::create(AstNetlist* netlistp, //
|
||||
AstCFunc* const initFuncp, //
|
||||
SenExprBuilder& senExprBuilder, //
|
||||
const std::vector<const AstSenTree*>& senTreeps, //
|
||||
const string& name, //
|
||||
const ExtraTriggers& extraTriggers, //
|
||||
bool slow) {
|
||||
FileLine* const flp = netlistp->topScopep()->fileline();
|
||||
|
||||
// Number of extra triggers, rounded up to a full word. These occupy the lowest words.
|
||||
const uint32_t nExtraTriggers = vlstd::roundUpToMultipleOf<WORD_SIZE>(extraTriggers.size());
|
||||
const uint32_t nExtraWords = nExtraTriggers / WORD_SIZE;
|
||||
|
||||
// Gather all the unique SenItems under the SenTrees
|
||||
// List of unique SenItems used by all 'senTreeps'
|
||||
std::vector<const AstSenItem*> senItemps;
|
||||
// Map from SenItem to the equivalent index in 'senItemps'
|
||||
std::unordered_map<const AstSenItem*, size_t> senItemp2Index;
|
||||
{
|
||||
// Set of unique SenItems
|
||||
std::unordered_set<VNRef<const AstSenItem>> uniqueSenItemps;
|
||||
for (const AstSenTree* const senTreep : senTreeps) {
|
||||
for (const AstSenItem *itemp = senTreep->sensesp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = VN_AS(itemp->nextp(), SenItem);
|
||||
const auto pair = uniqueSenItemps.emplace(*itemp);
|
||||
if (pair.second) {
|
||||
senItemp2Index.emplace(itemp, senItemps.size());
|
||||
senItemps.push_back(itemp);
|
||||
}
|
||||
senItemp2Index.emplace(itemp, senItemp2Index.at(&(pair.first->get())));
|
||||
}
|
||||
// Map from SenItem to tigger bit standing for that SenItem. There might
|
||||
// be duplicate SenItems, we map all of them to the same index.
|
||||
std::unordered_map<VNRef<const AstSenItem>, size_t> senItem2TrigIdx;
|
||||
for (const AstSenTree* const senTreep : senTreeps) {
|
||||
for (const AstSenItem *itemp = senTreep->sensesp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = VN_AS(itemp->nextp(), SenItem);
|
||||
UASSERT_OBJ(itemp->isClocked() || itemp->isHybrid(), itemp,
|
||||
"Cannot create trigger expression for non-clocked sensitivity");
|
||||
|
||||
const auto pair = senItem2TrigIdx.emplace(*itemp, nExtraTriggers + senItemps.size());
|
||||
if (pair.second) senItemps.push_back(itemp);
|
||||
}
|
||||
}
|
||||
UASSERT(senItemps.size() == senItem2TrigIdx.size(), "Inconsitent SenItem to trigger map");
|
||||
|
||||
std::unordered_map<const AstSenTree*, AstSenTree*> map;
|
||||
// Number of sense triggers, rounded up to a full word
|
||||
const uint32_t nSenseTriggers = vlstd::roundUpToMultipleOf<WORD_SIZE>(senItemps.size());
|
||||
// Total number of trigger words
|
||||
const uint32_t nWords = (nSenseTriggers / WORD_SIZE) + nExtraWords;
|
||||
|
||||
const uint32_t nTriggers = senItemps.size() + extraTriggers.size();
|
||||
// Pad 'senItemps' to nSenseTriggers with nullptr
|
||||
senItemps.resize(nSenseTriggers);
|
||||
|
||||
// Create the TRIGGERVEC variable
|
||||
AstBasicDType* const tDtypep
|
||||
= new AstBasicDType{flp, VBasicDTypeKwd::TRIGGERVEC, VSigning::UNSIGNED,
|
||||
static_cast<int>(nTriggers), static_cast<int>(nTriggers)};
|
||||
netlistp->typeTablep()->addTypesp(tDtypep);
|
||||
AstVarScope* const vscp = scopeTopp->createTemp("__V" + name + "Triggered", tDtypep);
|
||||
// We can now construct the trigger kit - this construct all items that will be kept
|
||||
TriggerKit kit{name, slow, nWords};
|
||||
|
||||
// Create the trigger computation function
|
||||
AstCFunc* const funcp = util::makeSubFunction(netlistp, "_eval_triggers__" + name, slow);
|
||||
if (v3Global.opt.profExec()) funcp->addStmtsp(util::profExecSectionPush(flp, "trig " + name));
|
||||
// If there are no triggers we are done
|
||||
if (!nWords) return kit;
|
||||
|
||||
// Create the trigger dump function (for debugging, always 'slow')
|
||||
AstCFunc* const dumpp = util::makeSubFunction(netlistp, "_dump_triggers__" + name, true);
|
||||
dumpp->ifdef("VL_DEBUG");
|
||||
// Construct the comp and dump functions
|
||||
|
||||
// Add arguments to the dump function. The trigger vector is passed into
|
||||
// the dumping function via reference so one dump function can dump all
|
||||
// different copies of the trigger vector. To do so, it also needs the tag
|
||||
// string at runtime, which is the second argument.
|
||||
AstVarScope* const dumpTrgp
|
||||
= newArgument(kit.m_dumpp, kit.m_trigDTypep, "triggers", VDirection::CONSTREF);
|
||||
AstVarScope* const dumpTagp
|
||||
= newArgument(kit.m_dumpp, netlistp->findStringDType(), "tag", VDirection::CONSTREF);
|
||||
|
||||
// Add a print to the dumping function if there are no triggers pending
|
||||
{
|
||||
AstCMethodHard* const callp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, vscp, VAccess::READ}, VCMethod::TRIGGER_ANY};
|
||||
callp->dtypeSetBit();
|
||||
AstIf* const ifp = new AstIf{flp, callp};
|
||||
dumpp->addStmtsp(ifp);
|
||||
ifp->addElsesp(new AstCStmt{flp, "VL_DBG_MSGF(\" No triggers active\\n\");"});
|
||||
AstIf* const ifp = new AstIf{flp, new AstLogNot{flp, kit.newAnySetCall(dumpTrgp)}};
|
||||
kit.m_dumpp->addStmtsp(ifp);
|
||||
AstCStmt* const cstmtp = new AstCStmt{flp};
|
||||
ifp->addThensp(cstmtp);
|
||||
cstmtp->add("VL_DBG_MSGS(\" No '\" + ");
|
||||
cstmtp->add(new AstVarRef{flp, dumpTagp, VAccess::READ});
|
||||
cstmtp->add(" + \"\' region triggers active\\n\");");
|
||||
}
|
||||
|
||||
// Set the given trigger to the given value
|
||||
const auto setTrigBit = [&](uint32_t index, AstNodeExpr* valp) {
|
||||
AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::WRITE};
|
||||
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, VCMethod::TRIGGER_SET_BIT};
|
||||
callp->addPinsp(new AstConst{flp, index});
|
||||
callp->addPinsp(valp);
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
};
|
||||
|
||||
// Create a reference to a trigger flag
|
||||
const auto getTrig = [&](uint32_t index) {
|
||||
AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
const uint32_t wordIndex = index / 64;
|
||||
const uint32_t bitIndex = index % 64;
|
||||
AstCMethodHard* const callp
|
||||
= new AstCMethodHard{flp, vrefp, VCMethod::TRIGGER_WORD, new AstConst{flp, wordIndex}};
|
||||
callp->dtypeSetUInt64();
|
||||
AstNodeExpr* const termp
|
||||
= new AstAnd{flp, new AstConst{flp, AstConst::Unsized64{}, 1ULL << bitIndex}, callp};
|
||||
return termp;
|
||||
};
|
||||
|
||||
// Add a debug dumping statement for this trigger
|
||||
const auto addDebug = [&](uint32_t index, const string& text = "") {
|
||||
std::stringstream ss;
|
||||
ss << "VL_DBG_MSGF(\" ";
|
||||
ss << "'" << name << "' region trigger index " << std::to_string(index) << " is active";
|
||||
if (!text.empty()) ss << ": " << text;
|
||||
ss << "\\n\");";
|
||||
|
||||
AstIf* const ifp = new AstIf{flp, getTrig(index)};
|
||||
dumpp->addStmtsp(ifp);
|
||||
ifp->addThensp(new AstCStmt{flp, ss.str()});
|
||||
// Adds a debug dumping statement for this trigger
|
||||
const auto addDebug = [&](uint32_t index, const string& text) {
|
||||
AstVarRef* const refp = new AstVarRef{flp, dumpTrgp, VAccess::READ};
|
||||
const int wrdIndex = static_cast<int>(index / WORD_SIZE);
|
||||
const int bitIndex = static_cast<int>(index % WORD_SIZE);
|
||||
AstNodeExpr* const aselp = new AstArraySel{flp, refp, wrdIndex};
|
||||
AstNodeExpr* const condp = new AstSel{flp, aselp, bitIndex, 1};
|
||||
AstIf* const ifp = new AstIf{flp, condp};
|
||||
kit.m_dumpp->addStmtsp(ifp);
|
||||
AstCStmt* const cstmtp = new AstCStmt{flp};
|
||||
ifp->addThensp(cstmtp);
|
||||
cstmtp->add("VL_DBG_MSGS(\" '\" + ");
|
||||
cstmtp->add(new AstVarRef{flp, dumpTagp, VAccess::READ});
|
||||
cstmtp->add(" + \"' region trigger index " + std::to_string(index) + " is active: " + text
|
||||
+ "\\n\");");
|
||||
};
|
||||
|
||||
// Add a print for each of the extra triggers
|
||||
for (unsigned i = 0; i < extraTriggers.size(); ++i) {
|
||||
addDebug(i, "Internal '" + name + "' trigger - " + extraTriggers.description(i));
|
||||
addDebug(i, "Internal '" + name + "' trigger - " + extraTriggers.m_descriptions.at(i));
|
||||
}
|
||||
|
||||
// Add trigger computation
|
||||
uint32_t triggerNumber = extraTriggers.size();
|
||||
uint32_t triggerBitIdx = triggerNumber;
|
||||
// Add sense trigger computation
|
||||
// List of trigger computation expressions
|
||||
std::vector<AstNodeExpr*> trigps;
|
||||
trigps.reserve(nSenseTriggers);
|
||||
// Statements to exectue at initialization time to fire initial triggers
|
||||
AstNodeStmt* initialTrigsp = nullptr;
|
||||
std::vector<uint32_t> senItemIndex2TriggerIndex;
|
||||
senItemIndex2TriggerIndex.reserve(senItemps.size());
|
||||
constexpr uint32_t TRIG_VEC_WORD_SIZE_LOG2 = 6; // 64-bits
|
||||
constexpr uint32_t TRIG_VEC_WORD_SIZE = 1 << TRIG_VEC_WORD_SIZE_LOG2;
|
||||
std::vector<AstNodeExpr*> trigExprps;
|
||||
trigExprps.reserve(TRIG_VEC_WORD_SIZE);
|
||||
for (const AstSenItem* const senItemp : senItemps) {
|
||||
UASSERT_OBJ(senItemp->isClocked() || senItemp->isHybrid(), senItemp,
|
||||
"Cannot create trigger expression for non-clocked sensitivity");
|
||||
for (size_t i = 0; i < senItemps.size(); ++i) {
|
||||
const AstSenItem* const senItemp = senItemps[i];
|
||||
|
||||
// Store the trigger number
|
||||
senItemIndex2TriggerIndex.push_back(triggerNumber);
|
||||
// If this is just paddign, use constant zero
|
||||
if (!senItemp) {
|
||||
trigps.emplace_back(new AstConst{flp, AstConst::BitFalse{}});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the trigger computation
|
||||
// Index of this trigger in the trigger vector
|
||||
const uint32_t index = nExtraTriggers + i;
|
||||
|
||||
// Create the trigger computation expression
|
||||
const auto& pair = senExprBuilder.build(senItemp);
|
||||
trigExprps.emplace_back(pair.first);
|
||||
trigps.emplace_back(pair.first);
|
||||
|
||||
// Add initialization time trigger
|
||||
if (pair.second || v3Global.opt.xInitialEdge()) {
|
||||
initialTrigsp
|
||||
= AstNode::addNext(initialTrigsp, setTrigBit(triggerNumber, new AstConst{flp, 1}));
|
||||
AstVarRef* const refp = new AstVarRef{flp, kit.m_vscp, VAccess::WRITE};
|
||||
const int wrdIndex = static_cast<int>(index / WORD_SIZE);
|
||||
const int bitIndex = static_cast<int>(index % WORD_SIZE);
|
||||
AstNodeExpr* const wordp = new AstArraySel{flp, refp, wrdIndex};
|
||||
AstNodeExpr* const lhsp = new AstSel{flp, wordp, bitIndex, 1};
|
||||
AstNodeExpr* const rhsp = new AstConst{flp, AstConst::BitTrue{}};
|
||||
initialTrigsp = AstNode::addNext(initialTrigsp, new AstAssign{flp, lhsp, rhsp});
|
||||
}
|
||||
|
||||
// Add a debug statement for this trigger
|
||||
|
|
@ -193,111 +489,85 @@ const TriggerKit TriggerKit::create(AstNetlist* netlistp, //
|
|||
ss << ")";
|
||||
std::string desc = VString::quoteBackslash(ss.str());
|
||||
desc = VString::replaceSubstr(desc, "\n", "\\n");
|
||||
addDebug(triggerNumber, desc);
|
||||
addDebug(index, desc);
|
||||
}
|
||||
UASSERT(trigps.size() == nSenseTriggers, "Inconsistent number of trigger expressions");
|
||||
|
||||
//
|
||||
++triggerNumber;
|
||||
|
||||
// Add statements on every word boundary
|
||||
if (triggerNumber % TRIG_VEC_WORD_SIZE == 0) {
|
||||
if (triggerBitIdx % TRIG_VEC_WORD_SIZE != 0) {
|
||||
// Set leading triggers bit-wise
|
||||
for (AstNodeExpr* const exprp : trigExprps) {
|
||||
funcp->addStmtsp(setTrigBit(triggerBitIdx++, exprp));
|
||||
}
|
||||
} else {
|
||||
// Set whole word as a unit
|
||||
UASSERT_OBJ(triggerNumber == triggerBitIdx + TRIG_VEC_WORD_SIZE, senItemp,
|
||||
"Mismatched index");
|
||||
UASSERT_OBJ(trigExprps.size() == TRIG_VEC_WORD_SIZE, senItemp,
|
||||
"There should be TRIG_VEC_WORD_SIZE expressions");
|
||||
// Concatenate all bits in a tree
|
||||
for (uint32_t level = 0; level < TRIG_VEC_WORD_SIZE_LOG2; ++level) {
|
||||
const uint32_t stride = 1 << level;
|
||||
for (uint32_t i = 0; i < TRIG_VEC_WORD_SIZE; i += 2 * stride) {
|
||||
trigExprps[i] = new AstConcat{trigExprps[i]->fileline(),
|
||||
trigExprps[i + stride], trigExprps[i]};
|
||||
trigExprps[i + stride] = nullptr;
|
||||
}
|
||||
}
|
||||
// Set the whole word in the trigger vector
|
||||
AstVarRef* const vrefp = new AstVarRef{flp, vscp, VAccess::WRITE};
|
||||
AstCMethodHard* const callp
|
||||
= new AstCMethodHard{flp, vrefp, VCMethod::TRIGGER_SET_WORD};
|
||||
callp->addPinsp(new AstConst{flp, triggerBitIdx / TRIG_VEC_WORD_SIZE});
|
||||
callp->addPinsp(trigExprps[0]);
|
||||
callp->dtypeSetVoid();
|
||||
funcp->addStmtsp(callp->makeStmt());
|
||||
triggerBitIdx += TRIG_VEC_WORD_SIZE;
|
||||
// Assign trigger vector one word at a time
|
||||
AstNodeStmt* trigStmtsp = nullptr;
|
||||
for (size_t i = 0; i < nSenseTriggers; i += WORD_SIZE) {
|
||||
// Concatenate all bits in this trigger word using a balanced
|
||||
for (uint32_t level = 0; level < WORD_SIZE_LOG2; ++level) {
|
||||
const uint32_t stride = 1 << level;
|
||||
for (uint32_t j = 0; j < WORD_SIZE; j += 2 * stride) {
|
||||
FileLine* const flp = trigps[i + j]->fileline();
|
||||
trigps[i + j] = new AstConcat{flp, trigps[i + j + stride], trigps[i + j]};
|
||||
trigps[i + j + stride] = nullptr;
|
||||
}
|
||||
UASSERT_OBJ(triggerNumber == triggerBitIdx, senItemp, "Mismatched index");
|
||||
trigExprps.clear();
|
||||
}
|
||||
|
||||
// Set the whole word in the trigger vector
|
||||
const uint32_t wordIndex = nExtraWords + i / WORD_SIZE;
|
||||
AstVarRef* const refp = new AstVarRef{flp, kit.m_vscp, VAccess::WRITE};
|
||||
AstArraySel* const aselp = new AstArraySel{flp, refp, static_cast<int>(wordIndex)};
|
||||
trigStmtsp = AstNode::addNext(trigStmtsp, new AstAssign{flp, aselp, trigps[i]});
|
||||
}
|
||||
// Set trailing triggers bit-wise
|
||||
for (AstNodeExpr* const exprp : trigExprps) {
|
||||
funcp->addStmtsp(setTrigBit(triggerBitIdx++, exprp));
|
||||
}
|
||||
trigExprps.clear();
|
||||
trigps.clear();
|
||||
|
||||
// Construct the map from old SenTrees to new SenTrees
|
||||
for (const AstSenTree* const senTreep : senTreeps) {
|
||||
AstSenTree* const trigpSenp = new AstSenTree{flp, nullptr};
|
||||
for (const AstSenItem *itemp = senTreep->sensesp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = VN_AS(itemp->nextp(), SenItem);
|
||||
const uint32_t tiggerIndex = senItemIndex2TriggerIndex.at(senItemp2Index.at(itemp));
|
||||
trigpSenp->addSensesp(new AstSenItem{flp, VEdgeType::ET_TRUE, getTrig(tiggerIndex)});
|
||||
{
|
||||
std::vector<uint32_t> indices;
|
||||
indices.reserve(32);
|
||||
for (const AstSenTree* const senTreep : senTreeps) {
|
||||
indices.clear();
|
||||
for (const AstSenItem *itemp = senTreep->sensesp(), *nextp; itemp; itemp = nextp) {
|
||||
nextp = VN_AS(itemp->nextp(), SenItem);
|
||||
indices.push_back(senItem2TrigIdx.at(*itemp));
|
||||
}
|
||||
kit.m_map[senTreep] = kit.newTriggerSenTree(kit.m_vscp, indices);
|
||||
}
|
||||
topScopep->addSenTreesp(trigpSenp);
|
||||
map[senTreep] = trigpSenp;
|
||||
}
|
||||
|
||||
// Get the SenExprBuilder results
|
||||
const SenExprBuilder::Results senResults = senExprBuilder.getAndClearResults();
|
||||
|
||||
// Add the init and update statements
|
||||
// Add the SenExprBuilder init statements to the static initialization functino
|
||||
for (AstNodeStmt* const nodep : senResults.m_inits) initFuncp->addStmtsp(nodep);
|
||||
for (AstNodeStmt* const nodep : senResults.m_postUpdates) funcp->addStmtsp(nodep);
|
||||
if (!senResults.m_preUpdates.empty()) {
|
||||
for (AstNodeStmt* const nodep : vlstd::reverse_view(senResults.m_preUpdates)) {
|
||||
UASSERT_OBJ(funcp->stmtsp(), funcp,
|
||||
"No statements in trigger eval function, but there are pre updates");
|
||||
funcp->stmtsp()->addHereThisAsNext(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the initialization statements
|
||||
if (initialTrigsp) {
|
||||
AstVarScope* const tempVscp = scopeTopp->createTemp("__V" + name + "DidInit", 1);
|
||||
AstVarRef* const condp = new AstVarRef{flp, tempVscp, VAccess::READ};
|
||||
AstIf* const ifp = new AstIf{flp, new AstNot{flp, condp}};
|
||||
funcp->addStmtsp(ifp);
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
ifp->addThensp(util::setVar(tempVscp, 1));
|
||||
ifp->addThensp(initialTrigsp);
|
||||
}
|
||||
|
||||
// Add a call to the dumping function if debug is enabled
|
||||
// Assemble the trigger computation function
|
||||
{
|
||||
AstCStmt* const stmtp = new AstCStmt{flp};
|
||||
funcp->addStmtsp(stmtp);
|
||||
stmtp->add("#ifdef VL_DEBUG\n");
|
||||
stmtp->add("if (VL_UNLIKELY(vlSymsp->_vm_contextp__->debug())) {\n");
|
||||
stmtp->add(util::callVoidFunc(dumpp));
|
||||
stmtp->add("}\n");
|
||||
stmtp->add("#endif");
|
||||
}
|
||||
|
||||
if (v3Global.opt.profExec()) funcp->addStmtsp(util::profExecSectionPop(flp));
|
||||
AstCFunc* const fp = kit.m_compp;
|
||||
// Profiling push
|
||||
if (v3Global.opt.profExec()) fp->addStmtsp(util::profExecSectionPush(flp, "trig " + name));
|
||||
// Trigger computation
|
||||
for (AstNodeStmt* const nodep : senResults.m_preUpdates) fp->addStmtsp(nodep);
|
||||
fp->addStmtsp(trigStmtsp);
|
||||
for (AstNodeStmt* const nodep : senResults.m_postUpdates) fp->addStmtsp(nodep);
|
||||
// Add the initialization time triggers
|
||||
if (initialTrigsp) {
|
||||
AstScope* const scopep = netlistp->topScopep()->scopep();
|
||||
AstVarScope* const vscp = scopep->createTemp("__V" + name + "DidInit", 1);
|
||||
AstVarRef* const condp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
AstIf* const ifp = new AstIf{flp, new AstNot{flp, condp}};
|
||||
fp->addStmtsp(ifp);
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
ifp->addThensp(util::setVar(vscp, 1));
|
||||
ifp->addThensp(initialTrigsp);
|
||||
}
|
||||
// Add a call to the dumping function if debug is enabled
|
||||
fp->addStmtsp(kit.newDumpCall(kit.m_vscp, name, true));
|
||||
// Profiling pop
|
||||
if (v3Global.opt.profExec()) fp->addStmtsp(util::profExecSectionPop(flp));
|
||||
// Done with the trigger computation function, split as might be large
|
||||
util::splitCheck(fp);
|
||||
};
|
||||
|
||||
// The debug code might leak signal names, so simply delete it when using --protect-ids
|
||||
if (v3Global.opt.protectIds()) dumpp->stmtsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
if (v3Global.opt.protectIds()) kit.m_dumpp->stmtsp()->unlinkFrBackWithNext()->deleteTree();
|
||||
// Done with the trigger dump function, split as might be large
|
||||
util::splitCheck(kit.m_dumpp);
|
||||
|
||||
// These might get large when we have a lot of triggers, so split if necessary
|
||||
util::splitCheck(funcp);
|
||||
util::splitCheck(dumpp);
|
||||
|
||||
return TriggerKit{vscp, funcp, dumpp, map};
|
||||
return kit;
|
||||
}
|
||||
|
||||
} // namespace V3Sched
|
||||
|
|
|
|||
|
|
@ -71,13 +71,14 @@ AstNodeStmt* incrementVar(AstVarScope* vscp) {
|
|||
}
|
||||
|
||||
AstNodeStmt* callVoidFunc(AstCFunc* funcp) {
|
||||
if (!funcp) return nullptr;
|
||||
AstCCall* const callp = new AstCCall{funcp->fileline(), funcp};
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
}
|
||||
|
||||
AstNodeStmt* checkIterationLimit(AstNetlist* netlistp, const string& name, AstVarScope* counterp,
|
||||
AstCFunc* trigDumpp) {
|
||||
AstNodeStmt* dumpCallp) {
|
||||
FileLine* const flp = netlistp->fileline();
|
||||
|
||||
// If we exceeded the iteration limit, die
|
||||
|
|
@ -88,14 +89,12 @@ AstNodeStmt* checkIterationLimit(AstNetlist* netlistp, const string& name, AstVa
|
|||
AstNodeExpr* const condp = new AstGt{flp, counterRefp, constp};
|
||||
AstIf* const ifp = new AstIf{flp, condp};
|
||||
ifp->branchPred(VBranchPred::BP_UNLIKELY);
|
||||
ifp->addThensp(dumpCallp);
|
||||
AstCStmt* const stmtp = new AstCStmt{flp};
|
||||
ifp->addThensp(stmtp);
|
||||
FileLine* const locp = netlistp->topModulep()->fileline();
|
||||
const std::string& file = VIdProtect::protect(locp->filename());
|
||||
const std::string& line = std::to_string(locp->lineno());
|
||||
stmtp->add("#ifdef VL_DEBUG\n");
|
||||
stmtp->add(callVoidFunc(trigDumpp));
|
||||
stmtp->add("#endif\n");
|
||||
stmtp->add("VL_FATAL_MT(\"" + V3OutFormatter::quoteNameControls(file) + "\", " + line
|
||||
+ ", \"\", \"" + name + " region did not converge after " + std::to_string(limit)
|
||||
+ " tries\");");
|
||||
|
|
@ -116,64 +115,105 @@ static AstCFunc* splitCheckCreateNewSubFunc(AstCFunc* ofuncp) {
|
|||
static std::map<AstCFunc*, uint32_t> s_funcNums; // What split number to attach to a function
|
||||
const uint32_t funcNum = s_funcNums[ofuncp]++;
|
||||
const std::string name = ofuncp->name() + "__" + cvtToStr(funcNum);
|
||||
AstCFunc* const subFuncp = new AstCFunc{ofuncp->fileline(), name, ofuncp->scopep()};
|
||||
AstScope* const scopep = ofuncp->scopep();
|
||||
AstCFunc* const subFuncp = new AstCFunc{ofuncp->fileline(), name, scopep};
|
||||
scopep->addBlocksp(subFuncp);
|
||||
subFuncp->dontCombine(true);
|
||||
subFuncp->isStatic(false);
|
||||
subFuncp->isStatic(ofuncp->isStatic());
|
||||
subFuncp->isLoose(true);
|
||||
subFuncp->slow(ofuncp->slow());
|
||||
subFuncp->declPrivate(ofuncp->declPrivate());
|
||||
if (ofuncp->needProcess()) subFuncp->setNeedProcess();
|
||||
for (AstVar* argp = ofuncp->argsp(); argp; argp = VN_AS(argp->nextp(), Var)) {
|
||||
AstVar* const clonep = argp->cloneTree(false);
|
||||
subFuncp->addArgsp(clonep);
|
||||
AstVarScope* const vscp = new AstVarScope{clonep->fileline(), scopep, clonep};
|
||||
scopep->addVarsp(vscp);
|
||||
argp->user3p(vscp);
|
||||
}
|
||||
return subFuncp;
|
||||
};
|
||||
|
||||
void splitCheckFinishSubFunc(AstCFunc* ofuncp, AstCFunc* subFuncp,
|
||||
const std::unordered_map<const AstVar*, AstVarScope*>& argVscps) {
|
||||
FileLine* const flp = subFuncp->fileline();
|
||||
AstCCall* const callp = new AstCCall{subFuncp->fileline(), subFuncp};
|
||||
callp->dtypeSetVoid();
|
||||
// Pass arguments through to subfunction
|
||||
for (AstVar* argp = ofuncp->argsp(); argp; argp = VN_AS(argp->nextp(), Var)) {
|
||||
UASSERT_OBJ(argp->direction() == VDirection::CONSTREF, argp, "Unexpected direction");
|
||||
callp->addArgsp(new AstVarRef{flp, argVscps.at(argp), VAccess::READ});
|
||||
}
|
||||
|
||||
bool containsAwait = false;
|
||||
subFuncp->foreach([&](AstNodeExpr* exprp) {
|
||||
// Record if it has a CAwait
|
||||
if (VN_IS(exprp, CAwait)) containsAwait = true;
|
||||
// Redirect references to arguments to the clone in the sub-function
|
||||
if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
|
||||
if (AstVarScope* const vscp = VN_AS(refp->varp()->user3p(), VarScope)) {
|
||||
refp->varp(vscp->varp());
|
||||
refp->varScopep(vscp);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (ofuncp->isCoroutine() && containsAwait) { // Wrap call with co_await
|
||||
subFuncp->rtnType("VlCoroutine");
|
||||
AstCAwait* const awaitp = new AstCAwait{flp, callp};
|
||||
awaitp->dtypeSetVoid();
|
||||
ofuncp->addStmtsp(awaitp->makeStmt());
|
||||
} else {
|
||||
ofuncp->addStmtsp(callp->makeStmt());
|
||||
}
|
||||
}
|
||||
|
||||
// Split large function according to --output-split-cfuncs
|
||||
void splitCheck(AstCFunc* ofuncp) {
|
||||
void splitCheck(AstCFunc* const ofuncp) {
|
||||
if (!ofuncp) return;
|
||||
UASSERT_OBJ(!ofuncp->varsp(), ofuncp, "Can't split function with local variables");
|
||||
if (!v3Global.opt.outputSplitCFuncs() || !ofuncp->stmtsp()) return;
|
||||
if (ofuncp->nodeCount() < v3Global.opt.outputSplitCFuncs()) return;
|
||||
|
||||
int func_stmts = 0;
|
||||
const bool is_ofuncp_coroutine = ofuncp->isCoroutine();
|
||||
AstCFunc* funcp = nullptr;
|
||||
|
||||
const auto finishSubFuncp = [&](AstCFunc* subFuncp) {
|
||||
ofuncp->scopep()->addBlocksp(subFuncp);
|
||||
AstCCall* const callp = new AstCCall{subFuncp->fileline(), subFuncp};
|
||||
callp->dtypeSetVoid();
|
||||
|
||||
if (is_ofuncp_coroutine && subFuncp->exists([](const AstCAwait*) {
|
||||
return true;
|
||||
})) { // Wrap call with co_await
|
||||
subFuncp->rtnType("VlCoroutine");
|
||||
|
||||
AstCAwait* const awaitp = new AstCAwait{subFuncp->fileline(), callp};
|
||||
awaitp->dtypeSetVoid();
|
||||
ofuncp->addStmtsp(awaitp->makeStmt());
|
||||
} else {
|
||||
ofuncp->addStmtsp(callp->makeStmt());
|
||||
// Need to find the AstVarScopes for the function arguments. They should be in the same Scope.
|
||||
std::unordered_map<const AstVar*, AstVarScope*> argVscps;
|
||||
for (AstVar* argp = ofuncp->argsp(); argp; argp = VN_AS(argp->nextp(), Var)) {
|
||||
UASSERT_OBJ(argVscps.size() < 2, argp, "There should be at most 2 arguments, or O(n^2)");
|
||||
bool found = false;
|
||||
for (AstVarScope *vscp = ofuncp->scopep()->varsp(), *nextp; vscp; vscp = nextp) {
|
||||
nextp = VN_AS(vscp->nextp(), VarScope);
|
||||
if (vscp->varp() != argp) continue;
|
||||
argVscps[argp] = vscp;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
funcp = splitCheckCreateNewSubFunc(ofuncp);
|
||||
func_stmts = 0;
|
||||
|
||||
// Unlink all statements, then add item by item to new sub-functions
|
||||
AstBegin* const tempp = new AstBegin{ofuncp->fileline(), "[EditWrapper]",
|
||||
ofuncp->stmtsp()->unlinkFrBackWithNext(), false};
|
||||
while (tempp->stmtsp()) {
|
||||
AstNode* const itemp = tempp->stmtsp()->unlinkFrBack();
|
||||
const int stmts = itemp->nodeCount();
|
||||
|
||||
if ((func_stmts + stmts) > v3Global.opt.outputSplitCFuncs()) {
|
||||
finishSubFuncp(funcp);
|
||||
funcp = splitCheckCreateNewSubFunc(ofuncp);
|
||||
func_stmts = 0;
|
||||
}
|
||||
|
||||
funcp->addStmtsp(itemp);
|
||||
func_stmts += stmts;
|
||||
UASSERT_OBJ(found, argp, "Can't find VarScope for function argument");
|
||||
}
|
||||
finishSubFuncp(funcp);
|
||||
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
||||
|
||||
// AstVar::user3p(): AstVarScope for function argument in clone
|
||||
const VNUser3InUse user3InUse;
|
||||
|
||||
size_t size = 0;
|
||||
AstCFunc* subFuncp = nullptr;
|
||||
|
||||
// Move statements one by one to the new sub-functions
|
||||
AstNode* stmtsp = ofuncp->stmtsp()->unlinkFrBackWithNext();
|
||||
while (AstNode* const itemp = stmtsp) {
|
||||
stmtsp = stmtsp->nextp();
|
||||
if (stmtsp) stmtsp->unlinkFrBackWithNext();
|
||||
const size_t itemSize = static_cast<size_t>(itemp->nodeCount());
|
||||
size += itemSize;
|
||||
|
||||
if (size > static_cast<size_t>(v3Global.opt.outputSplitCFuncs())) {
|
||||
if (subFuncp) splitCheckFinishSubFunc(ofuncp, subFuncp, argVscps);
|
||||
subFuncp = nullptr;
|
||||
size = itemSize;
|
||||
}
|
||||
|
||||
if (!subFuncp) subFuncp = splitCheckCreateNewSubFunc(ofuncp);
|
||||
subFuncp->addStmtsp(itemp);
|
||||
}
|
||||
if (subFuncp) splitCheckFinishSubFunc(ofuncp, subFuncp, argVscps);
|
||||
}
|
||||
|
||||
// Build an AstIf conditional on the given SenTree being triggered
|
||||
|
|
|
|||
|
|
@ -63,9 +63,6 @@ class GatherMTaskAffinity final : VNVisitorConst {
|
|||
// Cheaper than relying on emplace().second
|
||||
if (nodep->user1SetOnce()) return;
|
||||
AstVar* const varp = nodep->varp();
|
||||
// Ignore TriggerVec. They are big and read-only in the MTask bodies
|
||||
AstBasicDType* const basicp = varp->dtypep()->basicp();
|
||||
if (basicp && basicp->isTriggerVec()) return;
|
||||
// Set affinity bit
|
||||
MTaskIdVec& affinity = m_results
|
||||
.emplace(std::piecewise_construct, //
|
||||
|
|
@ -277,7 +274,7 @@ void V3VariableOrder::orderAll(AstNetlist* netlistp) {
|
|||
for (AstNodeModule* modp = v3Global.rootp()->modulesp(); modp;
|
||||
modp = VN_AS(modp->nextp(), NodeModule)) {
|
||||
std::vector<AstVar*>& varps = sortedVars[modp];
|
||||
threadScope.enqueue([modp, mTaskAffinity, &varps]() {
|
||||
threadScope.enqueue([modp, &mTaskAffinity, &varps]() {
|
||||
VariableOrder::processModule(modp, mTaskAffinity, varps);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ def check_evals():
|
|||
if re.search(r'__eval_nba__[0-9]+\(.*\)\s*{', wholefile):
|
||||
got += 1
|
||||
|
||||
if got < 3:
|
||||
if got < 2:
|
||||
test.error("Too few _eval functions found: " + str(got))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_clas
|
|||
test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2")
|
||||
|
||||
# Check combine count
|
||||
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (230 if test.vltmt else 216))
|
||||
test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (220 if test.vltmt else 206))
|
||||
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2)
|
||||
test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2)
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,3 +1,3 @@
|
|||
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] a)
|
||||
-V{t#,#} 'stl' region trigger index 64 is active: @([hybrid] a)
|
||||
%Error: t/t_lint_didnotconverge_bad.v:7: Settle region did not converge after 100 tries
|
||||
Aborting...
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ test.compile(verilator_flags2=["--stats"])
|
|||
test.execute()
|
||||
|
||||
# Value must differ from that in t_opt_localize_max_size.py
|
||||
test.file_grep(test.stats, r'Optimizations, Vars localized\s+(\d+)', 5)
|
||||
test.file_grep(test.stats, r'Optimizations, Vars localized\s+(\d+)', 1)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t;
|
||||
int x;
|
||||
initial begin
|
||||
x = $c32(1);
|
||||
$display(x);
|
||||
x = $c32(2);
|
||||
$display(x);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ test.compile(verilator_flags2=["--stats --localize-max-size 1"])
|
|||
test.execute()
|
||||
|
||||
# Value must differ from that in t_opt_localize_max_size.py
|
||||
test.file_grep(test.stats, r'Optimizations, Vars localized\s+(\d+)', 4)
|
||||
test.file_grep(test.stats, r'Optimizations, Vars localized\s+(\d+)', 0)
|
||||
|
||||
test.passes()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ test.scenarios('simulator_st')
|
|||
test.compile(verilator_flags2=["--stats"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Substituted temps\s+(\d+)', 42)
|
||||
test.file_grep(test.stats, r'Optimizations, Substituted temps\s+(\d+)', 43)
|
||||
|
||||
test.execute()
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,3 +1,3 @@
|
|||
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] x)
|
||||
-V{t#,#} 'stl' region trigger index 64 is active: @([hybrid] x)
|
||||
%Error: t/t_unopt_converge_initial.v:7: Settle region did not converge after 100 tries
|
||||
Aborting...
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] x)
|
||||
-V{t#,#} 'stl' region trigger index 64 is active: @([hybrid] x)
|
||||
%Error: t/t_unopt_converge.v:7: Settle region did not converge after 100 tries
|
||||
Aborting...
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
-V{t#,#} 'stl' region trigger index 1 is active: @([hybrid] x)
|
||||
-V{t#,#} 'stl' region trigger index 64 is active: @([hybrid] x)
|
||||
%Error: t/t_unopt_converge.v:7: Settle region did not converge after 5 tries
|
||||
Aborting...
|
||||
|
|
|
|||
|
|
@ -18,8 +18,12 @@ internalsDump:
|
|||
-V{t#,#}+ Vt_verilated_debug___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_triggers__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___dump_triggers__act
|
||||
-V{t#,#} No triggers active
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_anySet__act
|
||||
-V{t#,#} No 'act' region triggers active
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_andNot__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_orInto__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_phase__nba
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_anySet__act
|
||||
-V{t#,#}End-of-eval cleanup
|
||||
-V{t#,#}+++++TOP Evaluate Vt_verilated_debug::eval_step
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_debug_assertions
|
||||
|
|
@ -28,20 +32,24 @@ internalsDump:
|
|||
-V{t#,#}+ Vt_verilated_debug___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_triggers__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___dump_triggers__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_anySet__act
|
||||
-V{t#,#} 'act' region trigger index 0 is active: @(posedge clk)
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_triggers__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___dump_triggers__act
|
||||
-V{t#,#} No triggers active
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_andNot__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_orInto__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_phase__nba
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_anySet__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_nba
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___nba_sequent__TOP__0
|
||||
*-* All Finished *-*
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_clear__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_phase__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_triggers__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___dump_triggers__act
|
||||
-V{t#,#} No triggers active
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_anySet__act
|
||||
-V{t#,#} No 'act' region triggers active
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_andNot__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_orInto__act
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_phase__nba
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___trigger_anySet__act
|
||||
-V{t#,#}End-of-eval cleanup
|
||||
-V{t#,#}+ Vt_verilated_debug___024root___eval_final
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue