Do not emit an input max_transition below the library default - #306
Do not emit an input max_transition below the library default#306appleweiping wants to merge 1 commit into
Conversation
mguthaus
left a comment
There was a problem hiding this comment.
Thanks for the careful writeup — the bug in #298 is real and the diff is complete for what it targets.
What it does
lib.py emits max_transition : self.slews[-1] on the addr and wmask input buses. With defaults (slew_scales = [0.25, 1, 8] in globals.py:545 times tech.spice["rise_time"] = 0.005) that is 0.04 ns, while the same file's header declares default_max_transition : 0.5. This PR lifts 0.5 into a lib.default_max_transition class attribute and emits max(default_max_transition, slews[-1]), then updates 10 golden .lib files (0.04 -> 0.5 for freepdk45, 0.4 -> 0.5 for scn4m_subm).
What's right about it
max_transition is emitted in only three places in lib.py (header, write_addr_bus, write_wmask_bus), and all three are covered. din, csb/web, and clk emit no per-pin limit at all, so they already inherit 0.5 — after this change the library is internally consistent, which is a genuine improvement. All 10 goldens in the repo that contain a max_transition line were updated, and the 0.40 isapproxdiff tolerance can't absorb a 0.04 -> 0.5 change, so they had to be.
Why I don't want to merge it as-is
1. It fixes the symptom, not the cause. The LUT index_1 axis still tops out at 0.04 ns. Declaring max_transition : 0.5 tells STA that 500 ps of input slew is legal, where setup/hold then comes from roughly 12x linear extrapolation off a 3-point table. That trades "the tool refuses an infeasible constraint" for "the tool silently uses unvalidated numbers." #298 asked for (a) widening the characterization slew range and/or (b) not emitting below the default; this does only (b). The root cause is that rise_time = 0.005 ns is an unrealistic basis for input slew characterization — 5 ps, when the smallest sky130 buffer can't beat ~98 ps.
2. The 0.5 floor is a hardcoded, technology-independent number. max(0.5, slews[-1]) clamps every technology to >= 0.5 ns. It loosens scn4m_subm from 0.4 to 0.5 for no physical reason, and 0.5 ns would be an absurd limit at an advanced node. If this direction is kept, it should be an OPTS option settable per config (like slew_scales), not a class attribute.
3. The change is unverified end to end. There are no CI check runs on the head commit, and as you note SPICE characterization aborts in your environment (identically on dev, so not a regression) — which means the goldens were hand-edited rather than regenerated. Low risk given it's one deterministic line per file, but it needs a real characterization run before merge.
4. The added test is the weakest part. It grafts a white-box unit test into the middle of 23_lib_sram_test.py's runTest via lib.__new__(lib), SimpleNamespace stubs, and a monkeypatched write_FF_setuphold. It breaks as soon as those writers touch another attribute, it asserts against lib.default_max_transition itself (so it cannot catch a wrong constant), and it never exercises the slews[-1] > 0.5 branch that motivates the max(). The goldens already encode this behavior. Please drop it or move it to its own file.
5. Hygiene. Please drop the Co-Authored-By: Claude Opus 5 trailer from the commit and the "Generated with Claude Code" footer from the PR description.
Where to go from here
Two paths, either is fine with me:
- Minimal: drop or relocate the test, promote
default_max_transitionto anOPTSoption, and get a characterization run confirming the regenerated goldens. Then this is a reasonable incremental fix. - Better: fix the root cause — widen the default
slew_scales(or therise_timebasis) so the input-slew axis spans realistic drive, and keepmax_transition = slews[-1]. That regenerates far more golden data, but the resulting limit is actually backed by characterized points instead of extrapolation.
If the goal is to unblock timing-driven flows now and do the real fix later, the minimal path is acceptable — just understand that it ships extrapolated setup/hold rather than validated setup/hold.
Addresses review feedback on VLSIDA#306. The floor was a hardcoded class attribute, which clamped every technology to 0.5 ns regardless of what it can drive. It is now `OPTS.max_transition`, declared in options.py and defaulted in set_default_corner() alongside slew_scales and load_scales, so a config can set it per design and per technology. The library header is written from the same value, so the header and the per-pin limits can no longer drift apart. Also drops the unit test added to 23_lib_sram_test.py. The criticism was correct: it asserted against the constant itself, so it could not catch a wrong value, and it never exercised the branch where slews[-1] exceeds the floor. The goldens already encode the emitted limits. Verified against origin/dev with freepdk45: the default resolves to 0.5; a characterized range topping out at 0.04 emits 0.5; a range topping out at 0.9 emits 0.9 rather than being clamped; and setting OPTS.max_transition to 0.25 emits 0.25. Signed-off-by: appleweiping <vipinapple986@gmail.com>
|
Thank you — this is a more careful reading than the PR deserved, and points 2, 4 and 5 were all correct. Pushed 2 — hardcoded floor. 4 — the test. Dropped. Your objection was the right one: it asserted against the constant itself, so it could not have caught a wrong value, and it never exercised the branch that motivates the 5 — hygiene. Removed from the commit and the PR description. For the avoidance of doubt rather than to bury it: this work was done with AI assistance, as I noted on #298. 3 — end-to-end verification. Still not done, and I cannot do it here. SPICE characterization does not complete in my environment — it aborts at 1 — symptom vs cause. Agreed, and I would not argue otherwise. Declaring 0.5 while |
|
Re-ran verification on the latest head c770dc6 after the requested changes.
A real SPICE characterization run is still blocked by the local environment. This checkout has no OpenRAM PDK model setup, and the local base branch fails at the same simulator setup stage. Please re-review the updated branch. I can respond to any remaining implementation concern. |
|
This seems AI generated. Did you actually run it? |
Use OPTS.max_transition for the Liberty default and for the address and write-mask bus limits. Keep each explicit bus limit at the greater of the configured floor and the largest characterized slew, with 0.5 as the default unless a config overrides it. Update the freepdk45 and scn4m_subm golden Liberty files. Signed-off-by: appleweiping <vipinapple986@gmail.com>
c770dc6 to
0215d76
Compare
|
Yes, the wording was AI-assisted, and it was too vague. When I posted that comment, I had run I have now run the repository test with Python 3.12.3 and ngspice 45 in the Nix environment: Characterization completed in 588 seconds and wrote the Liberty file. The generated I also found two problems in my earlier update. Sorry. The previous update was not precise enough. |
Fixes #298.
write_addr_bus()andwrite_wmask_bus()currently set each input busmax_transitionto the largest slew used during characterization. With the default slew set this is 0.04 ns, while the same Liberty file declaresdefault_max_transition : 0.5. Downstream timing tools can reject the tighter input constraint as infeasible.This change adds
OPTS.max_transition, defaults it to 0.5 inset_default_corner(), and uses that option for the Liberty header. The address and write-mask buses emit the greater of the configured value and the largest characterized slew. A design or technology config can override the default.The freepdk45 and scn4m_subm golden Liberty files are updated for the emitted input-bus limit.
Testing
Environment: Python 3.12.3, ngspice 45, and the repository Nix development shell.
The characterization completed in 588 seconds and wrote the Liberty file. The generated file contains:
The test then exited with status 1 at the golden comparison. It reported three formatting differences involving spaces around
pulling_resistance_unit,min_pulse_width, andminimum_period. The same three differences reproduce on unmodifieddevwith23_lib_sram_model_test.py.I am not reporting the test as passing. I also found that
testutils.isapproxdiff()currently buildsline2_floatsfromline1_floats, so I manually checked the generated target lines instead of relying on its numeric comparison.The modified Python files pass
py_compile, andgit diff --checkpasses.