feat: enhance commercial flow with new features and improvements

- Added support for optional files in GDS generation to handle legacy layouts.
- Introduced macro-level LVS/ERC processing to improve verification of hard macros.
- Updated STA scripts to adopt PrimeTime analysis settings from legacy flows.
- Enhanced synthesis settings to align with CX55 legacy flow, including clock gating and area optimization.
- Improved reporting functions to include detailed synthesis summaries and area metrics.
- Updated package generation script to support both TINY and MINI SOC configurations.
- Added comprehensive tests for ECO translation, timing contract generation, and constraint handling.
- Ensured compatibility with new timing contract formats and enhanced error handling for domain drift.
This commit is contained in:
maksyuki committed 2026-10-08 15:20:39 +08:00
1 parent 8a2dad4a93
commit bab01352f8
32 files changed
+2174 -342

No files matched your search

@@ -3,15 +3,22 @@
Read the [commercial guide](../../../../physical/commercial/README.md), its
[Makefile](../../../../physical/commercial/Makefile), and the selected local
configuration without copying site-sensitive values into tracked material.
The current flow is Mini/ICS55, not a generic adapter for every product/PDK.
The flow is parameterized per product through `SOC` (tracked
`config/products/<soc>.mk` + `tcl/common/products/<soc>.tcl`): Mini
(`retrosoc_asic`) and Tiny (`retrosoc_tiny_asic`, SAFE24 with the PLL parked)
are supported on ICS55; future series plug into the same layer. It is not a
generic adapter for arbitrary designs or other PDKs.
## Inputs and execution boundary
The development zone creates the production RTL package from the committed
`configs/cluster/ics55.mk` profile with the guide's PLL/SRAM settings. The EDA
zone consumes that archive through a real, ignored `LOCAL_CONFIG` and `RUN_ID`.
Validate the source/configuration/clock inventory and archive identity before
using it. An inspection request must not generate the package or submit jobs.
product profile — `configs/cluster/ics55.mk` with the guide's PLL/SRAM
settings for Mini, `configs/ci/ics55-tiny.mk` for Tiny. The EDA zone consumes
that archive through a real, ignored `LOCAL_CONFIG` (default
`local/ics55-<soc>.mk`) and `RUN_ID`. Validate the source/configuration/clock
inventory and archive identity before using it; the packaged
`rtl/contracts/commercial_timing_contract.tcl` must match the product domain
policy. An inspection request must not generate the package or submit jobs.
For authorized execution, use the repository Make targets and site-selected
LSF mechanism. Never invoke licensed tools directly on the development host
@@ -27,21 +34,24 @@ There are two different acceptance boundaries:
Its top-level I/O exclusions must be visible in the summary; it is not input
qualification for Innovus/PrimeTime implementation.
- Full implementation uses the strict `doctor` and the selected stages up to
`signoff`. Qualified I/O budgets, local pad timing hook, library/corner and
extraction coverage, and zero-threshold verification rules remain mandatory.
Never set `IO_TIMING_QUALIFIED=YES` without the supporting reviewed budgets.
`signoff`. Qualified I/O budgets, the product pad timing hook when required
(Mini), library/corner and extraction coverage, and zero-threshold
verification rules remain mandatory. Never set `IO_TIMING_QUALIFIED=YES`
without the supporting reviewed budgets.
Follow the current stage graph through synthesis/equivalence, APR,
post-route equivalence, extraction/STA/ECO, and physical verification only as
far as requested. The guide and executable targets, not a skill-maintained
copy of the graph, define dependencies and required evidence.
post-route equivalence, extraction/STA/ECO, and physical verification
(including the optional macro-level LVS/ERC stage) only as far as requested.
The guide and executable targets, not a skill-maintained copy of the graph,
define dependencies and required evidence.
## Resume and handoff
Runs live under `build/commercial/ics55/<run-id>`. Before resuming, compare
original input/configuration hashes and the source identity, then check each
completed stage's result, required outputs, and verdict marker against its
stamp. Stamps alone are insufficient, especially after input or tool changes.
Runs live under `build/commercial/ics55/<soc>/<run-id>`. Before resuming,
compare original input/configuration hashes and the source identity, then
check each completed stage's result, required outputs, and verdict marker
against its stamp. Stamps alone are insufficient, especially after input or
tool changes.
For unchanged valid inputs, repeating the requested endpoint with the same
local configuration and run ID uses the existing first-missing-stage behavior.
+10
View File
@@ -257,3 +257,13 @@ for its current reporting information.
Thanks to everyone contributing to retroSoC.
[![retroSoC contributors](https://contrib.rocks/image?repo=retroSoC/retroSoC)](https://github.com/retroSoC/retroSoC/graphs/contributors)
## Star History
<a href="https://www.star-history.com/?repos=retrosoc%2Fretrosoc&type=date&logscale=&legend=bottom-right">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=retrosoc/retrosoc&type=date&theme=dark&legend=bottom-right" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=retrosoc/retrosoc&type=date&legend=bottom-right" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=retrosoc/retrosoc&type=date&legend=bottom-right" />
</picture>
</a>
+6 -3
View File
@@ -12,9 +12,12 @@ drivers.
and `librelane/tiny/` (Tiny full-chip pad ring).
- `ecc/` contains the ICS55 padless-core adapter and the official ECC `latest`
installer entrypoint. Installation does not qualify physical-flow compatibility.
- `commercial/` contains licensed-tool orchestration and flow logic. PDKs,
foundry decks, commercial libraries, site configuration, and results remain
outside Git.
- `commercial/` contains licensed-tool orchestration and flow logic for the
ICS55 implementation flow, parameterized per product line (`SOC=MINI` and
`SOC=TINY`, extensible to future series). PDKs, foundry decks, commercial
libraries, site configuration, and results remain outside Git; see
[commercial/README.md](commercial/README.md) for the three-layer
configuration model and stage graph.
- `sdf/`, when supplied by an implementation flow, contains post-layout
netlist and SDF collateral consumed by post-layout simulation.
+58 -12
View File
@@ -15,17 +15,46 @@ SHELL := /bin/sh
FLOW_ROOT := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
ROOT_PATH := $(abspath $(FLOW_ROOT)/../..)
LOCAL_CONFIG ?= $(FLOW_ROOT)/local/ics55-production.mk
LOCAL_CONFIG ?=
SOC ?= MINI
TECHNOLOGY ?= ICS55
SOC_LOWER := $(shell echo '$(SOC)' | tr 'A-Z' 'a-z')
TECH_LOWER := $(shell echo '$(TECHNOLOGY)' | tr 'A-Z' 'a-z')
TECH_CONFIG := $(FLOW_ROOT)/config/technology/$(TECH_LOWER).mk
PRODUCT_CONFIG := $(FLOW_ROOT)/config/products/$(SOC_LOWER).mk
ifeq ($(wildcard $(TECH_CONFIG)),)
$(error unsupported TECHNOLOGY=$(TECHNOLOGY): missing $(TECH_CONFIG))
endif
ifeq ($(wildcard $(PRODUCT_CONFIG)),)
$(error unsupported SOC=$(SOC): missing $(PRODUCT_CONFIG))
endif
include $(TECH_CONFIG)
include $(PRODUCT_CONFIG)
ifeq ($(LOCAL_CONFIG),)
LOCAL_CONFIG := $(FLOW_ROOT)/local/$(TECH_LOWER)-$(SOC_LOWER).mk
endif
-include $(LOCAL_CONFIG)
TOP ?= retrosoc_asic
TECHNOLOGY ?= ICS55
FLOW_PYTHON ?= python
TCLSH ?= tclsh
DOCTOR_ARGS ?=
RUN_ID ?= current
BUILD_ROOT ?= $(ROOT_PATH)/build
RUN_ROOT ?= $(abspath $(BUILD_ROOT))/commercial/ics55/$(RUN_ID)
RUN_ROOT ?= $(abspath $(BUILD_ROOT))/commercial/$(TECH_LOWER)/$(SOC_LOWER)/$(RUN_ID)
# Optional ECO subflows and Calibre LVS controls; the local configuration may
# override them. All optional subflows default off (legacy
# pd_data/pteco/scr/flow/{swap,size_down,remove_buffer}.tcl semantics).
ECO_ENABLE_VT_SWAP ?= NO
ECO_ENABLE_SIZE_DOWN ?= NO
ECO_ENABLE_REMOVE_BUFFER ?= NO
ECO_PBA_MODE ?= none
ECO_VT_PATTERN_PRIORITY ?=
MACRO_LVS_CELLS ?=
CALIBRE_LVS_ARGS ?=
INPUT_DIR := $(RUN_ROOT)/input
META_DIR := $(RUN_ROOT)/meta
@@ -51,6 +80,7 @@ PV_MERGE_STAMP := $(STAMP_DIR)/pv-merge.stamp
PV_DRC_STAMP := $(STAMP_DIR)/pv-drc.stamp
PV_ANT_STAMP := $(STAMP_DIR)/pv-antenna.stamp
PV_LVS_STAMP := $(STAMP_DIR)/pv-lvs.stamp
PV_MACRO_LVS_STAMP := $(STAMP_DIR)/pv-macro-lvs.stamp
RUN_STAGE := $(FLOW_PYTHON) $(FLOW_ROOT)/scripts/run_stage.py
SUBMIT_JOB := $(FLOW_PYTHON) $(FLOW_ROOT)/scripts/submit_job.py
@@ -66,7 +96,7 @@ export
.PHONY: help check-config doctor doctor-syn boundary-check input syn fm-rtl2syn \
apr-initialize apr-floorplan apr-preplace apr-place apr-cts apr-route \
fm-syn2pr extract sta eco apr-eco reextract resta pv-merge pv-drc \
pv-antenna pv-lvs signoff clean-stage list-stages
pv-antenna pv-lvs pv-macro-lvs signoff clean-stage list-stages
help:
@printf '%s\n' \
@@ -83,25 +113,29 @@ help:
' apr-eco apply ECO changes and incrementally route' \
' reextract extract the ECO design' \
' resta sign off the ECO design' \
' pv-* merge GDS and run Calibre DRC/antenna/LVS' \
' pv-* merge GDS and run Calibre DRC/antenna/LVS/macro-LVS' \
' signoff run the complete strict stage graph' \
' clean-stage remove STAGE and all downstream stage results' \
'' \
'Set LOCAL_CONFIG=local/ics55-production.mk and optionally RUN_ID=<id>.'
'Set SOC=MINI|TINY (default MINI) and TECHNOLOGY=ICS55, point' \
'LOCAL_CONFIG at the ignored local configuration (default' \
'local/<technology>-<soc>.mk), and optionally set RUN_ID=<id>.'
list-stages:
@printf '%s\n' input syn fm-rtl2syn apr-initialize apr-floorplan \
apr-preplace apr-place apr-cts apr-route fm-syn2pr extract sta eco \
apr-eco reextract resta pv-merge pv-drc pv-antenna pv-lvs
apr-eco reextract resta pv-merge pv-drc pv-antenna pv-lvs pv-macro-lvs
check-config:
@test -f '$(LOCAL_CONFIG)' || { \
echo "missing ignored local configuration: $(LOCAL_CONFIG)" >&2; \
echo "copy config/ics55.example.mk to local/ics55-production.mk" >&2; \
echo "copy config/$(TECH_LOWER)-$(SOC_LOWER).example.mk to $(LOCAL_CONFIG)" >&2; \
exit 2; \
}
@test -n '$(TOP)' || { \
echo "TOP is not set by config/products/$(SOC_LOWER).mk" >&2; \
exit 2; \
}
@test '$(TECHNOLOGY)' = ICS55
@test '$(TOP)' = retrosoc_asic
doctor: check-config
@mkdir -p $(META_DIR)
@@ -394,8 +428,20 @@ $(PV_LVS_STAMP): $(PV_ANT_STAMP)
pv-lvs: $(PV_LVS_STAMP)
$(PV_MACRO_LVS_STAMP): $(PV_LVS_STAMP)
@mkdir -p $(RUN_ROOT)/pv/macro_lvs/work $(RUN_ROOT)/pv/macro_lvs/log $(RUN_ROOT)/pv/macro_lvs/reports $(RUN_ROOT)/pv/macro_lvs/output
PV_MODE=macro_lvs $(TCLSH) $(FLOW_ROOT)/tcl/pv/generate.tcl
$(RUN_STAGE) --stage pv-macro-lvs --cwd $(RUN_ROOT)/pv/macro_lvs/work \
--log $(RUN_ROOT)/pv/macro_lvs/log/lsf.log \
--result $(RUN_ROOT)/pv/macro_lvs/result.json --stamp $@ \
--expect $(RUN_ROOT)/pv/macro_lvs/output/verdict.pass -- \
$(call submit-job,LSF_PV_ARGS,$(RUN_ROOT)/pv/macro_lvs/log/tool.log) \
$(TCLSH) $(RUN_ROOT)/pv/macro_lvs/work/macro_lvs_driver.tcl
pv-macro-lvs: $(PV_MACRO_LVS_STAMP)
signoff: doctor
$(MAKE) pv-lvs LOCAL_CONFIG=$(LOCAL_CONFIG) RUN_ID=$(RUN_ID)
$(MAKE) pv-macro-lvs LOCAL_CONFIG=$(LOCAL_CONFIG) RUN_ID=$(RUN_ID)
@printf 'commercial signoff passed: %s\n' '$(RUN_ROOT)'
clean-stage:
+165 -55
View File
@@ -1,15 +1,44 @@
# Commercial Physical Design
This directory owns the self-developed integration for the licensed ICS55
implementation flow. It does not contain the PDK, foundry rule decks, standard
cell or macro libraries, commercial tool installations, or generated results.
implementation flow. It contains only flow logic and configuration policy: the
PDK, foundry rule decks, standard-cell or macro libraries, commercial tool
installations, site configuration, and generated results all remain outside
Git.
## Configuration layers
Configuration is resolved in three layers; later layers override earlier ones.
1. `config/technology/<technology>.mk` (tracked) — technology policy shared by
every product: corner/scenario names, OCV derate values, DRV limits, CTS
targets, and library-family naming conventions.
2. `config/products/<soc>.mk` (tracked) — product policy: `TOP`, the canonical
clock-domain list, qualified-I/O interface groups, PLL mode policy
(`qualified` or `parked`), and non-sensitive floorplan/timing defaults.
`tcl/common/products/<soc>.tcl` mirrors it with the product clock overlays
and I/O port mapping. Adding a product (for example Std/Pro) means adding
these two files plus one ignored local configuration; the Makefile and the
shared Tcl do not change.
3. `local/<technology>-<soc>.mk` (ignored) — site values: tool commands and
LSF queues, Liberty/DB/LEF/GDS/CDL/RC/deck paths, PDK cell lists, routing
layers, and reviewed I/O timing budgets. Copy the matching
`config/<technology>-<soc>.example.mk` template and replace every
`REQUIRED` value.
Run outputs land below `build/commercial/<technology>/<soc>/<run-id>/`. The
`<soc>` path component was added with product parameterization; runs created
at `build/commercial/ics55/<run-id>/` predate it and are not migrated. A
pre-parameterization `local/ics55-production.mk` remains valid for Mini when
passed explicitly through `LOCAL_CONFIG`; rename it to `local/ics55-mini.mk`
to use the default lookup.
## Execution boundary
The development and EDA zones share the repository and build directories
through a site-mounted shared filesystem, but have different responsibilities:
1. In the development zone, generate a production RTL package:
1. In the development zone, generate a production RTL package. For Mini:
```sh
make CONFIG=configs/cluster/ics55.mk \
@@ -17,30 +46,41 @@ through a site-mounted shared filesystem, but have different responsibilities:
commercial-package
```
2. In the EDA zone, create the ignored local configuration from
`config/ics55.example.mk`, point `RTL_ARCHIVE` at the generated
`retrosoc_mini_sources.tar.gz`, and choose one of two entry points.
For synthesis with internal timing only:
For Tiny (the committed `configs/ci/ics55-tiny.mk` profile already carries
the required PLL/SRAM settings):
```sh
make -C physical/commercial \
LOCAL_CONFIG=local/ics55-production.mk doctor-syn
make -C physical/commercial \
LOCAL_CONFIG=local/ics55-production.mk RUN_ID=<run-id> syn
make -C physical/commercial \
LOCAL_CONFIG=local/ics55-production.mk RUN_ID=<run-id> fm-rtl2syn
make CONFIG=configs/ci/ics55-tiny.mk commercial-package
```
Both packages carry `rtl/filelist.fl` and the generated
`rtl/contracts/commercial_timing_contract.tcl`; Tiny additionally carries
`contracts/tiny_core.sdc` for the smoke STA flow. Packages created before
the nine-domain Mini clock inventory (2026-08-30) are incompatible with
the current constraint stack and must be regenerated.
2. In the EDA zone, create the ignored local configuration from the matching
example template and point `RTL_ARCHIVE` at the generated
`retrosoc_<product>_sources.tar.gz`. For synthesis with internal timing
only:
```sh
make -C physical/commercial SOC=MINI doctor-syn
make -C physical/commercial SOC=MINI RUN_ID=<run-id> syn
make -C physical/commercial SOC=MINI RUN_ID=<run-id> fm-rtl2syn
```
For production implementation, first populate every interface timing
budget, set `IO_TIMING_QUALIFIED=YES`, and run:
budget of the selected product, set `IO_TIMING_QUALIFIED=YES`, and run:
```sh
make -C physical/commercial \
LOCAL_CONFIG=local/ics55-production.mk RUN_ID=<run-id> doctor
make -C physical/commercial \
LOCAL_CONFIG=local/ics55-production.mk RUN_ID=<run-id> signoff
make -C physical/commercial SOC=MINI RUN_ID=<run-id> doctor
make -C physical/commercial SOC=MINI RUN_ID=<run-id> signoff
```
`SOC` defaults to `MINI`; `LOCAL_CONFIG` defaults to
`local/<technology>-<soc>.mk`.
Commercial tools are never executed directly by this Makefile. Every tool
stage is submitted as an independent blocking LSF job. `LSF_MODE=batch` uses
`bsub -K`; sites that expose only an interactive queue use
@@ -61,9 +101,10 @@ site-specific values, including:
- tool commands and LSF queues/resources;
- Liberty/DB, LEF, GDS, CDL, and RC technology files;
- stream maps and Calibre DRC, antenna, and LVS decks;
- PDK cell lists, routing layers, sites, and power nets.
- PDK cell lists, routing layers, sites, and power nets;
- reviewed min/max board and external-device I/O timing budgets;
- an audited ICS55 pad-mode timing hook when bidirectional arcs require it.
- an audited pad-mode timing hook when the product requires one (Mini does;
Tiny does not).
Do not add absolute PDK/library paths to tracked Make, Tcl, Python, shell, or
documentation files. Run `python3 scripts/audit_boundary.py --root ../..`
@@ -82,20 +123,89 @@ input
-> fm-syn2pr
-> extract -> sta
-> eco -> apr-eco -> reextract -> resta
-> pv-merge -> pv-drc -> pv-antenna -> pv-lvs
-> pv-merge -> pv-drc -> pv-antenna -> pv-lvs -> pv-macro-lvs
```
Each stage writes `log/`, `reports/`, `output/`, a result JSON, and a success
stamp below:
stamp below the run root. A stamp is created only when LSF reports success and
every required output, including the stage verdict marker, exists. Re-running
`signoff` resumes from the first missing stamp. Use `force-<stage>` to rerun
one stage and downstream dependencies; use `clean-stage STAGE=<stage>` for
targeted cleanup.
```text
build/commercial/ics55/<run-id>/
```
`pv-macro-lvs` verifies the macros listed in `MACRO_LVS_CELLS` (for example
`PLL_TOP`) individually against `MACRO_GDS`/`MACRO_CDL` with LVS+ERC; with an
empty list the stage records a harmless skip.
A stamp is created only when LSF reports success and every required output,
including the stage verdict marker, exists. Re-running `signoff` resumes from
the first missing stamp. Use `force-<stage>` to rerun one stage and downstream
dependencies; use `clean-stage STAGE=<stage>` for targeted cleanup.
## Timing constraint model
Constraints are generated from the canonical per-product inventory
(`rtl/<product>/integration/clock_reset_domains.json` + `pin_map.json`) into
the packaged `commercial_timing_contract.tcl` by
`scripts/generate_timing_contract.py --soc <SOC>`, which rejects any drift
from the product domain list in `config/products/<soc>.mk`. The shared Tcl
(`tcl/common/clocks.tcl`) creates one master clock per source-port domain at
its contract observation and period, a `-divide_by 1` generated clock for
domains derived from another domain, groups clocks by the contract
`async_group`, applies uncertainty/transition from configuration, and
false-paths the canonical reset ports. Product overlays
(`tcl/common/products/<soc>.tcl`) own the rest:
- Mini (`mini.tcl`): the LP root mux is modeled with physically exclusive
generated clocks — `clk_lp_ext`/`clk_pclk_ext` from the 24 MHz reference
(bypass) and `clk_lp_pll`/`clk_pclk_pll` from `clk_pll` (PLL performance
mode, master on `u_rcu/u_clock_reset_subsystem/u_tc_pll/u_PLL_TOP/CKOUT1`).
Qualified I/O covers JTAG, DVP (GPIO10-20), ULPI, SDRAM, SDIO, XPI, and
asynchronous pads (UART0/1, remaining GPIO); a reviewed
`TIMING_IO_MODE_HOOK` is mandatory in qualified mode.
- Tiny (`tiny.tcl`): SAFE24 boot — `clk_system` (external 24 MHz) and
`clk_jtag` only. The PLL_TOP macro is parked (EN=0), is not a clock source,
and has no characterized timing arcs yet (see `docs/ip/tiny-soc.md`
TINY-055); its DB/LEF/GDS/CDL views are still required for link, placement,
and LVS. Qualified I/O covers JTAG, XPI, and asynchronous pads.
## Legacy-flow parity
This implementation re-expresses the legacy CX55 flow
(DC + Formality + Innovus + StarRC + PrimeTime/PT-ECO + Calibre) and carries
over every capability the legacy project actually exercised on CX55:
- OCV derate policy in PrimeTime per scenario PVT and in Innovus per stage,
with the legacy per-stage setup uncertainty tightening
(`flow::apply_signoff_derate`, `flow::apply_apr_derate`,
`flow::apply_apr_stage_uncertainty` in `tcl/common/corners.tcl`);
- DC compile policy: critical range, high-effort TNS optimization, path
groups, latch-based clock-gating style with synchronizer exclusion,
`set_max_area 0`, `set_wire_load_mode top`, macro `set_dont_touch`, and the
legacy hdlin/suppress-message subset;
- PrimeTime CRPR, signal-integrity analysis settings, the full legacy
`check_timing` include list, PBA-exhaustive reports, optional SAIF power
analysis (`STA_SAIF_FILE`), and a configurable SDF scenario
(`STA_SDF_SCENARIO`);
- Innovus engine settings (OCV analysis, CPPR, postRoute extraction,
SI/timing-driven routing), per-stage DRV limits, macro preplacement
(`APR_MACRO_LOC_FILE`) with halos, CTS NDR with trunk shielding and ccopt
targets, full `streamOut` options, a reviewed pad-order file slot
(`APR_IO_ORDER_FILE`), and optional multi-power-net mapping
(`APR_POWER_PIN_MAP`/`APR_GROUND_PIN_MAP`, for example a separate PLL AVDD);
- PrimeTime ECO over all 13 restored DMSA scenarios with merged changes, plus
optional VT-swap, size-down, and buffer-removal sub-flows
(`ECO_ENABLE_*`, off by default) and `freeze_silicon` physical mode;
- Calibre merge with an `OTHER_GDS` slot (seal ring), macro-level LVS/ERC,
and `CALIBRE_LVS_ARGS` performance options.
Capabilities the legacy framework carried but never exercised on CX55 are
deliberately not migrated: DCG/DCNXT physical synthesis (only ever configured
for tsmc28), the Cadence Genus setup (SMIC110 remnant), DFT/scan hooks
(inactive in both flows), UPF low-power, ETM hardening and the hierarchical
harden tree, IP/library preparation helpers (`fake_db.py`, `itf_to_ict.pl`,
`lib2db`, `std_wrapper_gen.py`, `gen_memory.sh`, `timed_run.py`), the legacy
`func/gdsout.tcl` (replaced by the calibredrv merge), `formate_pt2edi.pl`
(replaced by `scripts/translate_eco.py`), the `bes_data_zcn` snapshot, and the
legacy automatic I/O delay decorator (replaced by the `doctor-syn` internal-QoR
mode plus qualified budget review). The legacy multi-design entry points
(`run_syn -d ...`) are superseded by the `SOC` product layer; external
non-retroSoC designs are out of scope.
## Strict verdict
@@ -113,19 +223,12 @@ The default policy is blocking:
- Calibre DRC and antenna counts must be zero and LVS must be clean.
The production `doctor` requires every violation threshold to remain zero.
It also requires qualified JTAG, DVP, ULPI, SDRAM, SDIO, XPI, and asynchronous
I/O budgets and an audited local hook that selects GPIO10-20 DVP input mode
and cuts invalid bidirectional-pad feedback arcs. `doctor-syn` is the only
relaxed entry point: Design Compiler then false-paths all top-level I/O and
reports internal sequential QoR. Its summary carries `io_qualified=no`, and
Innovus and PrimeTime reject it.
The canonical six-domain clock inventory is generated from
`rtl/mini/integration/clock_reset_domains.json` into the commercial RTL
package. Commercial constraints add the crystal and PLL overlays and model
the system clock mux as physically exclusive external-source and PLL-source
generated clocks. The asynchronous relationships cover system, audio,
crystal, JTAG, DVP, and ULPI domains.
It also requires the product's qualified I/O budgets and, for Mini, an audited
local hook that selects GPIO10-20 DVP input mode and cuts invalid
bidirectional-pad feedback arcs. `doctor-syn` is the only relaxed entry point:
Design Compiler then false-paths all top-level I/O and reports internal
sequential QoR. Its summary carries `io_qualified=no`, and Innovus and
PrimeTime reject it.
The standard-cell family is LLSC H7C across DB, Liberty, LEF, GDS, and CDL.
Design Compiler links one TYP set and targets only H7CR (SVT) plus H7CL
@@ -141,15 +244,6 @@ syn/output/synthesis.path_groups.tsv
Detailed clock, exception, setup/hold, QoR, design-rule, library-binding, and
reference reports are written below `syn/reports/`.
## Legacy-flow audit
The legacy source contains valid DC, Formality, Innovus, StarRC, PrimeTime,
PrimeTime ECO, Calibre, and `calibredrv` behavior, but also contains unrelated
process branches, old top-level names, copied RTL, absolute site paths, and
many generated sessions and reports. This implementation re-expresses only
the ICS55/`retrosoc_asic` behavior and does not source or execute legacy
scripts.
The common corner contract is:
| PVT | RC views |
@@ -158,7 +252,23 @@ The common corner contract is:
| `TYP` | `TYP` |
| `MIN`, `ML` | `Cworst`, `RCworst`, `Cbest`, `RCbest` |
The old flow used a single typical Innovus analysis view and delegated broad
corner coverage to separate StarRC/PrimeTime runs. The new flow derives APR,
extraction, STA, and ECO scenarios from one configuration and rejects missing
views.
APR, extraction, STA, and ECO scenarios all derive from this single
configuration, and the flow rejects missing views.
## Known gaps and risks
- The APR-and-later stages have not yet executed on any product; only
`input`, `syn`, and `fm-rtl2syn` have run to completion. Expect first-run
issues in later stages and retain logs when they surface.
- PLL_TOP ships without characterized timing arcs, so PLL output timing is
modeled by the product overlays, not by library arcs. Tiny additionally
parks the PLL (EN=0). Timing closure is mandatory only in the final
complete-product campaign (`docs/ip/tiny-soc.md` policy).
- Without `APR_IO_ORDER_FILE`, the floorplan stage distributes pads
round-robin at a fixed pitch; production runs must supply a reviewed pad
order. The stage records `io_order=file|round_robin` in
`apr/floorplan/output/floorplan.io.json`.
- The default power hook maps every `APR_POWER_PINS` entry (including the PLL
analog supplies) onto the single digital power net; use
`APR_POWER_PIN_MAP`/`APR_GROUND_PIN_MAP` when a separate PLL supply domain
is required.
@@ -8,11 +8,11 @@
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# Copy this file to ../local/ics55-production.mk and replace every REQUIRED
# value there. The local copy is ignored by Git.
# Mini/ICS55 local configuration template. Copy this file to
# ../local/ics55-mini.mk and replace every REQUIRED value there. The local
# copy is ignored by Git. Product and technology policy defaults come from
# ../config/products/mini.mk and ../config/technology/ics55.mk.
TOP := retrosoc_asic
TECHNOLOGY := ICS55
RTL_ARCHIVE := REQUIRED
BUILD_ROOT := $(ROOT_PATH)/build
@@ -104,6 +104,7 @@ STARRC_MAP := REQUIRED
STD_GDS := REQUIRED
IO_GDS := REQUIRED
MACRO_GDS := REQUIRED
OTHER_GDS :=
STD_CDL := REQUIRED
IO_CDL := REQUIRED
MACRO_CDL := REQUIRED
@@ -149,21 +150,20 @@ ECO_HOLD_MARGIN_NS := 0.0
ECO_MAX_PROCESSES := 8
ECO_PHYSICAL_MODE := open_site
# Non-sensitive design intent. Units are micrometres and nanoseconds.
DIE_WIDTH := 2400
DIE_HEIGHT := 2400
CORE_MARGIN_LEFT := 120
CORE_MARGIN_BOTTOM := 120
CORE_MARGIN_RIGHT := 120
CORE_MARGIN_TOP := 120
CORE_UTILIZATION := 0.60
XTAL_CLK_PERIOD_NS := 41.666666667
PLL_OUTPUT_PERIOD_NS := 13.888888889
CLOCK_SETUP_UNCERTAINTY_NS := 0.20
CLOCK_HOLD_UNCERTAINTY_NS := 0.10
CLOCK_TRANSITION_NS := 0.10
MAX_TRANSITION_NS := 0.50
MAX_FANOUT := 32
# Optional product-intent overrides (defaults in ../config/products/mini.mk).
# DIE_WIDTH := 2400
# DIE_HEIGHT := 2400
# CORE_MARGIN_LEFT := 258
# CORE_MARGIN_BOTTOM := 258
# CORE_MARGIN_RIGHT := 258
# CORE_MARGIN_TOP := 258
# CORE_UTILIZATION := 0.60
# PLL_OUTPUT_PERIOD_NS := 13.888888889
# CLOCK_SETUP_UNCERTAINTY_NS := 0.20
# CLOCK_HOLD_UNCERTAINTY_NS := 0.10
# CLOCK_TRANSITION_NS := 0.10
# MAX_TRANSITION_NS := 0.50
# MAX_FANOUT := 32
# Board/device timing is local qualification data. Set YES only when every
# interface budget below is taken from reviewed board and component timing.
@@ -219,10 +219,9 @@ ASYNC_OUTPUT_DELAY_MAX_NS := REQUIRED
ASYNC_OUTPUT_DELAY_MIN_NS := REQUIRED
ASYNC_OUTPUT_LOAD_PF := REQUIRED
# The qualified ICS55 PLL mode is fixed. Other runtime selections fail to lock.
ICS55_PLL_SUPPORTED_SEL := 0
ICS55_PLL_N := 2
ICS55_PLL_OD := 2
# The qualified ICS55 PLL mode for Mini is fixed by
# ../config/products/mini.mk (SEL=0, N=2, OD=2). Other runtime selections
# fail to lock.
# TIMING_IO_MODE_HOOK is required when IO_TIMING_QUALIFIED=YES. It must select
# the reviewed GPIO10-20 DVP mode and cut invalid bidirectional pad feedback.
@@ -235,6 +234,27 @@ APR_ROUTE_HOOK :=
TIMING_IO_MODE_HOOK := REQUIRED
TIMING_EXCEPTION_HOOK :=
# Optional flow behavior slots (technology defaults live in
# ../config/technology/ics55.mk; see README.md for the full reference).
# STA_SAIF_FILE := # SAIF for per-scenario report_power
# STA_SDF_SCENARIO := func_TYP_TYP_25
# SYN_CLOCK_GATING_EXCLUDE_PATTERNS := # clock-gating exclusion name globs
# APR_IO_ORDER_FILE := # reviewed pad order; default round-robin
# APR_MACRO_LOC_FILE := # fixed macro coordinates (place_macro)
# APR_MACRO_HALO_UM := 5
# APR_NETLIST_EXCLUDE_CELLS := # e.g. seal-ring cells excluded from netlist
# APR_POWER_PIN_MAP := # "PLL_AVDD:AVDD PLL_AVDD_DRV:DVDD_DRV"
# APR_GROUND_PIN_MAP := # "PLL_AVSS:AVSS PLL_AVSS_DRV:DVSS_DRV"
# APR_EARLY_GLOBAL_MIN_LAYER :=
# APR_EARLY_GLOBAL_MAX_LAYER :=
# ECO_ENABLE_VT_SWAP := NO
# ECO_VT_PATTERN_PRIORITY := # required when ECO_ENABLE_VT_SWAP=YES
# ECO_ENABLE_SIZE_DOWN := NO
# ECO_ENABLE_REMOVE_BUFFER := NO
# ECO_PBA_MODE := none
# MACRO_LVS_CELLS := PLL_TOP # macro-level LVS/ERC (legacy CX55 practice)
# CALIBRE_LVS_ARGS := -turbo -hyper
# Required strict thresholds.
MAX_SETUP_VIOLATIONS := 0
MAX_HOLD_VIOLATIONS := 0
@@ -0,0 +1,234 @@
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
# retroSoC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# Tiny/ICS55 local configuration template. Copy this file to
# ../local/ics55-tiny.mk and replace every REQUIRED value there. The local
# copy is ignored by Git. Product and technology policy defaults come from
# ../config/products/tiny.mk and ../config/technology/ics55.mk.
#
# Tiny boots SAFE24: the PLL_TOP macro is instantiated in the netlist but
# parked off (EN=0) and is not a clock source. Its DB/LEF/GDS/CDL views are
# still required below because the macro must link, place, and verify.
RTL_ARCHIVE := REQUIRED
BUILD_ROOT := $(ROOT_PATH)/build
FLOW_PYTHON := python
TCLSH := tclsh
LSF_COMMAND := bsub
LSF_MODE := batch
LSF_COMMON_ARGS :=
LSF_SYN_ARGS := REQUIRED
LSF_FM_ARGS := REQUIRED
LSF_APR_ARGS := REQUIRED
LSF_EXTRACT_ARGS := REQUIRED
LSF_STA_ARGS := REQUIRED
LSF_ECO_ARGS := REQUIRED
LSF_PV_ARGS := REQUIRED
DC_SHELL := dc_shell
FM_SHELL := fm_shell
INNOVUS := innovus
STARRC := StarXtract
PT_SHELL := pt_shell
CALIBRE := calibre
CALIBREDRV := calibredrv
V2LVS := v2lvs
# Synopsys DB views. Every list must be explicit; wildcards are rejected.
STD_DB_MAX := REQUIRED
STD_DB_WCL := REQUIRED
STD_DB_TYP := REQUIRED
STD_DB_MIN := REQUIRED
STD_DB_ML := REQUIRED
# DC target subset: H7CR (SVT) and H7CL (LVT) TYP DBs only.
SYN_STD_DB_TYP := REQUIRED
IO_DB_MAX := REQUIRED
IO_DB_WCL := REQUIRED
IO_DB_TYP := REQUIRED
IO_DB_MIN := REQUIRED
IO_DB_ML := REQUIRED
SRAM_DB_MAX := REQUIRED
SRAM_DB_WCL := REQUIRED
SRAM_DB_TYP := REQUIRED
SRAM_DB_MIN := REQUIRED
SRAM_DB_ML := REQUIRED
PLL_DB := REQUIRED
# Liberty views consumed by Innovus MMMC.
STD_LIB_MAX := REQUIRED
STD_LIB_WCL := REQUIRED
STD_LIB_TYP := REQUIRED
STD_LIB_MIN := REQUIRED
STD_LIB_ML := REQUIRED
IO_LIB_MAX := REQUIRED
IO_LIB_WCL := REQUIRED
IO_LIB_TYP := REQUIRED
IO_LIB_MIN := REQUIRED
IO_LIB_ML := REQUIRED
SRAM_LIB_MAX := REQUIRED
SRAM_LIB_WCL := REQUIRED
SRAM_LIB_TYP := REQUIRED
SRAM_LIB_MIN := REQUIRED
SRAM_LIB_ML := REQUIRED
PLL_LIB := REQUIRED
# Exact internal name from the selected H7CR TYP DB.
SYN_OPERATING_CONDITION_LIBRARY := REQUIRED
SYN_OPERATING_CONDITION := REQUIRED
# Innovus physical inputs.
TECH_LEF := REQUIRED
STD_LEFS := REQUIRED
IO_LEFS := REQUIRED
MACRO_LEFS := REQUIRED
CAP_TABLE_CWORST := REQUIRED
CAP_TABLE_RCWORST := REQUIRED
CAP_TABLE_CBEST := REQUIRED
CAP_TABLE_RCBEST := REQUIRED
CAP_TABLE_TYP := REQUIRED
STREAM_MAP := REQUIRED
# StarRC technology inputs.
NXTGRD_CWORST := REQUIRED
NXTGRD_RCWORST := REQUIRED
NXTGRD_CBEST := REQUIRED
NXTGRD_RCBEST := REQUIRED
NXTGRD_TYP := REQUIRED
STARRC_MAP := REQUIRED
# Calibre and merge inputs.
STD_GDS := REQUIRED
IO_GDS := REQUIRED
MACRO_GDS := REQUIRED
OTHER_GDS :=
STD_CDL := REQUIRED
IO_CDL := REQUIRED
MACRO_CDL := REQUIRED
CALIBRE_DRC_DECK := REQUIRED
CALIBRE_ANT_DECK := REQUIRED
CALIBRE_LVS_DECK := REQUIRED
STARRC_CORES := 8
# PDK cell/site/layer names belong in this ignored local configuration.
APR_SITE := REQUIRED
APR_CORE_FILLERS := REQUIRED
APR_IO_FILLERS := REQUIRED
APR_SIGNAL_PAD_CELLS := REQUIRED
APR_IO_CORNER_CELL := REQUIRED
APR_IO_POWER_CELLS := REQUIRED
APR_IO_OFFSET := REQUIRED
APR_IO_PITCH := REQUIRED
APR_ENDCAP_CELLS := REQUIRED
APR_TIE_HIGH_CELL := REQUIRED
APR_TIE_LOW_CELL := REQUIRED
APR_CTS_BUFFER_CELLS := REQUIRED
APR_CTS_INVERTER_CELLS := REQUIRED
APR_CLOCK_ROUTING_LAYERS := REQUIRED
APR_SIGNAL_MIN_LAYER := REQUIRED
APR_SIGNAL_MAX_LAYER := REQUIRED
APR_POWER_NET := REQUIRED
APR_GROUND_NET := REQUIRED
APR_POWER_PINS := REQUIRED
APR_GROUND_PINS := REQUIRED
APR_RING_LAYERS := REQUIRED
APR_STRIPE_LAYER := REQUIRED
APR_RING_WIDTH := REQUIRED
APR_RING_SPACING := REQUIRED
APR_RING_OFFSET := REQUIRED
APR_STRIPE_WIDTH := REQUIRED
APR_STRIPE_SPACING := REQUIRED
APR_STRIPE_PITCH := REQUIRED
SYN_DONT_USE := REQUIRED
ECO_SETUP_BUFFERS := REQUIRED
ECO_HOLD_BUFFERS := REQUIRED
ECO_SETUP_MARGIN_NS := 0.0
ECO_HOLD_MARGIN_NS := 0.0
ECO_MAX_PROCESSES := 8
ECO_PHYSICAL_MODE := open_site
# Tiny has no tracked die defaults yet; size the pad ring and core from the
# product floorplan review before implementation.
DIE_WIDTH := REQUIRED
DIE_HEIGHT := REQUIRED
CORE_MARGIN_LEFT := REQUIRED
CORE_MARGIN_BOTTOM := REQUIRED
CORE_MARGIN_RIGHT := REQUIRED
CORE_MARGIN_TOP := REQUIRED
CORE_UTILIZATION := REQUIRED
# CLOCK_SETUP_UNCERTAINTY_NS := 0.20
# CLOCK_HOLD_UNCERTAINTY_NS := 0.10
# CLOCK_TRANSITION_NS := 0.10
# MAX_TRANSITION_NS := 0.50
# MAX_FANOUT := 32
# Board/device timing is local qualification data. Set YES only when every
# interface budget below is taken from reviewed board and component timing.
IO_TIMING_QUALIFIED := NO
JTAG_INPUT_DELAY_MAX_NS := REQUIRED
JTAG_INPUT_DELAY_MIN_NS := REQUIRED
JTAG_INPUT_TRANSITION_NS := REQUIRED
JTAG_OUTPUT_DELAY_MAX_NS := REQUIRED
JTAG_OUTPUT_DELAY_MIN_NS := REQUIRED
JTAG_OUTPUT_LOAD_PF := REQUIRED
XPI_CLOCK_PERIOD_NS := REQUIRED
XPI_INPUT_DELAY_MAX_NS := REQUIRED
XPI_INPUT_DELAY_MIN_NS := REQUIRED
XPI_INPUT_TRANSITION_NS := REQUIRED
XPI_OUTPUT_DELAY_MAX_NS := REQUIRED
XPI_OUTPUT_DELAY_MIN_NS := REQUIRED
XPI_OUTPUT_LOAD_PF := REQUIRED
ASYNC_CLOCK_PERIOD_NS := REQUIRED
ASYNC_INPUT_DELAY_MAX_NS := REQUIRED
ASYNC_INPUT_DELAY_MIN_NS := REQUIRED
ASYNC_INPUT_TRANSITION_NS := REQUIRED
ASYNC_OUTPUT_DELAY_MAX_NS := REQUIRED
ASYNC_OUTPUT_DELAY_MIN_NS := REQUIRED
ASYNC_OUTPUT_LOAD_PF := REQUIRED
# Optional hooks.
APR_FLOORPLAN_HOOK :=
APR_POWER_HOOK :=
APR_PLACE_HOOK :=
APR_CTS_HOOK :=
APR_ROUTE_HOOK :=
TIMING_IO_MODE_HOOK :=
TIMING_EXCEPTION_HOOK :=
# Optional flow behavior slots (technology defaults live in
# ../config/technology/ics55.mk; see README.md for the full reference).
# STA_SAIF_FILE := # SAIF for per-scenario report_power
# STA_SDF_SCENARIO := func_TYP_TYP_25
# SYN_CLOCK_GATING_EXCLUDE_PATTERNS := # clock-gating exclusion name globs
# APR_IO_ORDER_FILE := # reviewed pad order; default round-robin
# APR_MACRO_LOC_FILE := # fixed macro coordinates (place_macro)
# APR_MACRO_HALO_UM := 5
# APR_NETLIST_EXCLUDE_CELLS := # e.g. seal-ring cells excluded from netlist
# APR_POWER_PIN_MAP := # "PLL_AVDD:AVDD" (PLL parked: tie to VDD)
# APR_GROUND_PIN_MAP := # "PLL_AVSS:AVSS"
# APR_EARLY_GLOBAL_MIN_LAYER :=
# APR_EARLY_GLOBAL_MAX_LAYER :=
# ECO_ENABLE_VT_SWAP := NO
# ECO_VT_PATTERN_PRIORITY := # required when ECO_ENABLE_VT_SWAP=YES
# ECO_ENABLE_SIZE_DOWN := NO
# ECO_ENABLE_REMOVE_BUFFER := NO
# ECO_PBA_MODE := none
# MACRO_LVS_CELLS := # e.g. PLL_TOP for macro-level LVS/ERC
# CALIBRE_LVS_ARGS := -turbo -hyper
# Required strict thresholds.
MAX_SETUP_VIOLATIONS := 0
MAX_HOLD_VIOLATIONS := 0
MAX_DRV_VIOLATIONS := 0
MAX_DRC_RESULTS := 0
MAX_ANTENNA_RESULTS := 0
@@ -0,0 +1,75 @@
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
# retroSoC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# retroSoC Mini product policy for the commercial flow. Non-sensitive design
# intent only; site values live in the ignored local configuration.
SOC := MINI
TOP := retrosoc_asic
# Canonical clock domains, mirrored by tcl/common/products/mini.tcl and
# validated against rtl/mini/integration/clock_reset_domains.json when the
# commercial timing contract is generated.
COMMERCIAL_CLOCK_DOMAINS := aon lp hp pclk memory audio jtag dvp usb2_ulpi
# Qualified-I/O interface groups and the budget variables each group requires.
COMMERCIAL_IO_INTERFACES := JTAG DVP ULPI SDRAM SDIO XPI ASYNC
COMMERCIAL_IO_BUDGET_VARIABLES := \
JTAG_INPUT_DELAY_MAX_NS JTAG_INPUT_DELAY_MIN_NS \
JTAG_INPUT_TRANSITION_NS JTAG_OUTPUT_DELAY_MAX_NS \
JTAG_OUTPUT_DELAY_MIN_NS JTAG_OUTPUT_LOAD_PF \
DVP_INPUT_DELAY_MAX_NS DVP_INPUT_DELAY_MIN_NS \
DVP_INPUT_TRANSITION_NS \
ULPI_INPUT_DELAY_MAX_NS ULPI_INPUT_DELAY_MIN_NS \
ULPI_INPUT_TRANSITION_NS ULPI_OUTPUT_DELAY_MAX_NS \
ULPI_OUTPUT_DELAY_MIN_NS ULPI_OUTPUT_LOAD_PF \
SDRAM_CLOCK_PERIOD_NS SDRAM_INPUT_DELAY_MAX_NS \
SDRAM_INPUT_DELAY_MIN_NS SDRAM_INPUT_TRANSITION_NS \
SDRAM_OUTPUT_DELAY_MAX_NS SDRAM_OUTPUT_DELAY_MIN_NS \
SDRAM_OUTPUT_LOAD_PF \
SDIO_CLOCK_PERIOD_NS SDIO_INPUT_DELAY_MAX_NS \
SDIO_INPUT_DELAY_MIN_NS SDIO_INPUT_TRANSITION_NS \
SDIO_OUTPUT_DELAY_MAX_NS SDIO_OUTPUT_DELAY_MIN_NS \
SDIO_OUTPUT_LOAD_PF \
XPI_CLOCK_PERIOD_NS XPI_INPUT_DELAY_MAX_NS \
XPI_INPUT_DELAY_MIN_NS XPI_INPUT_TRANSITION_NS \
XPI_OUTPUT_DELAY_MAX_NS XPI_OUTPUT_DELAY_MIN_NS \
XPI_OUTPUT_LOAD_PF \
ASYNC_CLOCK_PERIOD_NS ASYNC_INPUT_DELAY_MAX_NS \
ASYNC_INPUT_DELAY_MIN_NS ASYNC_INPUT_TRANSITION_NS \
ASYNC_OUTPUT_DELAY_MAX_NS ASYNC_OUTPUT_DELAY_MIN_NS \
ASYNC_OUTPUT_LOAD_PF
# Qualified Mini I/O requires a reviewed pad-mode hook that selects the
# GPIO10-20 DVP input mode and cuts invalid bidirectional-pad feedback arcs.
PRODUCT_REQUIRES_IO_MODE_HOOK := YES
# The Mini RCU clocks the system from PLL_TOP in the qualified production
# mode; doctor enforces the reviewed SEL/N/OD values below.
PRODUCT_PLL_MODE := qualified
ICS55_PLL_SUPPORTED_SEL ?= 0
ICS55_PLL_N ?= 2
ICS55_PLL_OD ?= 2
# Non-sensitive floorplan/timing defaults; the local configuration may
# override them.
DIE_WIDTH ?= 2400
DIE_HEIGHT ?= 2400
CORE_MARGIN_LEFT ?= 258
CORE_MARGIN_BOTTOM ?= 258
CORE_MARGIN_RIGHT ?= 258
CORE_MARGIN_TOP ?= 258
CORE_UTILIZATION ?= 0.60
PLL_OUTPUT_PERIOD_NS ?= 13.888888889
CLOCK_SETUP_UNCERTAINTY_NS ?= 0.20
CLOCK_HOLD_UNCERTAINTY_NS ?= 0.10
CLOCK_TRANSITION_NS ?= 0.10
MAX_TRANSITION_NS ?= 0.50
MAX_FANOUT ?= 32
@@ -0,0 +1,50 @@
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
# retroSoC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# retroSoC Tiny product policy for the commercial flow. Non-sensitive design
# intent only; site values live in the ignored local configuration.
SOC := TINY
TOP := retrosoc_tiny_asic
# Canonical clock domains, mirrored by tcl/common/products/tiny.tcl and
# validated against rtl/tiny/integration/clock_reset_domains.json when the
# commercial timing contract is generated. Tiny boots SAFE24: the PLL_TOP
# macro is present in the netlist but parked off and is not a clock source.
COMMERCIAL_CLOCK_DOMAINS := system jtag
# Qualified-I/O interface groups and the budget variables each group requires.
COMMERCIAL_IO_INTERFACES := JTAG XPI ASYNC
COMMERCIAL_IO_BUDGET_VARIABLES := \
JTAG_INPUT_DELAY_MAX_NS JTAG_INPUT_DELAY_MIN_NS \
JTAG_INPUT_TRANSITION_NS JTAG_OUTPUT_DELAY_MAX_NS \
JTAG_OUTPUT_DELAY_MIN_NS JTAG_OUTPUT_LOAD_PF \
XPI_CLOCK_PERIOD_NS XPI_INPUT_DELAY_MAX_NS \
XPI_INPUT_DELAY_MIN_NS XPI_INPUT_TRANSITION_NS \
XPI_OUTPUT_DELAY_MAX_NS XPI_OUTPUT_DELAY_MIN_NS \
XPI_OUTPUT_LOAD_PF \
ASYNC_CLOCK_PERIOD_NS ASYNC_INPUT_DELAY_MAX_NS \
ASYNC_INPUT_DELAY_MIN_NS ASYNC_INPUT_TRANSITION_NS \
ASYNC_OUTPUT_DELAY_MAX_NS ASYNC_OUTPUT_DELAY_MIN_NS \
ASYNC_OUTPUT_LOAD_PF
# Tiny has no bidirectional pad-mode selection requirement.
PRODUCT_REQUIRES_IO_MODE_HOOK := NO
# The Tiny PLL_TOP macro is parked (EN=0) and has no characterized timing
# arcs; doctor skips the qualified PLL-mode checks for this product. See
# docs/ip/tiny-soc.md (TINY-055) for the qualification boundary.
PRODUCT_PLL_MODE := parked
CLOCK_SETUP_UNCERTAINTY_NS ?= 0.20
CLOCK_HOLD_UNCERTAINTY_NS ?= 0.10
CLOCK_TRANSITION_NS ?= 0.10
MAX_TRANSITION_NS ?= 0.50
MAX_FANOUT ?= 32
@@ -0,0 +1,68 @@
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
# retroSoC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# ICS55 (ICsprout 55LLULP1233) technology policy: non-sensitive naming and
# corner conventions shared by every product on this technology. Library
# paths, cell lists, and deck locations remain in the ignored local
# configuration.
# PVT corner set used across synthesis, extraction, STA, and ECO.
COMMERCIAL_PVT_CORNERS := MAX WCL TYP MIN ML
# StarRC extraction corners written by the extract stage; check_outputs.py
# requires one SPEF per entry.
COMMERCIAL_EXTRACTION_CORNERS := \
Cworst_m40 Cworst_125 \
RCworst_m40 RCworst_125 \
Cbest_m40 Cbest_125 \
RCbest_m40 RCbest_125 \
TYP_25
# H7C standard-cell family naming policy: cell-name suffix tokens feed the
# synthesis VT utilization report, and library-name glob patterns bind the
# threshold-voltage groups in the TYP link set.
SYN_VT_SUFFIX_MAP := HVT H7H LVT H7L SVT H7R
SYN_VT_LIBRARY_MAP := HVT *H7CH* LVT *H7CL* SVT *H7CR*
# OCV derate policy (legacy bes_data/common/signoff_table.tcl, CX55 section;
# PrimeTime-only — legacy DC synthesis applied no derate):
TIMING_DERATE_SETUP_CELL_CLOCK_EARLY := 0.95
TIMING_DERATE_SETUP_NET_CLOCK_EARLY := 0.95
TIMING_DERATE_HOLD_CELL_CLOCK_EARLY := 0.90
TIMING_DERATE_HOLD_NET_CLOCK_EARLY := 0.90
TIMING_DERATE_DEFAULT_EARLY := 0.95
# Innovus derate and per-stage setup uncertainty (legacy
# pd_data/pr/scr/CL1/set_derate_uncertainty.tcl).
APR_DERATE_CLOCK_EARLY := 0.95
APR_DERATE_CLOCK_LATE := 1.10
APR_DERATE_DATA_LATE := 1.10
APR_SETUP_UNCERTAINTY_PLACE_NS := 0.25
APR_SETUP_UNCERTAINTY_CTS_NS := 0.225
APR_SETUP_UNCERTAINTY_ROUTE_NS := 0.20
# Innovus per-stage DRV limits (legacy pd_data/pr/scr/CL1/update_sdc.tcl) and
# CTS targets (legacy pd_data/pr/scr/setting/cts_setting.tcl).
APR_DESIGN_PROCESS ?= 55
APR_MAX_FANOUT ?= 32
APR_MAX_TRANSITION_NS ?= 0.08
APR_MAX_CAPACITANCE_PF ?= 0.15
APR_CTS_NDR_WIDTH_UM ?= 0.2
APR_CTS_NDR_SPACING_UM ?= 0.2
APR_CTS_TARGET_SKEW_NS ?= 0.08
APR_CTS_TARGET_MAX_TRANS_LEAF_NS ?= 0.78
APR_CTS_TARGET_MAX_TRANS_TRUNK_NS ?= 0.78
APR_CTS_TARGET_INSERTION_DELAY_NS ?= 0.05
APR_CTS_MAX_FANOUT ?= 4
# Synthesis compile policy (legacy bes_data/syn/scr/flow_com/
# syn_common_flow.tcl).
SYN_CRITICAL_RANGE_NS ?= 0.2
SYN_COMPILE_CORES ?= 16
+9 -5
View File
@@ -22,7 +22,11 @@ def read_text(path):
return handle.read()
SPEF_CORNERS = (
def spef_corners():
configured = os.environ.get("COMMERCIAL_EXTRACTION_CORNERS", "").split()
if configured:
return configured
return [
"Cworst_m40",
"Cworst_125",
"RCworst_m40",
@@ -32,7 +36,7 @@ SPEF_CORNERS = (
"RCbest_m40",
"RCbest_125",
"TYP_25",
)
]
def find_files(root, suffixes):
@@ -63,15 +67,15 @@ def apply_check(kind, root, limit, top=None, minimum_mtime=None):
else:
files = [
os.path.join(root, "{0}.{1}.spef.gz".format(top, corner))
for corner in SPEF_CORNERS
for corner in spef_corners()
]
missing = [path for path in files if not os.path.isfile(path)]
if missing:
error = "missing ICS55 SPEF corners: {0}".format(
error = "missing SPEF corners: {0}".format(
", ".join(os.path.basename(path) for path in missing)
)
elif any(os.path.getsize(path) == 0 for path in files):
error = "an ICS55 SPEF corner is empty"
error = "an SPEF corner is empty"
elif kind in ("calibre-drc", "calibre-antenna"):
reports = find_files(root, (".summary", ".sum", ".rpt"))
files = reports
@@ -38,6 +38,7 @@ STAGES = (
("pv-drc", "pv/drc"),
("pv-antenna", "pv/antenna"),
("pv-lvs", "pv/lvs"),
("pv-macro-lvs", "pv/macro_lvs"),
)
+76 -31
View File
@@ -14,6 +14,7 @@ from __future__ import print_function
import argparse
import json
import os
import re
import shlex
import subprocess
import sys
@@ -68,6 +69,18 @@ SYNTHESIS_FILE_VARIABLES = (
"PLL_DB",
)
# Optional file lists are validated only when the local configuration sets
# them; they never become required.
OPTIONAL_FILE_LIST_VARIABLES = (
"OTHER_GDS",
)
OPTIONAL_SWITCH_VARIABLES = (
"ECO_ENABLE_VT_SWAP",
"ECO_ENABLE_SIZE_DOWN",
"ECO_ENABLE_REMOVE_BUFFER",
)
VALUE_VARIABLES = (
"TOP", "TECHNOLOGY", "RTL_ARCHIVE", "LSF_MODE", "APR_SITE", "APR_CORE_FILLERS",
"APR_IO_FILLERS", "APR_SIGNAL_PAD_CELLS", "APR_IO_CORNER_CELL",
@@ -98,32 +111,7 @@ SYNTHESIS_VALUE_VARIABLES = (
"SYN_OPERATING_CONDITION",
)
IO_BUDGET_VARIABLES = (
"JTAG_INPUT_DELAY_MAX_NS", "JTAG_INPUT_DELAY_MIN_NS",
"JTAG_INPUT_TRANSITION_NS", "JTAG_OUTPUT_DELAY_MAX_NS",
"JTAG_OUTPUT_DELAY_MIN_NS", "JTAG_OUTPUT_LOAD_PF",
"DVP_INPUT_DELAY_MAX_NS", "DVP_INPUT_DELAY_MIN_NS",
"DVP_INPUT_TRANSITION_NS",
"ULPI_INPUT_DELAY_MAX_NS", "ULPI_INPUT_DELAY_MIN_NS",
"ULPI_INPUT_TRANSITION_NS", "ULPI_OUTPUT_DELAY_MAX_NS",
"ULPI_OUTPUT_DELAY_MIN_NS", "ULPI_OUTPUT_LOAD_PF",
"SDRAM_CLOCK_PERIOD_NS", "SDRAM_INPUT_DELAY_MAX_NS",
"SDRAM_INPUT_DELAY_MIN_NS", "SDRAM_INPUT_TRANSITION_NS",
"SDRAM_OUTPUT_DELAY_MAX_NS", "SDRAM_OUTPUT_DELAY_MIN_NS",
"SDRAM_OUTPUT_LOAD_PF",
"SDIO_CLOCK_PERIOD_NS", "SDIO_INPUT_DELAY_MAX_NS",
"SDIO_INPUT_DELAY_MIN_NS", "SDIO_INPUT_TRANSITION_NS",
"SDIO_OUTPUT_DELAY_MAX_NS", "SDIO_OUTPUT_DELAY_MIN_NS",
"SDIO_OUTPUT_LOAD_PF",
"XPI_CLOCK_PERIOD_NS", "XPI_INPUT_DELAY_MAX_NS",
"XPI_INPUT_DELAY_MIN_NS", "XPI_INPUT_TRANSITION_NS",
"XPI_OUTPUT_DELAY_MAX_NS", "XPI_OUTPUT_DELAY_MIN_NS",
"XPI_OUTPUT_LOAD_PF",
"ASYNC_CLOCK_PERIOD_NS", "ASYNC_INPUT_DELAY_MAX_NS",
"ASYNC_INPUT_DELAY_MIN_NS", "ASYNC_INPUT_TRANSITION_NS",
"ASYNC_OUTPUT_DELAY_MAX_NS", "ASYNC_OUTPUT_DELAY_MIN_NS",
"ASYNC_OUTPUT_LOAD_PF",
)
IO_BUDGET_VARIABLES = ()
H7C_PVT_TOKENS = {
"MAX": "_ss_rcworst_1p08_125",
@@ -208,6 +196,8 @@ def main():
if name in required_values and (not value or value == "REQUIRED"):
errors.append("{0} is not configured".format(name))
pll_mode = os.environ.get("PRODUCT_PLL_MODE", "").strip().lower()
if pll_mode == "qualified":
qualified_pll = {
"ICS55_PLL_SUPPORTED_SEL": "0",
"ICS55_PLL_N": "2",
@@ -220,6 +210,10 @@ def main():
errors.append(
"{0} must match the qualified value {1}".format(name, expected)
)
elif pll_mode == "parked":
pass
else:
errors.append("PRODUCT_PLL_MODE must be qualified or parked")
if not args.allow_internal_qor:
for name in (
"MAX_SETUP_VIOLATIONS",
@@ -236,14 +230,46 @@ def main():
raise ValueError
except ValueError:
errors.append("ECO_MAX_PROCESSES must be a positive integer")
if values.get("ECO_PHYSICAL_MODE") not in ("open_site", "occupied_site"):
errors.append("ECO_PHYSICAL_MODE must be open_site or occupied_site")
if values.get("ECO_PHYSICAL_MODE") not in (
"open_site",
"occupied_site",
"freeze_silicon",
):
errors.append(
"ECO_PHYSICAL_MODE must be open_site, occupied_site, or freeze_silicon"
)
for name in OPTIONAL_SWITCH_VARIABLES:
value = os.environ.get(name, "").strip().upper()
if value and value not in ("YES", "NO"):
errors.append("{0} must be YES or NO".format(name))
if (
os.environ.get("ECO_ENABLE_VT_SWAP", "").strip().upper() == "YES"
and not split_value(os.environ.get("ECO_VT_PATTERN_PRIORITY", ""))
):
errors.append(
"ECO_VT_PATTERN_PRIORITY is required when ECO_ENABLE_VT_SWAP is YES"
)
pba_mode = os.environ.get("ECO_PBA_MODE", "").strip().lower()
if pba_mode and pba_mode not in ("none", "path", "exhaustive"):
errors.append("ECO_PBA_MODE must be none, path, or exhaustive")
for cell in split_value(os.environ.get("MACRO_LVS_CELLS", "")):
if re.match(r"^[A-Za-z0-9_]+$", cell) is None:
errors.append(
"MACRO_LVS_CELLS contains an invalid cell name: {0}".format(cell)
)
if values.get("LSF_MODE") not in ("batch", "interactive"):
errors.append("LSF_MODE must be batch or interactive")
io_mode = os.environ.get("IO_TIMING_QUALIFIED", "").strip().upper()
if io_mode == "YES":
for name in IO_BUDGET_VARIABLES:
budget_variables = split_value(
os.environ.get("COMMERCIAL_IO_BUDGET_VARIABLES", "")
)
if not budget_variables:
errors.append(
"COMMERCIAL_IO_BUDGET_VARIABLES is not set by the product configuration"
)
for name in budget_variables:
value = os.environ.get(name, "").strip()
if not value or value == "REQUIRED":
errors.append("{0} is not configured".format(name))
@@ -252,10 +278,15 @@ def main():
float(value)
except ValueError:
errors.append("{0} must be numeric".format(name))
hook_required = (
os.environ.get("PRODUCT_REQUIRES_IO_MODE_HOOK", "").strip().upper() == "YES"
)
io_hook = os.environ.get("TIMING_IO_MODE_HOOK", "").strip()
if not io_hook or io_hook == "REQUIRED":
if hook_required and (not io_hook or io_hook == "REQUIRED"):
errors.append("TIMING_IO_MODE_HOOK is required for qualified I/O")
elif not os.path.isfile(io_hook) or not os.access(io_hook, os.R_OK):
elif io_hook and io_hook != "REQUIRED" and (
not os.path.isfile(io_hook) or not os.access(io_hook, os.R_OK)
):
errors.append("TIMING_IO_MODE_HOOK is not readable: {0}".format(io_hook))
elif io_mode == "NO" and args.allow_internal_qor:
pass
@@ -298,6 +329,18 @@ def main():
elif not os.path.isfile(path) or not os.access(path, os.R_OK):
errors.append("{0} is not readable: {1}".format(name, path))
for name in OPTIONAL_FILE_LIST_VARIABLES:
value = os.environ.get(name, "").strip()
paths = split_value(value) if value and value != "REQUIRED" else []
checked_files[name] = paths
if args.allow_internal_qor:
continue
for path in paths:
if any(token in path for token in ("*", "?", "[")):
errors.append("{0} contains a wildcard: {1}".format(name, path))
elif not os.path.isfile(path) or not os.access(path, os.R_OK):
errors.append("{0} is not readable: {1}".format(name, path))
for prefix in ("STD", "IO", "SRAM"):
for pvt in ("MAX", "WCL", "TYP", "MIN", "ML"):
if args.allow_internal_qor and pvt != "TYP":
@@ -394,6 +437,8 @@ def main():
result = {
"schema_version": 1,
"status": "failed" if errors else "passed",
"soc": os.environ.get("SOC", ""),
"technology": os.environ.get("TECHNOLOGY", ""),
"runtime": runtime,
"tools": tools,
"checked_file_variables": checked_files,
@@ -17,17 +17,23 @@ from scripts.check_clock_reset_domains import validate # noqa: E402
from scripts.setup_helpers import atomic_write # noqa: E402
EXPECTED_DOMAINS = {
"aon",
"lp",
"hp",
"pclk",
"memory",
"audio",
"jtag",
"dvp",
"usb2_ulpi",
}
def expected_domains(soc: str) -> set[str]:
"""Read the product clock-domain policy from config/products/<soc>.mk."""
config = ROOT / "physical/commercial/config/products" / f"{soc.lower()}.mk"
if not config.is_file():
raise ValueError(f"unsupported SOC {soc!r}: missing {config}")
text = ""
for line in config.read_text(encoding="utf-8").splitlines():
line = line.rstrip()
if line.endswith("\\"):
text += line[:-1] + " "
continue
text += line
if text.lstrip().startswith("COMMERCIAL_CLOCK_DOMAINS"):
_, _, value = text.partition("=")
return set(value.split())
text = ""
raise ValueError(f"{config} does not define COMMERCIAL_CLOCK_DOMAINS")
def tcl_value(value: object) -> str:
@@ -35,7 +41,9 @@ def tcl_value(value: object) -> str:
return "{" + text.replace("\\", "\\\\").replace("}", "\\}") + "}"
def load_contract(domains_path: Path, pin_map_path: Path) -> tuple[list[dict[str, Any]], list[str]]:
def load_contract(
domains_path: Path, pin_map_path: Path, expected: set[str]
) -> tuple[list[dict[str, Any]], list[str]]:
validate(domains_path, ROOT)
document = json.loads(domains_path.read_text(encoding="utf-8"))
domains = document.get("domains")
@@ -43,7 +51,7 @@ def load_contract(domains_path: Path, pin_map_path: Path) -> tuple[list[dict[str
if not isinstance(domains, list) or not isinstance(reset_ports, list):
raise ValueError("clock/reset inventory has an invalid shape")
names = {domain.get("name") for domain in domains if isinstance(domain, dict)}
if names != EXPECTED_DOMAINS:
if names != expected:
raise ValueError(
"commercial clock domains differ from the canonical set: "
+ ", ".join(sorted(str(name) for name in names))
@@ -85,10 +93,10 @@ def load_contract(domains_path: Path, pin_map_path: Path) -> tuple[list[dict[str
return parsed, parsed_resets
def render(domains: list[dict[str, Any]], reset_ports: list[str]) -> str:
def render(soc: str, domains: list[dict[str, Any]], reset_ports: list[str]) -> str:
lines = [
"# Generated by physical/commercial/scripts/generate_timing_contract.py.",
"# Source: rtl/mini/integration/clock_reset_domains.json and pin_map.json.",
f"# Source: rtl/{soc.lower()}/integration/clock_reset_domains.json and pin_map.json.",
"set flow::canonical_clock_domains [dict create \\",
]
for domain in domains:
@@ -120,13 +128,16 @@ def render(domains: list[dict[str, Any]], reset_ports: list[str]) -> str:
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--soc", required=True, choices=("MINI", "TINY"))
parser.add_argument("--domains", type=Path, required=True)
parser.add_argument("--pin-map", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
try:
domains, reset_ports = load_contract(args.domains, args.pin_map)
atomic_write(args.output, render(domains, reset_ports))
domains, reset_ports = load_contract(
args.domains, args.pin_map, expected_domains(args.soc)
)
atomic_write(args.output, render(args.soc, domains, reset_ports))
except (OSError, ValueError, json.JSONDecodeError) as error:
parser.error(str(error))
return 0
@@ -66,6 +66,13 @@ def main():
filelist = os.path.join(output_dir, "rtl", "filelist.fl")
if not os.path.isfile(filelist):
parser.error("RTL archive does not contain rtl/filelist.fl")
contract = os.path.join(
output_dir, "rtl", "contracts", "commercial_timing_contract.tcl"
)
if not os.path.isfile(contract):
parser.error(
"RTL archive does not contain rtl/contracts/commercial_timing_contract.tcl"
)
# Tar extraction preserves the development-zone timestamp. Refresh the
# staged entry point so the stage runner can distinguish this extraction
# from output left by an earlier run.
+26 -3
View File
@@ -21,8 +21,12 @@ CURRENT = re.compile(r"^current_instance(?:\s+\{([^}]*)\})?\s*$")
SIZE = re.compile(r"^size_cell\s+\{([^}]+)\}\s+\{([^}]+)\}\s*$")
INSERT = re.compile(
r"^insert_buffer\s+\[get_pins\s+\{([^}]+)\}\]\s+(\S+)"
r"\s+-new_net_names\s+\{([^}]+)\}\s+-new_cell_names\s+\{([^}]+)\}\s*$"
r"\s+-new_net_names\s+\{([^}]+)\}\s+-new_cell_names\s+\{([^}]+)\}"
r"(?:\s+-location\s+\{([^}]+)\})?\s*$"
)
# Legacy pd_data/pteco/rpt/formate_pt2edi.pl maps a single-cell buffer
# removal to the Innovus ecoDeleteRepeater command.
REMOVE = re.compile(r"^remove_buffer\s+\[get_cells\s+\{([^\s}]+)\}\]\s*$")
def full_name(parent, name):
@@ -54,7 +58,7 @@ def translate(paths):
continue
match = INSERT.match(line)
if match:
output.append(
command = (
"ecoAddRepeater -term {{{0}}} -cell {{{1}}} "
"-newNetName {{{2}}} -name {{{3}}}".format(
full_name(current, match.group(1)),
@@ -63,13 +67,32 @@ def translate(paths):
match.group(4),
)
)
if match.group(5) is not None:
command += " -loc {{{0}}}".format(match.group(5).strip())
output.append(command)
continue
match = REMOVE.match(line)
if match:
output.append(
"ecoDeleteRepeater -inst {{{0}}}".format(
full_name(current, match.group(1))
)
)
continue
raise ValueError(
"{0}:{1}: unsupported PrimeTime ECO command: {2}".format(
path, number, line
)
)
return output
# DMSA exports one change list per scenario; identical changes repeat in
# every scenario, so keep the first occurrence of each translated command.
unique = []
seen = set()
for command in output:
if command not in seen:
seen.add(command)
unique.append(command)
return unique
def main():
+226 -8
View File
@@ -33,15 +33,61 @@ proc restore_previous {run_root top stage} {
restoreDesign $database $top
}
proc connect_pin_map {map type} {
foreach group $map {
set fields [split $group :]
if {[llength $fields] != 2} {
flow::fail "invalid APR pin map group (expect NET:pin1,pin2): $group"
}
set net [string trim [lindex $fields 0]]
set pins [string trim [lindex $fields 1]]
if {$net eq "" || $pins eq ""} {
flow::fail "invalid APR pin map group (expect NET:pin1,pin2): $group"
}
# Create the physical net when the netlist does not carry it, mirroring
# legacy CL1/user_add_pg.tcl:4-15.
if {[dbGet top.nets.name $net] eq "0x0"} {
if {$type eq "power"} {
addNet $net -physical -power
} else {
addNet $net -physical -ground
}
setNet -type special -net $net
}
foreach pin [split $pins ,] {
set pin [string trim $pin]
if {$pin eq ""} {
continue
}
globalNetConnect $net -type pgpin -pin $pin -all -verbose
}
}
}
# Legacy CL1/user_add_pg.tcl connects every power domain to its own pin group
# (independent PLL_AVDD/PLL_AVSS pairs for the PLL macro at lines 22-23 and
# 32-33). APR_POWER_PIN_MAP / APR_GROUND_PIN_MAP express the same grouping as
# "NET:pin1,pin2 NET2:pin3"; when a map is empty every configured pin joins
# the single APR_POWER_NET / APR_GROUND_NET as before.
proc connect_power_nets {} {
set power [flow::env APR_POWER_NET]
set ground [flow::env APR_GROUND_NET]
set power_map [string trim [flow::env APR_POWER_PIN_MAP ""]]
set ground_map [string trim [flow::env APR_GROUND_PIN_MAP ""]]
if {$power_map ne ""} {
connect_pin_map $power_map power
} else {
foreach pin [flow::env APR_POWER_PINS] {
globalNetConnect $power -type pgpin -pin $pin -all -verbose
}
}
if {$ground_map ne ""} {
connect_pin_map $ground_map ground
} else {
foreach pin [flow::env APR_GROUND_PINS] {
globalNetConnect $ground -type pgpin -pin $pin -all -verbose
}
}
globalNetConnect $power -type tiehi -all -verbose
globalNetConnect $ground -type tielo -all -verbose
applyGlobalNets
@@ -140,6 +186,113 @@ proc write_io_side {handle side signals power_records offset pitch length} {
puts $handle " )"
}
# Innovus engine modes from legacy setting/common_setting.tcl (lines 17 and
# 21-22). Session modes are not preserved across restoreDesign, so every
# stage re-applies them; the APR stages run in a fresh Innovus process, so the
# legacy "-reset" prelude is not needed.
proc apply_engine_settings {} {
setDesignMode -process [flow::env APR_DESIGN_PROCESS 55]
setAnalysisMode -cppr both -analysisType onChipVariation
}
# Placement modes from legacy setting/place_setting.tcl (effective lines 1-11).
proc apply_place_settings {} {
setPlaceMode -timingDriven true
setPlaceMode -place_global_clock_gate_aware true
setPlaceMode -place_global_place_io_pins false
setPlaceMode -honorSoftBlockage true
setPlaceMode -place_detail_honor_inst_pad true
setPlaceMode -place_global_uniform_density true
setPlaceMode -place_global_clock_power_driven true
setPlaceMode -place_global_cong_effort auto
setPlaceMode -place_detail_legalization_inst_gap 1
}
# Routing modes from legacy setting/route_setting.tcl (effective lines 1-16).
# Layer bounds stay name-based through APR_SIGNAL_MIN_LAYER/MAX_LAYER; the
# legacy numeric earlyGlobal window (lines 5-6) applies only when the optional
# APR_EARLY_GLOBAL_MIN_LAYER / APR_EARLY_GLOBAL_MAX_LAYER indexes are set.
proc apply_route_settings {} {
setNanoRouteMode -routeBottomRoutingLayer [flow::env APR_SIGNAL_MIN_LAYER]
setNanoRouteMode -routeTopRoutingLayer [flow::env APR_SIGNAL_MAX_LAYER]
setNanoRouteMode -drouteEndIteration 20
setNanoRouteMode -routeWithSiDriven true
setNanoRouteMode -routeWithTimingDriven true
setNanoRouteMode -drouteNoTaperOnoutputPin false
setTrialRouteMode -skipTracks {}
setTrialRouteMode -ignoreAbutted2TermNet true
setOptMode -maxLength 500
set early_min [string trim [flow::env APR_EARLY_GLOBAL_MIN_LAYER ""]]
if {$early_min ne ""} {
setRouteMode -earlyGlobalMinRouteLayer $early_min
}
set early_max [string trim [flow::env APR_EARLY_GLOBAL_MAX_LAYER ""]]
if {$early_max ne ""} {
setRouteMode -earlyGlobalMaxRouteLayer $early_max
}
}
# Per-stage timing tightening: derate plus setup uncertainty from legacy
# CL1/set_derate_uncertainty.tcl, and the design-rule limits from legacy
# CL1/update_sdc.tcl:3-10 (max_fanout 32, max_transition 0.08 ns,
# max_capacitance 0.15 pF). MMMC constraint modes are read-only until enabled
# interactively (legacy place.tcl:19-25).
proc apply_stage_constraints {stage} {
set_interactive_constraint_modes [all_constraint_modes]
flow::apply_apr_derate
flow::apply_apr_stage_uncertainty $stage
set_max_fanout [flow::env APR_MAX_FANOUT 32] [current_design]
set_max_transition [flow::env APR_MAX_TRANSITION_NS 0.08] [current_design]
set_max_capacitance [flow::env APR_MAX_CAPACITANCE_PF 0.15] [current_design]
set_interactive_constraint_mode {}
}
# 2x-width 2x-spacing clock NDR across the configured clock routing layers
# (legacy setting/cts_setting.tcl:21-23: CLKNDR on MET3:MET5 with
# width/spacing 0.2/0.2 um).
proc create_clock_ndr {} {
set name retrosoc_clock_ndr
if {[dbGet head.rules.name $name] eq "0x0"} {
set layers [flow::env APR_CLOCK_ROUTING_LAYERS]
set range "[lindex $layers 0]:[lindex $layers end]"
add_ndr -name $name \
-width [list $range [flow::env APR_CTS_NDR_WIDTH_UM 0.2]] \
-spacing [list $range [flow::env APR_CTS_NDR_SPACING_UM 0.2]]
}
return $name
}
# Legacy generate_ccopt_spec.tcl:17-27 binds every clock route type to the
# NDR and shields the top route type with the ground net (VSS, line 22). The
# legacy trunk and top types share the same layer window, so the single
# retrosoc trunk route type carries both the NDR and the ground shield.
proc create_clock_trunk_route_type {ndr} {
set name retrosoc_clock_trunk
if {[dbGet head.routeTypes.name $name] eq "0x0"} {
create_route_type -name $name \
-top_preferred_layer [lindex [flow::env APR_CLOCK_ROUTING_LAYERS] end] \
-bottom_preferred_layer [lindex [flow::env APR_CLOCK_ROUTING_LAYERS] 0] \
-non_default_rule $ndr \
-shield_net [flow::env APR_GROUND_NET]
}
set_ccopt_property -net_type trunk route_type $name
}
# ccopt targets from legacy setting/cts_setting.tcl:1-12 and
# generate_ccopt_spec.tcl:29-36: skew 0.08 ns, leaf/trunk transition 0.78 ns
# (max_sink_tran/max_buf_tran), insertion delay 0.05 ns (cts_max_delay),
# fanout 4.
proc configure_ccopt_targets {} {
set_ccopt_property target_skew [flow::env APR_CTS_TARGET_SKEW_NS 0.08]
set_ccopt_property target_max_trans -net_type leaf \
[flow::env APR_CTS_TARGET_MAX_TRANS_LEAF_NS 0.78]
set_ccopt_property target_max_trans -net_type trunk \
[flow::env APR_CTS_TARGET_MAX_TRANS_TRUNK_NS 0.78]
set_ccopt_property target_insertion_delay \
[flow::env APR_CTS_TARGET_INSERTION_DELAY_NS 0.05]
set_ccopt_property max_fanout [flow::env APR_CTS_MAX_FANOUT 4]
}
proc write_apr_reports {report_dir stage} {
redirect [file join $report_dir ${stage}.summary.rpt] { summaryReport }
redirect [file join $report_dir ${stage}.timing.rpt] {
@@ -165,12 +318,25 @@ proc verify_route {report_dir} {
proc write_data_out {run_root top stage output_dir report_dir} {
set prefix [file join $output_dir ${top}.${stage}]
defOut -floorplan -netlist -routing ${prefix}.def
# Legacy dataOut.tcl:15,19 drop pad and seal-ring instances from the
# netlists; APR_NETLIST_EXCLUDE_CELLS carries the same exclusion list.
set exclude [string trim [flow::env APR_NETLIST_EXCLUDE_CELLS ""]]
if {$exclude eq ""} {
saveNetlist ${prefix}.v
saveNetlist -includePowerGround ${prefix}.pg.v
} else {
saveNetlist ${prefix}.v -excludeCellInst $exclude
saveNetlist -includePowerGround ${prefix}.pg.v -excludeCellInst $exclude
}
write_sdc ${prefix}.sdc
write_sdf -version 3.0 ${prefix}.sdf
# Legacy dataOut.tcl:26-35 stream-out contract.
setStreamOutMode -virtualConnection false
streamOut ${prefix}.gds -mapFile [flow::env STREAM_MAP] \
-structureName $top -mode ALL
-structureName $top -mode ALL \
-stripes 1 -units 1000 \
-attachInstanceName 2 -attachNetName 2 \
-dieAreaAsBoundary
verify_route $report_dir
}
@@ -208,6 +374,11 @@ proc run_apr {} {
switch -- $stage {
initialize {
# MMMC is ready once init_design completes; apply the engine modes
# (legacy setting/common_setting.tcl) and derate every delay
# corner (legacy CL1/set_derate_uncertainty.tcl:1-3).
apply_engine_settings
flow::apply_apr_derate
checkDesign -all
}
floorplan {
@@ -215,11 +386,39 @@ proc run_apr {} {
[flow::env DIE_WIDTH] [flow::env DIE_HEIGHT] \
[flow::env CORE_MARGIN_LEFT] [flow::env CORE_MARGIN_BOTTOM] \
[flow::env CORE_MARGIN_RIGHT] [flow::env CORE_MARGIN_TOP]
# Legacy floorplan.tcl:6 loads a reviewed IO order file; the
# generated round-robin ring stays the default fallback.
set io_order_file [string trim [flow::env APR_IO_ORDER_FILE ""]]
if {$io_order_file ne ""} {
if {![file isfile $io_order_file] || \
![file readable $io_order_file]} {
flow::fail "APR_IO_ORDER_FILE is not readable: $io_order_file"
}
set io_file [file normalize $io_order_file]
set io_order file
} else {
set io_file [file join $work_dir ${top}.io]
write_io_ring $io_file
set io_order round_robin
}
loadIoFile $io_file
fixAllIos
puts "APR floorplan io_order=$io_order io_file=$io_file"
flow::write_text [file join $output_dir floorplan.io.json] \
"{\n \"stage\": \"floorplan\",\n \"io_order\": \"$io_order\",\n \"io_file\": \"$io_file\"\n}\n"
addIoFiller -cell [flow::env APR_IO_FILLERS] -prefix IOFILL
# Optional macro pre-placement (legacy floorplan.tcl:11 sources
# CL1/<design>_macro_loc.tcl; the file carries
# placeInstance/place_macro/createPlaceBlockage commands) plus a
# uniform macro placement halo (legacy prePlace.tcl:9 uses
# "addHaloToBlock -allMacro 2 2 2 2"; CL1/add_mem_blk.tcl instead
# extends every SRAM by 1.5 um as a pgnet route blockage).
set macro_loc [string trim [flow::env APR_MACRO_LOC_FILE ""]]
if {$macro_loc ne ""} {
flow::source_hook APR_MACRO_LOC_FILE
set halo [flow::env APR_MACRO_HALO_UM 5]
addHaloToBlock -allMacro $halo $halo $halo $halo
}
flow::source_hook APR_FLOORPLAN_HOOK
connect_power_nets
set ring_layers [flow::env APR_RING_LAYERS]
@@ -251,20 +450,33 @@ proc run_apr {} {
checkFPlan -reportUtil
}
place {
# Legacy place.tcl:7-23 re-sources every setting file plus the
# per-stage constraint updates after restoreDesign.
apply_engine_settings
apply_place_settings
apply_route_settings
apply_stage_constraints place
# Legacy place_setting.tcl:1 fixes density at 0.7; the retroSoC
# product policy keeps it driven by CORE_UTILIZATION.
setPlaceMode -place_global_max_density [flow::env CORE_UTILIZATION]
placeDesign -concurrent_macros
optDesign -preCTS
flow::source_hook APR_PLACE_HOOK
}
cts {
# Legacy CTS.tcl:7-25 applies the same settings and constraints.
apply_engine_settings
apply_place_settings
apply_route_settings
apply_stage_constraints cts
set_ccopt_property buffer_cells [flow::env APR_CTS_BUFFER_CELLS]
set_ccopt_property inverter_cells [flow::env APR_CTS_INVERTER_CELLS]
set_ccopt_property route_type -net_type trunk \
-route_type_name retrosoc_clock_trunk
create_route_type -name retrosoc_clock_trunk \
-top_preferred_layer [lindex [flow::env APR_CLOCK_ROUTING_LAYERS] end] \
-bottom_preferred_layer [lindex [flow::env APR_CLOCK_ROUTING_LAYERS] 0]
# Legacy generate_ccopt_spec.tcl: snapshot the tree spec, then
# bind the NDR route type and targets before ccopt_design.
create_ccopt_clock_tree_spec -file [file join $work_dir ccopt.spec]
set clock_ndr [create_clock_ndr]
create_clock_trunk_route_type $clock_ndr
configure_ccopt_targets
source [file join $work_dir ccopt.spec]
ccopt_design
optDesign -postCTS
@@ -272,8 +484,14 @@ proc run_apr {} {
flow::source_hook APR_CTS_HOOK
}
route {
setNanoRouteMode -routeBottomRoutingLayer [flow::env APR_SIGNAL_MIN_LAYER]
setNanoRouteMode -routeTopRoutingLayer [flow::env APR_SIGNAL_MAX_LAYER]
# Legacy route.tcl:6-25 applies the same settings and constraints.
apply_engine_settings
apply_place_settings
apply_route_settings
apply_stage_constraints route
# Legacy setting/common_setting.tcl:29-33 enables coupled
# postRoute extraction once the route step starts.
setExtractRCMode -engine postRoute -effortLevel medium -couples true
routeDesign -globalDetail
optDesign -postRoute
optDesign -postRoute -hold
+43 -45
View File
@@ -9,6 +9,8 @@ namespace eval flow {
variable canonical_clock_domains {}
variable canonical_reset_ports {}
variable commercial_clock_names {}
variable commercial_clock_groups
array set commercial_clock_groups {}
}
proc flow::required_ports {label names} {
@@ -53,6 +55,13 @@ proc flow::domain_object {domain} {
return [flow::required_pins "clock $domain" $observation]
}
proc flow::register_clock {name async_group} {
variable commercial_clock_names
variable commercial_clock_groups
lappend commercial_clock_names $name
lappend commercial_clock_groups($async_group) $name
}
proc flow::load_canonical_timing_contract {} {
variable canonical_clock_domains
variable canonical_reset_ports
@@ -62,69 +71,58 @@ proc flow::load_canonical_timing_contract {} {
flow::fail "canonical commercial timing contract is missing: $contract"
}
source $contract
set expected {external system audio jtag dvp usb2_ulpi}
set expected [product::expected_clock_domains]
if {[lsort [dict keys $canonical_clock_domains]] ne [lsort $expected]} {
flow::fail "canonical commercial clock-domain set is invalid"
flow::fail "canonical commercial clock-domain set is invalid for $flow::soc"
}
if {[llength $canonical_reset_ports] == 0} {
flow::fail "canonical reset-port list is empty"
}
}
proc flow::create_canonical_master_clock {domain} {
variable canonical_clock_domains
set values [dict get $canonical_clock_domains $domain]
set name clk_$domain
create_clock -name $name -period [dict get $values period_ns] \
[flow::domain_object $domain]
return $name
}
proc flow::apply_clock_constraints {} {
variable canonical_clock_domains
variable canonical_reset_ports
variable commercial_clock_names
variable commercial_clock_groups
set commercial_clock_names {}
foreach domain {external audio jtag dvp usb2_ulpi} {
lappend commercial_clock_names [flow::create_canonical_master_clock $domain]
array set commercial_clock_groups {}
set custom [product::custom_clock_domains]
array set objects {}
foreach domain [dict keys $canonical_clock_domains] {
set objects($domain) [flow::domain_object $domain]
}
foreach domain [dict keys $canonical_clock_domains] {
if {$domain in $custom} {
continue
}
set values [dict get $canonical_clock_domains $domain]
set name clk_$domain
set source_domain [dict get $values source_domain]
if {$source_domain eq ""} {
create_clock -name $name -period [dict get $values period_ns] \
$objects($domain)
} else {
if {$source_domain in $custom} {
flow::fail "domain $domain derives from the product-managed \
domain $source_domain"
}
create_generated_clock -name $name -source $objects($source_domain) \
-divide_by 1 $objects($domain)
}
flow::register_clock $name [dict get $values async_group]
}
product::apply_clock_overlays
set xtal_pin [flow::required_pins crystal_clock u_rcu/u_xtal_buf/clk_o]
create_clock -name clk_xtal -period [flow::env XTAL_CLK_PERIOD_NS] $xtal_pin
lappend commercial_clock_names clk_xtal
set pll_pin [flow::required_pins pll_clock u_rcu/u_tc_pll/u_PLL_TOP/CKOUT1]
create_clock -name clk_pll -period [flow::env PLL_OUTPUT_PERIOD_NS] $pll_pin
lappend commercial_clock_names clk_pll
set ext_pin [flow::domain_object external]
set sys_pin [flow::domain_object system]
create_generated_clock -name clk_system_ext -master_clock clk_external \
-source $ext_pin -divide_by 1 $sys_pin
create_generated_clock -name clk_system_pll -master_clock clk_pll \
-source $pll_pin -divide_by 1 -add $sys_pin
lappend commercial_clock_names clk_system_ext clk_system_pll
set ext_group [get_clocks {clk_external clk_system_ext}]
set pll_group [get_clocks {clk_pll clk_system_pll}]
set_clock_groups -name retrosoc_system_sources -physically_exclusive \
-group $ext_group -group $pll_group
set system_group [get_clocks \
{clk_external clk_system_ext clk_pll clk_system_pll}]
set async_groups [list \
$system_group \
[get_clocks clk_audio] \
[get_clocks clk_xtal] \
[get_clocks clk_jtag] \
[get_clocks clk_dvp] \
[get_clocks clk_usb2_ulpi]]
if {[array size commercial_clock_groups] > 1} {
set command [list set_clock_groups -name retrosoc_async -asynchronous]
foreach group $async_groups {
lappend command -group $group
foreach group [array names commercial_clock_groups] {
lappend command -group [get_clocks $commercial_clock_groups($group)]
}
eval $command
}
set clocks [get_clocks $commercial_clock_names]
if {[sizeof_collection $clocks] != [llength $commercial_clock_names]} {
+29 -22
View File
@@ -9,7 +9,9 @@
# See the Mulan PSL v2 for more details.
namespace eval flow {
variable top [expr {[info exists ::env(TOP)] ? $::env(TOP) : "retrosoc_asic"}]
variable top [expr {[info exists ::env(TOP)] ? $::env(TOP) : ""}]
variable soc [expr {[info exists ::env(SOC)] ? [string toupper $::env(SOC)] : "MINI"}]
variable technology [expr {[info exists ::env(TECHNOLOGY)] ? [string toupper $::env(TECHNOLOGY)] : "ICS55"}]
variable root [file normalize [file join [file dirname [info script]] ../..]]
variable run_root [expr {[info exists ::env(RUN_ROOT)] ? $::env(RUN_ROOT) : ""}]
}
@@ -209,6 +211,16 @@ proc flow::report_has_failure {path patterns} {
if {![file isfile $path]} {
flow::fail "required report was not written: $path"
}
set handle [open $path r]
set text [read $handle]
close $handle
foreach pattern $patterns {
if {[regexp -nocase -- $pattern $text]} {
return 1
}
}
return 0
}
proc flow::count_report_matches {path pattern} {
if {![file isfile $path]} {
@@ -220,6 +232,14 @@ proc flow::report_has_failure {path patterns} {
return [regexp -all -nocase -- $pattern $text]
}
proc flow::require_commercial_clock_inventory {} {
foreach name [product::clock_names] {
if {[sizeof_collection [get_clocks -quiet $name]] != 1} {
flow::fail "commercial clock is missing or ambiguous: $name"
}
}
}
proc flow::require_qualified_synthesis {} {
variable run_root
set summary [file join $run_root syn output synthesis.summary.tsv]
@@ -233,18 +253,6 @@ proc flow::report_has_failure {path patterns} {
set values($name) $value
}
}
proc flow::require_commercial_clock_inventory {} {
set expected {
clk_external clk_audio clk_jtag clk_dvp clk_usb2_ulpi
clk_xtal clk_pll clk_system_ext clk_system_pll
}
foreach name $expected {
if {[sizeof_collection [get_clocks -quiet $name]] != 1} {
flow::fail "commercial clock is missing or ambiguous: $name"
}
}
}
close $handle
foreach name {flow_pass constraints_complete io_qualified} {
if {![info exists values($name)] || $values($name) ne "yes"} {
@@ -252,13 +260,12 @@ proc flow::report_has_failure {path patterns} {
}
}
}
set handle [open $path r]
set text [read $handle]
close $handle
foreach pattern $patterns {
if {[regexp -nocase -- $pattern $text]} {
return 1
}
}
return 0
set flow_product_tcl [file join $flow::root tcl common products \
[string tolower $flow::soc].tcl]
if {![file isfile $flow_product_tcl]} {
puts stderr \
"COMMERCIAL_FLOW_ERROR: unsupported SOC=$flow::soc (missing $flow_product_tcl)"
exit 2
}
source $flow_product_tcl
@@ -52,3 +52,76 @@ proc flow::rc_cap_table {corner} {
proc flow::spef_name {top corner} {
return "${top}.${corner}.spef.gz"
}
# CX55 OCV policy for PrimeTime signoff (legacy bes_data/common/
# signoff_table.tcl:364-390, expanded by bes_data/sta/scr/flow_com/
# transform_signoff_table.tcl:10-25; command forms verified in legacy PT logs
# bes_data/sta/log/asic_top_V2026_1_CTS_MIN_CWORST.log and
# asic_top_CTS_TYP_TYP.log:2672-2673):
# MAX/WCL: clock-path early derate 0.95 (cell and net), all other arcs unity
# MIN/ML: clock-path early derate 0.90 (cell and net), all other arcs unity
# TYP: unqualified "set_timing_derate -early 0.95" with unity late (the
# legacy "default default" entry)
# The legacy DC synthesis run linked the TYP library and applied no timing
# derate at all (bes_data/syn/scr/flow_com/syn_common_flow.tcl never sources
# signoff_table.tcl; bes_data/syn/log/asic_top.log contains no
# set_timing_derate), so the synthesis flow does not call this helper.
proc flow::apply_signoff_derate {pvt} {
switch -glob -- $pvt {
MAX - WCL {
set cell_early [flow::env TIMING_DERATE_SETUP_CELL_CLOCK_EARLY 0.95]
set net_early [flow::env TIMING_DERATE_SETUP_NET_CLOCK_EARLY 0.95]
}
MIN - ML {
set cell_early [flow::env TIMING_DERATE_HOLD_CELL_CLOCK_EARLY 0.90]
set net_early [flow::env TIMING_DERATE_HOLD_NET_CLOCK_EARLY 0.90]
}
default {
set early [flow::env TIMING_DERATE_DEFAULT_EARLY 0.95]
if {$early != 1.0} {
set_timing_derate -late 1.0
set_timing_derate -early $early
}
return
}
}
set_timing_derate -cell_delay -clock -early $cell_early
set_timing_derate -cell_delay -clock -late 1.0
set_timing_derate -cell_delay -data -early 1.0
set_timing_derate -cell_delay -data -late 1.0
set_timing_derate -net_delay -clock -early $net_early
set_timing_derate -net_delay -clock -late 1.0
set_timing_derate -net_delay -data -early 1.0
set_timing_derate -net_delay -data -late 1.0
}
# Innovus derate and per-stage setup uncertainty (legacy
# pd_data/pr/scr/CL1/set_derate_uncertainty.tcl).
proc flow::apply_apr_derate {} {
set clock_early [flow::env APR_DERATE_CLOCK_EARLY 1.0]
set clock_late [flow::env APR_DERATE_CLOCK_LATE 1.0]
set data_late [flow::env APR_DERATE_DATA_LATE 1.0]
if {$clock_early != 1.0} {
set_timing_derate -early $clock_early -clock \
-delay_corner [all_delay_corners]
}
if {$clock_late != 1.0} {
set_timing_derate -late $clock_late -clock \
-delay_corner [all_delay_corners]
}
if {$data_late != 1.0} {
set_timing_derate -late $data_late -data -delay_corner [all_delay_corners]
}
}
proc flow::apply_apr_stage_uncertainty {stage} {
switch -- $stage {
place { set value [flow::env APR_SETUP_UNCERTAINTY_PLACE_NS ""] }
cts { set value [flow::env APR_SETUP_UNCERTAINTY_CTS_NS ""] }
route { set value [flow::env APR_SETUP_UNCERTAINTY_ROUTE_NS ""] }
default { set value "" }
}
if {$value ne ""} {
set_clock_uncertainty -setup $value [all_clocks]
}
}
@@ -1,7 +1,7 @@
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
# retroSoC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# You may obtain a copy of MulanPSL2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
@@ -41,68 +41,6 @@ proc flow::numbered_ports {prefix first last suffix} {
return $names
}
proc flow::apply_qualified_io_constraints {} {
set jtag_in [flow::interface_ports jtag {jtag_tms_i_pad jtag_tdi_i_pad}]
set jtag_out [flow::interface_ports jtag {jtag_tdo_o_pad}]
flow::apply_input_budget JTAG clk_jtag $jtag_in
flow::apply_output_budget JTAG clk_jtag $jtag_out
set dvp_names [flow::numbered_ports gpio_ 11 20 _io_pad]
set dvp_in [flow::interface_ports dvp $dvp_names]
flow::apply_input_budget DVP clk_dvp $dvp_in
set ulpi_data [flow::numbered_ports usb2_ulpi_data 0 7 _io_pad]
set ulpi_in [flow::interface_ports ulpi \
[concat {usb2_ulpi_dir_i_pad usb2_ulpi_nxt_i_pad} $ulpi_data]]
set ulpi_out [flow::interface_ports ulpi \
[concat $ulpi_data {usb2_ulpi_stp_o_pad}]]
flow::apply_input_budget ULPI clk_usb2_ulpi $ulpi_in
flow::apply_output_budget ULPI clk_usb2_ulpi $ulpi_out
set sdram_data [flow::numbered_ports sdram_dq 0 15 _io_pad]
set sdram_out_names {
sdram_clk_o_pad sdram_cke_o_pad sdram_cs_n_o_pad
sdram_ras_n_o_pad sdram_cas_n_o_pad sdram_we_n_o_pad
sdram_ba0_o_pad sdram_ba1_o_pad sdram_dqm0_o_pad sdram_dqm1_o_pad
}
set sdram_out_names [concat $sdram_out_names \
[flow::numbered_ports sdram_addr 0 12 _o_pad] $sdram_data]
set sdram_clock [flow::create_virtual_interface_clock SDRAM]
flow::apply_input_budget SDRAM $sdram_clock \
[flow::interface_ports sdram $sdram_data]
flow::apply_output_budget SDRAM $sdram_clock \
[flow::interface_ports sdram $sdram_out_names]
set sdio_data {
sdio1_cmd_io_pad sdio1_dat0_io_pad sdio1_dat1_io_pad
sdio1_dat2_io_pad sdio1_dat3_io_pad
}
set sdio_clock [flow::create_virtual_interface_clock SDIO]
flow::apply_input_budget SDIO $sdio_clock [flow::interface_ports sdio $sdio_data]
flow::apply_output_budget SDIO $sdio_clock \
[flow::interface_ports sdio [concat {sdio1_clk_o_pad} $sdio_data]]
set xpi_data [flow::numbered_ports xpi_dat 0 3 _io_pad]
set xpi_clock [flow::create_virtual_interface_clock XPI]
flow::apply_input_budget XPI $xpi_clock [flow::interface_ports xpi $xpi_data]
flow::apply_output_budget XPI $xpi_clock [flow::interface_ports xpi \
[concat {xpi_sck_o_pad xpi_nss0_o_pad xpi_nss1_o_pad
xpi_nss2_o_pad xpi_nss3_o_pad} $xpi_data]]
set gpio_names [concat \
[flow::numbered_ports gpio_ 0 9 _io_pad] \
[flow::numbered_ports gpio_ 21 31 _io_pad]]
set async_clock [flow::create_virtual_interface_clock ASYNC]
flow::apply_input_budget ASYNC $async_clock \
[flow::interface_ports asynchronous [concat {uart0_rx_i_pad} $gpio_names]]
flow::apply_output_budget ASYNC $async_clock \
[flow::interface_ports asynchronous [concat {uart0_tx_o_pad} $gpio_names]]
set_false_path -to [flow::interface_ports asynchronous_control \
{xo_o_pad usb2_ulpi_reset_n_o_pad}]
flow::source_hook TIMING_IO_MODE_HOOK
}
proc flow::apply_io_constraints {} {
variable io_qualified
variable unqualified_interfaces
@@ -110,10 +48,10 @@ proc flow::apply_io_constraints {} {
if {$mode eq "YES"} {
set io_qualified 1
set unqualified_interfaces {}
flow::apply_qualified_io_constraints
product::apply_io_constraints
} elseif {$mode eq "NO"} {
set io_qualified 0
set unqualified_interfaces {JTAG DVP ULPI SDRAM SDIO XPI ASYNC}
set unqualified_interfaces [product::io_interface_groups]
# Internal-QoR mode closes all sequential domains while explicitly
# excluding board and package paths that do not yet have a budget.
set_false_path -from [all_inputs]
@@ -0,0 +1,131 @@
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
# retroSoC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# retroSoC Mini (retrosoc_asic) product overlay.
#
# Clock intent: the canonical contract (rtl/mini/integration/
# clock_reset_domains.json) owns nine domains. This overlay takes over the
# lp/pclk domains because the LP root mux selects between the 24 MHz reference
# (bypass) and the PLL (performance) source; both modes are modeled as
# physically exclusive generated clocks, preserving the legacy commercial
# system-clock mux semantics on the current hierarchy.
namespace eval product {}
proc product::expected_clock_domains {} {
return {aon lp hp pclk memory audio jtag dvp usb2_ulpi}
}
proc product::custom_clock_domains {} {
return {lp pclk}
}
proc product::clock_names {} {
return {
clk_aon clk_hp clk_memory clk_audio clk_jtag clk_dvp clk_usb2_ulpi
clk_pll clk_lp_ext clk_lp_pll clk_pclk_ext clk_pclk_pll
}
}
proc product::io_interface_groups {} {
return {JTAG DVP ULPI SDRAM SDIO XPI ASYNC}
}
proc product::apply_clock_overlays {} {
set pll_pin [flow::required_pins pll_clock \
u_rcu/u_clock_reset_subsystem/u_tc_pll/u_PLL_TOP/CKOUT1]
create_clock -name clk_pll -period [flow::env PLL_OUTPUT_PERIOD_NS] $pll_pin
flow::register_clock clk_pll lp
set aon_object [flow::domain_object aon]
set lp_object [flow::domain_object lp]
create_generated_clock -name clk_lp_ext -master_clock clk_aon \
-source $aon_object -divide_by 1 $lp_object
flow::register_clock clk_lp_ext lp
create_generated_clock -name clk_lp_pll -master_clock clk_pll \
-source $pll_pin -divide_by 1 -add $lp_object
flow::register_clock clk_lp_pll lp
set pclk_object [flow::domain_object pclk]
create_generated_clock -name clk_pclk_ext -master_clock clk_lp_ext \
-source $lp_object -divide_by 1 $pclk_object
flow::register_clock clk_pclk_ext lp
create_generated_clock -name clk_pclk_pll -master_clock clk_lp_pll \
-source $lp_object -divide_by 1 -add $pclk_object
flow::register_clock clk_pclk_pll lp
set_clock_groups -name retrosoc_system_sources -physically_exclusive \
-group [get_clocks {clk_aon clk_lp_ext clk_pclk_ext}] \
-group [get_clocks {clk_pll clk_lp_pll clk_pclk_pll}]
}
proc product::apply_io_constraints {} {
set jtag_in [flow::interface_ports jtag {jtag_tms_i_pad jtag_tdi_i_pad}]
set jtag_out [flow::interface_ports jtag {jtag_tdo_o_pad}]
flow::apply_input_budget JTAG clk_jtag $jtag_in
flow::apply_output_budget JTAG clk_jtag $jtag_out
set dvp_names [flow::numbered_ports gpio_ 11 20 _io_pad]
set dvp_in [flow::interface_ports dvp $dvp_names]
flow::apply_input_budget DVP clk_dvp $dvp_in
set ulpi_data [flow::numbered_ports usb2_ulpi_data 0 7 _io_pad]
set ulpi_in [flow::interface_ports ulpi \
[concat {usb2_ulpi_dir_i_pad usb2_ulpi_nxt_i_pad} $ulpi_data]]
set ulpi_out [flow::interface_ports ulpi \
[concat $ulpi_data {usb2_ulpi_stp_o_pad}]]
flow::apply_input_budget ULPI clk_usb2_ulpi $ulpi_in
flow::apply_output_budget ULPI clk_usb2_ulpi $ulpi_out
set sdram_data [flow::numbered_ports sdram_dq 0 15 _io_pad]
set sdram_out_names {
sdram_clk_o_pad sdram_cke_o_pad sdram_cs_n_o_pad
sdram_ras_n_o_pad sdram_cas_n_o_pad sdram_we_n_o_pad
sdram_ba0_o_pad sdram_ba1_o_pad sdram_dqm0_o_pad sdram_dqm1_o_pad
}
set sdram_out_names [concat $sdram_out_names \
[flow::numbered_ports sdram_addr 0 12 _o_pad] $sdram_data]
set sdram_clock [flow::create_virtual_interface_clock SDRAM]
flow::apply_input_budget SDRAM $sdram_clock \
[flow::interface_ports sdram $sdram_data]
flow::apply_output_budget SDRAM $sdram_clock \
[flow::interface_ports sdram $sdram_out_names]
set sdio_data {
sdio1_cmd_io_pad sdio1_dat0_io_pad sdio1_dat1_io_pad
sdio1_dat2_io_pad sdio1_dat3_io_pad
}
set sdio_clock [flow::create_virtual_interface_clock SDIO]
flow::apply_input_budget SDIO $sdio_clock [flow::interface_ports sdio $sdio_data]
flow::apply_output_budget SDIO $sdio_clock \
[flow::interface_ports sdio [concat {sdio1_clk_o_pad} $sdio_data]]
set xpi_data [flow::numbered_ports xpi_dat 0 3 _io_pad]
set xpi_clock [flow::create_virtual_interface_clock XPI]
flow::apply_input_budget XPI $xpi_clock [flow::interface_ports xpi $xpi_data]
flow::apply_output_budget XPI $xpi_clock [flow::interface_ports xpi \
[concat {xpi_sck_o_pad xpi_nss0_o_pad xpi_nss1_o_pad
xpi_nss2_o_pad xpi_nss3_o_pad} $xpi_data]]
set gpio_names [concat \
[flow::numbered_ports gpio_ 0 9 _io_pad] \
[flow::numbered_ports gpio_ 21 31 _io_pad]]
set async_clock [flow::create_virtual_interface_clock ASYNC]
flow::apply_input_budget ASYNC $async_clock \
[flow::interface_ports asynchronous \
[concat {uart0_rx_i_pad uart1_rx_i_pad} $gpio_names]]
flow::apply_output_budget ASYNC $async_clock \
[flow::interface_ports asynchronous \
[concat {uart0_tx_o_pad uart1_tx_o_pad} $gpio_names]]
set_false_path -to [flow::interface_ports asynchronous_control \
{usb2_ulpi_reset_n_o_pad}]
flow::source_hook TIMING_IO_MODE_HOOK
}
@@ -0,0 +1,61 @@
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
# retroSoC is licensed under Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
# http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# retroSoC Tiny (retrosoc_tiny_asic) product overlay.
#
# Clock intent: the canonical contract (rtl/tiny/integration/
# clock_reset_domains.json) owns two domains, system (SAFE24 external clock)
# and jtag. The ICS55 PLL_TOP macro is present but parked off (EN=0) and is
# not a clock source; it carries no characterized timing arcs yet, so no PLL
# overlay clock is created here. See docs/ip/tiny-soc.md (TINY-055).
namespace eval product {}
proc product::expected_clock_domains {} {
return {system jtag}
}
proc product::custom_clock_domains {} {
return {}
}
proc product::clock_names {} {
return {clk_system clk_jtag}
}
proc product::io_interface_groups {} {
return {JTAG XPI ASYNC}
}
proc product::apply_clock_overlays {} {
}
proc product::apply_io_constraints {} {
set jtag_in [flow::interface_ports jtag {jtag_tms_i_pad jtag_tdi_i_pad}]
set jtag_out [flow::interface_ports jtag {jtag_tdo_o_pad}]
flow::apply_input_budget JTAG clk_jtag $jtag_in
flow::apply_output_budget JTAG clk_jtag $jtag_out
set xpi_data [flow::numbered_ports xpi_dat 0 3 _io_pad]
set xpi_clock [flow::create_virtual_interface_clock XPI]
flow::apply_input_budget XPI $xpi_clock [flow::interface_ports xpi $xpi_data]
flow::apply_output_budget XPI $xpi_clock [flow::interface_ports xpi \
[concat {xpi_sck_o_pad xpi_nss0_o_pad xpi_nss1_o_pad
xpi_nss2_o_pad xpi_nss3_o_pad} $xpi_data]]
set gpio_names [flow::numbered_ports gpio_ 0 31 _io_pad]
set async_clock [flow::create_virtual_interface_clock ASYNC]
flow::apply_input_budget ASYNC $async_clock \
[flow::interface_ports asynchronous \
[concat {uart0_rx_i_pad uart1_rx_i_pad} $gpio_names]]
flow::apply_output_budget ASYNC $async_clock \
[flow::interface_ports asynchronous \
[concat {uart0_tx_o_pad uart1_tx_o_pad} $gpio_names]]
}
+68 -3
View File
@@ -12,6 +12,22 @@ set flow_root [file normalize [file join [file dirname [info script]] ../..]]
source [file join $flow_root tcl common common.tcl]
source [file join $flow_root tcl common corners.tcl]
proc eco_flag {name} {
set value [string toupper [flow::env $name NO]]
if {$value ni {YES NO}} {
flow::fail "$name must be YES or NO"
}
return [expr {$value eq "YES"}]
}
proc eco_pba_mode {} {
set mode [string tolower [flow::env ECO_PBA_MODE none]]
if {$mode ni {none path exhaustive}} {
flow::fail "ECO_PBA_MODE must be none, path, or exhaustive"
}
return $mode
}
proc run_eco {} {
set run_root [flow::env RUN_ROOT]
set top [flow::env TOP]
@@ -68,6 +84,34 @@ proc run_eco {} {
-hold_margin [flow::env ECO_HOLD_MARGIN_NS] \
-physical_mode [flow::env ECO_PHYSICAL_MODE] -verbose
}
# Optional power-recovery subflows (legacy pd_data/pteco/scr/flow/swap.tcl
# and size_down.tcl); both default off and run after the setup fix.
if {[eco_flag ECO_ENABLE_VT_SWAP]} {
define_user_attribute -type string -class lib_cell cell_footprint -import
set_app_var eco_alternative_cell_attribute_restrictions \
"area cell_footprint"
set_app_var eco_alternative_area_ratio_threshold 2
remote_execute {
define_user_attribute -type string -class lib_cell cell_footprint -import
set_app_var eco_alternative_cell_attribute_restrictions \
"area cell_footprint"
set_app_var eco_alternative_area_ratio_threshold 2
update_timing -full
}
redirect [file join $report_dir vt_swap.rpt] {
fix_eco_power -methods size_cell \
-pattern_priority [flow::env ECO_VT_PATTERN_PRIORITY] \
-setup_margin [flow::env ECO_SETUP_MARGIN_NS] \
-pba_mode [eco_pba_mode] -verbose
}
}
if {[eco_flag ECO_ENABLE_SIZE_DOWN]} {
redirect [file join $report_dir size_down.rpt] {
fix_eco_power -methods size_cell \
-setup_margin [flow::env ECO_SETUP_MARGIN_NS] \
-pba_mode [eco_pba_mode] -verbose
}
}
redirect [file join $report_dir hold.rpt] {
fix_eco_timing -type hold -methods insert_buffer \
-buffer_list [flow::env ECO_HOLD_BUFFERS] \
@@ -75,6 +119,17 @@ proc run_eco {} {
-hold_margin [flow::env ECO_HOLD_MARGIN_NS] \
-physical_mode [flow::env ECO_PHYSICAL_MODE] -verbose
}
# Optional redundant-buffer removal (legacy
# pd_data/pteco/scr/flow/remove_buffer.tcl); defaults off and runs after
# the hold fix, matching the legacy flow order.
if {[eco_flag ECO_ENABLE_REMOVE_BUFFER]} {
redirect [file join $report_dir remove_buffer.rpt] {
fix_eco_power -methods remove_buffer \
-setup_margin [flow::env ECO_SETUP_MARGIN_NS] \
-hold_margin [flow::env ECO_HOLD_MARGIN_NS] \
-pba_mode [eco_pba_mode] -verbose
}
}
redirect [file join $report_dir drc.rpt] {
foreach type {max_transition max_capacitance} {
fix_eco_drc -type $type -methods size_cell \
@@ -89,15 +144,25 @@ proc run_eco {} {
}
}
set raw [file join $work_dir func_TYP_TYP_25.icc2.tcl]
set eco_raw $raw
# Every scenario records its own change list in DMSA (legacy
# pd_data/pteco/scr/app/output.tcl writes changes from every worker);
# exporting only one scenario drops scenario-specific fixes. Iterate over
# every scenario with a current session so each write_changes lands in a
# dedicated file, then let translate_eco.py merge and deduplicate them.
set raw_changes {}
dict for {scenario unused} $::flow::scenarios {
set eco_raw [file join $work_dir ${scenario}.icc2.tcl]
set_distributed_variables {eco_raw}
current_session func_TYP_TYP_25
current_session $scenario
remote_execute { write_changes -format icc2tcl -output $eco_raw }
lappend raw_changes $eco_raw
}
set command [list [flow::env FLOW_PYTHON python] \
[file join $::flow::root scripts translate_eco.py] \
--output [file join $output_dir eco.tcl] --allow-empty]
foreach raw $raw_changes {
lappend command --input $raw
}
if {[catch {exec {*}$command} message]} {
flow::fail "PrimeTime ECO translation failed: $message"
}
+115 -2
View File
@@ -15,13 +15,29 @@ proc quote_svrf {value} {
return "\"[string map {\" \\\"} $value]\""
}
proc optional_files {name} {
set result {}
foreach item [flow::env $name ""] {
if {[regexp {[*?\[]} $item]} {
flow::fail "$name contains a wildcard: $item"
}
if {![file isfile $item] || ![file readable $item]} {
flow::fail "$name is not readable: $item"
}
lappend result [file normalize $item]
}
return $result
}
proc write_merge {run_root top} {
set base [flow::stage_dirs pv merge]
set input [file join $run_root apr eco output ${top}.eco.gds]
set output [file join $base output ${top}.merged.gds]
set verdict [file join $base output verdict.pass]
# OTHER_GDS carries optional extra layouts such as seal rings (legacy
# pd_data/pv/scr/CL1/core.merge merges project_config(physical,gds,other)).
set gds [concat [flow::env_list STD_GDS] [flow::env_list IO_GDS] \
[flow::env_list MACRO_GDS]]
[flow::env_list MACRO_GDS] [optional_files OTHER_GDS]]
set handle [open [file join $base work merge.tcl] w]
puts $handle [list set input_gds $input]
puts $handle [list set output_gds $output]
@@ -124,6 +140,7 @@ proc write_lvs {run_root top} {
puts $handle [list set runset $runset]
puts $handle [list set report [file join $base reports ${top}.lvs.rpt]]
puts $handle [list set verdict $verdict]
puts $handle [list set lvs_args [flow::env CALIBRE_LVS_ARGS ""]]
puts $handle {if {[file exists $verdict]} {
file delete -force $verdict
}
@@ -133,7 +150,7 @@ if {[catch {exec {*}$convert >@ stdout 2>@ stderr} message]} {
puts stderr $message
exit 2
}
set verify [concat $calibre [list -64 -lvs -hier -hcell $hcells $runset]]
set verify [concat $calibre [list -64 -lvs] $lvs_args [list -hier -hcell $hcells $runset]]
if {[catch {exec {*}$verify >@ stdout 2>@ stderr} message]} {
puts stderr $message
exit 2
@@ -167,6 +184,101 @@ close $marker}
close $handle
}
# Macro-level LVS/ERC (legacy pd_data/pv/scr/CL1/_calibre_PLL_lvs.rul_ and
# PLL.lvs.cmd run a standalone hierarchical Calibre LVS+ERC per hard macro and
# keep reports such as PLL_TOP_lvs.full.rpt). MACRO_LVS_CELLS lists the macro
# primary cells; an empty list is a harmless skip with a passing verdict.
proc write_macro_lvs {run_root top} {
set base [flow::stage_dirs pv macro_lvs]
set verdict [file join $base output verdict.pass]
set driver [file join $base work macro_lvs_driver.tcl]
set cells {}
foreach cell [flow::env MACRO_LVS_CELLS ""] {
if {![regexp {^[A-Za-z0-9_]+$} $cell]} {
flow::fail "MACRO_LVS_CELLS contains an invalid cell name: $cell"
}
lappend cells $cell
}
if {[llength $cells] == 0} {
flow::write_text $driver {puts "MACRO_LVS_CELLS is empty; macro-level LVS is skipped"
}
flow::write_pass $verdict
puts "MACRO_LVS_CELLS is empty; macro-level LVS is skipped"
return
}
set deck [lindex [flow::env_list CALIBRE_LVS_DECK] 0]
set layouts {}
foreach path [flow::env_list MACRO_GDS] {
lappend layouts [quote_svrf $path]
}
set sources {}
foreach path [flow::env_list MACRO_CDL] {
lappend sources [quote_svrf $path]
}
set jobs {}
foreach cell $cells {
set runset [file join $base work ${cell}.lvs.runset]
set report [file join $base reports ${cell}.lvs.rpt]
set handle [open $runset w]
puts $handle "LAYOUT PATH [join $layouts { }]"
puts $handle "LAYOUT PRIMARY [quote_svrf $cell]"
puts $handle "LAYOUT SYSTEM GDSII"
puts $handle "SOURCE PATH [join $sources { }]"
puts $handle "SOURCE PRIMARY [quote_svrf $cell]"
puts $handle "SOURCE SYSTEM SPICE"
puts $handle "LVS REPORT [quote_svrf $report]"
puts $handle "LVS REPORT MAXIMUM ALL"
puts $handle "MASK SVDB DIRECTORY [quote_svrf [file join $base work svdb_${cell}]] QUERY"
puts $handle "LVS SPICE CULL PRIMITIVE SUBCIRCUITS YES"
puts $handle "LVS SPICE OVERRIDE GLOBALS YES"
puts $handle "LVS RECOGNIZE GATES NONE"
puts $handle "LVS EXECUTE ERC YES"
puts $handle "ERC RESULTS DATABASE [quote_svrf [file join $base work ${cell}.erc.db]]"
puts $handle "ERC SUMMARY REPORT [quote_svrf [file join $base reports ${cell}.erc.rpt]]"
puts $handle "ERC MAXIMUM RESULTS ALL"
puts $handle "INCLUDE [quote_svrf $deck]"
close $handle
lappend jobs $cell $runset $report
}
set handle [open $driver w]
puts $handle [list set calibre [flow::env CALIBRE]]
puts $handle [list set lvs_args [flow::env CALIBRE_LVS_ARGS ""]]
puts $handle [list set checker [list [flow::env FLOW_PYTHON python] \
[file join $::flow::root scripts check_outputs.py]]]
puts $handle [list set reports [file join $base reports]]
puts $handle [list set verdict $verdict]
puts $handle [list set jobs $jobs]
puts $handle {if {[file exists $verdict]} {
file delete -force $verdict
}
set started [clock seconds]
foreach {cell runset report} $jobs {
if {[file exists $report]} {
file delete -force $report
}
set command [concat $calibre [list -64 -lvs] $lvs_args [list -hier $runset]]
if {[catch {exec {*}$command >@ stdout 2>@ stderr} message]} {
puts stderr $message
exit 2
}
if {![file isfile $report]} {
puts stderr "missing macro LVS report: $report"
exit 2
}
if {[file mtime $report] < ($started - 2)} {
puts stderr "macro LVS report was not refreshed: $report"
exit 2
}
}
set check [concat $checker [list --kind calibre-lvs --root $reports \
--verdict $verdict]]
if {[catch {exec {*}$check >@ stdout 2>@ stderr} message]} {
puts stderr $message
exit 2
}}
close $handle
}
proc generate_pv {} {
set run_root [flow::env RUN_ROOT]
set top [flow::env TOP]
@@ -175,6 +287,7 @@ proc generate_pv {} {
merge { write_merge $run_root $top }
drc - antenna { write_drc $run_root $top $mode }
lvs { write_lvs $run_root $top }
macro_lvs { write_macro_lvs $run_root $top }
default { flow::fail "unsupported PV mode: $mode" }
}
}
+42
View File
@@ -8,10 +8,49 @@
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# See the Mulan PSL v2 for more details.
# PrimeTime analysis settings adopted from the CX55 legacy signoff flow
# (Flow_CX55_SoC_CL_2026 bes_data/sta/scr/flow_com/pt_common_flow.tcl:78-105;
# only the legacy lines that are actually uncommented are enabled here). The
# legacy script assigns these with plain `set`; current PrimeTime releases
# expose them as application variables, so set_app_var is used instead.
proc flow::apply_pt_analysis_settings {} {
# Clock reconvergence pessimism reduction (legacy lines 78 and 85).
set_app_var timing_remove_clock_reconvergence_pessimism true
set_app_var timing_crpr_threshold_ps 1
# SMIC-recommended timing behavior (legacy lines 81-84).
set_app_var timing_save_pin_arrival_and_slack true
set_app_var timing_early_launch_at_borrowing_latches false
set_app_var timing_clock_gating_propagate_enable true
set_app_var timing_report_use_worst_parallel_cell_arc true
# Path-based analysis behavior (legacy lines 87 and 99).
set_app_var pba_exhaustive_endpoint_path_limit infinity
set_app_var pba_enable_path_based_physical_exclusivity true
# Constraint, parasitic, and linking behavior (legacy lines 90-92).
set_app_var timing_enable_max_capacitance_set_case_analysis true
set_app_var read_parasitics_load_locations true
set_app_var link_create_black_boxes false
# Signal-integrity and waveform-accurate delay calculation (legacy
# lines 94-102; the commented-out si_xtalk_composite_aggr_* candidates
# stay disabled). Coupling capacitors come from read_parasitics
# -keep_capacitive_coupling in flow::load_pt_scenario.
set_app_var si_enable_analysis true
set_app_var si_xtalk_double_switching_mode full_design
set_app_var si_xtalk_composite_aggr_mode statistical
set_app_var si_noise_composite_aggr_mode statistical
set_app_var si_xtalk_delay_analysis_mode all_path_edges
set_app_var delay_calc_waveform_analysis_mode full_design
set_app_var delay_calc_waveform_analysis_constraint_arcs_compatibility false
# Automatic mux clock exclusivity (legacy line 105). The site-specific
# legacy parasitics_log_file path (line 103) is not adopted; annotation
# evidence is covered by report_annotated_parasitics in main.tcl.
set_app_var timing_enable_auto_mux_clock_exclusivity true
}
proc flow::load_pt_scenario {tag scenario} {
variable scenarios
set top [flow::env TOP]
set run_root [flow::env RUN_ROOT]
flow::apply_pt_analysis_settings
set netlist [file join $run_root apr $tag output ${top}.${tag}.v]
set sdc [file join $run_root apr $tag output ${top}.${tag}.sdc]
set spef_root [file join $run_root extract $tag output]
@@ -39,6 +78,9 @@ proc flow::load_pt_scenario {tag scenario} {
}
set_operating_conditions -analysis_type on_chip_variation
read_sdc $sdc
# OCV clock derate per the scenario PVT corner (CX55 legacy
# bes_data/common/signoff_table.tcl policy).
flow::apply_signoff_derate $pvt
set spef [file join $spef_root [flow::spef_name $top $rc]]
if {![file isfile $spef]} {
flow::fail "SPEF is missing for scenario $scenario: $spef"
+66 -2
View File
@@ -31,6 +31,10 @@ proc run_sta {} {
if {![dict exists $::flow::scenarios $scenario]} {
flow::fail "unsupported STA scenario: $scenario"
}
set sdf_scenario [flow::env STA_SDF_SCENARIO func_TYP_TYP_25]
if {![dict exists $::flow::scenarios $sdf_scenario]} {
flow::fail "unsupported STA_SDF_SCENARIO: $sdf_scenario"
}
set base [flow::stage_dirs sta $tag]
set report_dir [file join $base reports]
set output_dir [file join $base output]
@@ -39,7 +43,36 @@ proc run_sta {} {
flow::require_commercial_clock_inventory
update_timing -full
if {![check_timing -include {no_clock unconstrained_endpoints}]} {
# Full CX55 legacy check_timing coverage (pt_common_flow.tcl:350); the
# redirect keeps the per-scenario report and preserves the pass/fail
# return value used as the verdict gate.
set check_timing_report [file join $report_dir ${scenario}.check_timing.rpt]
if {![redirect $check_timing_report {
check_timing -verbose -include {
clock_crossing
data_check_multiple_clock
data_check_no_clock
generated_clocks
generic
ideal_clocks
latch_fanout
latency_override
loops
ms_separation
no_clock
no_driving_cell
no_input_delay
partial_input_delay
pll_configuration
retain
signal_level
supply_net_voltage
unconstrained_endpoints
unexpandable_clocks
pulse_clock_non_pulse_clock_merge
pulse_clock_no_pulse_generator
}
}]} {
flow::fail "check_timing failed in scenario $scenario"
}
@@ -61,6 +94,22 @@ proc run_sta {} {
-max_paths 100000 -path_type full_clock_expanded \
-input_pins -nets -transition_time -capacitance -derate
}
# Exhaustive path-based recheck next to the graph-based reports above
# (legacy pt_common_flow.tcl:381-392 emits the PBA violation variants at
# signoff; there report_constraint -pba exhaustive is used, here
# report_timing -pba_mode exhaustive keeps the full path detail).
redirect [file join $report_dir ${scenario}.setup.pba.rpt] {
report_timing -delay_type max -slack_lesser_than 0.0 \
-max_paths 100000 -path_type full_clock_expanded \
-input_pins -nets -transition_time -capacitance -derate \
-pba_mode exhaustive
}
redirect [file join $report_dir ${scenario}.hold.pba.rpt] {
report_timing -delay_type min -slack_lesser_than 0.0 \
-max_paths 100000 -path_type full_clock_expanded \
-input_pins -nets -transition_time -capacitance -derate \
-pba_mode exhaustive
}
redirect $constraint_report { report_constraint -all_violators }
redirect [file join $report_dir ${scenario}.coverage.rpt] {
report_analysis_coverage -status_details untested
@@ -79,6 +128,21 @@ proc run_sta {} {
puts [get_object_name $library]
}
}
# Optional SAIF-annotated power analysis (legacy pt_common_flow.tcl:283-314
# activity annotation and :325-331 report_power). An empty STA_SAIF_FILE
# keeps the previous timing-only behavior.
set saif_file [string trim [flow::env STA_SAIF_FILE ""]]
if {$saif_file eq ""} {
puts "STA_SAIF_FILE is not set; skipping SAIF power analysis"
} else {
if {![file isfile $saif_file] || ![file readable $saif_file]} {
flow::fail "STA_SAIF_FILE is not readable: $saif_file"
}
read_saif $saif_file
redirect [file join $report_dir ${scenario}.power.rpt] {
report_power -nosplit
}
}
set drv_count [count_violations $constraint_report]
flow::write_text [file join $output_dir ${scenario}.summary.tsv] \
"$scenario\t$setup_count\t$hold_count\t$drv_count\n"
@@ -89,7 +153,7 @@ proc run_sta {} {
flow::fail "incomplete parasitic annotation in scenario $scenario"
}
if {$scenario eq "func_TYP_TYP_25"} {
if {$scenario eq $sdf_scenario} {
write_sdf -version 3.0 -context verilog \
[file join $output_dir ${top}.${tag}.sdf]
}
+85 -2
View File
@@ -11,6 +11,7 @@
set flow_root [file normalize [file join [file dirname [info script]] ../..]]
source [file join $flow_root tcl common common.tcl]
source [file join $flow_root tcl common constraints.tcl]
source [file join $flow_root tcl common corners.tcl]
source [file join $flow_root tcl syn reporting.tcl]
proc run_synthesis {} {
@@ -38,6 +39,31 @@ proc run_synthesis {} {
set_app_var verilogout_no_tri true
set_app_var compile_clock_gating_through_hierarchy true
# Tool setup migrated from the CX55 legacy flow
# (bes_data/syn/scr/flow_com/syn_common_flow.tcl:62-121); only the
# settings that were active (not commented out) there are kept.
set_app_var hdlin_vhdl_std 1987
set_app_var hdlin_vrlg_std 2001
set_app_var hdlin_sverilog_std 2012
set_app_var alib_library_analysis_path $work_dir
set_host_options -max_cores [flow::env SYN_COMPILE_CORES 16]
suppress_message {
VER-936 VER-129 VER-173 VHD-4 VER-314 VER-318 ELAB-924 ELAB-130
ELAB-909 VHD-7 ELAB-931 PWR-429 PWR-415 PWR-416 PWR-12 PWR-414
PWR-428 UID-401 VER-281 ELAB-910 TIM-250 PSYN-1002 DEFR-103
PSYN-950 DEFR-35 DCT-197 PSYN-123 ELAB-311 ELAB-388 TIM-134
OPT-806 OPT-805 DCT-055 DDB-74 UCN-1
}
set_app_var verilogout_show_unconnected_pins true
set_app_var hdlin_check_no_latch true
set_app_var timing_enable_multiple_clocks_per_reg true
set_app_var compile_seqmap_identify_shift_registers false
set_app_var verilogout_higher_designs_first true
set_app_var hdlin_shorten_long_module_name true
set_app_var hdlin_analyze_verbose_mode 2
history keep 200
set_app_var report_default_significant_digits 4
define_design_lib WORK -path [file join $work_dir WORK]
set analyze_command [list analyze -format sverilog -work WORK]
if {[llength [dict get $rtl defines]] > 0} {
@@ -63,7 +89,49 @@ proc run_synthesis {} {
}
}
# Legacy syn_common_flow.tcl:247-250: macro/IO cells that are already
# mapped must survive optimization untouched.
set mapped [get_cells -quiet -hierarchical \
-filter "is_mapped == true && is_hierarchical == false && ref_name !~ *logic_*"]
if {[sizeof_collection $mapped] > 0} {
set_dont_touch $mapped
}
# Legacy syn_common_flow.tcl:273.
set_max_area 0
# Legacy syn_common_flow.tcl:281-292: latch-based clock-gating style and
# exclusion of the input synchronizer registers.
set_clock_gating_style -sequential_cell latch \
-control_point before -control_signal scan_enable \
-minimum_bitwidth 4 -observation_point false -max_fanout 32
set sync_patterns [flow::env SYN_CLOCK_GATING_EXCLUDE_PATTERNS \
"{*/synch_toggle} {*/synch_preset} {*/synch_enable} {*/synch_clear} {*/next_state}"]
set sync_terms {}
foreach pattern $sync_patterns {
lappend sync_terms "full_name =~ $pattern"
}
set sync_endpoints ""
foreach_in_collection input [all_inputs] {
set fanout [all_fanout -from $input -flat -endpoints_only]
append_to_collection -unique sync_endpoints \
[filter_collection $fanout [join $sync_terms " || "]]
}
set sync_cells [get_cells -quiet -of_objects $sync_endpoints]
if {[sizeof_collection $sync_cells] > 0} {
set_clock_gating_objects -exclude $sync_cells
}
# Legacy syn_common_flow.tcl:300.
set_dynamic_optimization true
flow::apply_constraints
# Synthesis links the TYP library set (flow::synthesis_library_files).
# The legacy CX55 DC run at TYP applied no timing derate at all
# (bes_data/syn/scr/flow_com/syn_common_flow.tcl never sources
# signoff_table.tcl; bes_data/syn/log/asic_top.log has no
# set_timing_derate), so no derate is applied here either; the OCV derate
# policy lives in PrimeTime signoff (flow::apply_signoff_derate).
redirect [file join $report_dir check_design.pre.rpt] { check_design }
redirect [file join $report_dir check_timing.pre.rpt] {
check_timing
@@ -77,10 +145,25 @@ proc run_synthesis {} {
set_fix_multiple_port_nets -all -buffer_constants
set_boundary_optimization [current_design] true
# Legacy syn_common_flow.tcl:500-502: separate I/O path groups (the
# legacy script names the -to [all_outputs] group "out2reg").
group_path -weight 0.1 -name in2reg -from [all_inputs]
group_path -weight 0.1 -name out2reg -to [all_outputs]
group_path -weight 0.1 -name in2out -from [all_inputs] -to [all_outputs]
# Legacy syn_common_flow.tcl:388: the legacy DC run was not in
# topographical mode, and neither is this one (DC_SHELL := dc_shell).
set_wire_load_mode top
# Legacy syn_common_flow.tcl:517 with critical_range default 0.2 (:24).
set_critical_range [flow::env SYN_CRITICAL_RANGE_NS 0.2] [current_design]
set svf [file join $output_dir ${top}.svf]
set_svf $svf
compile_ultra -no_autoungroup -gate_clock
compile_ultra -incremental -no_autoungroup -gate_clock
# Legacy syn_common_flow.tcl:31-32,533: high-effort TNS optimization and
# compile_ultra without sequential output inversion.
set_app_var compile_timing_high_effort_tns true
compile_ultra -no_autoungroup -no_seq_output_inversion -gate_clock
compile_ultra -incremental -no_autoungroup -no_seq_output_inversion \
-gate_clock
set_svf -off
change_names -rules verilog -hierarchy
+41 -6
View File
@@ -52,6 +52,15 @@ proc flow::vt_cells {suffix} {
-filter "is_hierarchical == false && ref_name =~ *$suffix"]
}
proc flow::vt_suffix_map {} {
set configured [flow::env SYN_VT_SUFFIX_MAP "HVT H7H LVT H7L SVT H7R"]
if {[llength $configured] == 0 || [llength $configured] % 2 != 0} {
flow::fail "SYN_VT_SUFFIX_MAP must be a flat group/suffix list"
}
array set map $configured
return [array get map]
}
proc flow::write_synthesis_summary {path drv_count} {
variable io_qualified
set setup [flow::timing_metrics "" max]
@@ -69,10 +78,10 @@ proc flow::write_synthesis_summary {path drv_count} {
set memories [get_cells -quiet -hierarchical \
-filter "is_hierarchical == false && is_memory_cell == true"]
array set vt_suffix {HVT H7H LVT H7L SVT H7R}
array set vt_suffix [flow::vt_suffix_map]
set std_area 0.0
set std_count 0
foreach group {HVT LVT SVT} {
foreach group [lsort [array names vt_suffix]] {
set cells [flow::vt_cells $vt_suffix($group)]
set vt_count($group) [sizeof_collection $cells]
set vt_area($group) [flow::collection_area $cells]
@@ -80,6 +89,18 @@ proc flow::write_synthesis_summary {path drv_count} {
set std_area [expr {$std_area + $vt_area($group)}]
}
# Area split adapted from the legacy syn_statistics.tcl:29-45 table. The
# new netlists contain only standard cells and macros, so the legacy
# mem/ipio/sub_harden buckets collapse into a single macro residual.
set total_leaf_area [flow::collection_area $leaf]
set macro_area [expr {$total_leaf_area - $std_area}]
set std_percent 0.0
set macro_percent 0.0
if {$total_leaf_area > 0.0} {
set std_percent [expr {100.0 * $std_area / $total_leaf_area}]
set macro_percent [expr {100.0 * $macro_area / $total_leaf_area}]
}
set handle [open $path w]
puts $handle "metric\tvalue"
puts $handle "flow_pass\tyes"
@@ -100,8 +121,11 @@ proc flow::write_synthesis_summary {path drv_count} {
puts $handle "memory_macros\t[sizeof_collection $memories]"
puts $handle "standard_cells\t$std_count"
puts $handle "standard_cell_area_um2\t[format %.4f $std_area]"
puts $handle "total_leaf_area_um2\t[format %.4f [flow::collection_area $leaf]]"
foreach group {HVT LVT SVT} {
puts $handle "macro_area_um2\t[format %.4f $macro_area]"
puts $handle "total_leaf_area_um2\t[format %.4f $total_leaf_area]"
puts $handle "standard_cell_area_percent\t[format %.2f $std_percent]"
puts $handle "macro_area_percent\t[format %.2f $macro_percent]"
foreach group [lsort [array names vt_suffix]] {
set percent 0.0
if {$std_area > 0.0} {
set percent [expr {100.0 * $vt_area($group) / $std_area}]
@@ -113,11 +137,22 @@ proc flow::write_synthesis_summary {path drv_count} {
close $handle
}
proc flow::vt_library_map {} {
set configured [flow::env SYN_VT_LIBRARY_MAP "HVT *H7CH* LVT *H7CL* SVT *H7CR*"]
if {[llength $configured] == 0 || [llength $configured] % 2 != 0} {
flow::fail "SYN_VT_LIBRARY_MAP must be a flat group/pattern list"
}
array set map $configured
return [array get map]
}
proc flow::configure_synthesis_libraries {} {
foreach {group pattern} {HVT *H7CH* LVT *H7CL* SVT *H7CR*} {
array set vt_library [flow::vt_library_map]
foreach group [array names vt_library] {
set pattern $vt_library($group)
set libraries [get_libs -quiet $pattern]
if {[sizeof_collection $libraries] == 0} {
flow::fail "missing $group H7C library in the TYP link set"
flow::fail "missing $group standard-cell library in the TYP link set"
}
set_attribute $libraries default_threshold_voltage_group $group -type string
}
+39 -19
View File
@@ -102,9 +102,43 @@ def main() -> int:
) as temp:
staging = Path(temp) / "package"
staging.mkdir()
timing_contract = Path(temp) / ("tiny_core.sdc" if soc == "TINY" else "commercial_timing_contract.tcl")
timing_generator = (root / "physical/smoke/sta/opensta/generate_sdc.py" if soc == "TINY"
else root / "physical/commercial/scripts/generate_timing_contract.py")
commercial_contract = Path(temp) / "commercial_timing_contract.tcl"
subprocess.run(
[
"python3",
str(root / "physical/commercial/scripts/generate_timing_contract.py"),
"--soc",
soc,
"--domains",
str(product_root / "integration/clock_reset_domains.json"),
"--pin-map",
str(product_root / "pin_map/pin_map.json"),
"--output",
str(commercial_contract),
],
cwd=root,
check=True,
)
metadata_files = [
f"contracts/{commercial_contract.name}={commercial_contract}",
]
if soc == "TINY":
tiny_sdc = Path(temp) / "tiny_core.sdc"
subprocess.run(
[
"python3",
str(root / "physical/smoke/sta/opensta/generate_sdc.py"),
"--domains",
str(product_root / "integration/clock_reset_domains.json"),
"--pin-map",
str(product_root / "pin_map/pin_map.json"),
"--output",
str(tiny_sdc),
],
cwd=root,
check=True,
)
metadata_files.append(f"contracts/{tiny_sdc.name}={tiny_sdc}")
if config.get("MINI_MODE", "PRODUCT") == "MPW":
dynamic_core_filelist = (
args.variant_root
@@ -125,20 +159,6 @@ def main() -> int:
dynamic_ip_filelist = (
args.variant_root / "generated/user_extensions/legacy_ip.fl"
)
subprocess.run(
[
"python3",
str(timing_generator),
"--domains",
str(product_root / "integration/clock_reset_domains.json"),
"--pin-map",
str(product_root / "pin_map/pin_map.json"),
"--output",
str(timing_contract),
],
cwd=root,
check=True,
)
command = [
"python3",
str(root / "physical/smoke/syn/tools/export_soc_sources.py"),
@@ -171,9 +191,9 @@ def main() -> int:
str(args.variant_root / "generated/pin_map/pin_map.fl"),
"--archinfo-incdir",
str(args.variant_root / "generated/archinfo"),
"--metadata-file",
f"contracts/{timing_contract.name}={timing_contract}",
]
for metadata in metadata_files:
command.extend(("--metadata-file", metadata))
if soc == "TINY":
if config.get("SYNTH") == "YOSYS":
recipe = config.get("SYNTH_RECIPE", "balanced")
+269 -4
View File
@@ -57,13 +57,60 @@ def test_translate_eco_accepts_only_reviewed_operations(tmp_path: Path) -> None:
translator.translate([str(unsupported)])
def test_translate_eco_merges_scenarios_and_drops_duplicates(tmp_path: Path) -> None:
translator = load_script("translate_eco")
first = tmp_path / "func_TYP_TYP_25.icc2.tcl"
first.write_text(
"current_instance {u_core}\n"
"size_cell {u_reg} {DFFQX2H7R}\n"
"insert_buffer [get_pins {u_src/Q}] BUFX2H7R "
"-new_net_names {eco_net} -new_cell_names {eco_buf}\n",
encoding="utf-8",
)
second = tmp_path / "func_MIN_Cworst_m40.icc2.tcl"
second.write_text(
"current_instance {u_core}\n"
"size_cell {u_reg} {DFFQX2H7R}\n"
"size_cell {u_hold} {DFFQX1H7R}\n",
encoding="utf-8",
)
assert translator.translate([str(first), str(second)]) == [
"ecoChangeCell -inst {u_core/u_reg} -cell {DFFQX2H7R}",
"ecoAddRepeater -term {u_core/u_src/Q} -cell {BUFX2H7R} "
"-newNetName {eco_net} -name {eco_buf}",
"ecoChangeCell -inst {u_core/u_hold} -cell {DFFQX1H7R}",
]
def test_translate_eco_supports_removal_and_placed_insert(tmp_path: Path) -> None:
translator = load_script("translate_eco")
source = tmp_path / "changes.icc2.tcl"
source.write_text(
"current_instance {u_core}\n"
"remove_buffer [get_cells {u_buf}]\n"
"insert_buffer [get_pins {u_src/Q}] BUFX2H7R -new_net_names {eco_net} "
"-new_cell_names {eco_buf} -location {10.5 20.25}\n",
encoding="utf-8",
)
assert translator.translate([str(source)]) == [
"ecoDeleteRepeater -inst {u_core/u_buf}",
"ecoAddRepeater -term {u_core/u_src/Q} -cell {BUFX2H7R} "
"-newNetName {eco_net} -name {eco_buf} -loc {10.5 20.25}",
]
ambiguous = tmp_path / "ambiguous.icc2.tcl"
ambiguous.write_text("remove_buffer [get_cells {u_a u_b}]\n", encoding="utf-8")
with pytest.raises(ValueError, match="unsupported PrimeTime ECO command"):
translator.translate([str(ambiguous)])
def test_spef_checker_requires_every_named_corner(tmp_path: Path) -> None:
checker = load_script("check_outputs")
for corner in checker.SPEF_CORNERS:
for corner in checker.spef_corners():
(tmp_path / f"retrosoc_asic.{corner}.spef.gz").write_bytes(b"spef")
assert checker.apply_check("spef", str(tmp_path), 0, "retrosoc_asic") is None
(tmp_path / "retrosoc_asic.TYP_25.spef.gz").unlink()
assert "missing ICS55 SPEF corners" in checker.apply_check(
assert "missing SPEF corners" in checker.apply_check(
"spef", str(tmp_path), 0, "retrosoc_asic"
)
@@ -116,11 +163,16 @@ def test_prepare_input_rejects_archive_escape(tmp_path: Path) -> None:
def test_prepare_input_refreshes_archived_filelist(tmp_path: Path) -> None:
archive_path = tmp_path / "rtl.tar"
payload = b"rtl/top.sv\n"
contract = b"# contract\n"
with tarfile.open(archive_path, "w") as archive:
member = tarfile.TarInfo("rtl/filelist.fl")
member.size = len(payload)
member.mtime = 1
archive.addfile(member, io.BytesIO(payload))
member = tarfile.TarInfo("rtl/contracts/commercial_timing_contract.tcl")
member.size = len(contract)
member.mtime = 1
archive.addfile(member, io.BytesIO(contract))
output = tmp_path / "output"
manifest = tmp_path / "manifest.json"
started = archive_path.stat().st_mtime
@@ -303,6 +355,8 @@ def test_commercial_timing_contract_covers_canonical_domains(tmp_path: Path) ->
[
sys.executable,
str(FLOW / "scripts/generate_timing_contract.py"),
"--soc",
"MINI",
"--domains",
str(ROOT / "rtl/mini/integration/clock_reset_domains.json"),
"--pin-map",
@@ -336,6 +390,67 @@ def test_commercial_timing_contract_covers_canonical_domains(tmp_path: Path) ->
assert "observation {s_sys_clk}" in output.read_text(encoding="utf-8")
def test_commercial_timing_contract_supports_tiny(tmp_path: Path) -> None:
output = tmp_path / "commercial_timing_contract.tcl"
subprocess.run(
[
sys.executable,
str(FLOW / "scripts/generate_timing_contract.py"),
"--soc",
"TINY",
"--domains",
str(ROOT / "rtl/tiny/integration/clock_reset_domains.json"),
"--pin-map",
str(ROOT / "rtl/tiny/pin_map/pin_map.json"),
"--output",
str(output),
],
check=True,
)
script = (
"namespace eval flow {}\n"
f"source {{{output}}}\n"
"puts [join [lsort [dict keys $flow::canonical_clock_domains]] ,]\n"
"puts [join $flow::canonical_reset_ports ,]\n"
)
result = subprocess.run(
["tclsh"],
input=script,
text=True,
capture_output=True,
check=True,
)
assert result.stdout.splitlines() == [
"jtag,system",
"ext_rst_n_i_pad,jtag_trst_n_i_pad",
]
assert "observation {u_clock_buffer/clk_o}" in output.read_text(
encoding="utf-8"
)
def test_commercial_timing_contract_rejects_domain_drift(tmp_path: Path) -> None:
result = subprocess.run(
[
sys.executable,
str(FLOW / "scripts/generate_timing_contract.py"),
"--soc",
"TINY",
"--domains",
str(ROOT / "rtl/mini/integration/clock_reset_domains.json"),
"--pin-map",
str(ROOT / "rtl/mini/pin_map/pin_map.json"),
"--output",
str(tmp_path / "drift.tcl"),
],
text=True,
capture_output=True,
check=False,
)
assert result.returncode != 0
assert "differ from the canonical set" in result.stderr
def test_commercial_constraints_do_not_apply_global_io_delays() -> None:
constraints = "\n".join(
path.read_text(encoding="utf-8")
@@ -352,7 +467,7 @@ def test_commercial_constraints_do_not_apply_global_io_delays() -> None:
def test_dc_uses_typ_link_set_and_svt_lvt_target_subset() -> None:
synthesis = (FLOW / "tcl/syn/main.tcl").read_text(encoding="utf-8")
common = (FLOW / "tcl/common/common.tcl").read_text(encoding="utf-8")
example = (FLOW / "config/ics55.example.mk").read_text(encoding="utf-8")
example = (FLOW / "config/ics55-mini.example.mk").read_text(encoding="utf-8")
assert "flow::synthesis_library_files" in synthesis
assert "flow::all_library_files" not in synthesis
assert "return [flow::library_files TYP]" in common
@@ -396,8 +511,10 @@ def test_internal_qor_doctor_does_not_require_backend_collateral(
archive = touch("rtl.tar.gz")
environment = {
"PATH": os.environ["PATH"],
"SOC": "MINI",
"TOP": "retrosoc_asic",
"TECHNOLOGY": "ICS55",
"PRODUCT_PLL_MODE": "qualified",
"RTL_ARCHIVE": archive,
"LSF_MODE": "batch",
"LSF_SYN_ARGS": "-q synth",
@@ -468,10 +585,158 @@ def test_ics55_pll_wrapper(tmp_path: Path) -> None:
def test_local_production_configuration_is_ignored() -> None:
local = FLOW / "local/ics55-production.mk"
for name in ("ics55-production.mk", "ics55-mini.mk", "ics55-tiny.mk"):
local = FLOW / "local" / name
result = subprocess.run(
["git", "check-ignore", str(local.relative_to(ROOT))],
cwd=ROOT,
check=False,
)
assert result.returncode == 0
CONSTRAINT_SMOKE_TCL = r"""
set soc [lindex $argv 0]
set ::env(SOC) $soc
set ::env(TOP) [expr {$soc eq "TINY" ? "retrosoc_tiny_asic" : "retrosoc_asic"}]
set ::env(RUN_ROOT) [lindex $argv 1]
set ::env(CLOCK_SETUP_UNCERTAINTY_NS) 0.2
set ::env(CLOCK_HOLD_UNCERTAINTY_NS) 0.1
set ::env(CLOCK_TRANSITION_NS) 0.1
set ::env(IO_TIMING_QUALIFIED) NO
set ::env(MAX_TRANSITION_NS) 0.5
set ::env(MAX_FANOUT) 32
if {$soc eq "MINI"} { set ::env(PLL_OUTPUT_PERIOD_NS) 13.888888889 }
set ::created_clocks_named {}
set ::generated_clocks {}
set ::clock_groups {}
set ::false_paths 0
proc get_ports {args} { return [lindex $args end] }
proc get_pins {args} { return [list pin_[string map {/ _} [lindex $args end]]] }
proc get_nets {args} { return [list net_[lindex $args end]] }
proc filter_collection {args} { return [list driver] }
proc sizeof_collection {c} { return [llength $c] }
proc get_clocks {args} { return [lindex $args end] }
proc create_clock {args} {
lappend ::created_clocks_named [lindex $args [expr {[lsearch $args -name] + 1}]]
}
proc create_generated_clock {args} {
lappend ::generated_clocks [lindex $args [expr {[lsearch $args -name] + 1}]]
}
proc set_clock_groups {args} { lappend ::clock_groups $args }
proc set_clock_uncertainty {args} {}
proc set_clock_transition {args} {}
proc set_false_path {args} { incr ::false_paths }
proc all_inputs {} { return {} }
proc all_outputs {} { return {} }
proc current_design {args} { return {} }
proc set_max_transition {args} {}
proc set_max_fanout {args} {}
source [file join $::env(FLOW_ROOT) tcl common common.tcl]
source [file join $::env(FLOW_ROOT) tcl common constraints.tcl]
flow::apply_constraints
puts "masters=$::created_clocks_named"
puts "generated=$::generated_clocks"
puts "groups=[llength $::clock_groups]"
puts "false_paths=$::false_paths"
flow::require_commercial_clock_inventory
puts "inventory-ok"
"""
CONSTRAINT_SMOKE_CASES = {
"MINI": {
"domains": "rtl/mini/integration/clock_reset_domains.json",
"pin_map": "rtl/mini/pin_map/pin_map.json",
"masters": (
"clk_aon clk_hp clk_memory clk_audio clk_jtag clk_dvp "
"clk_usb2_ulpi clk_pll"
),
"generated": "clk_lp_ext clk_lp_pll clk_pclk_ext clk_pclk_pll",
"groups": "2",
},
"TINY": {
"domains": "rtl/tiny/integration/clock_reset_domains.json",
"pin_map": "rtl/tiny/pin_map/pin_map.json",
"masters": "clk_system clk_jtag",
"generated": "",
"groups": "1",
},
}
@pytest.mark.parametrize("soc", sorted(CONSTRAINT_SMOKE_CASES))
def test_constraint_stack_models_product_clocks(tmp_path: Path, soc: str) -> None:
case = CONSTRAINT_SMOKE_CASES[soc]
run_root = tmp_path / soc.lower()
contract = run_root / "input/rtl/contracts/commercial_timing_contract.tcl"
contract.parent.mkdir(parents=True)
subprocess.run(
[
sys.executable,
str(FLOW / "scripts/generate_timing_contract.py"),
"--soc",
soc,
"--domains",
str(ROOT / case["domains"]),
"--pin-map",
str(ROOT / case["pin_map"]),
"--output",
str(contract),
],
check=True,
)
environment = dict(os.environ)
environment["FLOW_ROOT"] = str(FLOW)
script = CONSTRAINT_SMOKE_TCL.replace(
"set soc [lindex $argv 0]", f"set soc {soc}"
).replace(
"set ::env(RUN_ROOT) [lindex $argv 1]", f"set ::env(RUN_ROOT) {run_root}"
)
result = subprocess.run(
["tclsh"],
input=script,
text=True,
capture_output=True,
check=False,
env=environment,
)
assert result.returncode == 0, result.stderr
lines = dict(
line.split("=", 1)
for line in result.stdout.strip().splitlines()
if "=" in line
)
assert lines["masters"] == case["masters"]
assert lines["generated"].strip("-") == case["generated"]
assert lines["groups"] == case["groups"]
assert "inventory-ok" in result.stdout
def test_commercial_make_graph_covers_both_products(tmp_path: Path) -> None:
stub = tmp_path / "local.mk"
stub.write_text("", encoding="utf-8")
for soc in ("MINI", "TINY"):
result = subprocess.run(
[
"make",
"-C",
str(FLOW),
"-n",
"signoff",
f"SOC={soc}",
f"LOCAL_CONFIG={stub}",
"RUN_ID=pytest",
],
text=True,
capture_output=True,
check=False,
)
assert result.returncode == 0, result.stderr
assert f"build/commercial/ics55/{soc.lower()}/pytest" in result.stdout
for stage in ("syn", "apr-route", "extract", "sta", "pv-lvs", "pv-macro-lvs"):
assert f"--stage {stage} " in result.stdout or f"--stage {stage}\n" in (
result.stdout + "\n"
)
+2
View File
@@ -282,6 +282,8 @@ def test_package_forwards_manifest_jtag_idcode() -> None:
assert f'"{option}"' in package_source
assert "generate_timing_contract.py" in package_source
assert "commercial_timing_contract.tcl" in package_source
assert '"--soc"' in package_source
assert "tiny_core.sdc" in package_source
def test_source_export_writes_tar_without_staging_rtl_tree(tmp_path: Path) -> None: