Support sequence intersect operator (#7374)

This commit is contained in:
Yilou Wang
2026-04-08 09:31:54 +02:00
committed by GitHub
parent 2736262b98
commit 141fe8bdad
17 changed files with 306 additions and 12 deletions
+78 -2
View File
@@ -136,8 +136,9 @@ static std::vector<SeqStep> extractTimeline(AstNodeExpr* nodep) {
if (AstConst* const constp = VN_CAST(dlyp->lhsp(), Const)) {
cycles = constp->toSInt();
} else {
dlyp->lhsp()->v3warn(E_UNSUPPORTED,
"Unsupported: non-constant cycle delay in sequence and/or");
dlyp->lhsp()->v3warn(
E_UNSUPPORTED,
"Unsupported: non-constant cycle delay in sequence and/or/intersect");
}
}
// The expression after the delay
@@ -158,6 +159,18 @@ static std::vector<SeqStep> extractTimeline(AstNodeExpr* nodep) {
return timeline;
}
// True if any AstSExpr in the subtree has a range delay (##[m:n]).
// Uses forall(): predicate returns false when a range delay is found,
// so !forall(...) means "at least one range delay exists".
static bool subtreeHasRangeDelay(const AstNode* nodep) {
return !nodep->forall([](const AstSExpr* sexprp) {
if (const AstDelay* const dlyp = VN_CAST(sexprp->delayp(), Delay)) {
if (dlyp->isRangeDelay()) return false;
}
return true;
});
}
// Lower sequence and/or to AST
class AssertPropLowerVisitor final : public VNVisitor {
// STATE
@@ -396,6 +409,47 @@ class AssertPropLowerVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
// Lower a multi-cycle sequence 'intersect' (IEEE 1800-2023 16.9.6).
// intersect = and + equal-length constraint: both operands must match with the same duration.
// When total delays are equal constants, this is identical to 'and'; otherwise constant-false.
void lowerSeqIntersect(AstNodeBiop* nodep) {
const std::vector<SeqStep> lhsTimeline = extractTimeline(nodep->lhsp());
const std::vector<SeqStep> rhsTimeline = extractTimeline(nodep->rhsp());
int lhsTotal = 0;
for (const auto& step : lhsTimeline) lhsTotal += step.delayCycles;
int rhsTotal = 0;
for (const auto& step : rhsTimeline) rhsTotal += step.delayCycles;
if (lhsTotal != rhsTotal) {
// Lengths differ: per IEEE 16.9.6, the match set is empty -- constant-false.
// Warn the user; mismatched lengths are almost always a mistake.
// Skip when either operand had a range delay: RangeDelayExpander already
// replaced it with an FSM expression (containsSExpr returns false) and
// issued UNSUPPORTED, so no second diagnostic is needed.
if (containsSExpr(nodep->lhsp()) && containsSExpr(nodep->rhsp())) {
if (lhsTotal > rhsTotal) {
nodep->v3warn(WIDTHEXPAND, "Intersect sequence length mismatch"
" (left "
<< lhsTotal << " cycles, right " << rhsTotal
<< " cycles) -- intersection is always empty");
} else {
nodep->v3warn(WIDTHTRUNC, "Intersect sequence length mismatch"
" (left "
<< lhsTotal << " cycles, right " << rhsTotal
<< " cycles) -- intersection is always empty");
}
}
FileLine* const flp = nodep->fileline();
AstBegin* const bodyp = new AstBegin{flp, "", nullptr, true};
bodyp->addStmtsp(new AstPExprClause{flp, false});
AstPExpr* const pexprp = new AstPExpr{flp, bodyp, nodep->dtypep()};
nodep->replaceWith(pexprp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
// Same length: length restriction is trivially satisfied, lower as 'and'.
lowerSeqAnd(nodep);
}
void visit(AstSAnd* nodep) override {
iterateChildren(nodep);
if (containsSExpr(nodep->lhsp()) || containsSExpr(nodep->rhsp())) {
@@ -409,6 +463,19 @@ class AssertPropLowerVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstSIntersect* nodep) override {
iterateChildren(nodep);
if (containsSExpr(nodep->lhsp()) || containsSExpr(nodep->rhsp())) {
lowerSeqIntersect(nodep);
} else {
// Pure boolean operands: length is always 0, lower to LogAnd
AstLogAnd* const newp = new AstLogAnd{nodep->fileline(), nodep->lhsp()->unlinkFrBack(),
nodep->rhsp()->unlinkFrBack()};
newp->dtypeFrom(nodep);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void visit(AstSOr* nodep) override {
iterateChildren(nodep);
if (containsSExpr(nodep->lhsp()) || containsSExpr(nodep->rhsp())) {
@@ -1099,6 +1166,15 @@ class RangeDelayExpander final : public VNVisitor {
}
}
void visit(AstSIntersect* nodep) override {
// intersect with a range-delay operand cannot be lowered: the length-pairing
// logic requires knowing each operand's concrete length, which is dynamic.
if (subtreeHasRangeDelay(nodep->lhsp()) || subtreeHasRangeDelay(nodep->rhsp())) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: intersect with ranged cycle-delay operand");
}
iterateChildren(nodep);
}
void visit(AstSThroughout* nodep) override {
// Reject throughout with range-delay sequences before FSM expansion
// would silently lose per-tick enforcement (IEEE 1800-2023 16.9.9)