mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
Fix $past in final blocks on a sampling-tick finish (#8153)
This commit is contained in:
+51
-5
@@ -292,6 +292,8 @@ class AssertVisitor final : public VNVisitor {
|
||||
// Map from (expression, senTree) to AstAlways that computes delayed values of the expression
|
||||
std::unordered_map<VNRef<AstNodeExpr>, std::unordered_map<VNRef<AstSenTree>, AstAlways*>>
|
||||
m_modExpr2Sen2DelayedAlwaysp;
|
||||
// Map from delayed-value AstAlways to its last-sampling-tick time variable
|
||||
std::unordered_map<const AstAlways*, AstVar*> m_delayedAlways2TickTimep;
|
||||
|
||||
// METHODS
|
||||
static string assertCtlGetCall(const char* query, VAssertType type,
|
||||
@@ -537,23 +539,43 @@ class AssertVisitor final : public VNVisitor {
|
||||
|
||||
AstNodeExpr* getPastValue(AstNodeExpr* exprp, AstSenTree* senTreep, uint32_t ticks) {
|
||||
UASSERT_OBJ(ticks > 0, exprp, "Delay must be > 0");
|
||||
AstAlways* const alwaysp = getDelayedAlways(exprp, senTreep);
|
||||
FileLine* const flp = exprp->fileline();
|
||||
return pastValueRef(getDelayedAlways(exprp, senTreep), flp, ticks);
|
||||
}
|
||||
|
||||
AstNodeExpr* pastValueRef(AstAlways* alwaysp, FileLine* flp, uint32_t ticks) {
|
||||
std::vector<AstVar*>& delayedr = m_delayed(alwaysp);
|
||||
// Ensure the required delay exists
|
||||
while (delayedr.size() < ticks) {
|
||||
AstVar* const firstp = delayedr.front();
|
||||
FileLine* const flp = firstp->fileline();
|
||||
FileLine* const varFlp = firstp->fileline();
|
||||
// Create once more delayed value
|
||||
std::string name = firstp->name();
|
||||
name.resize(name.size() - 1);
|
||||
name += std::to_string(delayedr.size() + 1);
|
||||
AstNodeExpr* const prevp = new AstVarRef{flp, delayedr.back(), VAccess::READ};
|
||||
AstNodeExpr* const prevp = new AstVarRef{varFlp, delayedr.back(), VAccess::READ};
|
||||
AstVar* const varp = createDelayedVar(name, alwaysp, prevp);
|
||||
// Add it to delayed variable vector
|
||||
delayedr.emplace_back(varp);
|
||||
}
|
||||
// Return a reference to the appropriately delayed variable
|
||||
return new AstVarRef{exprp->fileline(), delayedr.at(ticks - 1), VAccess::READ};
|
||||
return new AstVarRef{flp, delayedr.at(ticks - 1), VAccess::READ};
|
||||
}
|
||||
|
||||
// Time of the pipeline's last sampling tick, for end-of-simulation readers
|
||||
AstVar* getPastTickTimeVar(AstAlways* alwaysp) {
|
||||
AstVar*& varpr = m_delayedAlways2TickTimep[alwaysp];
|
||||
if (!varpr) {
|
||||
FileLine* const flp = alwaysp->fileline();
|
||||
varpr = new AstVar{flp, VVarType::MODULETEMP,
|
||||
"_Vpast_" + cvtToStr(m_modPastNum++) + "_t",
|
||||
m_modp->findUInt64DType()};
|
||||
m_modp->addStmtsp(varpr);
|
||||
AstNodeExpr* const timep = new AstCExpr{flp, "vlSymsp->_vm_contextp__->time()", 64};
|
||||
alwaysp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, varpr, VAccess::WRITE}, timep});
|
||||
}
|
||||
return varpr;
|
||||
}
|
||||
|
||||
void visitAssertionIterate(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
|
||||
@@ -975,7 +997,25 @@ class AssertVisitor final : public VNVisitor {
|
||||
}
|
||||
UASSERT_OBJ(ticks >= 1, nodep, "0 tick should have been checked in V3Width");
|
||||
AstNodeExpr* const exprp = newSampledExpr(nodep->exprp()->unlinkFrBack());
|
||||
AstNodeExpr* inp = getPastValue(exprp, nodep->sentreep()->unlinkFrBack(), ticks);
|
||||
AstSenTree* const senTreep = nodep->sentreep()->unlinkFrBack();
|
||||
AstNodeExpr* inp = nullptr;
|
||||
// Deliberately emitted for every ftask $past; calls outside final take the normal stage
|
||||
if (VN_IS(m_procedurep, Final) || m_ftaskp) {
|
||||
// A sampling-tick finish reads one stage deeper (IEEE 1800-2023 16.9.3).
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstAlways* const alwaysp = getDelayedAlways(exprp, senTreep);
|
||||
AstNodeExpr* const betweenTicksp = pastValueRef(alwaysp, flp, ticks);
|
||||
AstNodeExpr* const onTickp = pastValueRef(alwaysp, flp, ticks + 1);
|
||||
AstNodeExpr* const onTickCondp = new AstLogAnd{
|
||||
flp, new AstCExpr{flp, "vlSymsp->_vm_contextp__->executingFinal()", 1},
|
||||
new AstEq{flp, new AstVarRef{flp, getPastTickTimeVar(alwaysp), VAccess::READ},
|
||||
new AstCExpr{flp, "vlSymsp->_vm_contextp__->finishPendingTime()", 64}}};
|
||||
AstCond* const condp = new AstCond{flp, onTickCondp, onTickp, betweenTicksp};
|
||||
condp->dtypeFrom(onTickp);
|
||||
inp = condp;
|
||||
} else {
|
||||
inp = getPastValue(exprp, senTreep, ticks);
|
||||
}
|
||||
nodep->replaceWith(inp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
@@ -1213,6 +1253,7 @@ class AssertVisitor final : public VNVisitor {
|
||||
VL_RESTORER(m_modStrobeNum);
|
||||
VL_RESTORER(m_finalp);
|
||||
VL_RESTORER_CLEAR(m_modExpr2Sen2DelayedAlwaysp);
|
||||
VL_RESTORER_CLEAR(m_delayedAlways2TickTimep);
|
||||
m_modp = nodep;
|
||||
m_modPastNum = 0;
|
||||
m_modStrobeNum = 0;
|
||||
@@ -1224,6 +1265,11 @@ class AssertVisitor final : public VNVisitor {
|
||||
m_procedurep = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_ftaskp);
|
||||
m_ftaskp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstGenBlock* nodep) override {
|
||||
// This code is needed rather than a visitor in V3Begin,
|
||||
// because V3Assert is called before V3Begin
|
||||
|
||||
Reference in New Issue
Block a user