Skip to content

Commit 56b5c86

Browse files
committed
[ExportVerilog] Enforce ltl.delay clock/edge match enclosing ltl.clock
Add validation so ltl.delay's clock and edge must match the enclosing ltl.clock, as SVA only supports a single clock context for delays. Emit errors for mismatches. Placeholder clocks (hw.constant true) are allowed and inherit the outer clock.
1 parent e2632c2 commit 56b5c86

File tree

1 file changed

+38
-2
lines changed

1 file changed

+38
-2
lines changed

lib/Conversion/ExportVerilog/ExportVerilog.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2304,8 +2304,7 @@ class ExprEmitter : public EmitterBase,
23042304

23052305
/// Emit braced list of values surrounded by `{` and `}`.
23062306
void emitBracedList(ValueRange ops) {
2307-
return emitBracedList(
2308-
ops, [&]() { ps << "{"; }, [&]() { ps << "}"; });
2307+
return emitBracedList(ops, [&]() { ps << "{"; }, [&]() { ps << "}"; });
23092308
}
23102309

23112310
/// Print an APInt constant.
@@ -3603,6 +3602,12 @@ class PropertyEmitter : public EmitterBase,
36033602
/// location information tracking.
36043603
SmallPtrSetImpl<Operation *> &emittedOps;
36053604

3605+
/// Current clock context from enclosing ltl.clock op. Used to verify that
3606+
/// ltl.delay ops have consistent clocks, since SVA does not support
3607+
/// per-delay clocks.
3608+
Value currentClock;
3609+
std::optional<ltl::ClockEdge> currentEdge;
3610+
36063611
/// Tokens buffered for inserting casts/parens after emitting children.
36073612
SmallVector<Token> localTokens;
36083613

@@ -3752,6 +3757,26 @@ EmittedProperty PropertyEmitter::visitLTL(ltl::IntersectOp op) {
37523757
}
37533758

37543759
EmittedProperty PropertyEmitter::visitLTL(ltl::DelayOp op) {
3760+
// Verify that the delay's clock matches the enclosing clock context.
3761+
// SVA does not support per-delay clocks; all delays inherit from the
3762+
// enclosing @(edge clock) specification.
3763+
Value delayClock = op.getClock();
3764+
if (currentClock) {
3765+
// Check if delay has a real clock (not a placeholder constant)
3766+
bool isPlaceholder = false;
3767+
if (auto constOp = delayClock.getDefiningOp<hw::ConstantOp>()) {
3768+
auto value = constOp.getValue();
3769+
isPlaceholder = value.getBitWidth() == 1 && value.isOne();
3770+
}
3771+
3772+
if (!isPlaceholder && delayClock != currentClock) {
3773+
emitOpError(op, "delay clock does not match enclosing clock; "
3774+
"SVA does not support per-delay clocks");
3775+
} else if (!isPlaceholder && currentEdge && op.getEdge() != *currentEdge) {
3776+
emitOpError(op, "delay clock edge does not match enclosing clock edge");
3777+
}
3778+
}
3779+
37553780
ps << "##";
37563781
if (auto length = op.getLength()) {
37573782
if (*length == 0) {
@@ -3907,7 +3932,18 @@ EmittedProperty PropertyEmitter::visitLTL(ltl::ClockOp op) {
39073932
ps << ")";
39083933
});
39093934
ps << PP::space;
3935+
3936+
// Set the clock context for nested property emission.
3937+
// Save and restore to handle nested clock ops correctly.
3938+
Value savedClock = currentClock;
3939+
auto savedEdge = currentEdge;
3940+
currentClock = op.getClock();
3941+
currentEdge = op.getEdge();
3942+
39103943
emitNestedProperty(op.getInput(), PropertyPrecedence::Clocking);
3944+
3945+
currentClock = savedClock;
3946+
currentEdge = savedEdge;
39113947
return {PropertyPrecedence::Clocking};
39123948
}
39133949

0 commit comments

Comments
 (0)