mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +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:
+77
-14
@@ -132,7 +132,8 @@ EvalLoop createEvalLoop(
|
||||
const string& name, // Name of current phase
|
||||
bool slow, // Should create slow functions
|
||||
const TriggerKit& trigKit, // The trigger kit
|
||||
AstVarScope* trigp, // The trigger vector - may be nullptr if no triggers
|
||||
AstVarScope* trigp, // The trigger vector - may be nullptr if no triggers or using 'condp'
|
||||
AstNodeExpr* condp, // Explicit condition that must be true to run 'phaseWorkp'
|
||||
AstNodeStmt* innerp, // The inner loop, if any
|
||||
AstNodeStmt* phasePrepp, // Prep statements run before checking triggers
|
||||
AstNodeStmt* phaseWorkp, // The work to do if anything triggered
|
||||
@@ -141,9 +142,11 @@ 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};
|
||||
UASSERT(!trigp || !condp, "Cannot use both 'trigp' and 'condp' in 'createEvalLoop'");
|
||||
|
||||
// All work is under a trigger or condition, so if there are none,
|
||||
// there is nothing to do besides executing the inner loop.
|
||||
if (!trigp && !condp) return {nullptr, innerp};
|
||||
|
||||
const std::string varPrefix = "__V" + tag;
|
||||
AstScope* const scopeTopp = netlistp->topScopep()->scopep();
|
||||
@@ -161,9 +164,10 @@ EvalLoop createEvalLoop(
|
||||
|
||||
// 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);
|
||||
// If using explicit condition, that directly determines whether to execute,
|
||||
// otherwise check if any triggers are fired
|
||||
AstNodeExpr* const rhsp = condp ? condp : trigKit.newAnySetCall(trigp);
|
||||
phaseFuncp->addStmtsp(new AstAssign{flp, lhsp, rhsp});
|
||||
|
||||
// Add the work
|
||||
@@ -216,8 +220,8 @@ EvalLoop createEvalLoop(
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
stmtps->addNext(loopp);
|
||||
|
||||
// Check the iteration limit (aborts if exceeded)
|
||||
AstNodeStmt* const dumpCallp = trigKit.newDumpCall(trigp, tag, false);
|
||||
// Check the iteration limit (aborts if exceeded). Dump triggers if using triggers.
|
||||
AstNodeStmt* dumpCallp = trigp ? trigKit.newDumpCall(trigp, tag, false) : nullptr;
|
||||
loopp->addStmtsp(util::checkIterationLimit(netlistp, name, counterp, dumpCallp));
|
||||
// Increment the iteration counter
|
||||
loopp->addStmtsp(util::incrementVar(counterp));
|
||||
@@ -442,7 +446,10 @@ void createSettle(AstNetlist* netlistp, AstCFunc* const initFuncp, SenExprBuilde
|
||||
|
||||
// Create the eval loop
|
||||
const EvalLoop stlLoop = createEvalLoop( //
|
||||
netlistp, "stl", "Settle", /* slow: */ true, trigKit, trigKit.vscp(),
|
||||
netlistp, "stl", "Settle", /* slow: */ true, trigKit,
|
||||
// Use trigger
|
||||
trigKit.vscp(), nullptr,
|
||||
// Explicit condition
|
||||
// Inner loop statements
|
||||
nullptr,
|
||||
// Prep statements: Compute the current 'stl' triggers
|
||||
@@ -550,7 +557,9 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
||||
|
||||
// Create the eval loop
|
||||
const EvalLoop icoLoop = createEvalLoop( //
|
||||
netlistp, "ico", "Input combinational", /* slow: */ false, trigKit, trigKit.vscp(),
|
||||
netlistp, "ico", "Input combinational", /* slow: */ false, trigKit,
|
||||
// Use trigger
|
||||
trigKit.vscp(), nullptr,
|
||||
// Inner loop statements
|
||||
nullptr,
|
||||
// Prep statements: Compute the current 'ico' triggers
|
||||
@@ -595,13 +604,18 @@ void createEval(AstNetlist* netlistp, //
|
||||
) {
|
||||
FileLine* const flp = netlistp->fileline();
|
||||
|
||||
// Grab the delay scheduler variable, if any
|
||||
AstVarScope* const delaySchedVscp = timingKit.getDelayScheduler(netlistp);
|
||||
|
||||
// 'createResume' consumes the contents that 'createReady' needs, so do the right order
|
||||
AstCCall* const timingReadyp = timingKit.createReady(netlistp);
|
||||
AstCCall* const timingResumep = timingKit.createResume(netlistp);
|
||||
|
||||
// Create the active eval loop
|
||||
EvalLoop topLoop = createEvalLoop( //
|
||||
netlistp, "act", "Active", /* slow: */ false, trigKit, actKit.m_vscp,
|
||||
netlistp, "act", "Active", /* slow: */ false, trigKit,
|
||||
// Use trigger
|
||||
actKit.m_vscp, nullptr,
|
||||
// Inner loop statements
|
||||
nullptr,
|
||||
// Prep statements
|
||||
@@ -640,9 +654,54 @@ void createEval(AstNetlist* netlistp, //
|
||||
return workp;
|
||||
}());
|
||||
|
||||
// Create if there are any delays, so we can check at runtime if a #0 is unexpected
|
||||
if (delaySchedVscp) {
|
||||
topLoop = createEvalLoop( //
|
||||
netlistp, "inact", "Inactive", /* slow: */ false, trigKit,
|
||||
// Use explicit condition
|
||||
nullptr,
|
||||
[&]() {
|
||||
// Run if any zero delays are pending
|
||||
AstNodeExpr* const callp
|
||||
= new AstCMethodHard{flp, new AstVarRef{flp, delaySchedVscp, VAccess::READ},
|
||||
VCMethod::SCHED_AWAITING_ZERO_DELAY};
|
||||
callp->dtypeSetBit();
|
||||
return callp;
|
||||
}(),
|
||||
// Inner loop statements
|
||||
topLoop.stmtsp,
|
||||
// Prep statements
|
||||
nullptr,
|
||||
// Work statements
|
||||
[&]() -> AstNodeStmt* {
|
||||
if (v3Global.usesZeroDelay()) {
|
||||
// Resume processes watiting for #0 delay
|
||||
AstCMethodHard* const callp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, delaySchedVscp, VAccess::READWRITE},
|
||||
VCMethod::SCHED_RESUME_ZERO_DELAY};
|
||||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
} else {
|
||||
// Assumption was that the design doesn't use #0 delays.
|
||||
// Die at run-time if it does.
|
||||
AstCStmt* const stmtp = new AstCStmt{flp};
|
||||
const 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(
|
||||
"VL_FATAL_MT(\"" + V3OutFormatter::quoteNameControls(file) + "\", " + line
|
||||
+ ", \"\", \"ZERODLY: Design Verilated with '--no-sched-zero-delay', "
|
||||
+ "but #0 delay executed at runtime\");");
|
||||
return stmtp;
|
||||
}
|
||||
}());
|
||||
}
|
||||
|
||||
// Create the NBA eval loop, which is the default top level loop.
|
||||
topLoop = createEvalLoop( //
|
||||
netlistp, "nba", "NBA", /* slow: */ false, trigKit, nbaKit.m_vscp,
|
||||
netlistp, "nba", "NBA", /* slow: */ false, trigKit,
|
||||
// Use trigger
|
||||
nbaKit.m_vscp, nullptr,
|
||||
// Inner loop statements
|
||||
topLoop.stmtsp,
|
||||
// Prep statements
|
||||
@@ -687,7 +746,9 @@ 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, trigKit, obsKit.m_vscp,
|
||||
netlistp, "obs", "Observed", /* slow: */ false, trigKit,
|
||||
// Use trigger
|
||||
obsKit.m_vscp, nullptr,
|
||||
// Inner loop statements
|
||||
topLoop.stmtsp,
|
||||
// Prep statements
|
||||
@@ -711,7 +772,9 @@ 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, trigKit, reactKit.m_vscp,
|
||||
netlistp, "react", "Reactive", /* slow: */ false, trigKit,
|
||||
// Use trigger
|
||||
reactKit.m_vscp, nullptr,
|
||||
// Inner loop statements
|
||||
topLoop.stmtsp,
|
||||
// Prep statements
|
||||
|
||||
Reference in New Issue
Block a user