6. Uniquify API#

This chapter describes the public API for hardening parameterized modules. For a worked walkthrough see the uniquify tutorial.

A hardened macro is parameter-free, so a parameterized module cannot be hardened and reused directly. Uniquified discovers the concrete parameter combinations a design uses, generates a parameter-free variant for each (to harden) plus a parameterized wrapper that dispatches to them, and integrates the results into a SiliconCompiler flow.

6.1. Uniquified#

Uniquified.write

Write the generated wrapper/variant sources to outdir (idempotent).

Uniquified.build

Harden the variants into macro libraries.

Uniquified.load_macros

Load previously built macros from libdir into state.

Uniquified.wireup

Wire the wrappers and macros into a parent ASIC project.

Uniquified.manifest

Path to the hardening run manifest for variant.

Uniquified.instance_path

Hierarchy path from a wrapper down to its hardened variant instance.

class siliconcompiler.tools.slang.utils.macro.Uniquified(design: Design | Project, modules: Sequence[str], filesets: Sequence[str] | None = None, libdir: str | None = None)[source]#

Uniquify parameterized modules of a design and integrate the results.

Note

Beta feature. This API is new and may change in a future release.

Construction performs setup only – enumeration, code generation (in memory) and fileset registration on design. It writes nothing to disk and runs no EDA tools; the generated sources are materialized on the first build()/wireup() (or an explicit write()). Call build() to harden the variants and wireup() to wire the wrappers and macros into a parent project.

Unparameterized modules are supported too: one with no overridable parameters needs no wrapper, so it is hardened directly and blackboxed at wireup() (the macro_reuse pattern). This means a single Uniquified can harden a mix of parameterized and unparameterized modules.

Parameters:
  • design (Design | Project) – The context that instantiates the target modules (its elaboration determines the real parameterizations). If a Project is given, its design and selected filesets are used.

  • modules (list) – Names of the modules to uniquify/harden (parameterized or not).

  • filesets (list, optional) – Design filesets to elaborate. Defaults to the project’s filesets, or ["rtl"] for a bare design.

  • libdir (str, optional) – Root directory for uniquify’s artifacts – the persisted macro manifests at its root and the generated sources + hardening build tree under <libdir>/build. Defaults to build/<design>/uniquify.

Raises:

UniquifyError – If a requested module is not found among the filesets or is not instantiated anywhere in the design.

__init__(design: Design | Project, modules: Sequence[str], filesets: Sequence[str] | None = None, libdir: str | None = None) None[source]#
build(target: Callable[[ASIC], None] | None = None, macros: str | Sequence[str] | None = None, parallel: bool = False, rebuild: bool = False) Dict[str, StdCellLibrary][source]#

Harden the variants into macro libraries.

Built macros are persisted under this instance’s libdir so subsequent runs reuse them (unless rebuild is set).

Parameters:
  • target – SiliconCompiler target callback fn(project) that sets the PDK/flow/SDC on each variant’s ASIC project (e.g. freepdk45_demo). Required unless every selected macro already has a cached build.

  • macros – Which macros (variants) to build. None builds all; a module name expands to its variants; anything else is matched as a variant name or glob (e.g. "mod__W*") – useful for a rebuild.

  • parallel – If True, harden the selected macros concurrently.

  • rebuild – Force re-hardening even if a cached macro exists.

Returns:

{variant: StdCellLibrary} for the selected macros.

Return type:

dict

property design: Design#

The design the generated filesets were added to.

property hardened_filesets: Dict[str, str]#

Mapping of variant name -> its hardening fileset.

instance_path(variant: str, parent: str | None = None) str[source]#

Hierarchy path from a wrapper down to its hardened variant instance.

The generated wrapper dispatches to each variant inside a generate block labelled g_<variant> holding an instance named after the configured instance name, so within the wrapper the variant sits at g_<variant>/<instance>. This is the scope needed to, e.g., map VCD switching activity onto the hardened netlist.

Only parameterized modules have a wrapper generate block; calling this for a variant of an unparameterized module is an error (it is instantiated directly, with no g_<variant> level).

Parameters:
  • variant (str) – A variant (macro) name.

  • parent (str, optional) – The wrapper’s own instance path (e.g. a testbench DUT scope). If given, it is prepended to form an absolute path.

Returns:

g_<variant>/<instance>, prefixed with parent if given.

Return type:

str

Raises:

UniquifyError – If variant is unknown or belongs to an unparameterized (unwrapped) module.

property libdir: str#

Directory the variants are hardened under and their macros persisted.

load_macros() Dict[str, StdCellLibrary][source]#

Load previously built macros from libdir into state.

Reuses macros persisted by an earlier build() without re-hardening.

Returns:

{variant: StdCellLibrary} for every macro found.

Return type:

dict

property macros: Dict[str, StdCellLibrary]#

Variant name -> built (or loaded) macro; populated by build().

manifest(variant: str) str[source]#

Path to the hardening run manifest for variant.

Points at the <design>.pkg.json written by build() under libdir; load it (ASIC.from_manifest) to locate the variant’s implementation results (netlist, SDC, parasitics).

Parameters:

variant (str) – A variant (macro) name.

Returns:

Absolute path to the run’s manifest.

Return type:

str

property modules: List[str]#

The uniquified module names.

property outdir: str#

Directory the generated wrapper/variant sources are written to.

property variant_names: List[str]#

All variant (macro) names across every module.

property variants: Dict[str, List[str]]#

Mapping of module name -> its variant (macro) names.

wireup(project: ASIC, require_all: bool = True) ASIC[source]#

Wire the wrappers and macros into a parent ASIC project.

For a parameterized module, aliases its RTL to the generated wrapper (so synthesis elaborates the dispatch). For an unparameterized module, blackboxes it (aliases to nothing) so the injected macro provides it. In both cases the hardened macros are added via add_asiclib.

Parameters:
  • project – The parent ASIC project instantiating the modules.

  • require_all – If True (default), raise unless every variant has a built/loaded macro – an unhardened but used variant would make the wrapper’s $error branch fire at elaboration.

Returns:

The same project, wired up.

Return type:

ASIC

Raises:

UniquifyError – If require_all and some variants have no macro.

property wrapper_filesets: Dict[str, str]#

Mapping of module name -> its wrapper fileset (plus ‘all’).

Only parameterized modules have a wrapper; unparameterized modules are omitted (they are hardened and blackboxed directly, with no wrapper).

write() str[source]#

Write the generated wrapper/variant sources to outdir (idempotent).

Construction registers the filesets but does not touch disk; the sources are materialized here. build() and wireup() call this automatically – call it directly only to materialize the sources without hardening (e.g. to lint the wrappers).

Returns:

The output directory the sources were written to.

Return type:

str

6.2. Packaging a macro#

siliconcompiler.tools.slang.utils.macro.build_macro(project: ASIC, name: str) StdCellLibrary[source]#

Package a completed variant hardening run into a StdCellLibrary.

Bundles the results of project (a finished ASIC run of one variant) into a macro library, following the macro_reuse example. Beyond the physical (LEF/GDS) and per-corner Liberty views a reusable macro needs, it also carries the implementation’s gate-level netlist and constraints so downstream flows (gate-level simulation, timing/power signoff) can pull them straight from the macro. The filesets are:

  • models.physical – LEF + GDS for place-and-route in a parent.

  • models.timing.<corner> – Liberty (nldm) per timing scenario ([constraint,timing,scenario,...]), so it adapts to single- and multi-corner PDKs.

  • rtl – the gate-level netlist as a simulation view, bundling the standard-cell Verilog models (as a dependency) so it simulates standalone.

  • netlist – the same gate-level netlist as a structural view for STA, dependency-free (STA resolves cells from Liberty).

  • sdc – the implementation-generated constraints (propagated clocks).

The rtl view’s cell-model dependency is serialized into the macro’s manifest (see DependencySchema), so it is recovered when the macro is reloaded: a consumer can simulate the rtl view without re-registering the PDK library. The rtl/netlist/sdc filesets are added only when the run produced the corresponding result, so flows that stop short of them still yield a valid macro.

Parameters:
  • project – A completed ASIC project whose top module is a variant.

  • name (str) – Name for the resulting macro library. Also the netlist’s top module name (the parameter-free variant).

Returns:

The packaged macro, ready for Uniquified.wireup().

Return type:

StdCellLibrary

Raises:

UniquifyError – If the project defines no timing scenario.