Prim Cell Replacement and Don’t-Touch Constraints
OCAH primitives (prim_buf, prim_and2, prim_clock_gating, the
prim_flop_*sync* family, and so on) ship as behavioral models with a
reference technology mapping. Adopters replace them with cells from their own
library.
Two rules govern those replacements. Both are the adopter’s responsibility — OCAH provides the instance names, but the names have no effect until the adopter’s synthesis and place-and-route flows constrain them.
1. Naming Requirement
Every library cell instantiated inside a replacement primitive must have
d0nt_ in its instance name.
The marker is deliberately misspelled with a zero. dont_touch is a reserved
keyword in several tools and a common substring in ordinary comments and signal
names; d0nt_ occurs only where the protection is intended, so a name-based
match cannot pick up unrelated cells.
Clock-path cells take the extended prefix d0nt_clk, which selects the stricter
rule below. The d0nt_* naming convention appears in the eFuse IP
(efuse_token_digest_sha256, efuse_triple_redundant_comparator) as the
primary example. The adopter is expected to carry the same convention into any
technology-mapped primitive wrapper they deliver. The intended name assignments
are:
| Instance name | Rule | Primitive |
|---|---|---|
|
Don’t touch |
|
|
Don’t touch |
|
|
Don’t touch |
|
|
Don’t touch |
|
|
Don’t touch |
|
|
Don’t touch |
|
|
Size only |
|
|
Size only |
|
|
Size only |
|
|
Size only |
|
|
Size only |
|
Keep the same names when substituting your own cells. Adopters who rename must apply the equivalent constraints to whatever names they use.
2. Constraint Requirement
In synthesis, apply set_size_only to every cell matching d0nt_, then
set_dont_touch to every cell matching d0nt_clk. Every step after synthesis
needs size_only alone — on all d0nt_ cells, clock cells included.
# Order matters: d0nt_clk* also matches d0nt_*, so the clock rule is applied
# second and overrides size-only for those cells.
set_size_only [get_cells -hierarchical -filter "full_name =~ *d0nt_*"]
set_dont_touch [get_cells -hierarchical -filter "full_name =~ *d0nt_clk*"]
size_only keeps the instance in the netlist — the tool may not delete, merge,
absorb, restructure, or buffer through it — while still allowing it to be
swapped for another library cell of the same logic function, typically to change
drive strength or threshold-voltage flavor for timing, area, and EM closure.
That is the intent for every replacement cell: these primitives exist because
the structure matters, not because the logic function could not be inferred.
Synthesis will otherwise remove them — an inverter pair that cancels,
redundant AND gates that always agree, a buffer driving a single load are all
dead weight to an optimizer.
Clock cells need the stronger dont_touch because that swap is exactly the
problem. Under size_only the tool treats the logic function as the contract
and every other property of the cell as free to change. On the clock path the
function is the least of the reasons the cell was chosen. Clock buffers,
inverters, muxes, and gaters are picked for electrical characteristics the
function does not capture — matched rise and fall times above all, and with
them duty-cycle preservation, balanced input-to-output delay, and low delay
variation across PVT. A functionally identical cell pulled from the
general-purpose library guarantees none of that. The netlist still says
"inverter", functional simulation still passes, and the duty-cycle distortion
shows up only in silicon or in a timing run late enough to be expensive.
dont_touch removes the tool’s discretion: the cell the designer selected is
the cell that reaches the netlist. Sizing and insertion-delay decisions on the
clock path belong to clock tree synthesis, which owns them explicitly, rather
than to a logic-optimization pass making a substitution it believes is free.
2.1. After Synthesis
The dont_touch on d0nt_clk* is a synthesis-only constraint. Once synthesis
has handed off the netlist, release the clock cells to size_only and carry
that — and nothing stronger — through placement, clock tree synthesis,
routing, and optimization:
# Post-synthesis: uniform size_only, clock cells included.
set_dont_touch [get_cells -hierarchical -filter "full_name =~ *d0nt_clk*"] false
set_size_only [get_cells -hierarchical -filter "full_name =~ *d0nt_*"]
The stricter rule exists to stop logic optimization from choosing the cell.
Downstream, clock cell sizing is CTS’s job: it cannot balance skew or close
transition violations with the cells it needs to resize frozen, and freezing
them there buys nothing. Keeping size_only in place still prevents deletion,
merging, and restructuring for the rest of the flow.
Restrict the physical flow to the library’s clock-cell list so a resize stays
inside the same clock cell family. Sizing a CLKBUF_D4 up to a CLKBUF_D8
keeps the matched rise and fall behavior the cell was chosen for; substituting a
general-purpose buffer of equivalent drive does not.
Additional requirements:
-
Carry
size_onlythrough PnR. Thedont_touchis synthesis-only, butsize_onlyis not. A cell protected in synthesis and left entirely unprotected downstream is not protected. -
Verify the match is non-zero. A typo in the filter, or a flow that renames instances before the constraint is read, silently protects nothing. Check the collection size and error out if it is empty.
-
Preserve the names. Uniquification, flattening, and name mapping must not strip the
d0nt_substring. If your flow renames instances, derive the collections before flattening and carry them forward. -
Restore protection after ECOs. Flows that relax preservation constraints for clock-tree or filler ECOs — clearing
eco_honor_dont_touch, for example — must restoresize_onlybefore any subsequent optimization pass. -
Check the final netlist, not just the post-synthesis one.
Flows other than Design Compiler and Fusion Compiler must apply the equivalent
size_only and dont_touch attributes to the same collections.
3. Simulation
These constraints have no functional effect and no simulation impact. RTL
simulation compiles the technology-independent primitive models (not(synth)).
Library cells are not on that filelist, so a simulator never elaborates them.
Confirming that the protected cells survived is therefore a gate-level netlist check. RTL simulation cannot detect their loss.