pub fn vehicle_macro_sdc(
pdk: &PdkTarget,
clock: HarnessClock,
) -> Result<String, PdkUnsupported>Expand description
The constraints a vehicle user macro is timed with: OpenLane’s own base.sdc, then the vehicle’s timing as it
characterizes it (HarnessTiming) — the clock model the chip is signed
off with, and every synchronous harness input’s minimum delay.
The inputs: base.sdc gives every input one delay, IO_PCT of the period, as both its earliest and latest
arrival — 8 ns at 40 ns — so a port-to-flop path looks hold-safe inside the macro and is violated one level
up, where the harness delivers the Wishbone bus under 2 ns after the edge. With the real minimum on the ports,
the macro’s own hold repair closes exactly those paths, and nothing else needs a margin. MEASURED on
wpe_user_project (RUN_2026.09.25_22.53.44): the global 0.7 ns hold margin that closed dram_user_project’s
97 port-to-flop paths found 35,656 hold endpoints in the engine’s 32,773 flops and inserted 81,316 buffers,
0.81 mm²; the macro went from 32% to 41% utilization and global routing failed on congestion (GRT-0119).
The clock: base.sdc’s 0.25 ns uncertainty and 5 % derate are OpenLane’s defaults, not the chip’s; Caravel
signs itself off at 0.1 ns and 3.75 %, and those are set after base.sdc so they are the ones in force.
MEASURED on the engine’s post-CTS placement (RUN_2026.09.26_03.38.30, all three corners, OpenLane’s 0.1 ns
repair margin): under the defaults 31,793 of its 35,747 flops fell inside the margin and took a buffer each
(32,005), leaving global routing 55 gcells short; 31,089 of them were flops holding their own value through
an enable mux — launch and capture on the same flop and the same edge, a path skew cannot reach — and only
87 had negative slack. Under the chip’s model 6 endpoints fall inside the margin and 1 is negative: the
wbs_cyc_i port path, a real violation the repair closes either way.
clock is the harness clock the macro’s logic runs on, its CLOCK_PORT. On HarnessClock::User2 the harness’s
bus keeps its own clock: wb_clk_i becomes a clock at the chip’s core period, asynchronous to the macro’s, and
every port the harness times against it (HarnessTiming::core_clock_inputs and core_clock_outputs) moves from
base.sdc’s delay against CLOCK_PORT to one against wb_clk_i. Each removal names the clock it removes:
MEASURED in the pinned OpenSTA, unset_input_delay without -clock removes only the delays that have no clock,
and write_sdc still showed wb_clk_i delayed against user_clock2.