@@ -61,10 +61,183 @@ 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+ ClockEdgeAttr:$edge,
209+ I1:$clock,
210+ I64Attr:$delay,
211+ OptionalAttr<I64Attr>:$length);
212+ let results = (outs LTLClockedSequenceType:$result);
213+ let assemblyFormat = [{
214+ $edge $clock `,` $delay (`,` $length^)? attr-dict
215+ }];
216+
217+ let summary = "Create an explicitly clocked delay sequence.";
218+ let description = [{
219+ Creates a standalone "pure delay" sequence that is explicitly bound to a
220+ specific clock. This sequence evaluates to true immediately and matches
221+ after the specified number of clock ticks.
222+
223+ The `$delay` specifies the number of clock cycles to delay, and the optional
224+ `$length` specifies the range of cycles during which the delay can match.
225+ Omitting `$length` indicates an unbounded but finite delay.
226+
227+ Examples:
228+ - `ltl.clocked_delay posedge %clk, 2, 0` creates a delay that matches
229+ exactly 2 cycles after the current time on the positive edge of %clk.
230+ - `ltl.clocked_delay posedge %clk, 2, 2` creates a delay that matches
231+ 2, 3, or 4 cycles after the current time.
232+ - `ltl.clocked_delay posedge %clk, 2` creates an unbounded but finite
233+ delay of 2 or more cycles.
234+
235+ This operation enables clean lowering of SVA expressions like `a ##1 b`
236+ by representing the `##1` as a first-class clocked sequence object that
237+ can be composed with `ltl.concat`.
238+ }];
239+ }
240+
68241def DelayOp : LTLOp<"delay", [Pure]> {
69242 let arguments = (ins
70243 LTLAnySequenceType:$input,
@@ -119,6 +292,27 @@ def PastOp : LTLOp<"past", [Pure]> {
119292 }];
120293}
121294
295+ def ClockedConcatOp : LTLOp<"clocked_concat", [Pure]> {
296+ let arguments = (ins
297+ Variadic<LTLAnyClockedSequenceType>:$inputs,
298+ ClockEdgeAttr:$edge,
299+ I1:$clock);
300+ let results = (outs LTLClockedSequenceType:$result);
301+ let assemblyFormat = [{
302+ $edge $clock `,` $inputs attr-dict `:` type($inputs)
303+ }];
304+ let hasFolder = 1;
305+ let hasCanonicalizer = 1;
306+
307+ let summary = "Concatenate explicitly clocked sequences with a specified clock domain.";
308+ let description = [{
309+ Concatenate multiple explicitly clocked sequences into a longer sequence,
310+ binding the result to a newly specified clock domain (`$edge`, `$clock`).
311+ All input sequences must share the same clock, but the result is explicitly
312+ re-clocked to the provided clock domain, regardless of the input clocks.
313+ }];
314+ }
315+
122316def ConcatOp : LTLOp<"concat", [Pure]> {
123317 let arguments = (ins Variadic<LTLAnySequenceType>:$inputs);
124318 let results = (outs LTLSequenceType:$result);
@@ -248,6 +442,35 @@ def NonConsecutiveRepeatOp : LTLOp<"non_consecutive_repeat", [Pure]> {
248442 }];
249443}
250444
445+ def ClockedImplicationOp : LTLOp<"clocked_implication", [Pure]> {
446+ let arguments = (ins
447+ LTLAnyClockedSequenceType:$antecedent,
448+ LTLAnyClockedSequenceType:$consequent,
449+ ClockEdgeAttr:$edge,
450+ I1:$clock);
451+ let results = (outs LTLPropertyType:$result);
452+ let assemblyFormat = [{
453+ $antecedent `,` $consequent `,` $edge $clock attr-dict `:` type($antecedent) `,` type($consequent)
454+ }];
455+
456+ let summary = "Cross-domain property assertion with explicit clocking.";
457+ let description = [{
458+ Creates a clock-agnostic property that asserts a relationship between
459+ two explicitly clocked sequences. The `$antecedent` and `$consequent`
460+ sequences can have different clocks, enabling cross-domain verification.
461+
462+ The `$edge` and `$clock` specify the clock domain for the property assertion.
463+
464+ This operation is crucial for Clock Domain Crossing (CDC) verification.
465+ Unlike the traditional implication, the clocked version explicitly handles
466+ the case where temporal patterns in different clock domains need to be
467+ related. The resulting property is clock-agnostic and represents a
468+ quantified statement about the relationship between the two sequences.
469+
470+ Example: A request in one clock domain must be followed by an acknowledgment
471+ in another clock domain within a certain time window.
472+ }];
473+ }
251474
252475//===----------------------------------------------------------------------===//
253476// Properties
@@ -322,45 +545,4 @@ def EventuallyOp : LTLOp<"eventually", [Pure]> {
322545 }];
323546}
324547
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-
366548#endif // CIRCT_DIALECT_LTL_LTLOPS_TD
0 commit comments