Most clocks are 50% duty, but not all: a 25% duty clock, an offset clock or a clock with a late falling edge changes the time between rising and falling edges, and so changes every half-cycle path. If the waveform is wrong, half-cycle budgets are wrong even when the period is right.
# not-run: SDC is not executable here
create_clock -name c50 -period 10 -waveform {0 5} [get_ports clk] ;# 50% duty
create_clock -name c25 -period 10 -waveform {0 2.5} [get_ports clk25] ;# 25% duty: high for 2.5, low for 7.5def half_cycle_budgets(period, rise, fall):
high = (fall - rise) % period
low = period - high
return high, low
h50, l50 = half_cycle_budgets(10.0, 0.0, 5.0)
h25, l25 = half_cycle_budgets(10.0, 0.0, 2.5)
assert (h50, l50) == (5.0, 5.0)
assert (h25, l25) == (2.5, 7.5)
# a rising-launch to falling-capture path gets the 'high' time as its budget, falling-to-rising gets 'low'
print("PASS: 25% duty gives half-cycle budgets", h25, "and", l25, "ns")For a 25% duty clock, a path launched on the rising edge and captured on the falling edge has only 2.5 ns, not 5 ns. A falling-launch to rising-capture path has 7.5 ns. The edge-pair budget is the difference between the edges, not half the period.
A clock can start with an offset, for example -waveform {2 7} for a 10 ns period: it rises at 2 ns and falls at 7 ns. Relative offsets between two clocks of the same period matter for paths between them; the closest launch/capture edge pair of the two waveforms sets the budget.
The waveform matches the real clock: duty cycle from the source (a PLL output, a divider) is stated in the clock documentation.
Half-cycle paths are reviewed explicitly; the report's launch and capture times for them equal the edge difference.
Clocks with non-default waveforms are called out in the constraint review notes.
3 ns, the time from the rising edge at 0 to the falling edge at 3, not 4 ns.