mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
This patch adds IEEE-1800 compliant scheduling support for the Inactive scheduling region used for #0 delays. Implementing this requires that **all** IEEE-1800 active region events are placed in the internal 'act' section. This has simulation performance implications. It prevents some optimizations (e.g. V3LifePost), which reduces single threaded performance. It also reduces the available work and parallelism in the internal 'nba' section, which reduced the effectiveness of multi-threading severely. Performance impact on RTLMeter when using scheduling adjusted to support proper #0 delays is ~10-20% slowdown in single-threaded mode, and ~100% (2x slower) with --threads 4. To avoid paying this performance penalty unconditionally, the scheduling is only adjusted if either: 1. The input contains a statically known #0 delay 2. The input contains a variable #x delay unknown at compile time If no #0 is present, but #x variable delays are, a ZERODLY warning is issued advising the use of '--no-sched-zero-delay' which is a promise by the user that none of the variable delays will evaluate to a zero delay at run-time. This warning is turned off if '--sched-zero-delay' is explicitly given. This is similar to the '--timing' option. If '--no-sched-zero-delay' was used at compile time, then executing a zero delay will fail at runtime. A ZERODLY warning is also issued if a static #0 if found, but the user specified '--no-sched-zero-delay'. In this case the scheduling is not adjusted to support #0, so executing it will fail at runtime. Presumably the user knows it won't be executed. The intended behaviour with all this is the following: No #0, no #var in the design (#constant is OK) -> Same as current behaviour, scheduling not adjusted, same code generated as before Has static #0 and '--no-sched-zero-delay' is NOT given: -> No warnings, scheduling adjusted so it just works, runs slow Has static #0 and '--no-sched-zero-delay' is given: -> ZERODLY on the #0, scheduling not adjusted, fails at runtime if hit No static #0, but has #var and no option is given: -> ZERODLY on the #var advising use of '--no-sched-zero-delay' or '--sched-zero-delay' (similar to '--timing'), scheduling adjusted assuming it can be a zero delay and it just works No static #0, but has #var and '--no-sched-zero-delay' is given: -> No warning, scheduling not adjusted, fails at runtime if zero delay No static #0, but has #var and '--sched-zero-delay' is given: -> No warning, scheduling adjusted so it just works
This commit is contained in:
+89
-18
@@ -66,10 +66,12 @@
|
||||
|
||||
#include "V3Const.h"
|
||||
#include "V3EmitV.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3SenExprBuilder.h"
|
||||
#include "V3SenTree.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <queue>
|
||||
@@ -475,6 +477,8 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
int m_forkCnt = 0; // Number of forks inside a module
|
||||
bool m_underJumpBlock = false; // True if we are inside of a jump-block
|
||||
bool m_underProcedure = false; // True if we are under an always or initial
|
||||
bool m_hasStaticZeroDelay = false; // True if we have a static #0 delay
|
||||
std::vector<FileLine*> m_unknownDelayFlps; // Locations of AstDelay with non-constant value
|
||||
|
||||
// Unique names
|
||||
V3UniqueNames m_dlyforkNames{"__Vdlyfork"}; // Names for temp AssignW vars
|
||||
@@ -501,6 +505,11 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
SenTreeFinder m_finder{m_netlistp}; // Sentree finder and uniquifier
|
||||
SenExprBuilder* m_senExprBuilderp = nullptr; // Sens expression builder for current m_scope
|
||||
|
||||
// Stats
|
||||
size_t m_statZeroDelays = 0; // Number of statically known #0 delays
|
||||
size_t m_statConstDelays = 0; // Number of statically known #const (non-zero) delays
|
||||
size_t m_statVariableDelays = 0; // Number of delays with value unknown at compile time
|
||||
|
||||
// METHODS
|
||||
// Transform an assignment with an intra timing control into a timing control with the
|
||||
// assignment under it
|
||||
@@ -916,27 +925,69 @@ class TimingControlVisitor final : public VNVisitor {
|
||||
UASSERT_OBJ(!nodep->isCycleDelay(), nodep,
|
||||
"Cycle delays should have been handled in V3AssertPre");
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
||||
AstConst* const constp = VN_CAST(valuep, Const);
|
||||
if (!constp || !constp->isZero()) {
|
||||
// Scale the delay
|
||||
const double timescaleFactor = calculateTimescaleFactor(nodep, nodep->timeunit());
|
||||
if (valuep->dtypep()->skipRefp()->isDouble()) {
|
||||
valuep = new AstRToIRoundS{
|
||||
flp, new AstMulD{flp, valuep,
|
||||
new AstConst{flp, AstConst::RealDouble{}, timescaleFactor}}};
|
||||
valuep->dtypeSetBitSized(64, VSigning::UNSIGNED);
|
||||
} else {
|
||||
valuep->dtypeSetBitSized(64, VSigning::UNSIGNED);
|
||||
valuep = new AstMul{flp, valuep,
|
||||
new AstConst{flp, AstConst::Unsized64{},
|
||||
static_cast<uint64_t>(timescaleFactor)}};
|
||||
}
|
||||
} else if (constp->num().is1Step()) {
|
||||
|
||||
AstNodeExpr* valuep = nodep->lhsp()->unlinkFrBack();
|
||||
if (VN_IS(valuep, Const) && VN_AS(valuep, Const)->num().is1Step()) {
|
||||
// #1step special case
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
valuep = new AstConst{flp, AstConst::Unsized64{}, 1};
|
||||
valuep->dtypeSetBitSized(64, VSigning::UNSIGNED);
|
||||
} else {
|
||||
// Scale the delay
|
||||
const double timescaleFactorD = calculateTimescaleFactor(nodep, nodep->timeunit());
|
||||
if (valuep->dtypep()->skipRefp()->isDouble()) {
|
||||
AstConst* const tsfp = new AstConst{flp, AstConst::RealDouble{}, timescaleFactorD};
|
||||
valuep = new AstMulD{flp, valuep, tsfp};
|
||||
valuep = new AstRToIRoundS{flp, valuep};
|
||||
valuep->dtypeSetBitSized(64, VSigning::UNSIGNED);
|
||||
} else {
|
||||
valuep->dtypeSetBitSized(64, VSigning::UNSIGNED);
|
||||
const uint64_t timescaleFactorU = static_cast<uint64_t>(timescaleFactorD);
|
||||
AstConst* const tsfp = new AstConst{flp, AstConst::Unsized64{}, timescaleFactorU};
|
||||
valuep = new AstMul{flp, valuep, tsfp};
|
||||
}
|
||||
// Simplify
|
||||
valuep = V3Const::constifyEdit(valuep);
|
||||
}
|
||||
|
||||
// Statistics
|
||||
if (valuep->isZero()) {
|
||||
++m_statZeroDelays;
|
||||
} else if (VN_IS(valuep, Const)) {
|
||||
++m_statConstDelays;
|
||||
} else {
|
||||
++m_statVariableDelays;
|
||||
}
|
||||
|
||||
// Decide scheduling support for #0
|
||||
if (v3Global.opt.schedZeroDelay().isSetTrue()) {
|
||||
// User said to schedule for #0 support, nothing else to do
|
||||
v3Global.setUsesZeroDelay();
|
||||
} else if (v3Global.opt.schedZeroDelay().isSetFalse()) {
|
||||
// User said to schedule without #0 support. Still warn if a static #0 delay exists
|
||||
if (valuep->isZero()) {
|
||||
nodep->v3warn(
|
||||
ZERODLY,
|
||||
"Static #0 delay exists, but '--no-sched-zero-delay' was given.\n"
|
||||
<< nodep->warnMore() //
|
||||
<< "... Can proceed, but this will fail at runtime if executed.");
|
||||
}
|
||||
} else {
|
||||
// User did not express preference, decide based on presence of delays
|
||||
if (valuep->isZero()) {
|
||||
// Statically known #0 delay exists, schedule for #0 support
|
||||
v3Global.setUsesZeroDelay();
|
||||
m_hasStaticZeroDelay = true;
|
||||
// Don't warn on variable delays, as no point
|
||||
m_unknownDelayFlps.clear();
|
||||
} else if (!VN_IS(valuep, Const)) {
|
||||
// Delay is not known at compiile time. Conservatively schedule for #0 support,
|
||||
// but warn if no static #0 delays used as performance might be improved
|
||||
v3Global.setUsesZeroDelay();
|
||||
if (!m_hasStaticZeroDelay) m_unknownDelayFlps.push_back(nodep->fileline());
|
||||
}
|
||||
}
|
||||
|
||||
// Replace self with a 'co_await dlySched.delay(<valuep>)'
|
||||
AstCMethodHard* const delayMethodp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, getCreateDelayScheduler(), VAccess::WRITE},
|
||||
@@ -1320,8 +1371,28 @@ public:
|
||||
explicit TimingControlVisitor(AstNetlist* nodep)
|
||||
: m_netlistp{nodep} {
|
||||
iterate(nodep);
|
||||
|
||||
// If there is no static #0 in the design, but an unknown delay was found,
|
||||
// and the user did not specify preference, then warn on all unknown delays
|
||||
// as we will be assuming they can be #0, which can cause performance degradation.
|
||||
for (FileLine* const flp : m_unknownDelayFlps) {
|
||||
flp->v3warn(ZERODLY,
|
||||
"Value of # delay control statically unknown. Assuming it can be #0.\n"
|
||||
<< flp->warnMore() //
|
||||
<< "... If all # delays are non-zero at runtime,\n"
|
||||
<< flp->warnMore() //
|
||||
<< "... use '--no-sched-zero-delay' for improved performance.\n"
|
||||
<< flp->warnMore() //
|
||||
<< "... If a real #0 is expected at runtime,\n"
|
||||
<< flp->warnMore() //
|
||||
<< "... use '--sched-zero-delay' to suppress this warning.");
|
||||
}
|
||||
}
|
||||
~TimingControlVisitor() override {
|
||||
V3Stats::addStat("Timing, known #0 delays", m_statZeroDelays);
|
||||
V3Stats::addStat("Timing, known #const delays", m_statConstDelays);
|
||||
V3Stats::addStat("Timing, unknown #variable delays", m_statVariableDelays);
|
||||
}
|
||||
~TimingControlVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
||||
Reference in New Issue
Block a user