Skip to content

Commit 6eb95c9

Browse files
committed
[LTL] Update delay syntax to include clock and edge specifications
1 parent 558d7c7 commit 6eb95c9

File tree

2 files changed

+93
-64
lines changed

2 files changed

+93
-64
lines changed

docs/Dialects/LTL.md

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,26 @@ Sequence and property expressions in SVAs can specify a clock with respect to wh
160160
- `@(negedge clk) seqOrProp`. **Trigger on high-to-low clock edge.** Equivalent to `ltl.clock %seqOrProp, negedge %clk`.
161161
- `@(edge clk) seqOrProp`. **Trigger on any clock edge.** Equivalent to `ltl.clock %seqOrProp, edge %clk`.
162162

163+
#### Delay clocking
164+
165+
`ltl.delay` takes the delayed input first, followed by the delay window. An optional clocking clause may be appended:
166+
167+
```
168+
ltl.delay %input, <delay>[, <length>] [clock %clock, <edge>]
169+
```
170+
171+
`%clock` is an optional `i1` value (e.g. a module clock); `<edge>` is `posedge`, `negedge`, or `edge`. When present, the `delay`/`length` are counted on the specified clock/edge. For example, `ltl.delay %s, 3 clock %clk, posedge` means `%s` must hold 3 cycles later on `%clk` rising edges.
172+
173+
When the clocking clause is omitted, the delay is unclocked and may be resolved by an enclosing `ltl.clock` or by the `InferLTLClocks` pass. `ltl.clock` globally associates a sequence/property with a clock/edge; using the same pair in `ltl.delay` keeps expressions in the same clocking domain.
174+
175+
Examples:
176+
177+
```mlir
178+
ltl.delay %s, 3
179+
ltl.delay %s, 3, 0
180+
ltl.delay %s, 3, 0 clock %clk, posedge
181+
```
182+
163183

164184
### Disable Iff
165185

@@ -273,13 +293,13 @@ where the `logic_to_int` conversion is only necessary if `%cond` is 4-valued.
273293
```mlir
274294
%ds1 = ltl.delay %s1, 1
275295
%s1s2 = ltl.concat %ds1, %s2 : !ltl.sequence
276-
```
296+
```
277297

278298
- **`s1 ##[*] s2`**:
279299
```mlir
280300
%ds1 = ltl.delay %s1, 0
281301
%s1s2 = ltl.concat %ds1, %s2 : !ltl.sequence
282-
```
302+
```
283303

284304
- **`s1 and s2`**:
285305
```mlir
@@ -369,12 +389,12 @@ ltl.not %s1 : !ltl.sequence
369389
- **`nexttime p`**:
370390
```mlir
371391
ltl.delay %p, 1, 0 : !ltl.sequence
372-
```
392+
```
373393

374394
- **`nexttime[n] p`**:
375395
```mlir
376396
ltl.delay %p, n, 0 : !ltl.sequence
377-
```
397+
```
378398

379399
- **`s_nexttime p`**: not really distinguishable from the weak version in CIRCT.
380400
- **`s_nexttime[n] p`**: not really distinguishable from the weak version in CIRCT.

include/circt/Dialect/LTL/LTLOps.td

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,61 @@ def IntersectOp : AssocLTLOp<"intersect"> {
6262
let hasCanonicalizeMethod = 1;
6363
}
6464

65+
//===----------------------------------------------------------------------===//
66+
// Clocking
67+
//===----------------------------------------------------------------------===//
68+
69+
// Edge behavior enum for always block. See SV Spec 9.4.2.
70+
71+
/// AtPosEdge triggers on a rise from 0 to 1/X/Z, or X/Z to 1.
72+
def AtPosEdge: I32EnumAttrCase<"Pos", 0, "posedge">;
73+
/// AtNegEdge triggers on a drop from 1 to 0/X/Z, or X/Z to 0.
74+
def AtNegEdge: I32EnumAttrCase<"Neg", 1, "negedge">;
75+
/// AtEdge is syntactic sugar for AtPosEdge or AtNegEdge.
76+
def AtEdge : I32EnumAttrCase<"Both", 2, "edge">;
77+
78+
def ClockEdgeAttr : I32EnumAttr<"ClockEdge", "clock edge",
79+
[AtPosEdge, AtNegEdge, AtEdge]> {
80+
let cppNamespace = "circt::ltl";
81+
}
82+
83+
def ClockOp : LTLOp<"clock", [
84+
Pure, InferTypeOpInterface, DeclareOpInterfaceMethods<InferTypeOpInterface>
85+
]> {
86+
let arguments = (ins LTLAnyPropertyType:$input, ClockEdgeAttr:$edge, I1:$clock);
87+
let results = (outs LTLSequenceOrPropertyType:$result);
88+
let assemblyFormat = [{
89+
$input `,` $edge $clock attr-dict `:` type($input)
90+
}];
91+
92+
let summary = "Specify the clock for a property or sequence.";
93+
let description = [{
94+
Specifies the `$edge` on a given `$clock` to be the clock for an `$input`
95+
property or sequence. All cycle delays in the `$input` implicitly refer to a
96+
clock that advances the state to the next cycle. The `ltl.clock` operation
97+
provides that clock. The clock applies to the entire property or sequence
98+
expression tree below `$input`, up to any other nested `ltl.clock`
99+
operations.
100+
101+
The operation returns a property if the `$input` is a property, and a
102+
sequence otherwise.
103+
}];
104+
}
105+
65106
//===----------------------------------------------------------------------===//
66107
// Sequences
67108
//===----------------------------------------------------------------------===//
68109

69110
def DelayOp : LTLOp<"delay", [Pure]> {
70111
let arguments = (ins
71-
LTLAnySequenceType:$input,
72-
I64Attr:$delay,
73-
OptionalAttr<I64Attr>:$length);
112+
LTLAnySequenceType:$input,
113+
I64Attr:$delay,
114+
OptionalAttr<I64Attr>:$length,
115+
Optional<I1>:$clock,
116+
OptionalAttr<ClockEdgeAttr>:$edge);
74117
let results = (outs LTLSequenceType:$result);
75118
let assemblyFormat = [{
76-
$input `,` $delay (`,` $length^)? attr-dict `:` type($input)
119+
$input `,` $delay (`,` $length^)? (`clock` $clock^ `,` $edge)? attr-dict `:` type($input)
77120
}];
78121
let hasFolder = 1;
79122
let hasCanonicalizer = 1;
@@ -83,25 +126,32 @@ def DelayOp : LTLOp<"delay", [Pure]> {
83126
Delays the `$input` sequence by the number of cycles specified by `$delay`.
84127
The delay must be greater than or equal to zero. The optional `$length`
85128
specifies during how many cycles after the initial delay the sequence can
86-
match. Omitting `$length` indicates an unbounded but finite delay. For
87-
example:
88-
89-
- `ltl.delay %seq, 2, 0` delays `%seq` by exactly 2 cycles. The resulting
90-
sequence matches if `%seq` matches exactly 2 cycles in the future.
91-
- `ltl.delay %seq, 2, 2` delays `%seq` by 2, 3, or 4 cycles. The resulting
92-
sequence matches if `%seq` matches 2, 3, or 4 cycles in the future.
93-
- `ltl.delay %seq, 2` delays `%seq` by 2 or more cycles. The number of
94-
cycles is unbounded but finite, which means that `%seq` *has* to match at
95-
some point, instead of effectively never occuring by being delayed an
96-
infinite number of cycles.
129+
match. Omitting `$length` indicates an unbounded but finite delay.
130+
131+
The optional `$clock` and `$edge` specify an explicit clock for the delay.
132+
When omitted, the delay is "unclocked" and must be resolved by an enclosing
133+
`ltl.clock` operation or by the `InferLTLClocks` pass.
134+
135+
For example:
136+
137+
- `ltl.delay %seq, 2, 0` delays `%seq` by exactly 2 cycles (unclocked).
138+
- `ltl.delay %seq, 2, 0 clock %clk, posedge` delays `%seq` by exactly 2
139+
cycles on the positive edge of `%clk`.
140+
- `ltl.delay %seq, 2, 2` delays `%seq` by 2, 3, or 4 cycles.
141+
- `ltl.delay %seq, 2` delays `%seq` by 2 or more cycles (unbounded but
142+
finite).
97143
- `ltl.delay %seq, 0, 0` is equivalent to just `%seq`.
98144

99145
#### Clocking
100146

101-
The cycle delay specified on the operation refers to a clocking event. This
102-
event is not directly specified by the delay operation itself. Instead, the
103-
[`ltl.clock`](#ltlclock-circtltlclockop) operation can be used to associate
104-
all delays within a sequence with a clock.
147+
When the `$clock` and `$edge` are present, the delay explicitly refers to
148+
that clock signal. When absent, the delay is unclocked and the
149+
`InferLTLClocks` pass (`--ltl-infer-clocks`) will propagate clock
150+
information from enclosing `ltl.clock` ops.
151+
152+
Backends (e.g., ExportVerilog) may auto-hoist a clock from explicitly
153+
clocked delays to the outermost SVA level when no enclosing `ltl.clock`
154+
is present.
105155
}];
106156
}
107157

@@ -368,45 +418,4 @@ def EventuallyOp : LTLOp<"eventually", [Pure]> {
368418
}];
369419
}
370420

371-
//===----------------------------------------------------------------------===//
372-
// Clocking
373-
//===----------------------------------------------------------------------===//
374-
375-
// Edge behavior enum for always block. See SV Spec 9.4.2.
376-
377-
/// AtPosEdge triggers on a rise from 0 to 1/X/Z, or X/Z to 1.
378-
def AtPosEdge: I32EnumAttrCase<"Pos", 0, "posedge">;
379-
/// AtNegEdge triggers on a drop from 1 to 0/X/Z, or X/Z to 0.
380-
def AtNegEdge: I32EnumAttrCase<"Neg", 1, "negedge">;
381-
/// AtEdge is syntactic sugar for AtPosEdge or AtNegEdge.
382-
def AtEdge : I32EnumAttrCase<"Both", 2, "edge">;
383-
384-
def ClockEdgeAttr : I32EnumAttr<"ClockEdge", "clock edge",
385-
[AtPosEdge, AtNegEdge, AtEdge]> {
386-
let cppNamespace = "circt::ltl";
387-
}
388-
389-
def ClockOp : LTLOp<"clock", [
390-
Pure, InferTypeOpInterface, DeclareOpInterfaceMethods<InferTypeOpInterface>
391-
]> {
392-
let arguments = (ins LTLAnyPropertyType:$input, ClockEdgeAttr:$edge, I1:$clock);
393-
let results = (outs LTLSequenceOrPropertyType:$result);
394-
let assemblyFormat = [{
395-
$input `,` $edge $clock attr-dict `:` type($input)
396-
}];
397-
398-
let summary = "Specify the clock for a property or sequence.";
399-
let description = [{
400-
Specifies the `$edge` on a given `$clock` to be the clock for an `$input`
401-
property or sequence. All cycle delays in the `$input` implicitly refer to a
402-
clock that advances the state to the next cycle. The `ltl.clock` operation
403-
provides that clock. The clock applies to the entire property or sequence
404-
expression tree below `$input`, up to any other nested `ltl.clock`
405-
operations.
406-
407-
The operation returns a property if the `$input` is a property, and a
408-
sequence otherwise.
409-
}];
410-
}
411-
412421
#endif // CIRCT_DIALECT_LTL_LTLOPS_TD

0 commit comments

Comments
 (0)