@@ -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+
68243def 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+
122318def 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+
181402def 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+
206449def 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+
228494def 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+
271566def 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