Skip to content

Conversation

@Clo91eaf
Copy link
Contributor

reopen #9311

@Clo91eaf
Copy link
Contributor Author

@fabianschuiki very sorry for delay!

@Clo91eaf
Copy link
Contributor Author

Clo91eaf commented Jan 9, 2026

@fabianschuiki @seldridge could u please take a look?

Copy link
Contributor

@fabianschuiki fabianschuiki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding an explicit clock operand to delays is a great idea, and the clocked delay op looks good.

You've had to go through a lot of pain in this PR to update every single use of DelayOp to deal with this new clock operand. May I suggest that you instead create a ClockedDelayOp such that we can add support for clocked delays step by step, and figure out how the rest of the code base interacts with them? Since this is a very complicated change, having both ops coexist for a while will help you migrate over step-by-step.

For example, the current lowering to DelayOp in ImportVerilog and FIRRTLToHW are clearly wrong, since they just hook a constant 1 clock up to the op. Since this op is load bearing in Chisel, we have to make sure that when we use the new clocked delay op, its use is actually correct. In FIRRTLToHW for example, we'd want to very carefully infer the clock by looking for FIRRTL clock intrinsics attached to the FIRRTL delay intrinsic. Also, if a single delay intrinsic is used by two different clock intrinsics, we need to lower to two copies of the ClockedDelayOp, one for each clock.

Another example is Verilog emission, where we currently just ignore the clock. One key design point of a ClockedDelayOp is going to be figuring out how we print it as Verilog. The explicit clock makes it possible to infer the @(posedge XYZ) pieces on the fly as we emit Verilog.

Comment on lines 72 to 74
// CHECK: assert property (##4 a);
%d1 = ltl.delay %a, 4, 0 : i1
%d1 = ltl.delay %clk, posedge, %a, 4, 0 : i1
sv.assert_property %d1 : !ltl.sequence
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Verilog emission feels incorrect. The IR says "delay a by 4 cycles of posedge clk", but the Verilog then says "delay a by 4 undefined cycles". I would expect the Verilog output to be:

assert property (@(posedge clk) ##4 a);

I think we need to consider the clocks of the delays when printing the Verilog, and insert @(posedge clk) where it is necessary to indicate the clock trigger.

For example, since the delays now nicely encode the clock, which is fantastic, we can figure out when to print the clock when we emit Verilog:

Same clocks:

%0 = delay(clk, a, 4)
%1 = delay(clk, b, 2)
%2 = concat(%0, %1)
assert(%2)
assert property (@(posedge clk) ##4 a ##2 b);

Different clocks in implication, same clocks in concat:

%0 = delay(clk1, a, 4)
%1 = delay(clk1, b, 2)
%2 = concat(%0, %1)
%3 = delay(clk2, c, 1)
%4 = implication(%3, %2)
assert(%4)
assert property ((@(posedge clk2) ##1 c) |-> (@(posedge clk1) ##4 a ##2 b));

Different clocks in implication and concat:

%0 = delay(clk1, a, 4)
%1 = delay(clk2, b, 2)
%2 = concat(%0, %1)
%3 = delay(clk3, c, 1)
%4 = implication(%3, %2)
assert(%4)
assert property ((@(posedge clk3) ##1 c) |-> (@(posedge clk2) (@(posedge clk1) ##4 a) ##2 b));

This would allow us to infer the clocking statements at Verilog printing, and probably get rid of ltl.clock almost everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. Refactoring the delay op has indeed been a challenging process. After reviewing your feedback, I realized the shortcomings in my previous handling of clock inference. Since I have already completed most of the structural changes to pass the type checks, I will now focus on fixing the logic to ensure the clock signal is correctly inferred from the context, rather than simply using a constant.

@Clo91eaf Clo91eaf marked this pull request as draft January 10, 2026 06:30
@Clo91eaf Clo91eaf force-pushed the sva_clock branch 2 times, most recently from df6d748 to 56b5c86 Compare January 22, 2026 10:45
Previously, ltl.delay operations inside ClockingAssertionExpr (e.g.,
`@(posedge clk) a #llvm#1 b`) used a dummy constant clock instead of the
actual clock from the timing control. This produced semantically
incorrect IR where delays would never trigger.

This patch adds clock context propagation to AssertionExprVisitor:
- Add `clockContext` field to store clock value and edge
- Add `getClockForDelay()` helper to retrieve clock for delay ops
- Extract clock from SignalEventControl before converting inner expr
- Pass clock context to inner visitor for recursive conversion

Now `@(posedge clk) a #llvm#1 b` correctly generates:
  %d = ltl.delay %clk, posedge, %a, 1, 0 : i1
instead of:
  %d = ltl.delay %true, posedge, %a, 1, 0 : i1
…l.delay

After FIRRTLToHW lowering, ltl.delay operations have placeholder clocks
because FIRRTL's delay intrinsic doesn't carry clock information.

Three approaches were considered:
A) Delayed lowering at ClockIntrinsicOp - complex, breaks lowering pattern
B) Two-pass scanning - extra overhead, complex use-def tracking
C) Post-processing pass - minimal changes, independent, testable

Chose C for minimal disruption and incremental implementation.

The new pass walks ltl.clock ops, finds ltl.delay ops with placeholder
clocks in their input chains, and replaces them with properly clocked
versions.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants