Skip to content

Bounded primary-input buffer trees

import-pdk can insert noninverting buffer trees on primary inputs whose functional and passive cell loads exceed an explicit fanout policy. This is opt-in through PhysicalConfig.input_buffer; ordinary imports remain unchanged. Configured clock ports and nets feeding LEF clock pins are excluded and remain under CTS. Reset and scan inputs retain their arbitrary logical values.

Use a fixed floorplan with sufficient space for the added cells. The automatic floorplan command explicitly rejects a profile containing input_buffer, because its area estimate does not yet include these trees. Derive the base floorplan first, then apply the policy during import. Placement, packing and all physical verification remain mandatory.

The two supplied policies are SKY130 and GF180. For example, the SKY130 policy uses sky130_fd_sc_hd__buf_4, input A, output X, and fanout eight. The cell must implement the declared identity function; the native structural checks alone do not establish that property.

The policy has independent bounds:

Field Meaning Supported maximum
max_fanout Loads on each source or generated output 128; minimum 2
max_buffers New buffers across all selected inputs 250,000
max_sinks Original moved cell loads 2,000,000
max_work Work per construction or validation pass 1,000,000,000
memory_mib Incremental scratch, appended AST and vector relocation 4,096 MiB

The parent max_added_cells and CLI cell limits also apply. The memory estimate includes existing vector reallocation peaks, 4,096 bytes per buffer, 96 bytes per moved sink, 128 bytes per primary bit, copied names and master classification. It is evaluated before collecting sink lists or creating buffers. The process must still run under the caller's whole-process guard.

Construction uses bounded pin-table scans and bottom-up groups. Every parent references an already planned group, with at most 64 levels. There is no path, cut or cone enumeration. Only identified cell input connections are rewritten; port declarations, output identities and clock ownership are preserved. The new cells participate in ordinary physical placement and supply connection.

The revision records source port/bit identities, original load counts, tree depths, and generated instance names and indices. Reload checks every root, buffer, fanout and leaf count, and rejects cycles with a bounded linear traversal. Passive antenna loads count toward fanout but do not become functional endpoints. A budget or validation failure does not publish a new HEAD.

Run the independent functional checks with:

python3 scripts/verify_input_buffers.py \
  --rustroad target/release/rustroad --out .work/input-buffer-proof

They use actual pinned LEF/Liberty files for both PDKs, external Yosys CEC, ascending/descending bus cases, multilevel trees, wrong-output controls and a configured inverter control. The complete physical gate can exercise this policy with --input-buffer-fanout 2. It verifies the unchanged base floorplan, binds the explicit policy and derived physical configuration before execution, and retains the usual timing, DRC, LVS, antenna, equivalence and determinism gates. That fixed-floorplan run does not claim automatic sizing for the added trees.