Skip to content

Write sky130 LEF with 1000 database units per micron - #322

Merged
mguthaus merged 1 commit into
VLSIDA:devfrom
TheAnimatrix:sky130-lef-units
Oct 13, 2026
Merged

mguthaus merged 1 commit into
VLSIDA:devfrom
TheAnimatrix:sky130-lef-units

Conversation

@TheAnimatrix

Copy link
Copy Markdown

Fixes #278.

lef.py hard-codes DATABASE MICRONS 2000. sky130 tech LEFs use 1000, and #278 reports OpenROAD failing to import the generated macro. The coordinates are written in microns and are already correct, so only the header is wrong. The released sky130 macros were fixed by hand in VLSIDA/sky130_sram_macros@c2333394, and #239 does the same.

This moves the value into the technology files as lef_units and lef.py reads it. sky130 (both tech configs) is 1000. freepdk45, scn4m_subm, scn3me_subm and gf180mcu stay at 2000, so their output does not change.

A single global 1000 would break freepdk45. Its manufacturing grid is 0.0025 um, which is not a whole number of 1/1000 um units. That is the open question in #278. I left gf180mcu at 2000 because I have not checked its flows.

Tests on dev (3608704)

24_lef_sram_test is skipped upstream, so I ran its body locally with the golden comparison replaced by a print of the header. sky130 needs word_size=4 with one spare row and column to build.

Tech Before After
sky130 2000 1000
scn4m_subm 2000 2000
freepdk45 2000 2000

04_pinv_1x (freepdk45) and 20_sram_1bank_nomux_1rw_1r (sky130, scn4m_subm) pass with the change. The golden LEFs for scn4m_subm and freepdk45 still say 2000 and are not affected.

We use 1000-unit sky130 LEFs from OpenRAM in an OpenROAD flow, with the same geometry and only the header changed. Macro power and signal routing connect there.

DATABASE MICRONS was hard-coded to 2000. sky130 tech LEFs use 1000. Each technology now sets lef_units, sky130 to 1000 and the rest to 2000 so their output does not change.
@mguthaus
mguthaus merged commit 398395c into VLSIDA:dev Oct 13, 2026
@mguthaus

Copy link
Copy Markdown
Collaborator

Merged. Thanks — and the reasoning about why this has to be per-technology rather than a single global value is exactly right.

I confirmed self.lef_units is referenced in precisely one place, lef.py:141's DATABASE MICRONS, and scales no coordinate, so this genuinely cannot move geometry. I also checked the grid arithmetic that motivated keeping freepdk45 at 2000:

tech lef_units drc["grid"] grid x units representable
sky130 1000 0.005 5.0 yes
freepdk45 2000 0.0025 5.0 yes (would be 2.5 at 1000)
scn4m_subm 2000 0.1 200.0 yes

All in-tree technologies are covered — there are only the two sky130 tech_configs, so nothing is missed.

Two notes, neither blocking:

  1. 24_lef_sram_test is not skipped upstream. It is not in BROKEN_STAMPS, and it does an exact isdiff() against golden/sram_2_16_1_<tech>.lef. It passes on dev for freepdk45 and scn4m_subm here, and it still passes with this change (the goldens keep DATABASE MICRONS 2000). So that test is available to you as a real guard next time.

  2. from openram.tech import layer_names, lef_units makes lef_units mandatory for every technology, including private out-of-tree ones that OpenRAM supports via the colon-separated OPENRAM_TECH. Those will now fail with an ImportError. magic.py:97-105 uses the try: from openram.tech import X / except ImportError: pattern with a default for blackbox_cells and flatglob; doing the same here with a default of 2000 would keep older technology directories working. Happy to take that as a follow-up if you agree.

Full regression on freepdk45 + scn4m_subm (30 tests): identical pass/fail to dev.

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