Use unsigned in NFA, guard against overflow using V3Width. (#8152)
Signed-off-by: Artur Bieniek <abieniek@antmicro.com>
This commit is contained in:
parent
f5ae58ddab
commit
06ee8b7261
|
|
@ -72,7 +72,7 @@ public:
|
||||||
std::vector<AstNodeExpr*> m_throughoutConds;
|
std::vector<AstNodeExpr*> m_throughoutConds;
|
||||||
// Nonzero for a bitset ring-buffer vertex for ## delays.
|
// Nonzero for a bitset ring-buffer vertex for ## delays.
|
||||||
bool m_isFixedDelayRing = false;
|
bool m_isFixedDelayRing = false;
|
||||||
int m_delayRingSize = 0; // Fixed delay cycles. Range: max-min+1.
|
unsigned m_delayRingSize = 0; // Fixed delay cycles. Range: max-min+1.
|
||||||
AstNodeExpr* m_delayRingClearCondp = nullptr; // local RHS for pure-boolean range
|
AstNodeExpr* m_delayRingClearCondp = nullptr; // local RHS for pure-boolean range
|
||||||
// OWNED; enclosing-abort fire condition clearing in-flight ring bits
|
// OWNED; enclosing-abort fire condition clearing in-flight ring bits
|
||||||
AstNodeExpr* m_abortClearp = nullptr;
|
AstNodeExpr* m_abortClearp = nullptr;
|
||||||
|
|
@ -324,7 +324,7 @@ class SvaNfaBuilder final {
|
||||||
|
|
||||||
struct RangeDelayRejectInfo final {
|
struct RangeDelayRejectInfo final {
|
||||||
SvaStateVertex* startp = nullptr;
|
SvaStateVertex* startp = nullptr;
|
||||||
int range = 0;
|
unsigned range = 0;
|
||||||
int rhsLen = 0;
|
int rhsLen = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -354,10 +354,10 @@ class SvaNfaBuilder final {
|
||||||
return guardp;
|
return guardp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getConstInt(AstNodeExpr* exprp) {
|
static unsigned getConstUInt(AstNodeExpr* exprp) {
|
||||||
AstNodeExpr* const constp = V3Const::constifyEdit(exprp->cloneTreePure(false));
|
AstNodeExpr* const constp = V3Const::constifyEdit(exprp->cloneTreePure(false));
|
||||||
const AstConst* const cp = VN_CAST(constp, Const);
|
const AstConst* const cp = VN_CAST(constp, Const);
|
||||||
const int val = cp ? cp->toSInt() : -1;
|
const unsigned val = cp ? cp->toUInt() : 0;
|
||||||
VL_DO_DANGLING(constp->deleteTree(), constp);
|
VL_DO_DANGLING(constp->deleteTree(), constp);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
@ -379,16 +379,15 @@ class SvaNfaBuilder final {
|
||||||
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
|
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
|
||||||
AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay);
|
AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay);
|
||||||
if (!delayp || !delayp->isCycleDelay()) return -1;
|
if (!delayp || !delayp->isCycleDelay()) return -1;
|
||||||
int delayCycles = -1;
|
unsigned delayCycles;
|
||||||
if (delayp->isRangeDelay()) {
|
if (delayp->isRangeDelay()) {
|
||||||
if (delayp->isUnbounded()) return -1; // LCOV_EXCL_LINE
|
if (delayp->isUnbounded()) return -1; // LCOV_EXCL_LINE
|
||||||
const int minD = getConstInt(delayp->lhsp());
|
const unsigned minD = getConstUInt(delayp->lhsp());
|
||||||
const int maxD = getConstInt(delayp->rhsp());
|
const unsigned maxD = getConstUInt(delayp->rhsp());
|
||||||
if (minD < 0 || maxD < 0 || minD != maxD) return -1;
|
if (minD != maxD) return -1;
|
||||||
delayCycles = minD;
|
delayCycles = minD;
|
||||||
} else {
|
} else {
|
||||||
delayCycles = getConstInt(delayp->lhsp());
|
delayCycles = getConstUInt(delayp->lhsp());
|
||||||
if (delayCycles < 0) return -1; // LCOV_EXCL_LINE
|
|
||||||
}
|
}
|
||||||
int preLen = 0;
|
int preLen = 0;
|
||||||
if (AstNodeExpr* const prep = sexprp->preExprp()) {
|
if (AstNodeExpr* const prep = sexprp->preExprp()) {
|
||||||
|
|
@ -439,13 +438,11 @@ class SvaNfaBuilder final {
|
||||||
std::pair<int, int> delayRange;
|
std::pair<int, int> delayRange;
|
||||||
if (delayp->isRangeDelay()) {
|
if (delayp->isRangeDelay()) {
|
||||||
if (delayp->isUnbounded()) return {-1, -1};
|
if (delayp->isUnbounded()) return {-1, -1};
|
||||||
const int minD = getConstInt(delayp->lhsp());
|
const unsigned minD = getConstUInt(delayp->lhsp());
|
||||||
const int maxD = getConstInt(delayp->rhsp());
|
const unsigned maxD = getConstUInt(delayp->rhsp());
|
||||||
if (minD < 0 || maxD < 0 || maxD < minD) return {-1, -1};
|
|
||||||
delayRange = {minD, maxD};
|
delayRange = {minD, maxD};
|
||||||
} else {
|
} else {
|
||||||
const int d = getConstInt(delayp->lhsp());
|
const unsigned d = getConstUInt(delayp->lhsp());
|
||||||
if (d < 0) return {-1, -1};
|
|
||||||
delayRange = {d, d};
|
delayRange = {d, d};
|
||||||
}
|
}
|
||||||
std::pair<int, int> preRange{0, 0};
|
std::pair<int, int> preRange{0, 0};
|
||||||
|
|
@ -478,15 +475,15 @@ class SvaNfaBuilder final {
|
||||||
AstDelay* rangeDelayp = nullptr;
|
AstDelay* rangeDelayp = nullptr;
|
||||||
clonep->foreach([&](AstDelay* dp) {
|
clonep->foreach([&](AstDelay* dp) {
|
||||||
if (!rangeDelayp && dp->isRangeDelay() && !dp->isUnbounded()
|
if (!rangeDelayp && dp->isRangeDelay() && !dp->isUnbounded()
|
||||||
&& getConstInt(dp->lhsp()) != getConstInt(dp->rhsp())) {
|
&& getConstUInt(dp->lhsp()) != getConstUInt(dp->rhsp())) {
|
||||||
rangeDelayp = dp;
|
rangeDelayp = dp;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (rangeDelayp) {
|
if (rangeDelayp) {
|
||||||
FileLine* const flp = rangeDelayp->fileline();
|
FileLine* const flp = rangeDelayp->fileline();
|
||||||
const int pinned = getConstInt(rangeDelayp->lhsp()) + (len - lo);
|
const unsigned pinned = getConstUInt(rangeDelayp->lhsp()) + (len - lo);
|
||||||
AstNodeExpr* const oldMinp = rangeDelayp->lhsp();
|
AstNodeExpr* const oldMinp = rangeDelayp->lhsp();
|
||||||
oldMinp->replaceWith(new AstConst{flp, static_cast<uint32_t>(pinned)});
|
oldMinp->replaceWith(new AstConst{flp, pinned});
|
||||||
VL_DO_DANGLING(oldMinp->deleteTree(), oldMinp);
|
VL_DO_DANGLING(oldMinp->deleteTree(), oldMinp);
|
||||||
// Drop the max bound so it lowers as a fixed `##d`, not `##[d:d]`.
|
// Drop the max bound so it lowers as a fixed `##d`, not `##[d:d]`.
|
||||||
AstNode* const oldMaxp = rangeDelayp->rhsp()->unlinkFrBack();
|
AstNode* const oldMaxp = rangeDelayp->rhsp()->unlinkFrBack();
|
||||||
|
|
@ -501,8 +498,8 @@ class SvaNfaBuilder final {
|
||||||
// any exprp -- even an impure one would now evaluate exactly once per
|
// any exprp -- even an impure one would now evaluate exactly once per
|
||||||
// clock instead of N times. Orphan temps from failed builds are unused
|
// clock instead of N times. Orphan temps from failed builds are unused
|
||||||
// MODULETEMPs and are removed by V3Dead.
|
// MODULETEMPs and are removed by V3Dead.
|
||||||
AstVar* tryHoistSampled(AstNodeExpr* exprp, FileLine* flp, int cloneCount) {
|
AstVar* tryHoistSampled(AstNodeExpr* exprp, FileLine* flp, unsigned cloneCount) {
|
||||||
constexpr int kHoistThreshold = 2;
|
constexpr unsigned kHoistThreshold = 2;
|
||||||
if (cloneCount < kHoistThreshold) return nullptr;
|
if (cloneCount < kHoistThreshold) return nullptr;
|
||||||
AstVar* const tempVarp
|
AstVar* const tempVarp
|
||||||
= new AstVar{flp, VVarType::MODULETEMP, m_propTempNames.get(exprp), exprp->dtypep()};
|
= new AstVar{flp, VVarType::MODULETEMP, m_propTempNames.get(exprp), exprp->dtypep()};
|
||||||
|
|
@ -520,9 +517,9 @@ class SvaNfaBuilder final {
|
||||||
|
|
||||||
// Reject concurrent assertions whose unrolled vertex count would exceed
|
// Reject concurrent assertions whose unrolled vertex count would exceed
|
||||||
// --assert-unroll-limit, so a pathological count cannot blow up compile time.
|
// --assert-unroll-limit, so a pathological count cannot blow up compile time.
|
||||||
static bool exceedsAssertUnrollLimit(AstNode* nodep, int requested) {
|
static bool exceedsAssertUnrollLimit(AstNode* nodep, unsigned requested) {
|
||||||
const int limit = v3Global.opt.assertUnrollLimit();
|
const int limit = v3Global.opt.assertUnrollLimit();
|
||||||
if (requested <= limit) return false;
|
if (limit >= 0 && requested <= static_cast<unsigned>(limit)) return false;
|
||||||
nodep->v3error("Concurrent assertion repetition count "
|
nodep->v3error("Concurrent assertion repetition count "
|
||||||
<< requested << " exceeds --assert-unroll-limit (" << limit
|
<< requested << " exceeds --assert-unroll-limit (" << limit
|
||||||
<< "); raise '--assert-unroll-limit' to compile");
|
<< "); raise '--assert-unroll-limit' to compile");
|
||||||
|
|
@ -556,7 +553,7 @@ class SvaNfaBuilder final {
|
||||||
return m_graph.addClockedEdge(fromp, top, throughoutCond(nullptr, flp));
|
return m_graph.addClockedEdge(fromp, top, throughoutCond(nullptr, flp));
|
||||||
}
|
}
|
||||||
|
|
||||||
SvaStateVertex* addDelayChain(SvaStateVertex* startp, int size, FileLine* flp,
|
SvaStateVertex* addDelayChain(SvaStateVertex* startp, unsigned size, FileLine* flp,
|
||||||
bool isFixed = true, AstNodeExpr* clearCondp = nullptr) {
|
bool isFixed = true, AstNodeExpr* clearCondp = nullptr) {
|
||||||
if (isFixed && size == 0) return startp;
|
if (isFixed && size == 0) return startp;
|
||||||
UASSERT_OBJ(size > 0, startp, "Delay chain needs at least one slot");
|
UASSERT_OBJ(size > 0, startp, "Delay chain needs at least one slot");
|
||||||
|
|
@ -587,13 +584,7 @@ class SvaNfaBuilder final {
|
||||||
bool applyRangeDelay(AstDelay* delayp, AstNodeExpr* rhsExprp, SvaStateVertex*& currentp,
|
bool applyRangeDelay(AstDelay* delayp, AstNodeExpr* rhsExprp, SvaStateVertex*& currentp,
|
||||||
std::vector<SvaStateVertex*>& midSources, FileLine* flp,
|
std::vector<SvaStateVertex*>& midSources, FileLine* flp,
|
||||||
bool& outErrorEmitted, RangeDelayRejectInfo* rangeRejectInfop = nullptr) {
|
bool& outErrorEmitted, RangeDelayRejectInfo* rangeRejectInfop = nullptr) {
|
||||||
const int minDelay = getConstInt(delayp->lhsp());
|
const unsigned minDelay = getConstUInt(delayp->lhsp());
|
||||||
if (minDelay < 0) {
|
|
||||||
delayp->v3error("Range delay minimum is not a non-negative elaboration-time constant"
|
|
||||||
" (IEEE 1800-2023 16.7)");
|
|
||||||
outErrorEmitted = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (delayp->isUnbounded()) {
|
if (delayp->isUnbounded()) {
|
||||||
// `##[M:$]`: wait M cycles, then self-loop waiting for the match
|
// `##[M:$]`: wait M cycles, then self-loop waiting for the match
|
||||||
// condition. Unbounded = liveness, so no reject.
|
// condition. Unbounded = liveness, so no reject.
|
||||||
|
|
@ -603,29 +594,18 @@ class SvaNfaBuilder final {
|
||||||
m_inUnboundedScope = true;
|
m_inUnboundedScope = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const int maxDelay = getConstInt(delayp->rhsp());
|
const unsigned maxDelay = getConstUInt(delayp->rhsp());
|
||||||
if (maxDelay < 0) {
|
|
||||||
delayp->v3error("Range delay maximum is not a non-negative elaboration-time constant"
|
|
||||||
" (IEEE 1800-2023 16.7)");
|
|
||||||
outErrorEmitted = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (maxDelay < minDelay) {
|
|
||||||
delayp->v3error("Range delay maximum must be >= minimum (IEEE 1800-2023 16.7)");
|
|
||||||
outErrorEmitted = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (minDelay == maxDelay) {
|
if (minDelay == maxDelay) {
|
||||||
currentp = addDelayChain(currentp, minDelay, flp);
|
currentp = addDelayChain(currentp, minDelay, flp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const int range = maxDelay - minDelay;
|
const unsigned range = maxDelay - minDelay;
|
||||||
currentp = addDelayChain(currentp, minDelay, flp);
|
currentp = addDelayChain(currentp, minDelay, flp);
|
||||||
// kChainLimit bounds per-attempt unrolled vertices. Above this, a
|
// kChainLimit bounds per-attempt unrolled vertices. Above this, a
|
||||||
// ring buffer (constant-size state) is used instead, so the vertex
|
// ring buffer (constant-size state) is used instead, so the vertex
|
||||||
// count is O(1) in range regardless of user input; no adversarial N
|
// count is O(1) in range regardless of user input; no adversarial N
|
||||||
// blowup is possible.
|
// blowup is possible.
|
||||||
constexpr int kChainLimit = 256;
|
constexpr unsigned kChainLimit = 256;
|
||||||
// IEEE 1800-2023 16.14.3: only a small bounded range before a plain
|
// IEEE 1800-2023 16.14.3: only a small bounded range before a plain
|
||||||
// boolean enumerates every end-of-match below. The counter FSM drops
|
// boolean enumerates every end-of-match below. The counter FSM drops
|
||||||
// overlapping ends and the nested-sequence merge collapses them, so
|
// overlapping ends and the nested-sequence merge collapses them, so
|
||||||
|
|
@ -636,7 +616,7 @@ class SvaNfaBuilder final {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (range > kChainLimit) {
|
if (range > kChainLimit) {
|
||||||
currentp = addDelayChain(currentp, range + 1, flp, false,
|
currentp = addDelayChain(currentp, range + 1U, flp, false,
|
||||||
rhsExprp->isMultiCycleSva() ? nullptr : rhsExprp);
|
rhsExprp->isMultiCycleSva() ? nullptr : rhsExprp);
|
||||||
} else if (VN_IS(rhsExprp, SExpr)) {
|
} else if (VN_IS(rhsExprp, SExpr)) {
|
||||||
// Nested-SExpr RHS: merge all [M,N] positions. Candidate-local misses
|
// Nested-SExpr RHS: merge all [M,N] positions. Candidate-local misses
|
||||||
|
|
@ -648,7 +628,7 @@ class SvaNfaBuilder final {
|
||||||
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
|
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
|
||||||
mergeVtxp->m_isUnbounded = true;
|
mergeVtxp->m_isUnbounded = true;
|
||||||
guardedLink(currentp, mergeVtxp, flp);
|
guardedLink(currentp, mergeVtxp, flp);
|
||||||
for (int i = 0; i < range; ++i) {
|
for (unsigned i = 0; i < range; ++i) {
|
||||||
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
||||||
guardedEdge(currentp, nextVtxp, flp);
|
guardedEdge(currentp, nextVtxp, flp);
|
||||||
guardedLink(nextVtxp, mergeVtxp, flp);
|
guardedLink(nextVtxp, mergeVtxp, flp);
|
||||||
|
|
@ -665,7 +645,7 @@ class SvaNfaBuilder final {
|
||||||
AstVar* const hoistVarp
|
AstVar* const hoistVarp
|
||||||
= m_isCoverSeq ? nullptr : tryHoistSampled(rhsExprp, flp, range);
|
= m_isCoverSeq ? nullptr : tryHoistSampled(rhsExprp, flp, range);
|
||||||
midSources.push_back(currentp);
|
midSources.push_back(currentp);
|
||||||
for (int i = 0; i < range; ++i) {
|
for (unsigned i = 0; i < range; ++i) {
|
||||||
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
||||||
if (m_isCoverSeq) {
|
if (m_isCoverSeq) {
|
||||||
guardedEdge(currentp, nextVtxp, flp);
|
guardedEdge(currentp, nextVtxp, flp);
|
||||||
|
|
@ -697,7 +677,7 @@ class SvaNfaBuilder final {
|
||||||
guardedLink(srcp, successNowp, condp, flp);
|
guardedLink(srcp, successNowp, condp, flp);
|
||||||
SvaStateVertex* stagep = successNowp;
|
SvaStateVertex* stagep = successNowp;
|
||||||
guardedLink(stagep, expiryMatchp, flp);
|
guardedLink(stagep, expiryMatchp, flp);
|
||||||
for (int i = 0; i < info.range; ++i) {
|
for (unsigned i = 0; i < info.range; ++i) {
|
||||||
SvaStateVertex* const nextp = scopedCreateVertex();
|
SvaStateVertex* const nextp = scopedCreateVertex();
|
||||||
guardedEdge(stagep, nextp, flp);
|
guardedEdge(stagep, nextp, flp);
|
||||||
stagep = nextp;
|
stagep = nextp;
|
||||||
|
|
@ -751,13 +731,7 @@ class SvaNfaBuilder final {
|
||||||
return BuildResult::fail(errorEmitted);
|
return BuildResult::fail(errorEmitted);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const int delayCycles = getConstInt(delayp->lhsp());
|
const unsigned delayCycles = getConstUInt(delayp->lhsp());
|
||||||
if (delayCycles < 0) {
|
|
||||||
delayp->v3error("Delay value is not a non-negative"
|
|
||||||
" elaboration-time constant"
|
|
||||||
" (IEEE 1800-2023 16.7)");
|
|
||||||
return BuildResult::failWithError();
|
|
||||||
}
|
|
||||||
currentp = addDelayChain(currentp, delayCycles, flp);
|
currentp = addDelayChain(currentp, delayCycles, flp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -780,16 +754,15 @@ class SvaNfaBuilder final {
|
||||||
" consecutive repetition (IEEE 1800-2023 16.9.2)");
|
" consecutive repetition (IEEE 1800-2023 16.9.2)");
|
||||||
return BuildResult::failWithError();
|
return BuildResult::failWithError();
|
||||||
}
|
}
|
||||||
const int minN = getConstInt(repp->countp());
|
const unsigned minN = getConstUInt(repp->countp());
|
||||||
UASSERT_OBJ(minN >= 0, repp, "ConsRep count must be non-negative (V3Width invariant)");
|
|
||||||
|
|
||||||
// Sum sites across prefix + unbounded/range tail so one hoist covers
|
// Sum sites across prefix + unbounded/range tail so one hoist covers
|
||||||
// every check edge of this repetition.
|
// every check edge of this repetition.
|
||||||
int totalSites = minN;
|
unsigned totalSites = minN;
|
||||||
if (repp->unbounded()) {
|
if (repp->unbounded()) {
|
||||||
totalSites += 1;
|
totalSites += 1;
|
||||||
} else if (repp->maxCountp()) {
|
} else if (repp->maxCountp()) {
|
||||||
totalSites += getConstInt(repp->maxCountp()) - minN;
|
totalSites += getConstUInt(repp->maxCountp()) - minN;
|
||||||
}
|
}
|
||||||
if (exceedsAssertUnrollLimit(repp, totalSites)) return BuildResult::failWithError();
|
if (exceedsAssertUnrollLimit(repp, totalSites)) return BuildResult::failWithError();
|
||||||
AstVar* const hoistVarp = tryHoistSampled(exprp, flp, totalSites);
|
AstVar* const hoistVarp = tryHoistSampled(exprp, flp, totalSites);
|
||||||
|
|
@ -799,7 +772,7 @@ class SvaNfaBuilder final {
|
||||||
std::vector<SvaStateVertex*> consMidSources;
|
std::vector<SvaStateVertex*> consMidSources;
|
||||||
|
|
||||||
SvaStateVertex* currentp = entryVtxp;
|
SvaStateVertex* currentp = entryVtxp;
|
||||||
for (int i = 0; i < minN; ++i) {
|
for (unsigned i = 0; i < minN; ++i) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
SvaStateVertex* const nextp = scopedCreateVertex();
|
SvaStateVertex* const nextp = scopedCreateVertex();
|
||||||
guardedEdge(currentp, nextp, flp);
|
guardedEdge(currentp, nextp, flp);
|
||||||
|
|
@ -838,11 +811,10 @@ class SvaNfaBuilder final {
|
||||||
currentp->m_isUnbounded = true;
|
currentp->m_isUnbounded = true;
|
||||||
m_inUnboundedScope = true;
|
m_inUnboundedScope = true;
|
||||||
} else if (repp->maxCountp()) {
|
} else if (repp->maxCountp()) {
|
||||||
const int maxN = getConstInt(repp->maxCountp());
|
const unsigned maxN = getConstUInt(repp->maxCountp());
|
||||||
UASSERT_OBJ(maxN >= minN, repp, "ConsRep range max < min (V3Width invariant)");
|
|
||||||
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
|
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
|
||||||
guardedLink(currentp, mergeVtxp, flp);
|
guardedLink(currentp, mergeVtxp, flp);
|
||||||
for (int i = minN; i < maxN; ++i) {
|
for (unsigned i = minN; i < maxN; ++i) {
|
||||||
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
SvaStateVertex* const nextVtxp = scopedCreateVertex();
|
||||||
guardedEdge(currentp, nextVtxp, flp);
|
guardedEdge(currentp, nextVtxp, flp);
|
||||||
SvaStateVertex* const checkVtxp = scopedCreateVertex();
|
SvaStateVertex* const checkVtxp = scopedCreateVertex();
|
||||||
|
|
@ -869,7 +841,7 @@ class SvaNfaBuilder final {
|
||||||
bool isTopLevelStep = false) {
|
bool isTopLevelStep = false) {
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
AstNodeExpr* const propp = nodep->propp();
|
AstNodeExpr* const propp = nodep->propp();
|
||||||
const int lo = getConstInt(nodep->loBoundp());
|
const unsigned lo = getConstUInt(nodep->loBoundp());
|
||||||
if (VN_IS(nodep->hiBoundp(), Unbounded)) {
|
if (VN_IS(nodep->hiBoundp(), Unbounded)) {
|
||||||
// Weak always [lo:$]: unbounded upper bound (IEEE 1800-2023 16.12.11).
|
// Weak always [lo:$]: unbounded upper bound (IEEE 1800-2023 16.12.11).
|
||||||
// p must hold at every clock tick at least lo cycles after the attempt
|
// p must hold at every clock tick at least lo cycles after the attempt
|
||||||
|
|
@ -877,8 +849,7 @@ class SvaNfaBuilder final {
|
||||||
// end-of-trace obligation (weak). The self-loop keeps the attempt live
|
// end-of-trace obligation (weak). The self-loop keeps the attempt live
|
||||||
// every cycle; each observed cycle is a safety obligation, so a false p
|
// every cycle; each observed cycle is a safety obligation, so a false p
|
||||||
// rejects immediately.
|
// rejects immediately.
|
||||||
UASSERT_OBJ(!nodep->isStrong() && lo >= 0, nodep,
|
UASSERT_OBJ(!nodep->isStrong(), nodep, "Unbounded always must be weak (V3Width)");
|
||||||
"Unbounded always must be weak with non-negative lo (V3Width)");
|
|
||||||
SvaStateVertex* const livep = addDelayChain(entryVtxp, lo, flp);
|
SvaStateVertex* const livep = addDelayChain(entryVtxp, lo, flp);
|
||||||
livep->m_isUnbounded = true;
|
livep->m_isUnbounded = true;
|
||||||
guardedEdge(livep, livep, flp); // stay active every subsequent cycle
|
guardedEdge(livep, livep, flp); // stay active every subsequent cycle
|
||||||
|
|
@ -889,8 +860,7 @@ class SvaNfaBuilder final {
|
||||||
if (isTopLevelStep) rejEdgep->m_rejectOnFail = true;
|
if (isTopLevelStep) rejEdgep->m_rejectOnFail = true;
|
||||||
return {livep, nullptr, {}};
|
return {livep, nullptr, {}};
|
||||||
}
|
}
|
||||||
const int hi = getConstInt(nodep->hiBoundp());
|
const unsigned hi = getConstUInt(nodep->hiBoundp());
|
||||||
UASSERT_OBJ(lo >= 0 && hi >= lo, nodep, "PropAlways bounds invariant (V3Width)");
|
|
||||||
// Strong s_always[m:n]: mark every in-window registered vertex so an
|
// Strong s_always[m:n]: mark every in-window registered vertex so an
|
||||||
// attempt still mid-window at end-of-simulation is reported as a liveness
|
// attempt still mid-window at end-of-simulation is reported as a liveness
|
||||||
// failure (IEEE strong: the n+1 ticks must exist). An attempt that has
|
// failure (IEEE strong: the n+1 ticks must exist). An attempt that has
|
||||||
|
|
@ -917,11 +887,10 @@ class SvaNfaBuilder final {
|
||||||
BuildResult buildGotoRep(AstSGotoRep* repp, SvaStateVertex* entryVtxp) {
|
BuildResult buildGotoRep(AstSGotoRep* repp, SvaStateVertex* entryVtxp) {
|
||||||
FileLine* const flp = repp->fileline();
|
FileLine* const flp = repp->fileline();
|
||||||
AstNodeExpr* const exprp = repp->exprp();
|
AstNodeExpr* const exprp = repp->exprp();
|
||||||
const int minN = getConstInt(repp->countp());
|
const unsigned minN = getConstUInt(repp->countp());
|
||||||
if (minN <= 0) return BuildResult::fail();
|
if (minN == 0) return BuildResult::fail();
|
||||||
const bool hasMax = repp->maxCountp() != nullptr;
|
const bool hasMax = repp->maxCountp() != nullptr;
|
||||||
const int maxN = hasMax ? getConstInt(repp->maxCountp()) : minN;
|
const unsigned maxN = hasMax ? getConstUInt(repp->maxCountp()) : minN;
|
||||||
UASSERT_OBJ(maxN >= minN, repp, "GotoRep range max < min (V3Width invariant)");
|
|
||||||
if (exceedsAssertUnrollLimit(repp, maxN)) return BuildResult::failWithError();
|
if (exceedsAssertUnrollLimit(repp, maxN)) return BuildResult::failWithError();
|
||||||
|
|
||||||
if (m_isCoverSeq) {
|
if (m_isCoverSeq) {
|
||||||
|
|
@ -935,10 +904,10 @@ class SvaNfaBuilder final {
|
||||||
// sites for every iteration in [0..maxN). NOT($sampled(x)) matches
|
// sites for every iteration in [0..maxN). NOT($sampled(x)) matches
|
||||||
// $sampled(NOT(x)) at the value level (IEEE 1800-2023 16.9.9);
|
// $sampled(NOT(x)) at the value level (IEEE 1800-2023 16.9.9);
|
||||||
// purity is enforced uniformly via cloneTreePure inside sampledRefOrClone.
|
// purity is enforced uniformly via cloneTreePure inside sampledRefOrClone.
|
||||||
AstVar* const hoistVarp = tryHoistSampled(exprp, flp, 2 * maxN);
|
AstVar* const hoistVarp = tryHoistSampled(exprp, flp, 2U * maxN);
|
||||||
SvaStateVertex* currentp = entryVtxp;
|
SvaStateVertex* currentp = entryVtxp;
|
||||||
// Build minN match-wait chains to reach the first accept point.
|
// Build minN match-wait chains to reach the first accept point.
|
||||||
for (int i = 0; i < minN; ++i) {
|
for (unsigned i = 0; i < minN; ++i) {
|
||||||
SvaStateVertex* const waitVtxp = scopedCreateVertex();
|
SvaStateVertex* const waitVtxp = scopedCreateVertex();
|
||||||
// Edge (not Link) for all iterations: IEEE expansion ##1 before each
|
// Edge (not Link) for all iterations: IEEE expansion ##1 before each
|
||||||
// match. A Link at i==0 was wrong -- it allowed same-cycle matching
|
// match. A Link at i==0 was wrong -- it allowed same-cycle matching
|
||||||
|
|
@ -961,7 +930,7 @@ class SvaNfaBuilder final {
|
||||||
// buildConsRep's range fan-out.
|
// buildConsRep's range fan-out.
|
||||||
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
|
SvaStateVertex* const mergeVtxp = scopedCreateVertex();
|
||||||
guardedLink(currentp, mergeVtxp, flp); // accept at match_M
|
guardedLink(currentp, mergeVtxp, flp); // accept at match_M
|
||||||
for (int i = minN; i < maxN; ++i) {
|
for (unsigned i = minN; i < maxN; ++i) {
|
||||||
SvaStateVertex* const waitVtxp = scopedCreateVertex();
|
SvaStateVertex* const waitVtxp = scopedCreateVertex();
|
||||||
guardedEdge(currentp, waitVtxp, flp);
|
guardedEdge(currentp, waitVtxp, flp);
|
||||||
AstNodeExpr* const waitCondp
|
AstNodeExpr* const waitCondp
|
||||||
|
|
@ -1172,9 +1141,9 @@ class SvaNfaBuilder final {
|
||||||
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
|
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
|
||||||
AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay);
|
AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay);
|
||||||
if (!delayp || !delayp->isCycleDelay() || delayp->isUnbounded()) return false;
|
if (!delayp || !delayp->isCycleDelay() || delayp->isUnbounded()) return false;
|
||||||
const int delayCycles = getConstInt(delayp->lhsp());
|
const unsigned delayCycles = getConstUInt(delayp->lhsp());
|
||||||
if (delayCycles < 0) return false;
|
if (delayp->isRangeDelay() && getConstUInt(delayp->rhsp()) != delayCycles)
|
||||||
if (delayp->isRangeDelay() && getConstInt(delayp->rhsp()) != delayCycles) return false;
|
return false;
|
||||||
int preLen = 0;
|
int preLen = 0;
|
||||||
if (AstNodeExpr* const prep = sexprp->preExprp()) {
|
if (AstNodeExpr* const prep = sexprp->preExprp()) {
|
||||||
if (!flattenFixedSeq(prep, baseOffset, out)) return false;
|
if (!flattenFixedSeq(prep, baseOffset, out)) return false;
|
||||||
|
|
@ -1246,7 +1215,7 @@ class SvaNfaBuilder final {
|
||||||
AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay);
|
AstDelay* const delayp = VN_CAST(sexprp->delayp(), Delay);
|
||||||
if (!delayp || !delayp->isCycleDelay() || !delayp->isRangeDelay() || delayp->isUnbounded())
|
if (!delayp || !delayp->isCycleDelay() || !delayp->isRangeDelay() || delayp->isUnbounded())
|
||||||
return {};
|
return {};
|
||||||
if (getConstInt(delayp->lhsp()) == getConstInt(delayp->rhsp())) return {};
|
if (getConstUInt(delayp->lhsp()) == getConstUInt(delayp->rhsp())) return {};
|
||||||
AstNodeExpr* const prep = sexprp->preExprp();
|
AstNodeExpr* const prep = sexprp->preExprp();
|
||||||
if (prep && fixedLength(prep) != 0) return {};
|
if (prep && fixedLength(prep) != 0) return {};
|
||||||
if (fixedLength(sexprp->exprp()) != 0) return {};
|
if (fixedLength(sexprp->exprp()) != 0) return {};
|
||||||
|
|
@ -3036,7 +3005,7 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
|
|
||||||
// Replace one VarRef to a captured local var with $past(rhs, K)
|
// Replace one VarRef to a captured local var with $past(rhs, K)
|
||||||
// (or rhs inline when K == 0). No-op if refp is not in matchMap.
|
// (or rhs inline when K == 0). No-op if refp is not in matchMap.
|
||||||
void substituteMatchItemRef(AstVarRef* refp, int K,
|
void substituteMatchItemRef(AstVarRef* refp, unsigned K,
|
||||||
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchMap) {
|
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchMap) {
|
||||||
const auto it = matchMap.find(refp->varp());
|
const auto it = matchMap.find(refp->varp());
|
||||||
if (it == matchMap.end()) return;
|
if (it == matchMap.end()) return;
|
||||||
|
|
@ -3057,7 +3026,7 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
// substitutes each VarRef to a captured local var with $past(rhs, K)
|
// substitutes each VarRef to a captured local var with $past(rhs, K)
|
||||||
// (or rhs inline when K == 0). Reports E_UNSUPPORTED on non-constant
|
// (or rhs inline when K == 0). Reports E_UNSUPPORTED on non-constant
|
||||||
// delays or composite sequence operators.
|
// delays or composite sequence operators.
|
||||||
int walkSubstituteMatchItems(AstNodeExpr* nodep, int K,
|
int walkSubstituteMatchItems(AstNodeExpr* nodep, unsigned K,
|
||||||
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchItems,
|
const std::unordered_map<const AstVar*, AstNodeExpr*>& matchItems,
|
||||||
bool& errorEmitted) {
|
bool& errorEmitted) {
|
||||||
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
|
if (AstSExpr* const sexprp = VN_CAST(nodep, SExpr)) {
|
||||||
|
|
@ -3075,7 +3044,7 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
errorEmitted = true;
|
errorEmitted = true;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
const int delayCycles = VN_AS(delayp->lhsp(), Const)->toSInt();
|
const unsigned delayCycles = VN_AS(delayp->lhsp(), Const)->toUInt();
|
||||||
int preLen = 0;
|
int preLen = 0;
|
||||||
if (AstNodeExpr* const prep = sexprp->preExprp()) {
|
if (AstNodeExpr* const prep = sexprp->preExprp()) {
|
||||||
preLen = walkSubstituteMatchItems(prep, K, matchItems, errorEmitted);
|
preLen = walkSubstituteMatchItems(prep, K, matchItems, errorEmitted);
|
||||||
|
|
@ -3121,7 +3090,7 @@ class AssertNfaVisitor final : public VNVisitor {
|
||||||
AstVarRef* const lhsRefp = VN_AS(assignp->lhsp(), VarRef);
|
AstVarRef* const lhsRefp = VN_AS(assignp->lhsp(), VarRef);
|
||||||
matchItems[lhsRefp->varp()] = assignp->rhsp();
|
matchItems[lhsRefp->varp()] = assignp->rhsp();
|
||||||
}
|
}
|
||||||
const int startK = parts.isOverlapped ? 0 : 1;
|
const unsigned startK = parts.isOverlapped ? 0 : 1;
|
||||||
bool errorEmitted = false;
|
bool errorEmitted = false;
|
||||||
walkSubstituteMatchItems(seqBodyp, startK, matchItems, errorEmitted);
|
walkSubstituteMatchItems(seqBodyp, startK, matchItems, errorEmitted);
|
||||||
// Match-item substitution / strip mutates ancestor purity. Release
|
// Match-item substitution / strip mutates ancestor purity. Release
|
||||||
|
|
|
||||||
|
|
@ -744,15 +744,39 @@ class WidthVisitor final : public VNVisitor {
|
||||||
// it's like an if() condition.
|
// it's like an if() condition.
|
||||||
iterateCheckBool(nodep, "default disable iff condition", nodep->condp(), BOTH);
|
iterateCheckBool(nodep, "default disable iff condition", nodep->condp(), BOTH);
|
||||||
}
|
}
|
||||||
|
static const AstConst* widthCheckSvaDelayBound(AstDelay* nodep, AstNodeExpr* boundp,
|
||||||
|
const char* what) {
|
||||||
|
const AstConst* const constp = VN_CAST(boundp, Const);
|
||||||
|
if (!constp || (constp->dtypep()->isSigned() && constp->num().isNegative())) {
|
||||||
|
nodep->v3error(what << " is not a non-negative elaboration-time constant"
|
||||||
|
" (IEEE 1800-2023 16.7)");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (constp->num().mostSetBitP1() > 31) {
|
||||||
|
nodep->v3warn(
|
||||||
|
E_UNSUPPORTED,
|
||||||
|
"Unsupported: SVA cycle delay exceeds implementation limit of 2147483647");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return constp;
|
||||||
|
}
|
||||||
void visit(AstDelay* nodep) override {
|
void visit(AstDelay* nodep) override {
|
||||||
if (nodep->isCycleDelay() && m_underSExpr) {
|
if (nodep->isCycleDelay() && m_underSExpr) {
|
||||||
// Fold parameterized SVA cycle-delay bounds
|
// Fold parameterized SVA cycle-delay bounds
|
||||||
userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
|
userIterateAndNext(nodep->lhsp(), WidthVP{SELF, BOTH}.p());
|
||||||
V3Const::constifyParamsNoWarnEdit(nodep->lhsp());
|
V3Const::constifyParamsNoWarnEdit(nodep->lhsp());
|
||||||
|
const AstConst* const minConstp = widthCheckSvaDelayBound(
|
||||||
|
nodep, nodep->lhsp(),
|
||||||
|
nodep->isRangeDelay() ? "Range delay minimum" : "Delay value");
|
||||||
if (nodep->rhsp() && !nodep->isUnbounded()) {
|
if (nodep->rhsp() && !nodep->isUnbounded()) {
|
||||||
// Fold parametrized SVA cycle-delay max bound
|
// Fold parametrized SVA cycle-delay max bound
|
||||||
userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
|
userIterateAndNext(nodep->rhsp(), WidthVP{SELF, BOTH}.p());
|
||||||
V3Const::constifyParamsNoWarnEdit(nodep->rhsp());
|
V3Const::constifyParamsNoWarnEdit(nodep->rhsp());
|
||||||
|
const AstConst* const maxConstp
|
||||||
|
= widthCheckSvaDelayBound(nodep, nodep->rhsp(), "Range delay maximum");
|
||||||
|
if (minConstp && maxConstp && maxConstp->toUInt() < minConstp->toUInt()) {
|
||||||
|
nodep->v3error("Range delay maximum must be >= minimum (IEEE 1800-2023 16.7)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1832,7 +1856,11 @@ class WidthVisitor final : public VNVisitor {
|
||||||
if (m_vup->prelim()) {
|
if (m_vup->prelim()) {
|
||||||
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
|
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
|
||||||
userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
|
userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
|
||||||
if (nodep->maxCountp()) widthCheckGotoRepRange(nodep, "Goto");
|
if (nodep->maxCountp()) {
|
||||||
|
widthCheckGotoRepRange(nodep, "Goto");
|
||||||
|
} else {
|
||||||
|
widthCheckRepCount(nodep);
|
||||||
|
}
|
||||||
nodep->dtypeSetBit();
|
nodep->dtypeSetBit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1841,10 +1869,33 @@ class WidthVisitor final : public VNVisitor {
|
||||||
if (m_vup->prelim()) {
|
if (m_vup->prelim()) {
|
||||||
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
|
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
|
||||||
userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
|
userIterateAndNext(nodep->countp(), WidthVP{SELF, BOTH}.p());
|
||||||
if (nodep->maxCountp()) widthCheckGotoRepRange(nodep, "Nonconsecutive");
|
if (nodep->maxCountp()) {
|
||||||
|
widthCheckGotoRepRange(nodep, "Nonconsecutive");
|
||||||
|
} else {
|
||||||
|
widthCheckRepCount(nodep);
|
||||||
|
}
|
||||||
nodep->dtypeSetBit();
|
nodep->dtypeSetBit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
template <typename T_Rep>
|
||||||
|
void widthCheckRepCount(T_Rep* nodep) {
|
||||||
|
V3Const::constifyParamsNoWarnEdit(nodep->countp());
|
||||||
|
const AstConst* const constp = VN_CAST(nodep->countp(), Const);
|
||||||
|
if (!constp) {
|
||||||
|
nodep->v3error("Repetition count is not an elaboration-time constant"
|
||||||
|
" (IEEE 1800-2023 16.9.2)");
|
||||||
|
} else if (constp->dtypep()->isSigned() && constp->num().isNegative()) {
|
||||||
|
nodep->v3error("Repetition count must be non-negative"
|
||||||
|
" (IEEE 1800-2023 16.9.2)");
|
||||||
|
} else if (constp->num().mostSetBitP1() > 31) {
|
||||||
|
nodep->v3warn(
|
||||||
|
E_UNSUPPORTED,
|
||||||
|
"Unsupported: SVA repetition count exceeds implementation limit of 2147483647");
|
||||||
|
} else if (constp->isZero()) {
|
||||||
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: zero repetition count"
|
||||||
|
" (IEEE 1800-2023 16.9.2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
// IEEE 1800-2023 16.9.2 range-form bound validation for goto/nonconsec.
|
// IEEE 1800-2023 16.9.2 range-form bound validation for goto/nonconsec.
|
||||||
// Parent accessors are re-fetched after constifyParamsEdit because that
|
// Parent accessors are re-fetched after constifyParamsEdit because that
|
||||||
// call can replace the node in-tree (Lesson: AstSConsRep visitor pattern).
|
// call can replace the node in-tree (Lesson: AstSConsRep visitor pattern).
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,35 @@
|
||||||
%Error: t/t_assert_rep_bad_count.v:14:42: Repetition count is not an elaboration-time constant (IEEE 1800-2023 16.9.2)
|
%Error: t/t_assert_rep_bad_count.v:14:36: Repetition count is not an elaboration-time constant (IEEE 1800-2023 16.9.2)
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
14 | assert property (@(posedge clk) a[->n] |-> b)
|
14 | assert property (@(posedge clk) a[->n] |-> b)
|
||||||
| ^~~
|
| ^~~
|
||||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
%Error-UNSUPPORTED: t/t_assert_rep_bad_count.v:18:42: Unsupported: zero repetition count (IEEE 1800-2023 16.9.2)
|
%Error-UNSUPPORTED: t/t_assert_rep_bad_count.v:18:36: Unsupported: zero repetition count (IEEE 1800-2023 16.9.2)
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
18 | assert property (@(posedge clk) a[->0] |-> b)
|
18 | assert property (@(posedge clk) a[->0] |-> b)
|
||||||
| ^~~
|
| ^~~
|
||||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
%Error: t/t_assert_rep_bad_count.v:22:43: Repetition count must be non-negative (IEEE 1800-2023 16.9.2)
|
%Error: t/t_assert_rep_bad_count.v:22:36: Repetition count must be non-negative (IEEE 1800-2023 16.9.2)
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
22 | assert property (@(posedge clk) a[->-1] |-> b)
|
22 | assert property (@(posedge clk) a[->-1] |-> b)
|
||||||
| ^~~
|
| ^~~
|
||||||
%Error: t/t_assert_rep_bad_count.v:28:41: Repetition count is not an elaboration-time constant (IEEE 1800-2023 16.9.2)
|
%Error: t/t_assert_rep_bad_count.v:28:36: Repetition count is not an elaboration-time constant (IEEE 1800-2023 16.9.2)
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
28 | assert property (@(posedge clk) a[=n] |-> b)
|
28 | assert property (@(posedge clk) a[=n] |-> b)
|
||||||
| ^~~
|
| ^~
|
||||||
%Error-UNSUPPORTED: t/t_assert_rep_bad_count.v:32:41: Unsupported: zero repetition count (IEEE 1800-2023 16.9.2)
|
%Error-UNSUPPORTED: t/t_assert_rep_bad_count.v:32:36: Unsupported: zero repetition count (IEEE 1800-2023 16.9.2)
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
32 | assert property (@(posedge clk) a[=0] |-> b)
|
32 | assert property (@(posedge clk) a[=0] |-> b)
|
||||||
| ^~~
|
| ^~
|
||||||
%Error: t/t_assert_rep_bad_count.v:36:42: Repetition count must be non-negative (IEEE 1800-2023 16.9.2)
|
%Error: t/t_assert_rep_bad_count.v:36:36: Repetition count must be non-negative (IEEE 1800-2023 16.9.2)
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
36 | assert property (@(posedge clk) a[=-1] |-> b)
|
36 | assert property (@(posedge clk) a[=-1] |-> b)
|
||||||
|
| ^~
|
||||||
|
%Error-UNSUPPORTED: t/t_assert_rep_bad_count.v:39:36: Unsupported: SVA repetition count exceeds implementation limit of 2147483647
|
||||||
|
: ... note: In instance 't'
|
||||||
|
39 | assert property (@(posedge clk) a[->32'h80000000] |-> b)
|
||||||
| ^~~
|
| ^~~
|
||||||
|
%Error-UNSUPPORTED: t/t_assert_rep_bad_count.v:42:36: Unsupported: SVA repetition count exceeds implementation limit of 2147483647
|
||||||
|
: ... note: In instance 't'
|
||||||
|
42 | assert property (@(posedge clk) a[=32'h80000000] |-> b)
|
||||||
|
| ^~
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -36,4 +36,10 @@ module t (input clk);
|
||||||
assert property (@(posedge clk) a[=-1] |-> b)
|
assert property (@(posedge clk) a[=-1] |-> b)
|
||||||
else $error("FAIL");
|
else $error("FAIL");
|
||||||
|
|
||||||
|
assert property (@(posedge clk) a[->32'h80000000] |-> b)
|
||||||
|
else $error("FAIL");
|
||||||
|
|
||||||
|
assert property (@(posedge clk) a[=32'h80000000] |-> b)
|
||||||
|
else $error("FAIL");
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,9 @@
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
21 | assert property (@(posedge clk) ##(1+clk) val);
|
21 | assert property (@(posedge clk) ##(1+clk) val);
|
||||||
| ^~
|
| ^~
|
||||||
|
%Error-UNSUPPORTED: t/t_property_sexpr2_bad.v:23:35: Unsupported: SVA cycle delay exceeds implementation limit of 2147483647
|
||||||
|
: ... note: In instance 't'
|
||||||
|
23 | assert property (@(posedge clk) ##32'h80000000 val);
|
||||||
|
| ^~
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,6 @@ module t ( /*AUTOARG*/
|
||||||
|
|
||||||
assert property (@(posedge clk) ##clk val);
|
assert property (@(posedge clk) ##clk val);
|
||||||
assert property (@(posedge clk) ##(1+clk) val);
|
assert property (@(posedge clk) ##(1+clk) val);
|
||||||
|
|
||||||
|
assert property (@(posedge clk) ##32'h80000000 val);
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,13 @@
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
34 | a6: assert property (@(posedge clk) a |-> ##[NEG:$] b);
|
34 | a6: assert property (@(posedge clk) a |-> ##[NEG:$] b);
|
||||||
| ^~
|
| ^~
|
||||||
|
%Error-UNSUPPORTED: t/t_property_sexpr_range_delay_bad.v:36:45: Unsupported: SVA cycle delay exceeds implementation limit of 2147483647
|
||||||
|
: ... note: In instance 't'
|
||||||
|
36 | a7: assert property (@(posedge clk) a |-> ##[1:32'h80000000] b);
|
||||||
|
| ^~
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error-UNSUPPORTED: t/t_property_sexpr_range_delay_bad.v:37:45: Unsupported: SVA cycle delay exceeds implementation limit of 2147483647
|
||||||
|
: ... note: In instance 't'
|
||||||
|
37 | a8: assert property (@(posedge clk) a |-> ##[32'h80000000:$] b);
|
||||||
|
| ^~
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -33,4 +33,7 @@ module t;
|
||||||
localparam int NEG = -1;
|
localparam int NEG = -1;
|
||||||
a6: assert property (@(posedge clk) a |-> ##[NEG:$] b);
|
a6: assert property (@(posedge clk) a |-> ##[NEG:$] b);
|
||||||
|
|
||||||
|
a7: assert property (@(posedge clk) a |-> ##[1:32'h80000000] b);
|
||||||
|
a8: assert property (@(posedge clk) a |-> ##[32'h80000000:$] b);
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue