Skip to content

Commit 6689f89

Browse files
committed
[LTL] Add explict clocked operations and types
- Introduced new clocked operations: ClockedAndOp, ClockedOrOp, ClockedIntersectOp, CreateClockedSequenceOp, ClockedDelayOp, ClockedConcatOp... - Added LTLClockedSequenceType to represent clocked sequences. - Updated LTLTypes and LTLVisitors to support new clocked operations.
1 parent bbe2444 commit 6689f89

File tree

4 files changed

+427
-51
lines changed

4 files changed

+427
-51
lines changed

include/circt/Dialect/LTL/LTLOps.td

Lines changed: 296 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,185 @@ def IntersectOp : AssocLTLOp<"intersect"> {
6161
let hasCanonicalizeMethod = 1;
6262
}
6363

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

111+
def CreateClockedSequenceOp : LTLOp<"create_clocked_sequence", [Pure]> {
112+
let arguments = (ins
113+
LTLAnySequenceType:$input,
114+
ClockEdgeAttr:$edge,
115+
I1:$clock);
116+
let results = (outs LTLClockedSequenceType:$result);
117+
let assemblyFormat = [{
118+
$input `,` $edge $clock attr-dict `:` type($input)
119+
}];
120+
121+
let summary = "Create an explicitly clocked sequence from input "
122+
"sequences/booleans.";
123+
let description = [{
124+
Creates an explicitly clocked sequence by binding a single input sequence
125+
or boolean value to a specific clock edge. This operation is fundamental to
126+
the explicit clocking model, ensuring that all temporal operations have
127+
well-defined clock semantics.
128+
}];
129+
}
130+
131+
def ClockedAndOp : LTLOp<"clocked_and", [Pure, Commutative]> {
132+
let arguments = (ins
133+
Variadic<LTLAnyClockedSequenceType>:$inputs,
134+
ClockEdgeAttr:$edge,
135+
I1:$clock);
136+
let results = (outs LTLClockedSequenceType:$result);
137+
let assemblyFormat = [{
138+
$edge $clock `,` $inputs attr-dict `:` type($inputs)
139+
}];
140+
let hasCanonicalizeMethod = 1;
141+
142+
let summary = "A conjunction of explicitly clocked sequences with a "
143+
"specified clock domain.";
144+
let description = [{
145+
Computes the logical AND of explicitly clocked sequences, binding the
146+
result to a newly specified clock domain (`$edge`, `$clock`). All
147+
input sequences must share the same clock, but the result is explicitly
148+
re-clocked to the provided clock domain, regardless of the input clocks.
149+
150+
Boolean inputs are implicitly lifted to zero-length clocked sequences
151+
with the specified clock.
152+
153+
This operation is useful for clock domain crossing or for enforcing a
154+
new clock context on the conjunction result.
155+
}];
156+
}
157+
158+
def ClockedOrOp : LTLOp<"clocked_or", [Pure, Commutative]> {
159+
let arguments = (ins
160+
Variadic<LTLAnyClockedSequenceType>:$inputs,
161+
ClockEdgeAttr:$edge,
162+
I1:$clock);
163+
let results = (outs LTLClockedSequenceType:$result);
164+
let assemblyFormat = [{
165+
$edge $clock `,` $inputs attr-dict `:` type($inputs)
166+
}];
167+
let hasCanonicalizeMethod = 1;
168+
169+
let summary = "A disjunction of explicitly clocked sequences with a specified clock domain.";
170+
let description = [{
171+
Computes the logical OR of explicitly clocked sequences, binding the result
172+
to a newly specified clock domain (`$edge`, `$clock`). All input sequences
173+
must share the same clock, but the result is explicitly re-clocked to the
174+
provided clock domain, regardless of the input clocks.
175+
176+
Boolean inputs are implicitly lifted to zero-length clocked sequences with
177+
the specified clock.
178+
179+
This operation is useful for clock domain crossing or for enforcing a new
180+
clock context on the disjunction result.
181+
}];
182+
}
183+
184+
def ClockedIntersectOp : LTLOp<"clocked_intersect", [Pure, Commutative]> {
185+
let arguments = (ins
186+
Variadic<LTLAnyClockedSequenceType>:$inputs,
187+
ClockEdgeAttr:$edge,
188+
I1:$clock);
189+
let results = (outs LTLClockedSequenceType:$result);
190+
let assemblyFormat = [{
191+
$edge $clock `,` $inputs attr-dict `:` type($inputs)
192+
}];
193+
let hasCanonicalizeMethod = 1;
194+
195+
let summary = "Intersection of explicitly clocked sequences with a specified clock domain.";
196+
let description = [{
197+
Computes the intersection of explicitly clocked sequences, binding the result to a newly
198+
specified clock domain (`$edge`, `$clock`). All input sequences must share the same clock,
199+
but the result is explicitly re-clocked to the provided clock domain, regardless of the input clocks.
200+
201+
This operation checks that all input sequences hold and have the same start and end times
202+
in the new clock domain. It differs from `ltl.clocked_and` by considering the timing of each operand.
203+
}];
204+
}
205+
206+
def ClockedDelayOp : LTLOp<"clocked_delay", [Pure]> {
207+
let arguments = (ins
208+
LTLAnyClockedSequenceType:$input,
209+
ClockEdgeAttr:$edge,
210+
I1:$clock,
211+
I64Attr:$delay,
212+
OptionalAttr<I64Attr>:$length);
213+
let results = (outs LTLClockedSequenceType:$result);
214+
let assemblyFormat = [{
215+
$edge $clock `,` $input `,` $delay (`,` $length^)? attr-dict `:` type($input)
216+
}];
217+
218+
let summary = "Create an explicitly clocked delay sequence.";
219+
let description = [{
220+
Creates a standalone "pure delay" sequence that is explicitly bound to a
221+
specific clock. This sequence evaluates to true immediately and matches
222+
after the specified number of clock ticks.
223+
224+
The `$input` must be an explicitly clocked sequence. The `$delay` specifies
225+
the number of clock cycles to delay, and the optional `$length` specifies
226+
the range of cycles during which the delay can match. Omitting `$length`
227+
indicates an unbounded but finite delay.
228+
229+
Examples:
230+
- `ltl.clocked_delay posedge %clk, %seq, 2, 0` creates a delay that matches
231+
exactly 2 cycles after the current time on the positive edge of %clk.
232+
- `ltl.clocked_delay posedge %clk, %seq, 2, 2` creates a delay that matches
233+
2, 3, or 4 cycles after the current time.
234+
- `ltl.clocked_delay posedge %clk, %seq, 2` creates an unbounded but finite
235+
delay of 2 or more cycles.
236+
237+
This operation enables clean lowering of SVA expressions like `a ##1 b`
238+
by representing the `##1` as a first-class clocked sequence object that
239+
can be composed with `ltl.clocked_concat`.
240+
}];
241+
}
242+
68243
def DelayOp : LTLOp<"delay", [Pure]> {
69244
let arguments = (ins
70245
LTLAnySequenceType:$input,
@@ -119,6 +294,27 @@ def PastOp : LTLOp<"past", [Pure]> {
119294
}];
120295
}
121296

297+
def ClockedConcatOp : LTLOp<"clocked_concat", [Pure]> {
298+
let arguments = (ins
299+
Variadic<LTLAnyClockedSequenceType>:$inputs,
300+
ClockEdgeAttr:$edge,
301+
I1:$clock);
302+
let results = (outs LTLClockedSequenceType:$result);
303+
let assemblyFormat = [{
304+
$edge $clock `,` $inputs attr-dict `:` type($inputs)
305+
}];
306+
let hasFolder = 1;
307+
let hasCanonicalizer = 1;
308+
309+
let summary = "Concatenate explicitly clocked sequences with a specified clock domain.";
310+
let description = [{
311+
Concatenate multiple explicitly clocked sequences into a longer sequence,
312+
binding the result to a newly specified clock domain (`$edge`, `$clock`).
313+
All input sequences must share the same clock, but the result is explicitly
314+
re-clocked to the provided clock domain, regardless of the input clocks.
315+
}];
316+
}
317+
122318
def ConcatOp : LTLOp<"concat", [Pure]> {
123319
let arguments = (ins Variadic<LTLAnySequenceType>:$inputs);
124320
let results = (outs LTLSequenceType:$result);
@@ -178,6 +374,31 @@ def ConcatOp : LTLOp<"concat", [Pure]> {
178374
}];
179375
}
180376

377+
def ClockedRepeatOp : LTLOp<"clocked_repeat", [Pure]> {
378+
let arguments = (ins
379+
LTLAnyClockedSequenceType:$input,
380+
I64Attr:$base,
381+
OptionalAttr<I64Attr>:$more);
382+
let results = (outs LTLClockedSequenceType:$result);
383+
let assemblyFormat = [{
384+
$input `,` $base (`,` $more^)? attr-dict `:` type($input)
385+
}];
386+
let hasFolder = 1;
387+
388+
let summary = "Repeats a clocked sequence by a number of times.";
389+
let description = [{
390+
Repeat the `$input` sequence at least `$base` times, at most `$base` +
391+
`$more` times. The number must be greater than or equal to zero. Omitting
392+
`$more` indicates an unbounded but finite repetition. For example:
393+
394+
- `ltl.clocked_repeat %seq, 2, 0` repeats `%seq` exactly 2 times.
395+
- `ltl.clocked_repeat %seq, 2, 2` repeats `%seq` 2, 3, or 4 times.
396+
- `ltl.clocked_repeat %seq, 2` repeats `%seq` 2 or more times. The number of times
397+
is unbounded but finite.
398+
- `ltl.clocked_repeat %seq, 0, 0` represents an empty clocked sequence.
399+
}];
400+
}
401+
181402
def RepeatOp : LTLOp<"repeat", [Pure]> {
182403
let arguments = (ins
183404
LTLAnySequenceType:$input,
@@ -203,6 +424,28 @@ def RepeatOp : LTLOp<"repeat", [Pure]> {
203424
}];
204425
}
205426

427+
def ClockedGoToRepeatOp : LTLOp<"clocked_goto_repeat", [Pure]> {
428+
let arguments = (ins
429+
LTLAnyClockedSequenceType:$input,
430+
I64Attr:$base,
431+
I64Attr:$more);
432+
let results = (outs LTLClockedSequenceType:$result);
433+
let assemblyFormat = [{
434+
$input `,` $base `,` $more attr-dict `:` type($input)
435+
}];
436+
437+
let hasFolder = 1;
438+
439+
let summary = "`goto`-style non-consecutively repeating clocked sequence.";
440+
let description = [{
441+
Non-consecutive repetition of the `$input` clocked sequence. This must hold between `$base`
442+
and `$base + $more` times in a finite number of evaluations. The final evaluation
443+
in the sequence has to match. The `$base` must be greater than or equal to zero
444+
and the range `$more` can't be omitted. For example, a !b b b !b !b b c represents
445+
a matching observation of `ltl.clocked_goto_repeat %b, 1, 2`, but a !b b b !b !b b !b c doesn't.
446+
}];
447+
}
448+
206449
def GoToRepeatOp : LTLOp<"goto_repeat", [Pure]> {
207450
let arguments = (ins
208451
LTLAnySequenceType:$input,
@@ -225,6 +468,29 @@ def GoToRepeatOp : LTLOp<"goto_repeat", [Pure]> {
225468
}];
226469
}
227470

471+
def ClockedNonConsecutiveRepeatOp : LTLOp<"clocked_non_consecutive_repeat", [Pure]> {
472+
let arguments = (ins
473+
LTLAnyClockedSequenceType:$input,
474+
I64Attr:$base,
475+
I64Attr:$more);
476+
let results = (outs LTLClockedSequenceType:$result);
477+
let assemblyFormat = [{
478+
$input `,` $base `,` $more attr-dict `:` type($input)
479+
}];
480+
481+
let hasFolder = 1;
482+
483+
let summary = "`goto`-style non-consecutively repeating sequence.";
484+
let description = [{
485+
Non-consecutive repetition of the `$input` sequence. This must hold between `$base`
486+
and `$base + $more` times in a finite number of evaluations. The final evaluation
487+
in the sequence does not have to match. The `$base` must be greater than or equal to zero
488+
and the range `$more` can't be omitted. For example, both a !b b b !b !b b c and
489+
a !b b b !b !b b !b c represent matching observations of
490+
`ltl.clocked_non_consecutive_repeat %b, 1, 2`.
491+
}];
492+
}
493+
228494
def NonConsecutiveRepeatOp : LTLOp<"non_consecutive_repeat", [Pure]> {
229495
let arguments = (ins
230496
LTLAnySequenceType:$input,
@@ -248,7 +514,6 @@ def NonConsecutiveRepeatOp : LTLOp<"non_consecutive_repeat", [Pure]> {
248514
}];
249515
}
250516

251-
252517
//===----------------------------------------------------------------------===//
253518
// Properties
254519
//===----------------------------------------------------------------------===//
@@ -268,6 +533,36 @@ def NotOp : LTLOp<"not", [Pure]> {
268533
}];
269534
}
270535

536+
def ClockedImplicationOp : LTLOp<"clocked_implication", [Pure]> {
537+
let arguments = (ins
538+
LTLAnyClockedSequenceType:$antecedent,
539+
LTLClockedSequenceOrPropertyType:$consequent,
540+
ClockEdgeAttr:$edge,
541+
I1:$clock);
542+
let results = (outs LTLPropertyType:$result);
543+
let assemblyFormat = [{
544+
$antecedent `,` $consequent `,` $edge $clock attr-dict `:` type($antecedent) `,` type($consequent)
545+
}];
546+
547+
let summary = "Cross-domain property assertion with explicit clocking.";
548+
let description = [{
549+
Creates a property in the specified clock domain (`$edge`, `$clock`) that asserts a relationship between
550+
two explicitly clocked sequences or properties. The `$antecedent` and `$consequent`
551+
can have different clocks, enabling cross-domain verification.
552+
553+
The `$edge` and `$clock` specify the clock domain for the resulting property.
554+
555+
This operation is crucial for Clock Domain Crossing (CDC) verification.
556+
Unlike the traditional implication, the clocked version explicitly handles
557+
the case where temporal patterns in different clock domains need to be
558+
related. The resulting property is explicitly clocked and represents a
559+
quantified statement about the relationship between the two sequences or properties.
560+
561+
Example: A request in one clock domain must be followed by an acknowledgment
562+
in another clock domain within a certain time window.
563+
}];
564+
}
565+
271566
def ImplicationOp : LTLOp<"implication", [Pure]> {
272567
let arguments = (ins LTLAnySequenceType:$antecedent,
273568
LTLAnyPropertyType:$consequent);
@@ -322,45 +617,4 @@ def EventuallyOp : LTLOp<"eventually", [Pure]> {
322617
}];
323618
}
324619

325-
//===----------------------------------------------------------------------===//
326-
// Clocking
327-
//===----------------------------------------------------------------------===//
328-
329-
// Edge behavior enum for always block. See SV Spec 9.4.2.
330-
331-
/// AtPosEdge triggers on a rise from 0 to 1/X/Z, or X/Z to 1.
332-
def AtPosEdge: I32EnumAttrCase<"Pos", 0, "posedge">;
333-
/// AtNegEdge triggers on a drop from 1 to 0/X/Z, or X/Z to 0.
334-
def AtNegEdge: I32EnumAttrCase<"Neg", 1, "negedge">;
335-
/// AtEdge is syntactic sugar for AtPosEdge or AtNegEdge.
336-
def AtEdge : I32EnumAttrCase<"Both", 2, "edge">;
337-
338-
def ClockEdgeAttr : I32EnumAttr<"ClockEdge", "clock edge",
339-
[AtPosEdge, AtNegEdge, AtEdge]> {
340-
let cppNamespace = "circt::ltl";
341-
}
342-
343-
def ClockOp : LTLOp<"clock", [
344-
Pure, InferTypeOpInterface, DeclareOpInterfaceMethods<InferTypeOpInterface>
345-
]> {
346-
let arguments = (ins LTLAnyPropertyType:$input, ClockEdgeAttr:$edge, I1:$clock);
347-
let results = (outs LTLSequenceOrPropertyType:$result);
348-
let assemblyFormat = [{
349-
$input `,` $edge $clock attr-dict `:` type($input)
350-
}];
351-
352-
let summary = "Specify the clock for a property or sequence.";
353-
let description = [{
354-
Specifies the `$edge` on a given `$clock` to be the clock for an `$input`
355-
property or sequence. All cycle delays in the `$input` implicitly refer to a
356-
clock that advances the state to the next cycle. The `ltl.clock` operation
357-
provides that clock. The clock applies to the entire property or sequence
358-
expression tree below `$input`, up to any other nested `ltl.clock`
359-
operations.
360-
361-
The operation returns a property if the `$input` is a property, and a
362-
sequence otherwise.
363-
}];
364-
}
365-
366620
#endif // CIRCT_DIALECT_LTL_LTLOPS_TD

0 commit comments

Comments
 (0)