mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
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:
32 files changed
+2240
-408
No files matched your search
@@ -3,15 +3,22 @@
|
|||||||
Read the [commercial guide](../../../../physical/commercial/README.md), its
|
Read the [commercial guide](../../../../physical/commercial/README.md), its
|
||||||
[Makefile](../../../../physical/commercial/Makefile), and the selected local
|
[Makefile](../../../../physical/commercial/Makefile), and the selected local
|
||||||
configuration without copying site-sensitive values into tracked material.
|
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
|
## Inputs and execution boundary
|
||||||
|
|
||||||
The development zone creates the production RTL package from the committed
|
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
|
product profile — `configs/cluster/ics55.mk` with the guide's PLL/SRAM
|
||||||
zone consumes that archive through a real, ignored `LOCAL_CONFIG` and `RUN_ID`.
|
settings for Mini, `configs/ci/ics55-tiny.mk` for Tiny. The EDA zone consumes
|
||||||
Validate the source/configuration/clock inventory and archive identity before
|
that archive through a real, ignored `LOCAL_CONFIG` (default
|
||||||
using it. An inspection request must not generate the package or submit jobs.
|
`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
|
For authorized execution, use the repository Make targets and site-selected
|
||||||
LSF mechanism. Never invoke licensed tools directly on the development host
|
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
|
Its top-level I/O exclusions must be visible in the summary; it is not input
|
||||||
qualification for Innovus/PrimeTime implementation.
|
qualification for Innovus/PrimeTime implementation.
|
||||||
- Full implementation uses the strict `doctor` and the selected stages up to
|
- Full implementation uses the strict `doctor` and the selected stages up to
|
||||||
`signoff`. Qualified I/O budgets, local pad timing hook, library/corner and
|
`signoff`. Qualified I/O budgets, the product pad timing hook when required
|
||||||
extraction coverage, and zero-threshold verification rules remain mandatory.
|
(Mini), library/corner and extraction coverage, and zero-threshold
|
||||||
Never set `IO_TIMING_QUALIFIED=YES` without the supporting reviewed budgets.
|
verification rules remain mandatory. Never set `IO_TIMING_QUALIFIED=YES`
|
||||||
|
without the supporting reviewed budgets.
|
||||||
|
|
||||||
Follow the current stage graph through synthesis/equivalence, APR,
|
Follow the current stage graph through synthesis/equivalence, APR,
|
||||||
post-route equivalence, extraction/STA/ECO, and physical verification only as
|
post-route equivalence, extraction/STA/ECO, and physical verification
|
||||||
far as requested. The guide and executable targets, not a skill-maintained
|
(including the optional macro-level LVS/ERC stage) only as far as requested.
|
||||||
copy of the graph, define dependencies and required evidence.
|
The guide and executable targets, not a skill-maintained copy of the graph,
|
||||||
|
define dependencies and required evidence.
|
||||||
|
|
||||||
## Resume and handoff
|
## Resume and handoff
|
||||||
|
|
||||||
Runs live under `build/commercial/ics55/<run-id>`. Before resuming, compare
|
Runs live under `build/commercial/ics55/<soc>/<run-id>`. Before resuming,
|
||||||
original input/configuration hashes and the source identity, then check each
|
compare original input/configuration hashes and the source identity, then
|
||||||
completed stage's result, required outputs, and verdict marker against its
|
check each completed stage's result, required outputs, and verdict marker
|
||||||
stamp. Stamps alone are insufficient, especially after input or tool changes.
|
against its stamp. Stamps alone are insufficient, especially after input or
|
||||||
|
tool changes.
|
||||||
|
|
||||||
For unchanged valid inputs, repeating the requested endpoint with the same
|
For unchanged valid inputs, repeating the requested endpoint with the same
|
||||||
local configuration and run ID uses the existing first-missing-stage behavior.
|
local configuration and run ID uses the existing first-missing-stage behavior.
|
||||||
|
|||||||
@@ -257,3 +257,13 @@ for its current reporting information.
|
|||||||
Thanks to everyone contributing to retroSoC.
|
Thanks to everyone contributing to retroSoC.
|
||||||
|
|
||||||
[](https://github.com/retroSoC/retroSoC/graphs/contributors)
|
[](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
@@ -12,9 +12,12 @@ drivers.
|
|||||||
and `librelane/tiny/` (Tiny full-chip pad ring).
|
and `librelane/tiny/` (Tiny full-chip pad ring).
|
||||||
- `ecc/` contains the ICS55 padless-core adapter and the official ECC `latest`
|
- `ecc/` contains the ICS55 padless-core adapter and the official ECC `latest`
|
||||||
installer entrypoint. Installation does not qualify physical-flow compatibility.
|
installer entrypoint. Installation does not qualify physical-flow compatibility.
|
||||||
- `commercial/` contains licensed-tool orchestration and flow logic. PDKs,
|
- `commercial/` contains licensed-tool orchestration and flow logic for the
|
||||||
foundry decks, commercial libraries, site configuration, and results remain
|
ICS55 implementation flow, parameterized per product line (`SOC=MINI` and
|
||||||
outside Git.
|
`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
|
- `sdf/`, when supplied by an implementation flow, contains post-layout
|
||||||
netlist and SDF collateral consumed by post-layout simulation.
|
netlist and SDF collateral consumed by post-layout simulation.
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,46 @@ SHELL := /bin/sh
|
|||||||
FLOW_ROOT := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
|
FLOW_ROOT := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
|
||||||
ROOT_PATH := $(abspath $(FLOW_ROOT)/../..)
|
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)
|
-include $(LOCAL_CONFIG)
|
||||||
|
|
||||||
TOP ?= retrosoc_asic
|
|
||||||
TECHNOLOGY ?= ICS55
|
|
||||||
FLOW_PYTHON ?= python
|
FLOW_PYTHON ?= python
|
||||||
TCLSH ?= tclsh
|
TCLSH ?= tclsh
|
||||||
DOCTOR_ARGS ?=
|
DOCTOR_ARGS ?=
|
||||||
RUN_ID ?= current
|
RUN_ID ?= current
|
||||||
BUILD_ROOT ?= $(ROOT_PATH)/build
|
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
|
INPUT_DIR := $(RUN_ROOT)/input
|
||||||
META_DIR := $(RUN_ROOT)/meta
|
META_DIR := $(RUN_ROOT)/meta
|
||||||
@@ -47,10 +76,11 @@ ECO_STAMP := $(STAMP_DIR)/eco.stamp
|
|||||||
APR_ECO_STAMP := $(STAMP_DIR)/apr-eco.stamp
|
APR_ECO_STAMP := $(STAMP_DIR)/apr-eco.stamp
|
||||||
REEXTRACT_STAMP := $(STAMP_DIR)/reextract.stamp
|
REEXTRACT_STAMP := $(STAMP_DIR)/reextract.stamp
|
||||||
RESTA_STAMP := $(STAMP_DIR)/resta.stamp
|
RESTA_STAMP := $(STAMP_DIR)/resta.stamp
|
||||||
PV_MERGE_STAMP := $(STAMP_DIR)/pv-merge.stamp
|
PV_MERGE_STAMP := $(STAMP_DIR)/pv-merge.stamp
|
||||||
PV_DRC_STAMP := $(STAMP_DIR)/pv-drc.stamp
|
PV_DRC_STAMP := $(STAMP_DIR)/pv-drc.stamp
|
||||||
PV_ANT_STAMP := $(STAMP_DIR)/pv-antenna.stamp
|
PV_ANT_STAMP := $(STAMP_DIR)/pv-antenna.stamp
|
||||||
PV_LVS_STAMP := $(STAMP_DIR)/pv-lvs.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
|
RUN_STAGE := $(FLOW_PYTHON) $(FLOW_ROOT)/scripts/run_stage.py
|
||||||
SUBMIT_JOB := $(FLOW_PYTHON) $(FLOW_ROOT)/scripts/submit_job.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 \
|
.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 \
|
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 \
|
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:
|
help:
|
||||||
@printf '%s\n' \
|
@printf '%s\n' \
|
||||||
@@ -83,25 +113,29 @@ help:
|
|||||||
' apr-eco apply ECO changes and incrementally route' \
|
' apr-eco apply ECO changes and incrementally route' \
|
||||||
' reextract extract the ECO design' \
|
' reextract extract the ECO design' \
|
||||||
' resta sign off 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' \
|
' signoff run the complete strict stage graph' \
|
||||||
' clean-stage remove STAGE and all downstream stage results' \
|
' 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:
|
list-stages:
|
||||||
@printf '%s\n' input syn fm-rtl2syn apr-initialize apr-floorplan \
|
@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-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:
|
check-config:
|
||||||
@test -f '$(LOCAL_CONFIG)' || { \
|
@test -f '$(LOCAL_CONFIG)' || { \
|
||||||
echo "missing ignored local configuration: $(LOCAL_CONFIG)" >&2; \
|
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; \
|
exit 2; \
|
||||||
}
|
}
|
||||||
@test '$(TECHNOLOGY)' = ICS55
|
|
||||||
@test '$(TOP)' = retrosoc_asic
|
|
||||||
|
|
||||||
doctor: check-config
|
doctor: check-config
|
||||||
@mkdir -p $(META_DIR)
|
@mkdir -p $(META_DIR)
|
||||||
@@ -394,8 +428,20 @@ $(PV_LVS_STAMP): $(PV_ANT_STAMP)
|
|||||||
|
|
||||||
pv-lvs: $(PV_LVS_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
|
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)'
|
@printf 'commercial signoff passed: %s\n' '$(RUN_ROOT)'
|
||||||
|
|
||||||
clean-stage:
|
clean-stage:
|
||||||
|
|||||||
+165
-55
@@ -1,15 +1,44 @@
|
|||||||
# Commercial Physical Design
|
# Commercial Physical Design
|
||||||
|
|
||||||
This directory owns the self-developed integration for the licensed ICS55
|
This directory owns the self-developed integration for the licensed ICS55
|
||||||
implementation flow. It does not contain the PDK, foundry rule decks, standard
|
implementation flow. It contains only flow logic and configuration policy: the
|
||||||
cell or macro libraries, commercial tool installations, or generated results.
|
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
|
## Execution boundary
|
||||||
|
|
||||||
The development and EDA zones share the repository and build directories
|
The development and EDA zones share the repository and build directories
|
||||||
through a site-mounted shared filesystem, but have different responsibilities:
|
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
|
```sh
|
||||||
make CONFIG=configs/cluster/ics55.mk \
|
make CONFIG=configs/cluster/ics55.mk \
|
||||||
@@ -17,30 +46,41 @@ through a site-mounted shared filesystem, but have different responsibilities:
|
|||||||
commercial-package
|
commercial-package
|
||||||
```
|
```
|
||||||
|
|
||||||
2. In the EDA zone, create the ignored local configuration from
|
For Tiny (the committed `configs/ci/ics55-tiny.mk` profile already carries
|
||||||
`config/ics55.example.mk`, point `RTL_ARCHIVE` at the generated
|
the required PLL/SRAM settings):
|
||||||
`retrosoc_mini_sources.tar.gz`, and choose one of two entry points.
|
|
||||||
For synthesis with internal timing only:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make -C physical/commercial \
|
make CONFIG=configs/ci/ics55-tiny.mk commercial-package
|
||||||
LOCAL_CONFIG=local/ics55-production.mk doctor-syn
|
```
|
||||||
make -C physical/commercial \
|
|
||||||
LOCAL_CONFIG=local/ics55-production.mk RUN_ID=<run-id> syn
|
Both packages carry `rtl/filelist.fl` and the generated
|
||||||
make -C physical/commercial \
|
`rtl/contracts/commercial_timing_contract.tcl`; Tiny additionally carries
|
||||||
LOCAL_CONFIG=local/ics55-production.mk RUN_ID=<run-id> fm-rtl2syn
|
`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
|
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
|
```sh
|
||||||
make -C physical/commercial \
|
make -C physical/commercial SOC=MINI RUN_ID=<run-id> doctor
|
||||||
LOCAL_CONFIG=local/ics55-production.mk RUN_ID=<run-id> doctor
|
make -C physical/commercial SOC=MINI RUN_ID=<run-id> signoff
|
||||||
make -C physical/commercial \
|
|
||||||
LOCAL_CONFIG=local/ics55-production.mk 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
|
Commercial tools are never executed directly by this Makefile. Every tool
|
||||||
stage is submitted as an independent blocking LSF job. `LSF_MODE=batch` uses
|
stage is submitted as an independent blocking LSF job. `LSF_MODE=batch` uses
|
||||||
`bsub -K`; sites that expose only an interactive queue use
|
`bsub -K`; sites that expose only an interactive queue use
|
||||||
@@ -61,9 +101,10 @@ site-specific values, including:
|
|||||||
- tool commands and LSF queues/resources;
|
- tool commands and LSF queues/resources;
|
||||||
- Liberty/DB, LEF, GDS, CDL, and RC technology files;
|
- Liberty/DB, LEF, GDS, CDL, and RC technology files;
|
||||||
- stream maps and Calibre DRC, antenna, and LVS decks;
|
- 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;
|
- 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
|
Do not add absolute PDK/library paths to tracked Make, Tcl, Python, shell, or
|
||||||
documentation files. Run `python3 scripts/audit_boundary.py --root ../..`
|
documentation files. Run `python3 scripts/audit_boundary.py --root ../..`
|
||||||
@@ -82,20 +123,89 @@ input
|
|||||||
-> fm-syn2pr
|
-> fm-syn2pr
|
||||||
-> extract -> sta
|
-> extract -> sta
|
||||||
-> eco -> apr-eco -> reextract -> resta
|
-> 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
|
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
|
`pv-macro-lvs` verifies the macros listed in `MACRO_LVS_CELLS` (for example
|
||||||
build/commercial/ics55/<run-id>/
|
`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,
|
## Timing constraint model
|
||||||
including the stage verdict marker, exists. Re-running `signoff` resumes from
|
|
||||||
the first missing stamp. Use `force-<stage>` to rerun one stage and downstream
|
Constraints are generated from the canonical per-product inventory
|
||||||
dependencies; use `clean-stage STAGE=<stage>` for targeted cleanup.
|
(`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
|
## Strict verdict
|
||||||
|
|
||||||
@@ -113,19 +223,12 @@ The default policy is blocking:
|
|||||||
- Calibre DRC and antenna counts must be zero and LVS must be clean.
|
- Calibre DRC and antenna counts must be zero and LVS must be clean.
|
||||||
|
|
||||||
The production `doctor` requires every violation threshold to remain zero.
|
The production `doctor` requires every violation threshold to remain zero.
|
||||||
It also requires qualified JTAG, DVP, ULPI, SDRAM, SDIO, XPI, and asynchronous
|
It also requires the product's qualified I/O budgets and, for Mini, an audited
|
||||||
I/O budgets and an audited local hook that selects GPIO10-20 DVP input mode
|
local hook that selects GPIO10-20 DVP input mode and cuts invalid
|
||||||
and cuts invalid bidirectional-pad feedback arcs. `doctor-syn` is the only
|
bidirectional-pad feedback arcs. `doctor-syn` is the only relaxed entry point:
|
||||||
relaxed entry point: Design Compiler then false-paths all top-level I/O and
|
Design Compiler then false-paths all top-level I/O and reports internal
|
||||||
reports internal sequential QoR. Its summary carries `io_qualified=no`, and
|
sequential QoR. Its summary carries `io_qualified=no`, and Innovus and
|
||||||
Innovus and PrimeTime reject it.
|
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.
|
|
||||||
|
|
||||||
The standard-cell family is LLSC H7C across DB, Liberty, LEF, GDS, and CDL.
|
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
|
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
|
Detailed clock, exception, setup/hold, QoR, design-rule, library-binding, and
|
||||||
reference reports are written below `syn/reports/`.
|
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:
|
The common corner contract is:
|
||||||
|
|
||||||
| PVT | RC views |
|
| PVT | RC views |
|
||||||
@@ -158,7 +252,23 @@ The common corner contract is:
|
|||||||
| `TYP` | `TYP` |
|
| `TYP` | `TYP` |
|
||||||
| `MIN`, `ML` | `Cworst`, `RCworst`, `Cbest`, `RCbest` |
|
| `MIN`, `ML` | `Cworst`, `RCworst`, `Cbest`, `RCbest` |
|
||||||
|
|
||||||
The old flow used a single typical Innovus analysis view and delegated broad
|
APR, extraction, STA, and ECO scenarios all derive from this single
|
||||||
corner coverage to separate StarRC/PrimeTime runs. The new flow derives APR,
|
configuration, and the flow rejects missing views.
|
||||||
extraction, STA, and ECO scenarios from one configuration and 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.
|
||||||
+44
-24
@@ -8,11 +8,11 @@
|
|||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
# Copy this file to ../local/ics55-production.mk and replace every REQUIRED
|
# Mini/ICS55 local configuration template. Copy this file to
|
||||||
# value there. The local copy is ignored by Git.
|
# ../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
|
RTL_ARCHIVE := REQUIRED
|
||||||
BUILD_ROOT := $(ROOT_PATH)/build
|
BUILD_ROOT := $(ROOT_PATH)/build
|
||||||
|
|
||||||
@@ -104,6 +104,7 @@ STARRC_MAP := REQUIRED
|
|||||||
STD_GDS := REQUIRED
|
STD_GDS := REQUIRED
|
||||||
IO_GDS := REQUIRED
|
IO_GDS := REQUIRED
|
||||||
MACRO_GDS := REQUIRED
|
MACRO_GDS := REQUIRED
|
||||||
|
OTHER_GDS :=
|
||||||
STD_CDL := REQUIRED
|
STD_CDL := REQUIRED
|
||||||
IO_CDL := REQUIRED
|
IO_CDL := REQUIRED
|
||||||
MACRO_CDL := REQUIRED
|
MACRO_CDL := REQUIRED
|
||||||
@@ -149,21 +150,20 @@ ECO_HOLD_MARGIN_NS := 0.0
|
|||||||
ECO_MAX_PROCESSES := 8
|
ECO_MAX_PROCESSES := 8
|
||||||
ECO_PHYSICAL_MODE := open_site
|
ECO_PHYSICAL_MODE := open_site
|
||||||
|
|
||||||
# Non-sensitive design intent. Units are micrometres and nanoseconds.
|
# Optional product-intent overrides (defaults in ../config/products/mini.mk).
|
||||||
DIE_WIDTH := 2400
|
# DIE_WIDTH := 2400
|
||||||
DIE_HEIGHT := 2400
|
# DIE_HEIGHT := 2400
|
||||||
CORE_MARGIN_LEFT := 120
|
# CORE_MARGIN_LEFT := 258
|
||||||
CORE_MARGIN_BOTTOM := 120
|
# CORE_MARGIN_BOTTOM := 258
|
||||||
CORE_MARGIN_RIGHT := 120
|
# CORE_MARGIN_RIGHT := 258
|
||||||
CORE_MARGIN_TOP := 120
|
# CORE_MARGIN_TOP := 258
|
||||||
CORE_UTILIZATION := 0.60
|
# CORE_UTILIZATION := 0.60
|
||||||
XTAL_CLK_PERIOD_NS := 41.666666667
|
# PLL_OUTPUT_PERIOD_NS := 13.888888889
|
||||||
PLL_OUTPUT_PERIOD_NS := 13.888888889
|
# CLOCK_SETUP_UNCERTAINTY_NS := 0.20
|
||||||
CLOCK_SETUP_UNCERTAINTY_NS := 0.20
|
# CLOCK_HOLD_UNCERTAINTY_NS := 0.10
|
||||||
CLOCK_HOLD_UNCERTAINTY_NS := 0.10
|
# CLOCK_TRANSITION_NS := 0.10
|
||||||
CLOCK_TRANSITION_NS := 0.10
|
# MAX_TRANSITION_NS := 0.50
|
||||||
MAX_TRANSITION_NS := 0.50
|
# MAX_FANOUT := 32
|
||||||
MAX_FANOUT := 32
|
|
||||||
|
|
||||||
# Board/device timing is local qualification data. Set YES only when every
|
# Board/device timing is local qualification data. Set YES only when every
|
||||||
# interface budget below is taken from reviewed board and component timing.
|
# 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_DELAY_MIN_NS := REQUIRED
|
||||||
ASYNC_OUTPUT_LOAD_PF := REQUIRED
|
ASYNC_OUTPUT_LOAD_PF := REQUIRED
|
||||||
|
|
||||||
# The qualified ICS55 PLL mode is fixed. Other runtime selections fail to lock.
|
# The qualified ICS55 PLL mode for Mini is fixed by
|
||||||
ICS55_PLL_SUPPORTED_SEL := 0
|
# ../config/products/mini.mk (SEL=0, N=2, OD=2). Other runtime selections
|
||||||
ICS55_PLL_N := 2
|
# fail to lock.
|
||||||
ICS55_PLL_OD := 2
|
|
||||||
|
|
||||||
# TIMING_IO_MODE_HOOK is required when IO_TIMING_QUALIFIED=YES. It must select
|
# 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.
|
# the reviewed GPIO10-20 DVP mode and cut invalid bidirectional pad feedback.
|
||||||
@@ -235,9 +234,30 @@ APR_ROUTE_HOOK :=
|
|||||||
TIMING_IO_MODE_HOOK := REQUIRED
|
TIMING_IO_MODE_HOOK := REQUIRED
|
||||||
TIMING_EXCEPTION_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_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.
|
# Required strict thresholds.
|
||||||
MAX_SETUP_VIOLATIONS := 0
|
MAX_SETUP_VIOLATIONS := 0
|
||||||
MAX_HOLD_VIOLATIONS := 0
|
MAX_HOLD_VIOLATIONS := 0
|
||||||
MAX_DRV_VIOLATIONS := 0
|
MAX_DRV_VIOLATIONS := 0
|
||||||
MAX_DRC_RESULTS := 0
|
MAX_DRC_RESULTS := 0
|
||||||
MAX_ANTENNA_RESULTS := 0
|
MAX_ANTENNA_RESULTS := 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
|
||||||
@@ -22,17 +22,21 @@ def read_text(path):
|
|||||||
return handle.read()
|
return handle.read()
|
||||||
|
|
||||||
|
|
||||||
SPEF_CORNERS = (
|
def spef_corners():
|
||||||
"Cworst_m40",
|
configured = os.environ.get("COMMERCIAL_EXTRACTION_CORNERS", "").split()
|
||||||
"Cworst_125",
|
if configured:
|
||||||
"RCworst_m40",
|
return configured
|
||||||
"RCworst_125",
|
return [
|
||||||
"Cbest_m40",
|
"Cworst_m40",
|
||||||
"Cbest_125",
|
"Cworst_125",
|
||||||
"RCbest_m40",
|
"RCworst_m40",
|
||||||
"RCbest_125",
|
"RCworst_125",
|
||||||
"TYP_25",
|
"Cbest_m40",
|
||||||
)
|
"Cbest_125",
|
||||||
|
"RCbest_m40",
|
||||||
|
"RCbest_125",
|
||||||
|
"TYP_25",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def find_files(root, suffixes):
|
def find_files(root, suffixes):
|
||||||
@@ -63,15 +67,15 @@ def apply_check(kind, root, limit, top=None, minimum_mtime=None):
|
|||||||
else:
|
else:
|
||||||
files = [
|
files = [
|
||||||
os.path.join(root, "{0}.{1}.spef.gz".format(top, corner))
|
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)]
|
missing = [path for path in files if not os.path.isfile(path)]
|
||||||
if missing:
|
if missing:
|
||||||
error = "missing ICS55 SPEF corners: {0}".format(
|
error = "missing SPEF corners: {0}".format(
|
||||||
", ".join(os.path.basename(path) for path in missing)
|
", ".join(os.path.basename(path) for path in missing)
|
||||||
)
|
)
|
||||||
elif any(os.path.getsize(path) == 0 for path in files):
|
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"):
|
elif kind in ("calibre-drc", "calibre-antenna"):
|
||||||
reports = find_files(root, (".summary", ".sum", ".rpt"))
|
reports = find_files(root, (".summary", ".sum", ".rpt"))
|
||||||
files = reports
|
files = reports
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ STAGES = (
|
|||||||
("pv-drc", "pv/drc"),
|
("pv-drc", "pv/drc"),
|
||||||
("pv-antenna", "pv/antenna"),
|
("pv-antenna", "pv/antenna"),
|
||||||
("pv-lvs", "pv/lvs"),
|
("pv-lvs", "pv/lvs"),
|
||||||
|
("pv-macro-lvs", "pv/macro_lvs"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from __future__ import print_function
|
|||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -68,6 +69,18 @@ SYNTHESIS_FILE_VARIABLES = (
|
|||||||
"PLL_DB",
|
"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 = (
|
VALUE_VARIABLES = (
|
||||||
"TOP", "TECHNOLOGY", "RTL_ARCHIVE", "LSF_MODE", "APR_SITE", "APR_CORE_FILLERS",
|
"TOP", "TECHNOLOGY", "RTL_ARCHIVE", "LSF_MODE", "APR_SITE", "APR_CORE_FILLERS",
|
||||||
"APR_IO_FILLERS", "APR_SIGNAL_PAD_CELLS", "APR_IO_CORNER_CELL",
|
"APR_IO_FILLERS", "APR_SIGNAL_PAD_CELLS", "APR_IO_CORNER_CELL",
|
||||||
@@ -98,32 +111,7 @@ SYNTHESIS_VALUE_VARIABLES = (
|
|||||||
"SYN_OPERATING_CONDITION",
|
"SYN_OPERATING_CONDITION",
|
||||||
)
|
)
|
||||||
|
|
||||||
IO_BUDGET_VARIABLES = (
|
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",
|
|
||||||
)
|
|
||||||
|
|
||||||
H7C_PVT_TOKENS = {
|
H7C_PVT_TOKENS = {
|
||||||
"MAX": "_ss_rcworst_1p08_125",
|
"MAX": "_ss_rcworst_1p08_125",
|
||||||
@@ -208,18 +196,24 @@ def main():
|
|||||||
if name in required_values and (not value or value == "REQUIRED"):
|
if name in required_values and (not value or value == "REQUIRED"):
|
||||||
errors.append("{0} is not configured".format(name))
|
errors.append("{0} is not configured".format(name))
|
||||||
|
|
||||||
qualified_pll = {
|
pll_mode = os.environ.get("PRODUCT_PLL_MODE", "").strip().lower()
|
||||||
"ICS55_PLL_SUPPORTED_SEL": "0",
|
if pll_mode == "qualified":
|
||||||
"ICS55_PLL_N": "2",
|
qualified_pll = {
|
||||||
"ICS55_PLL_OD": "2",
|
"ICS55_PLL_SUPPORTED_SEL": "0",
|
||||||
}
|
"ICS55_PLL_N": "2",
|
||||||
for name, expected in qualified_pll.items():
|
"ICS55_PLL_OD": "2",
|
||||||
value = os.environ.get(name, "").strip()
|
}
|
||||||
values[name] = value
|
for name, expected in qualified_pll.items():
|
||||||
if value != expected:
|
value = os.environ.get(name, "").strip()
|
||||||
errors.append(
|
values[name] = value
|
||||||
"{0} must match the qualified value {1}".format(name, expected)
|
if value != expected:
|
||||||
)
|
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:
|
if not args.allow_internal_qor:
|
||||||
for name in (
|
for name in (
|
||||||
"MAX_SETUP_VIOLATIONS",
|
"MAX_SETUP_VIOLATIONS",
|
||||||
@@ -236,14 +230,46 @@ def main():
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
errors.append("ECO_MAX_PROCESSES must be a positive integer")
|
errors.append("ECO_MAX_PROCESSES must be a positive integer")
|
||||||
if values.get("ECO_PHYSICAL_MODE") not in ("open_site", "occupied_site"):
|
if values.get("ECO_PHYSICAL_MODE") not in (
|
||||||
errors.append("ECO_PHYSICAL_MODE must be open_site or occupied_site")
|
"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"):
|
if values.get("LSF_MODE") not in ("batch", "interactive"):
|
||||||
errors.append("LSF_MODE must be batch or interactive")
|
errors.append("LSF_MODE must be batch or interactive")
|
||||||
|
|
||||||
io_mode = os.environ.get("IO_TIMING_QUALIFIED", "").strip().upper()
|
io_mode = os.environ.get("IO_TIMING_QUALIFIED", "").strip().upper()
|
||||||
if io_mode == "YES":
|
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()
|
value = os.environ.get(name, "").strip()
|
||||||
if not value or value == "REQUIRED":
|
if not value or value == "REQUIRED":
|
||||||
errors.append("{0} is not configured".format(name))
|
errors.append("{0} is not configured".format(name))
|
||||||
@@ -252,10 +278,15 @@ def main():
|
|||||||
float(value)
|
float(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
errors.append("{0} must be numeric".format(name))
|
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()
|
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")
|
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))
|
errors.append("TIMING_IO_MODE_HOOK is not readable: {0}".format(io_hook))
|
||||||
elif io_mode == "NO" and args.allow_internal_qor:
|
elif io_mode == "NO" and args.allow_internal_qor:
|
||||||
pass
|
pass
|
||||||
@@ -298,6 +329,18 @@ def main():
|
|||||||
elif not os.path.isfile(path) or not os.access(path, os.R_OK):
|
elif not os.path.isfile(path) or not os.access(path, os.R_OK):
|
||||||
errors.append("{0} is not readable: {1}".format(name, path))
|
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 prefix in ("STD", "IO", "SRAM"):
|
||||||
for pvt in ("MAX", "WCL", "TYP", "MIN", "ML"):
|
for pvt in ("MAX", "WCL", "TYP", "MIN", "ML"):
|
||||||
if args.allow_internal_qor and pvt != "TYP":
|
if args.allow_internal_qor and pvt != "TYP":
|
||||||
@@ -394,6 +437,8 @@ def main():
|
|||||||
result = {
|
result = {
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"status": "failed" if errors else "passed",
|
"status": "failed" if errors else "passed",
|
||||||
|
"soc": os.environ.get("SOC", ""),
|
||||||
|
"technology": os.environ.get("TECHNOLOGY", ""),
|
||||||
"runtime": runtime,
|
"runtime": runtime,
|
||||||
"tools": tools,
|
"tools": tools,
|
||||||
"checked_file_variables": checked_files,
|
"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
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
EXPECTED_DOMAINS = {
|
def expected_domains(soc: str) -> set[str]:
|
||||||
"aon",
|
"""Read the product clock-domain policy from config/products/<soc>.mk."""
|
||||||
"lp",
|
config = ROOT / "physical/commercial/config/products" / f"{soc.lower()}.mk"
|
||||||
"hp",
|
if not config.is_file():
|
||||||
"pclk",
|
raise ValueError(f"unsupported SOC {soc!r}: missing {config}")
|
||||||
"memory",
|
text = ""
|
||||||
"audio",
|
for line in config.read_text(encoding="utf-8").splitlines():
|
||||||
"jtag",
|
line = line.rstrip()
|
||||||
"dvp",
|
if line.endswith("\\"):
|
||||||
"usb2_ulpi",
|
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:
|
def tcl_value(value: object) -> str:
|
||||||
@@ -35,7 +41,9 @@ def tcl_value(value: object) -> str:
|
|||||||
return "{" + text.replace("\\", "\\\\").replace("}", "\\}") + "}"
|
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)
|
validate(domains_path, ROOT)
|
||||||
document = json.loads(domains_path.read_text(encoding="utf-8"))
|
document = json.loads(domains_path.read_text(encoding="utf-8"))
|
||||||
domains = document.get("domains")
|
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):
|
if not isinstance(domains, list) or not isinstance(reset_ports, list):
|
||||||
raise ValueError("clock/reset inventory has an invalid shape")
|
raise ValueError("clock/reset inventory has an invalid shape")
|
||||||
names = {domain.get("name") for domain in domains if isinstance(domain, dict)}
|
names = {domain.get("name") for domain in domains if isinstance(domain, dict)}
|
||||||
if names != EXPECTED_DOMAINS:
|
if names != expected:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"commercial clock domains differ from the canonical set: "
|
"commercial clock domains differ from the canonical set: "
|
||||||
+ ", ".join(sorted(str(name) for name in names))
|
+ ", ".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
|
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 = [
|
lines = [
|
||||||
"# Generated by physical/commercial/scripts/generate_timing_contract.py.",
|
"# 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 \\",
|
"set flow::canonical_clock_domains [dict create \\",
|
||||||
]
|
]
|
||||||
for domain in domains:
|
for domain in domains:
|
||||||
@@ -120,13 +128,16 @@ def render(domains: list[dict[str, Any]], reset_ports: list[str]) -> str:
|
|||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
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("--domains", type=Path, required=True)
|
||||||
parser.add_argument("--pin-map", type=Path, required=True)
|
parser.add_argument("--pin-map", type=Path, required=True)
|
||||||
parser.add_argument("--output", type=Path, required=True)
|
parser.add_argument("--output", type=Path, required=True)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
try:
|
try:
|
||||||
domains, reset_ports = load_contract(args.domains, args.pin_map)
|
domains, reset_ports = load_contract(
|
||||||
atomic_write(args.output, render(domains, reset_ports))
|
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:
|
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||||
parser.error(str(error))
|
parser.error(str(error))
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ def main():
|
|||||||
filelist = os.path.join(output_dir, "rtl", "filelist.fl")
|
filelist = os.path.join(output_dir, "rtl", "filelist.fl")
|
||||||
if not os.path.isfile(filelist):
|
if not os.path.isfile(filelist):
|
||||||
parser.error("RTL archive does not contain rtl/filelist.fl")
|
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
|
# Tar extraction preserves the development-zone timestamp. Refresh the
|
||||||
# staged entry point so the stage runner can distinguish this extraction
|
# staged entry point so the stage runner can distinguish this extraction
|
||||||
# from output left by an earlier run.
|
# from output left by an earlier run.
|
||||||
|
|||||||
@@ -21,8 +21,12 @@ CURRENT = re.compile(r"^current_instance(?:\s+\{([^}]*)\})?\s*$")
|
|||||||
SIZE = re.compile(r"^size_cell\s+\{([^}]+)\}\s+\{([^}]+)\}\s*$")
|
SIZE = re.compile(r"^size_cell\s+\{([^}]+)\}\s+\{([^}]+)\}\s*$")
|
||||||
INSERT = re.compile(
|
INSERT = re.compile(
|
||||||
r"^insert_buffer\s+\[get_pins\s+\{([^}]+)\}\]\s+(\S+)"
|
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):
|
def full_name(parent, name):
|
||||||
@@ -54,7 +58,7 @@ def translate(paths):
|
|||||||
continue
|
continue
|
||||||
match = INSERT.match(line)
|
match = INSERT.match(line)
|
||||||
if match:
|
if match:
|
||||||
output.append(
|
command = (
|
||||||
"ecoAddRepeater -term {{{0}}} -cell {{{1}}} "
|
"ecoAddRepeater -term {{{0}}} -cell {{{1}}} "
|
||||||
"-newNetName {{{2}}} -name {{{3}}}".format(
|
"-newNetName {{{2}}} -name {{{3}}}".format(
|
||||||
full_name(current, match.group(1)),
|
full_name(current, match.group(1)),
|
||||||
@@ -63,13 +67,32 @@ def translate(paths):
|
|||||||
match.group(4),
|
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
|
continue
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"{0}:{1}: unsupported PrimeTime ECO command: {2}".format(
|
"{0}:{1}: unsupported PrimeTime ECO command: {2}".format(
|
||||||
path, number, line
|
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():
|
def main():
|
||||||
|
|||||||
@@ -33,14 +33,60 @@ proc restore_previous {run_root top stage} {
|
|||||||
restoreDesign $database $top
|
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 {} {
|
proc connect_power_nets {} {
|
||||||
set power [flow::env APR_POWER_NET]
|
set power [flow::env APR_POWER_NET]
|
||||||
set ground [flow::env APR_GROUND_NET]
|
set ground [flow::env APR_GROUND_NET]
|
||||||
foreach pin [flow::env APR_POWER_PINS] {
|
set power_map [string trim [flow::env APR_POWER_PIN_MAP ""]]
|
||||||
globalNetConnect $power -type pgpin -pin $pin -all -verbose
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
foreach pin [flow::env APR_GROUND_PINS] {
|
if {$ground_map ne ""} {
|
||||||
globalNetConnect $ground -type pgpin -pin $pin -all -verbose
|
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 $power -type tiehi -all -verbose
|
||||||
globalNetConnect $ground -type tielo -all -verbose
|
globalNetConnect $ground -type tielo -all -verbose
|
||||||
@@ -140,6 +186,113 @@ proc write_io_side {handle side signals power_records offset pitch length} {
|
|||||||
puts $handle " )"
|
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} {
|
proc write_apr_reports {report_dir stage} {
|
||||||
redirect [file join $report_dir ${stage}.summary.rpt] { summaryReport }
|
redirect [file join $report_dir ${stage}.summary.rpt] { summaryReport }
|
||||||
redirect [file join $report_dir ${stage}.timing.rpt] {
|
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} {
|
proc write_data_out {run_root top stage output_dir report_dir} {
|
||||||
set prefix [file join $output_dir ${top}.${stage}]
|
set prefix [file join $output_dir ${top}.${stage}]
|
||||||
defOut -floorplan -netlist -routing ${prefix}.def
|
defOut -floorplan -netlist -routing ${prefix}.def
|
||||||
saveNetlist ${prefix}.v
|
# Legacy dataOut.tcl:15,19 drop pad and seal-ring instances from the
|
||||||
saveNetlist -includePowerGround ${prefix}.pg.v
|
# 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_sdc ${prefix}.sdc
|
||||||
write_sdf -version 3.0 ${prefix}.sdf
|
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] \
|
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
|
verify_route $report_dir
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +374,11 @@ proc run_apr {} {
|
|||||||
|
|
||||||
switch -- $stage {
|
switch -- $stage {
|
||||||
initialize {
|
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
|
checkDesign -all
|
||||||
}
|
}
|
||||||
floorplan {
|
floorplan {
|
||||||
@@ -215,11 +386,39 @@ proc run_apr {} {
|
|||||||
[flow::env DIE_WIDTH] [flow::env DIE_HEIGHT] \
|
[flow::env DIE_WIDTH] [flow::env DIE_HEIGHT] \
|
||||||
[flow::env CORE_MARGIN_LEFT] [flow::env CORE_MARGIN_BOTTOM] \
|
[flow::env CORE_MARGIN_LEFT] [flow::env CORE_MARGIN_BOTTOM] \
|
||||||
[flow::env CORE_MARGIN_RIGHT] [flow::env CORE_MARGIN_TOP]
|
[flow::env CORE_MARGIN_RIGHT] [flow::env CORE_MARGIN_TOP]
|
||||||
set io_file [file join $work_dir ${top}.io]
|
# Legacy floorplan.tcl:6 loads a reviewed IO order file; the
|
||||||
write_io_ring $io_file
|
# 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
|
loadIoFile $io_file
|
||||||
fixAllIos
|
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
|
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
|
flow::source_hook APR_FLOORPLAN_HOOK
|
||||||
connect_power_nets
|
connect_power_nets
|
||||||
set ring_layers [flow::env APR_RING_LAYERS]
|
set ring_layers [flow::env APR_RING_LAYERS]
|
||||||
@@ -251,20 +450,33 @@ proc run_apr {} {
|
|||||||
checkFPlan -reportUtil
|
checkFPlan -reportUtil
|
||||||
}
|
}
|
||||||
place {
|
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]
|
setPlaceMode -place_global_max_density [flow::env CORE_UTILIZATION]
|
||||||
placeDesign -concurrent_macros
|
placeDesign -concurrent_macros
|
||||||
optDesign -preCTS
|
optDesign -preCTS
|
||||||
flow::source_hook APR_PLACE_HOOK
|
flow::source_hook APR_PLACE_HOOK
|
||||||
}
|
}
|
||||||
cts {
|
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 buffer_cells [flow::env APR_CTS_BUFFER_CELLS]
|
||||||
set_ccopt_property inverter_cells [flow::env APR_CTS_INVERTER_CELLS]
|
set_ccopt_property inverter_cells [flow::env APR_CTS_INVERTER_CELLS]
|
||||||
set_ccopt_property route_type -net_type trunk \
|
# Legacy generate_ccopt_spec.tcl: snapshot the tree spec, then
|
||||||
-route_type_name retrosoc_clock_trunk
|
# bind the NDR route type and targets before ccopt_design.
|
||||||
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]
|
|
||||||
create_ccopt_clock_tree_spec -file [file join $work_dir ccopt.spec]
|
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]
|
source [file join $work_dir ccopt.spec]
|
||||||
ccopt_design
|
ccopt_design
|
||||||
optDesign -postCTS
|
optDesign -postCTS
|
||||||
@@ -272,8 +484,14 @@ proc run_apr {} {
|
|||||||
flow::source_hook APR_CTS_HOOK
|
flow::source_hook APR_CTS_HOOK
|
||||||
}
|
}
|
||||||
route {
|
route {
|
||||||
setNanoRouteMode -routeBottomRoutingLayer [flow::env APR_SIGNAL_MIN_LAYER]
|
# Legacy route.tcl:6-25 applies the same settings and constraints.
|
||||||
setNanoRouteMode -routeTopRoutingLayer [flow::env APR_SIGNAL_MAX_LAYER]
|
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
|
routeDesign -globalDetail
|
||||||
optDesign -postRoute
|
optDesign -postRoute
|
||||||
optDesign -postRoute -hold
|
optDesign -postRoute -hold
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ namespace eval flow {
|
|||||||
variable canonical_clock_domains {}
|
variable canonical_clock_domains {}
|
||||||
variable canonical_reset_ports {}
|
variable canonical_reset_ports {}
|
||||||
variable commercial_clock_names {}
|
variable commercial_clock_names {}
|
||||||
|
variable commercial_clock_groups
|
||||||
|
array set commercial_clock_groups {}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc flow::required_ports {label names} {
|
proc flow::required_ports {label names} {
|
||||||
@@ -53,6 +55,13 @@ proc flow::domain_object {domain} {
|
|||||||
return [flow::required_pins "clock $domain" $observation]
|
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 {} {
|
proc flow::load_canonical_timing_contract {} {
|
||||||
variable canonical_clock_domains
|
variable canonical_clock_domains
|
||||||
variable canonical_reset_ports
|
variable canonical_reset_ports
|
||||||
@@ -62,69 +71,58 @@ proc flow::load_canonical_timing_contract {} {
|
|||||||
flow::fail "canonical commercial timing contract is missing: $contract"
|
flow::fail "canonical commercial timing contract is missing: $contract"
|
||||||
}
|
}
|
||||||
source $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]} {
|
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} {
|
if {[llength $canonical_reset_ports] == 0} {
|
||||||
flow::fail "canonical reset-port list is empty"
|
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 {} {
|
proc flow::apply_clock_constraints {} {
|
||||||
variable canonical_clock_domains
|
variable canonical_clock_domains
|
||||||
variable canonical_reset_ports
|
variable canonical_reset_ports
|
||||||
variable commercial_clock_names
|
variable commercial_clock_names
|
||||||
|
variable commercial_clock_groups
|
||||||
|
|
||||||
set commercial_clock_names {}
|
set commercial_clock_names {}
|
||||||
foreach domain {external audio jtag dvp usb2_ulpi} {
|
array set commercial_clock_groups {}
|
||||||
lappend commercial_clock_names [flow::create_canonical_master_clock $domain]
|
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]
|
if {[array size commercial_clock_groups] > 1} {
|
||||||
create_clock -name clk_xtal -period [flow::env XTAL_CLK_PERIOD_NS] $xtal_pin
|
set command [list set_clock_groups -name retrosoc_async -asynchronous]
|
||||||
lappend commercial_clock_names clk_xtal
|
foreach group [array names commercial_clock_groups] {
|
||||||
|
lappend command -group [get_clocks $commercial_clock_groups($group)]
|
||||||
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
|
eval $command
|
||||||
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]]
|
|
||||||
set command [list set_clock_groups -name retrosoc_async -asynchronous]
|
|
||||||
foreach group $async_groups {
|
|
||||||
lappend command -group $group
|
|
||||||
}
|
}
|
||||||
eval $command
|
|
||||||
|
|
||||||
set clocks [get_clocks $commercial_clock_names]
|
set clocks [get_clocks $commercial_clock_names]
|
||||||
if {[sizeof_collection $clocks] != [llength $commercial_clock_names]} {
|
if {[sizeof_collection $clocks] != [llength $commercial_clock_names]} {
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
# See the Mulan PSL v2 for more details.
|
# See the Mulan PSL v2 for more details.
|
||||||
|
|
||||||
namespace eval flow {
|
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 root [file normalize [file join [file dirname [info script]] ../..]]
|
||||||
variable run_root [expr {[info exists ::env(RUN_ROOT)] ? $::env(RUN_ROOT) : ""}]
|
variable run_root [expr {[info exists ::env(RUN_ROOT)] ? $::env(RUN_ROOT) : ""}]
|
||||||
}
|
}
|
||||||
@@ -209,49 +211,6 @@ proc flow::report_has_failure {path patterns} {
|
|||||||
if {![file isfile $path]} {
|
if {![file isfile $path]} {
|
||||||
flow::fail "required report was not written: $path"
|
flow::fail "required report was not written: $path"
|
||||||
}
|
}
|
||||||
|
|
||||||
proc flow::count_report_matches {path pattern} {
|
|
||||||
if {![file isfile $path]} {
|
|
||||||
flow::fail "required report was not written: $path"
|
|
||||||
}
|
|
||||||
set handle [open $path r]
|
|
||||||
set text [read $handle]
|
|
||||||
close $handle
|
|
||||||
return [regexp -all -nocase -- $pattern $text]
|
|
||||||
}
|
|
||||||
|
|
||||||
proc flow::require_qualified_synthesis {} {
|
|
||||||
variable run_root
|
|
||||||
set summary [file join $run_root syn output synthesis.summary.tsv]
|
|
||||||
if {![file isfile $summary]} {
|
|
||||||
flow::fail "qualified synthesis summary is missing: $summary"
|
|
||||||
}
|
|
||||||
array set values {}
|
|
||||||
set handle [open $summary r]
|
|
||||||
while {[gets $handle line] >= 0} {
|
|
||||||
if {[regexp {^([^\t]+)\t([^\t]+)$} $line unused name value]} {
|
|
||||||
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"} {
|
|
||||||
flow::fail "synthesis is not qualified for implementation: $name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set handle [open $path r]
|
set handle [open $path r]
|
||||||
set text [read $handle]
|
set text [read $handle]
|
||||||
close $handle
|
close $handle
|
||||||
@@ -262,3 +221,51 @@ proc flow::report_has_failure {path patterns} {
|
|||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc flow::count_report_matches {path pattern} {
|
||||||
|
if {![file isfile $path]} {
|
||||||
|
flow::fail "required report was not written: $path"
|
||||||
|
}
|
||||||
|
set handle [open $path r]
|
||||||
|
set text [read $handle]
|
||||||
|
close $handle
|
||||||
|
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]
|
||||||
|
if {![file isfile $summary]} {
|
||||||
|
flow::fail "qualified synthesis summary is missing: $summary"
|
||||||
|
}
|
||||||
|
array set values {}
|
||||||
|
set handle [open $summary r]
|
||||||
|
while {[gets $handle line] >= 0} {
|
||||||
|
if {[regexp {^([^\t]+)\t([^\t]+)$} $line unused name value]} {
|
||||||
|
set values($name) $value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close $handle
|
||||||
|
foreach name {flow_pass constraints_complete io_qualified} {
|
||||||
|
if {![info exists values($name)] || $values($name) ne "yes"} {
|
||||||
|
flow::fail "synthesis is not qualified for implementation: $name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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} {
|
proc flow::spef_name {top corner} {
|
||||||
return "${top}.${corner}.spef.gz"
|
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>
|
# Copyright (c) 2023-2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||||
# retroSoC is licensed under Mulan PSL v2.
|
# retroSoC is licensed under Mulan PSL v2.
|
||||||
# You can use this software according to the terms and conditions of the 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
|
# http://license.coscl.org.cn/MulanPSL2
|
||||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
|
# 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
|
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 {} {
|
proc flow::apply_io_constraints {} {
|
||||||
variable io_qualified
|
variable io_qualified
|
||||||
variable unqualified_interfaces
|
variable unqualified_interfaces
|
||||||
@@ -110,10 +48,10 @@ proc flow::apply_io_constraints {} {
|
|||||||
if {$mode eq "YES"} {
|
if {$mode eq "YES"} {
|
||||||
set io_qualified 1
|
set io_qualified 1
|
||||||
set unqualified_interfaces {}
|
set unqualified_interfaces {}
|
||||||
flow::apply_qualified_io_constraints
|
product::apply_io_constraints
|
||||||
} elseif {$mode eq "NO"} {
|
} elseif {$mode eq "NO"} {
|
||||||
set io_qualified 0
|
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
|
# Internal-QoR mode closes all sequential domains while explicitly
|
||||||
# excluding board and package paths that do not yet have a budget.
|
# excluding board and package paths that do not yet have a budget.
|
||||||
set_false_path -from [all_inputs]
|
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]]
|
||||||
|
}
|
||||||
@@ -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 common.tcl]
|
||||||
source [file join $flow_root tcl common corners.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 {} {
|
proc run_eco {} {
|
||||||
set run_root [flow::env RUN_ROOT]
|
set run_root [flow::env RUN_ROOT]
|
||||||
set top [flow::env TOP]
|
set top [flow::env TOP]
|
||||||
@@ -68,6 +84,34 @@ proc run_eco {} {
|
|||||||
-hold_margin [flow::env ECO_HOLD_MARGIN_NS] \
|
-hold_margin [flow::env ECO_HOLD_MARGIN_NS] \
|
||||||
-physical_mode [flow::env ECO_PHYSICAL_MODE] -verbose
|
-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] {
|
redirect [file join $report_dir hold.rpt] {
|
||||||
fix_eco_timing -type hold -methods insert_buffer \
|
fix_eco_timing -type hold -methods insert_buffer \
|
||||||
-buffer_list [flow::env ECO_HOLD_BUFFERS] \
|
-buffer_list [flow::env ECO_HOLD_BUFFERS] \
|
||||||
@@ -75,6 +119,17 @@ proc run_eco {} {
|
|||||||
-hold_margin [flow::env ECO_HOLD_MARGIN_NS] \
|
-hold_margin [flow::env ECO_HOLD_MARGIN_NS] \
|
||||||
-physical_mode [flow::env ECO_PHYSICAL_MODE] -verbose
|
-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] {
|
redirect [file join $report_dir drc.rpt] {
|
||||||
foreach type {max_transition max_capacitance} {
|
foreach type {max_transition max_capacitance} {
|
||||||
fix_eco_drc -type $type -methods size_cell \
|
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]
|
# Every scenario records its own change list in DMSA (legacy
|
||||||
set eco_raw $raw
|
# pd_data/pteco/scr/app/output.tcl writes changes from every worker);
|
||||||
set_distributed_variables {eco_raw}
|
# exporting only one scenario drops scenario-specific fixes. Iterate over
|
||||||
current_session func_TYP_TYP_25
|
# every scenario with a current session so each write_changes lands in a
|
||||||
remote_execute { write_changes -format icc2tcl -output $eco_raw }
|
# 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 $scenario
|
||||||
|
remote_execute { write_changes -format icc2tcl -output $eco_raw }
|
||||||
|
lappend raw_changes $eco_raw
|
||||||
|
}
|
||||||
set command [list [flow::env FLOW_PYTHON python] \
|
set command [list [flow::env FLOW_PYTHON python] \
|
||||||
[file join $::flow::root scripts translate_eco.py] \
|
[file join $::flow::root scripts translate_eco.py] \
|
||||||
--output [file join $output_dir eco.tcl] --allow-empty]
|
--output [file join $output_dir eco.tcl] --allow-empty]
|
||||||
lappend command --input $raw
|
foreach raw $raw_changes {
|
||||||
|
lappend command --input $raw
|
||||||
|
}
|
||||||
if {[catch {exec {*}$command} message]} {
|
if {[catch {exec {*}$command} message]} {
|
||||||
flow::fail "PrimeTime ECO translation failed: $message"
|
flow::fail "PrimeTime ECO translation failed: $message"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,29 @@ proc quote_svrf {value} {
|
|||||||
return "\"[string map {\" \\\"} $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} {
|
proc write_merge {run_root top} {
|
||||||
set base [flow::stage_dirs pv merge]
|
set base [flow::stage_dirs pv merge]
|
||||||
set input [file join $run_root apr eco output ${top}.eco.gds]
|
set input [file join $run_root apr eco output ${top}.eco.gds]
|
||||||
set output [file join $base output ${top}.merged.gds]
|
set output [file join $base output ${top}.merged.gds]
|
||||||
set verdict [file join $base output verdict.pass]
|
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] \
|
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]
|
set handle [open [file join $base work merge.tcl] w]
|
||||||
puts $handle [list set input_gds $input]
|
puts $handle [list set input_gds $input]
|
||||||
puts $handle [list set output_gds $output]
|
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 runset $runset]
|
||||||
puts $handle [list set report [file join $base reports ${top}.lvs.rpt]]
|
puts $handle [list set report [file join $base reports ${top}.lvs.rpt]]
|
||||||
puts $handle [list set verdict $verdict]
|
puts $handle [list set verdict $verdict]
|
||||||
|
puts $handle [list set lvs_args [flow::env CALIBRE_LVS_ARGS ""]]
|
||||||
puts $handle {if {[file exists $verdict]} {
|
puts $handle {if {[file exists $verdict]} {
|
||||||
file delete -force $verdict
|
file delete -force $verdict
|
||||||
}
|
}
|
||||||
@@ -133,7 +150,7 @@ if {[catch {exec {*}$convert >@ stdout 2>@ stderr} message]} {
|
|||||||
puts stderr $message
|
puts stderr $message
|
||||||
exit 2
|
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]} {
|
if {[catch {exec {*}$verify >@ stdout 2>@ stderr} message]} {
|
||||||
puts stderr $message
|
puts stderr $message
|
||||||
exit 2
|
exit 2
|
||||||
@@ -167,6 +184,101 @@ close $marker}
|
|||||||
close $handle
|
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 {} {
|
proc generate_pv {} {
|
||||||
set run_root [flow::env RUN_ROOT]
|
set run_root [flow::env RUN_ROOT]
|
||||||
set top [flow::env TOP]
|
set top [flow::env TOP]
|
||||||
@@ -175,6 +287,7 @@ proc generate_pv {} {
|
|||||||
merge { write_merge $run_root $top }
|
merge { write_merge $run_root $top }
|
||||||
drc - antenna { write_drc $run_root $top $mode }
|
drc - antenna { write_drc $run_root $top $mode }
|
||||||
lvs { write_lvs $run_root $top }
|
lvs { write_lvs $run_root $top }
|
||||||
|
macro_lvs { write_macro_lvs $run_root $top }
|
||||||
default { flow::fail "unsupported PV mode: $mode" }
|
default { flow::fail "unsupported PV mode: $mode" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,49 @@
|
|||||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
# See the Mulan PSL v2 for more details.
|
# 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} {
|
proc flow::load_pt_scenario {tag scenario} {
|
||||||
variable scenarios
|
variable scenarios
|
||||||
set top [flow::env TOP]
|
set top [flow::env TOP]
|
||||||
set run_root [flow::env RUN_ROOT]
|
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 netlist [file join $run_root apr $tag output ${top}.${tag}.v]
|
||||||
set sdc [file join $run_root apr $tag output ${top}.${tag}.sdc]
|
set sdc [file join $run_root apr $tag output ${top}.${tag}.sdc]
|
||||||
set spef_root [file join $run_root extract $tag output]
|
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
|
set_operating_conditions -analysis_type on_chip_variation
|
||||||
read_sdc $sdc
|
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]]
|
set spef [file join $spef_root [flow::spef_name $top $rc]]
|
||||||
if {![file isfile $spef]} {
|
if {![file isfile $spef]} {
|
||||||
flow::fail "SPEF is missing for scenario $scenario: $spef"
|
flow::fail "SPEF is missing for scenario $scenario: $spef"
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ proc run_sta {} {
|
|||||||
if {![dict exists $::flow::scenarios $scenario]} {
|
if {![dict exists $::flow::scenarios $scenario]} {
|
||||||
flow::fail "unsupported STA scenario: $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 base [flow::stage_dirs sta $tag]
|
||||||
set report_dir [file join $base reports]
|
set report_dir [file join $base reports]
|
||||||
set output_dir [file join $base output]
|
set output_dir [file join $base output]
|
||||||
@@ -39,7 +43,36 @@ proc run_sta {} {
|
|||||||
flow::require_commercial_clock_inventory
|
flow::require_commercial_clock_inventory
|
||||||
|
|
||||||
update_timing -full
|
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"
|
flow::fail "check_timing failed in scenario $scenario"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +94,22 @@ proc run_sta {} {
|
|||||||
-max_paths 100000 -path_type full_clock_expanded \
|
-max_paths 100000 -path_type full_clock_expanded \
|
||||||
-input_pins -nets -transition_time -capacitance -derate
|
-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 $constraint_report { report_constraint -all_violators }
|
||||||
redirect [file join $report_dir ${scenario}.coverage.rpt] {
|
redirect [file join $report_dir ${scenario}.coverage.rpt] {
|
||||||
report_analysis_coverage -status_details untested
|
report_analysis_coverage -status_details untested
|
||||||
@@ -79,6 +128,21 @@ proc run_sta {} {
|
|||||||
puts [get_object_name $library]
|
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]
|
set drv_count [count_violations $constraint_report]
|
||||||
flow::write_text [file join $output_dir ${scenario}.summary.tsv] \
|
flow::write_text [file join $output_dir ${scenario}.summary.tsv] \
|
||||||
"$scenario\t$setup_count\t$hold_count\t$drv_count\n"
|
"$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"
|
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 \
|
write_sdf -version 3.0 -context verilog \
|
||||||
[file join $output_dir ${top}.${tag}.sdf]
|
[file join $output_dir ${top}.${tag}.sdf]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
set flow_root [file normalize [file join [file dirname [info script]] ../..]]
|
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 common.tcl]
|
||||||
source [file join $flow_root tcl common constraints.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]
|
source [file join $flow_root tcl syn reporting.tcl]
|
||||||
|
|
||||||
proc run_synthesis {} {
|
proc run_synthesis {} {
|
||||||
@@ -38,6 +39,31 @@ proc run_synthesis {} {
|
|||||||
set_app_var verilogout_no_tri true
|
set_app_var verilogout_no_tri true
|
||||||
set_app_var compile_clock_gating_through_hierarchy 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]
|
define_design_lib WORK -path [file join $work_dir WORK]
|
||||||
set analyze_command [list analyze -format sverilog -work WORK]
|
set analyze_command [list analyze -format sverilog -work WORK]
|
||||||
if {[llength [dict get $rtl defines]] > 0} {
|
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
|
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_design.pre.rpt] { check_design }
|
||||||
redirect [file join $report_dir check_timing.pre.rpt] {
|
redirect [file join $report_dir check_timing.pre.rpt] {
|
||||||
check_timing
|
check_timing
|
||||||
@@ -77,10 +145,25 @@ proc run_synthesis {} {
|
|||||||
set_fix_multiple_port_nets -all -buffer_constants
|
set_fix_multiple_port_nets -all -buffer_constants
|
||||||
set_boundary_optimization [current_design] true
|
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 [file join $output_dir ${top}.svf]
|
||||||
set_svf $svf
|
set_svf $svf
|
||||||
compile_ultra -no_autoungroup -gate_clock
|
# Legacy syn_common_flow.tcl:31-32,533: high-effort TNS optimization and
|
||||||
compile_ultra -incremental -no_autoungroup -gate_clock
|
# 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
|
set_svf -off
|
||||||
|
|
||||||
change_names -rules verilog -hierarchy
|
change_names -rules verilog -hierarchy
|
||||||
|
|||||||
@@ -52,6 +52,15 @@ proc flow::vt_cells {suffix} {
|
|||||||
-filter "is_hierarchical == false && ref_name =~ *$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} {
|
proc flow::write_synthesis_summary {path drv_count} {
|
||||||
variable io_qualified
|
variable io_qualified
|
||||||
set setup [flow::timing_metrics "" max]
|
set setup [flow::timing_metrics "" max]
|
||||||
@@ -69,10 +78,10 @@ proc flow::write_synthesis_summary {path drv_count} {
|
|||||||
set memories [get_cells -quiet -hierarchical \
|
set memories [get_cells -quiet -hierarchical \
|
||||||
-filter "is_hierarchical == false && is_memory_cell == true"]
|
-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_area 0.0
|
||||||
set std_count 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 cells [flow::vt_cells $vt_suffix($group)]
|
||||||
set vt_count($group) [sizeof_collection $cells]
|
set vt_count($group) [sizeof_collection $cells]
|
||||||
set vt_area($group) [flow::collection_area $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)}]
|
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]
|
set handle [open $path w]
|
||||||
puts $handle "metric\tvalue"
|
puts $handle "metric\tvalue"
|
||||||
puts $handle "flow_pass\tyes"
|
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 "memory_macros\t[sizeof_collection $memories]"
|
||||||
puts $handle "standard_cells\t$std_count"
|
puts $handle "standard_cells\t$std_count"
|
||||||
puts $handle "standard_cell_area_um2\t[format %.4f $std_area]"
|
puts $handle "standard_cell_area_um2\t[format %.4f $std_area]"
|
||||||
puts $handle "total_leaf_area_um2\t[format %.4f [flow::collection_area $leaf]]"
|
puts $handle "macro_area_um2\t[format %.4f $macro_area]"
|
||||||
foreach group {HVT LVT SVT} {
|
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
|
set percent 0.0
|
||||||
if {$std_area > 0.0} {
|
if {$std_area > 0.0} {
|
||||||
set percent [expr {100.0 * $vt_area($group) / $std_area}]
|
set percent [expr {100.0 * $vt_area($group) / $std_area}]
|
||||||
@@ -113,11 +137,22 @@ proc flow::write_synthesis_summary {path drv_count} {
|
|||||||
close $handle
|
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 {} {
|
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]
|
set libraries [get_libs -quiet $pattern]
|
||||||
if {[sizeof_collection $libraries] == 0} {
|
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
|
set_attribute $libraries default_threshold_voltage_group $group -type string
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-19
@@ -102,9 +102,43 @@ def main() -> int:
|
|||||||
) as temp:
|
) as temp:
|
||||||
staging = Path(temp) / "package"
|
staging = Path(temp) / "package"
|
||||||
staging.mkdir()
|
staging.mkdir()
|
||||||
timing_contract = Path(temp) / ("tiny_core.sdc" if soc == "TINY" else "commercial_timing_contract.tcl")
|
commercial_contract = Path(temp) / "commercial_timing_contract.tcl"
|
||||||
timing_generator = (root / "physical/smoke/sta/opensta/generate_sdc.py" if soc == "TINY"
|
subprocess.run(
|
||||||
else root / "physical/commercial/scripts/generate_timing_contract.py")
|
[
|
||||||
|
"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":
|
if config.get("MINI_MODE", "PRODUCT") == "MPW":
|
||||||
dynamic_core_filelist = (
|
dynamic_core_filelist = (
|
||||||
args.variant_root
|
args.variant_root
|
||||||
@@ -125,20 +159,6 @@ def main() -> int:
|
|||||||
dynamic_ip_filelist = (
|
dynamic_ip_filelist = (
|
||||||
args.variant_root / "generated/user_extensions/legacy_ip.fl"
|
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 = [
|
command = [
|
||||||
"python3",
|
"python3",
|
||||||
str(root / "physical/smoke/syn/tools/export_soc_sources.py"),
|
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"),
|
str(args.variant_root / "generated/pin_map/pin_map.fl"),
|
||||||
"--archinfo-incdir",
|
"--archinfo-incdir",
|
||||||
str(args.variant_root / "generated/archinfo"),
|
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 soc == "TINY":
|
||||||
if config.get("SYNTH") == "YOSYS":
|
if config.get("SYNTH") == "YOSYS":
|
||||||
recipe = config.get("SYNTH_RECIPE", "balanced")
|
recipe = config.get("SYNTH_RECIPE", "balanced")
|
||||||
|
|||||||
@@ -57,13 +57,60 @@ def test_translate_eco_accepts_only_reviewed_operations(tmp_path: Path) -> None:
|
|||||||
translator.translate([str(unsupported)])
|
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:
|
def test_spef_checker_requires_every_named_corner(tmp_path: Path) -> None:
|
||||||
checker = load_script("check_outputs")
|
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")
|
(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
|
assert checker.apply_check("spef", str(tmp_path), 0, "retrosoc_asic") is None
|
||||||
(tmp_path / "retrosoc_asic.TYP_25.spef.gz").unlink()
|
(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"
|
"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:
|
def test_prepare_input_refreshes_archived_filelist(tmp_path: Path) -> None:
|
||||||
archive_path = tmp_path / "rtl.tar"
|
archive_path = tmp_path / "rtl.tar"
|
||||||
payload = b"rtl/top.sv\n"
|
payload = b"rtl/top.sv\n"
|
||||||
|
contract = b"# contract\n"
|
||||||
with tarfile.open(archive_path, "w") as archive:
|
with tarfile.open(archive_path, "w") as archive:
|
||||||
member = tarfile.TarInfo("rtl/filelist.fl")
|
member = tarfile.TarInfo("rtl/filelist.fl")
|
||||||
member.size = len(payload)
|
member.size = len(payload)
|
||||||
member.mtime = 1
|
member.mtime = 1
|
||||||
archive.addfile(member, io.BytesIO(payload))
|
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"
|
output = tmp_path / "output"
|
||||||
manifest = tmp_path / "manifest.json"
|
manifest = tmp_path / "manifest.json"
|
||||||
started = archive_path.stat().st_mtime
|
started = archive_path.stat().st_mtime
|
||||||
@@ -303,6 +355,8 @@ def test_commercial_timing_contract_covers_canonical_domains(tmp_path: Path) ->
|
|||||||
[
|
[
|
||||||
sys.executable,
|
sys.executable,
|
||||||
str(FLOW / "scripts/generate_timing_contract.py"),
|
str(FLOW / "scripts/generate_timing_contract.py"),
|
||||||
|
"--soc",
|
||||||
|
"MINI",
|
||||||
"--domains",
|
"--domains",
|
||||||
str(ROOT / "rtl/mini/integration/clock_reset_domains.json"),
|
str(ROOT / "rtl/mini/integration/clock_reset_domains.json"),
|
||||||
"--pin-map",
|
"--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")
|
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:
|
def test_commercial_constraints_do_not_apply_global_io_delays() -> None:
|
||||||
constraints = "\n".join(
|
constraints = "\n".join(
|
||||||
path.read_text(encoding="utf-8")
|
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:
|
def test_dc_uses_typ_link_set_and_svt_lvt_target_subset() -> None:
|
||||||
synthesis = (FLOW / "tcl/syn/main.tcl").read_text(encoding="utf-8")
|
synthesis = (FLOW / "tcl/syn/main.tcl").read_text(encoding="utf-8")
|
||||||
common = (FLOW / "tcl/common/common.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::synthesis_library_files" in synthesis
|
||||||
assert "flow::all_library_files" not in synthesis
|
assert "flow::all_library_files" not in synthesis
|
||||||
assert "return [flow::library_files TYP]" in common
|
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")
|
archive = touch("rtl.tar.gz")
|
||||||
environment = {
|
environment = {
|
||||||
"PATH": os.environ["PATH"],
|
"PATH": os.environ["PATH"],
|
||||||
|
"SOC": "MINI",
|
||||||
"TOP": "retrosoc_asic",
|
"TOP": "retrosoc_asic",
|
||||||
"TECHNOLOGY": "ICS55",
|
"TECHNOLOGY": "ICS55",
|
||||||
|
"PRODUCT_PLL_MODE": "qualified",
|
||||||
"RTL_ARCHIVE": archive,
|
"RTL_ARCHIVE": archive,
|
||||||
"LSF_MODE": "batch",
|
"LSF_MODE": "batch",
|
||||||
"LSF_SYN_ARGS": "-q synth",
|
"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:
|
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"):
|
||||||
result = subprocess.run(
|
local = FLOW / "local" / name
|
||||||
["git", "check-ignore", str(local.relative_to(ROOT))],
|
result = subprocess.run(
|
||||||
cwd=ROOT,
|
["git", "check-ignore", str(local.relative_to(ROOT))],
|
||||||
check=False,
|
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,
|
||||||
)
|
)
|
||||||
assert result.returncode == 0
|
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"
|
||||||
|
)
|
||||||
@@ -282,6 +282,8 @@ def test_package_forwards_manifest_jtag_idcode() -> None:
|
|||||||
assert f'"{option}"' in package_source
|
assert f'"{option}"' in package_source
|
||||||
assert "generate_timing_contract.py" in package_source
|
assert "generate_timing_contract.py" in package_source
|
||||||
assert "commercial_timing_contract.tcl" 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:
|
def test_source_export_writes_tar_without_staging_rtl_tree(tmp_path: Path) -> None:
|
||||||
|
|||||||
Reference in new issue
Block a user