mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: add syn and floorplan design for librelane
This commit is contained in:
13 files changed
+329
-102
No files matched your search
@@ -77,3 +77,4 @@ socver.*
|
|||||||
!/rtl/managed/clusterip/setup.py
|
!/rtl/managed/clusterip/setup.py
|
||||||
/rtl/managed/mpw/
|
/rtl/managed/mpw/
|
||||||
/rtl/managed/third_party/
|
/rtl/managed/third_party/
|
||||||
|
*result.json
|
||||||
+1
-1
@@ -7,7 +7,7 @@ drivers.
|
|||||||
- `pdk/` contains the locked PDK setup helpers and managed PDK checkouts.
|
- `pdk/` contains the locked PDK setup helpers and managed PDK checkouts.
|
||||||
- `smoke/syn/` contains Yosys synthesis and source-export integration.
|
- `smoke/syn/` contains Yosys synthesis and source-export integration.
|
||||||
- `smoke/sta/` contains OpenSTA constraints and timing integration.
|
- `smoke/sta/` contains OpenSTA constraints and timing integration.
|
||||||
- `librelane/` contains the single-level open-source IHP130 full-chip pad-ring flow.
|
- `librelane/` contains the open-source IHP130 core-hardening and full-chip pad-ring flows.
|
||||||
- `commercial/` contains licensed-tool orchestration and flow logic. PDKs,
|
- `commercial/` contains licensed-tool orchestration and flow logic. PDKs,
|
||||||
foundry decks, commercial libraries, site configuration, and results remain
|
foundry decks, commercial libraries, site configuration, and results remain
|
||||||
outside Git.
|
outside Git.
|
||||||
|
|||||||
+57
-27
@@ -1,16 +1,18 @@
|
|||||||
LIBRELANE_FLOW_ROOT := $(ROOT_PATH)/physical/librelane
|
LIBRELANE_FLOW_ROOT := $(ROOT_PATH)/physical/librelane
|
||||||
LIBRELANE_RUN_ROOT := $(VARIANT_ROOT)/physical/librelane/chip
|
LIBRELANE_TARGET ?= chip
|
||||||
|
LIBRELANE_RUN_ROOT := $(VARIANT_ROOT)/physical/librelane/$(LIBRELANE_TARGET)
|
||||||
LIBRELANE_INPUT_DIR := $(LIBRELANE_RUN_ROOT)/input
|
LIBRELANE_INPUT_DIR := $(LIBRELANE_RUN_ROOT)/input
|
||||||
LIBRELANE_RTL := $(LIBRELANE_INPUT_DIR)/retrosoc_asic_sources.sv
|
LIBRELANE_RTL := $(LIBRELANE_INPUT_DIR)/retrosoc_asic_sources.sv
|
||||||
LIBRELANE_SDC := $(LIBRELANE_INPUT_DIR)/retrosoc_asic.sdc
|
LIBRELANE_SDC := $(LIBRELANE_INPUT_DIR)/retrosoc_$(LIBRELANE_TARGET).sdc
|
||||||
LIBRELANE_BONDPAD_GDS := $(LIBRELANE_INPUT_DIR)/bondpad_70x70.gds
|
LIBRELANE_BONDPAD_GDS := $(LIBRELANE_INPUT_DIR)/bondpad_70x70.gds
|
||||||
LIBRELANE_BONDPAD_LEF := $(LIBRELANE_FLOW_ROOT)/bondpad/bondpad_70x70.lef
|
LIBRELANE_BONDPAD_LEF := $(LIBRELANE_FLOW_ROOT)/bondpad/bondpad_70x70.lef
|
||||||
LIBRELANE_CHIP_CONFIG := $(LIBRELANE_RUN_ROOT)/config.json
|
LIBRELANE_CONFIG := $(LIBRELANE_RUN_ROOT)/config.json
|
||||||
LIBRELANE_LOG := $(LIBRELANE_RUN_ROOT)/librelane.log
|
LIBRELANE_LOG := $(LIBRELANE_RUN_ROOT)/librelane.log
|
||||||
LIBRELANE_RESULT := $(LIBRELANE_RUN_ROOT)/result.json
|
LIBRELANE_RESULT := $(LIBRELANE_RUN_ROOT)/result.json
|
||||||
LIBRELANE_META_RESULT := $(META_DIR)/librelane-chip.json
|
LIBRELANE_META_RESULT := $(META_DIR)/librelane-$(LIBRELANE_TARGET).json
|
||||||
LIBRELANE_DOCTOR_RESULT := $(META_DIR)/librelane-doctor.json
|
LIBRELANE_DOCTOR_RESULT := $(META_DIR)/librelane-$(LIBRELANE_TARGET)-doctor.json
|
||||||
LIBRELANE_FINAL_DIR := $(LIBRELANE_RUN_ROOT)/final
|
LIBRELANE_FINAL_DIR := $(LIBRELANE_RUN_ROOT)/final
|
||||||
|
LIBRELANE_ARCHIVE := $(LIBRELANE_RUN_ROOT)/retrosoc-ihp130-$(LIBRELANE_TARGET).tar.gz
|
||||||
LIBRELANE_RUN_TAG ?= current
|
LIBRELANE_RUN_TAG ?= current
|
||||||
LIBRELANE_PDK_ROOT := $(ROOT_PATH)/physical/pdk/IHP-Open-PDK
|
LIBRELANE_PDK_ROOT := $(ROOT_PATH)/physical/pdk/IHP-Open-PDK
|
||||||
LIBRELANE_PDK := ihp-sg13g2
|
LIBRELANE_PDK := ihp-sg13g2
|
||||||
@@ -27,9 +29,17 @@ LIBRELANE_TRACKED_RTL := $(shell git -C $(ROOT_PATH) ls-files \
|
|||||||
|
|
||||||
LIBRELANE_SOURCE_PYTHON ?= /usr/bin/python3
|
LIBRELANE_SOURCE_PYTHON ?= /usr/bin/python3
|
||||||
LIBRELANE_PYTHON ?= python3
|
LIBRELANE_PYTHON ?= python3
|
||||||
|
# LibreLane 3.0.5 writes 1000 expanded paths per group at every mid-PnR STA.
|
||||||
|
# Final multi-corner RCX STA remains mandatory; skip only the redundant reports.
|
||||||
|
LIBRELANE_MID_PNR_SKIP ?= --skip OpenROAD.STAMidPNR
|
||||||
|
|
||||||
ifneq ($(filter librelane-input librelane-doctor librelane-chip librelane-openroad \
|
ifneq ($(filter-out chip core,$(LIBRELANE_TARGET)),)
|
||||||
librelane-klayout librelane-package,$(MAKECMDGOALS)),)
|
$(error LIBRELANE_TARGET must be chip or core, got '$(LIBRELANE_TARGET)')
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(filter librelane-input librelane-doctor librelane-run librelane-chip \
|
||||||
|
librelane-core librelane-openroad librelane-klayout librelane-package \
|
||||||
|
librelane-core-package librelane-package-run,$(MAKECMDGOALS)),)
|
||||||
FLOW_PYTHON := $(LIBRELANE_SOURCE_PYTHON)
|
FLOW_PYTHON := $(LIBRELANE_SOURCE_PYTHON)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -39,14 +49,22 @@ LIBRELANE_FEATURE_ARGS := \
|
|||||||
$(if $(filter YES,$(HAVE_SRAM_MACRO)),--have-sram-macro) \
|
$(if $(filter YES,$(HAVE_SRAM_MACRO)),--have-sram-macro) \
|
||||||
$(if $(filter YES,$(HAVE_SVA)),--have-sva)
|
$(if $(filter YES,$(HAVE_SVA)),--have-sva)
|
||||||
|
|
||||||
.PHONY: librelane-check-pdk librelane-input librelane-doctor librelane-chip \
|
LIBRELANE_CONFIG_DEPS := $(LIBRELANE_RTL) $(LIBRELANE_SDC) \
|
||||||
librelane-openroad librelane-klayout librelane-package
|
$(LIBRELANE_PDN) $(LIBRELANE_SRAM_VH) \
|
||||||
|
$(LIBRELANE_FLOW_ROOT)/scripts/generate_chip_config.py
|
||||||
|
ifeq ($(LIBRELANE_TARGET),chip)
|
||||||
|
LIBRELANE_CONFIG_DEPS += $(LIBRELANE_BONDPAD_GDS) $(LIBRELANE_BONDPAD_LEF)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: librelane-check-pdk librelane-input librelane-doctor librelane-run \
|
||||||
|
librelane-chip librelane-core librelane-openroad librelane-klayout \
|
||||||
|
librelane-package librelane-core-package librelane-package-run
|
||||||
|
|
||||||
librelane-check-pdk:
|
librelane-check-pdk:
|
||||||
@test '$(PDK)' = IHP130 || { \
|
@test '$(PDK)' = IHP130 || { \
|
||||||
echo 'physical/librelane currently implements only PDK=IHP130' >&2; exit 2; }
|
echo 'physical/librelane currently implements only PDK=IHP130' >&2; exit 2; }
|
||||||
@test '$(HAVE_PLL)' = NO || { \
|
@test '$(HAVE_PLL)' = NO || { \
|
||||||
echo 'the IHP130 LibreLane pad-ring flow has no qualified crystal PAD' >&2; exit 2; }
|
echo 'the IHP130 LibreLane flow has no qualified crystal PAD' >&2; exit 2; }
|
||||||
|
|
||||||
$(LIBRELANE_RTL): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP) $(LIBRELANE_TRACKED_RTL) \
|
$(LIBRELANE_RTL): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP) $(LIBRELANE_TRACKED_RTL) \
|
||||||
$(ROOT_PATH)/physical/smoke/syn/tools/export_soc_sources.py | librelane-check-pdk
|
$(ROOT_PATH)/physical/smoke/syn/tools/export_soc_sources.py | librelane-check-pdk
|
||||||
@@ -72,7 +90,8 @@ $(LIBRELANE_SDC): $(LIBRELANE_FLOW_ROOT)/scripts/generate_sdc.py \
|
|||||||
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/generate_sdc.py \
|
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/generate_sdc.py \
|
||||||
--domains $(LIBRELANE_CLOCK_DOMAINS) --pin-map $(LIBRELANE_PIN_MAP) \
|
--domains $(LIBRELANE_CLOCK_DOMAINS) --pin-map $(LIBRELANE_PIN_MAP) \
|
||||||
--ext-clk-hz $(EXT_CLK_HZ) --aud-clk-hz $(AUD_CLK_HZ) \
|
--ext-clk-hz $(EXT_CLK_HZ) --aud-clk-hz $(AUD_CLK_HZ) \
|
||||||
$(if $(filter YES,$(HAVE_PLL)),--have-pll) --output $@
|
$(if $(filter YES,$(HAVE_PLL)),--have-pll) --target $(LIBRELANE_TARGET) \
|
||||||
|
--output $@
|
||||||
|
|
||||||
$(LIBRELANE_BONDPAD_GDS): $(LIBRELANE_BONDPAD_SCRIPT) | librelane-check-pdk
|
$(LIBRELANE_BONDPAD_GDS): $(LIBRELANE_BONDPAD_SCRIPT) | librelane-check-pdk
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
@@ -81,51 +100,62 @@ $(LIBRELANE_BONDPAD_GDS): $(LIBRELANE_BONDPAD_SCRIPT) | librelane-check-pdk
|
|||||||
-rd output=$@
|
-rd output=$@
|
||||||
@test -s $@
|
@test -s $@
|
||||||
|
|
||||||
$(LIBRELANE_CHIP_CONFIG): $(LIBRELANE_RTL) $(LIBRELANE_SDC) \
|
$(LIBRELANE_CONFIG): $(LIBRELANE_CONFIG_DEPS)
|
||||||
$(LIBRELANE_BONDPAD_GDS) $(LIBRELANE_BONDPAD_LEF) $(LIBRELANE_PDN) \
|
|
||||||
$(LIBRELANE_SRAM_VH) $(LIBRELANE_FLOW_ROOT)/scripts/generate_chip_config.py
|
|
||||||
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/generate_chip_config.py \
|
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/generate_chip_config.py \
|
||||||
--pin-map $(LIBRELANE_PIN_MAP) --rtl $(LIBRELANE_RTL) --sdc $(LIBRELANE_SDC) \
|
--pin-map $(LIBRELANE_PIN_MAP) --rtl $(LIBRELANE_RTL) --sdc $(LIBRELANE_SDC) \
|
||||||
--pdn $(LIBRELANE_PDN) --bondpad-gds $(LIBRELANE_BONDPAD_GDS) \
|
--pdn $(LIBRELANE_PDN) --bondpad-gds $(LIBRELANE_BONDPAD_GDS) \
|
||||||
--bondpad-lef $(LIBRELANE_BONDPAD_LEF) --sram-vh $(LIBRELANE_SRAM_VH) \
|
--bondpad-lef $(LIBRELANE_BONDPAD_LEF) --sram-vh $(LIBRELANE_SRAM_VH) \
|
||||||
--ext-clk-hz $(EXT_CLK_HZ) \
|
--ext-clk-hz $(EXT_CLK_HZ) --aud-clk-hz $(AUD_CLK_HZ) \
|
||||||
--aud-clk-hz $(AUD_CLK_HZ) $(if $(filter YES,$(HAVE_PLL)),--have-pll) \
|
$(if $(filter YES,$(HAVE_PLL)),--have-pll) --target $(LIBRELANE_TARGET) \
|
||||||
--output $@
|
--output $@
|
||||||
|
|
||||||
librelane-input: $(LIBRELANE_CHIP_CONFIG)
|
librelane-input: $(LIBRELANE_CONFIG)
|
||||||
|
|
||||||
$(LIBRELANE_DOCTOR_RESULT): $(LIBRELANE_CHIP_CONFIG) \
|
$(LIBRELANE_DOCTOR_RESULT): $(LIBRELANE_CONFIG) \
|
||||||
$(LIBRELANE_FLOW_ROOT)/scripts/doctor.py
|
$(LIBRELANE_FLOW_ROOT)/scripts/doctor.py
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
$(LIBRELANE_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/doctor.py \
|
$(LIBRELANE_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/doctor.py \
|
||||||
--root $(ROOT_PATH) --config $(LIBRELANE_CHIP_CONFIG) --lock $(LOCK_FILE) \
|
--root $(ROOT_PATH) --config $(LIBRELANE_CONFIG) --lock $(LOCK_FILE) \
|
||||||
--pdk-root $(LIBRELANE_PDK_ROOT) --output $@
|
--pdk-root $(LIBRELANE_PDK_ROOT) --output $@
|
||||||
|
|
||||||
librelane-doctor: $(LIBRELANE_DOCTOR_RESULT)
|
librelane-doctor: $(LIBRELANE_DOCTOR_RESULT)
|
||||||
|
|
||||||
librelane-chip: librelane-doctor manifest
|
librelane-run: librelane-doctor manifest
|
||||||
$(FLOW_PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool librelane \
|
$(FLOW_PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool librelane \
|
||||||
--log $(LIBRELANE_LOG) --result $(LIBRELANE_RESULT) --cwd $(LIBRELANE_RUN_ROOT) -- \
|
--log $(LIBRELANE_LOG) --result $(LIBRELANE_RESULT) --cwd $(LIBRELANE_RUN_ROOT) -- \
|
||||||
librelane $(LIBRELANE_CHIP_CONFIG) --manual-pdk --pdk $(LIBRELANE_PDK) \
|
librelane $(LIBRELANE_CONFIG) --manual-pdk --pdk $(LIBRELANE_PDK) \
|
||||||
--pdk-root $(LIBRELANE_PDK_ROOT) --scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
--pdk-root $(LIBRELANE_PDK_ROOT) --scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
||||||
--run-tag $(LIBRELANE_RUN_TAG) --overwrite --jobs $(JOBS) --condensed \
|
--run-tag $(LIBRELANE_RUN_TAG) --overwrite --jobs $(JOBS) $(LIBRELANE_MID_PNR_SKIP) \
|
||||||
--hide-progress-bar --save-views-to $(LIBRELANE_FINAL_DIR)
|
--condensed --hide-progress-bar --save-views-to $(LIBRELANE_FINAL_DIR)
|
||||||
@mkdir -p $(META_DIR)
|
@mkdir -p $(META_DIR)
|
||||||
@cp $(LIBRELANE_RESULT) $(LIBRELANE_META_RESULT)
|
@cp $(LIBRELANE_RESULT) $(LIBRELANE_META_RESULT)
|
||||||
|
|
||||||
|
librelane-chip:
|
||||||
|
+$(MAKE) LIBRELANE_TARGET=chip librelane-run
|
||||||
|
|
||||||
|
librelane-core:
|
||||||
|
+$(MAKE) LIBRELANE_TARGET=core librelane-run
|
||||||
|
|
||||||
librelane-openroad: librelane-doctor
|
librelane-openroad: librelane-doctor
|
||||||
cd $(LIBRELANE_RUN_ROOT) && librelane $(LIBRELANE_CHIP_CONFIG) --manual-pdk \
|
cd $(LIBRELANE_RUN_ROOT) && librelane $(LIBRELANE_CONFIG) --manual-pdk \
|
||||||
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
|
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
|
||||||
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
||||||
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInOpenROAD
|
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInOpenROAD
|
||||||
|
|
||||||
librelane-klayout: librelane-doctor
|
librelane-klayout: librelane-doctor
|
||||||
cd $(LIBRELANE_RUN_ROOT) && librelane $(LIBRELANE_CHIP_CONFIG) --manual-pdk \
|
cd $(LIBRELANE_RUN_ROOT) && librelane $(LIBRELANE_CONFIG) --manual-pdk \
|
||||||
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
|
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
|
||||||
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
||||||
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInKLayout
|
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInKLayout
|
||||||
|
|
||||||
librelane-package: librelane-chip
|
librelane-package-run: librelane-run
|
||||||
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/package.py \
|
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/package.py \
|
||||||
--root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \
|
--root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \
|
||||||
--run-root $(LIBRELANE_RUN_ROOT) --output $(LIBRELANE_RUN_ROOT)/retrosoc-ihp130-chip.tar.gz
|
--run-root $(LIBRELANE_RUN_ROOT) --target $(LIBRELANE_TARGET) \
|
||||||
|
--output $(LIBRELANE_ARCHIVE)
|
||||||
|
|
||||||
|
librelane-package:
|
||||||
|
+$(MAKE) LIBRELANE_TARGET=chip librelane-package-run
|
||||||
|
|
||||||
|
librelane-core-package:
|
||||||
|
+$(MAKE) LIBRELANE_TARGET=core librelane-package-run
|
||||||
@@ -1,29 +1,45 @@
|
|||||||
# LibreLane Full-Chip Flow
|
# LibreLane IHP130 Flows
|
||||||
|
|
||||||
This directory owns the open-source, single-level IHP130 pad-ring implementation
|
This directory owns the open-source IHP130 implementation entry points for two
|
||||||
for `retrosoc_asic`. It does not harden or deliver a separate digital-core macro.
|
independent `retrosoc` deliverables:
|
||||||
The three USB packet SRAMs remain PDK macros inside the single chip run.
|
|
||||||
|
|
||||||
The flow consumes the committed `configs/ci/ihp130.mk` profile, the canonical
|
- `librelane-core` hardens `retrosoc_core`, a padless logical macro with the
|
||||||
SoC pin map, and the IHP-Open-PDK revision in
|
canonical external pin contract and the three USB packet SRAM macros.
|
||||||
`dependencies/dependencies.lock.json`. LibreLane reads the upstream
|
- `librelane-chip` implements the bare-die `retrosoc_asic` package-facing SoC,
|
||||||
`ihp-sg13g2/libs.tech/librelane` configuration directly through manual-PDK
|
including its pad ring, bondpads, fillers, corners, seal ring, and the same
|
||||||
mode. Generated RTL, SDC, configuration, run databases, reports, and final
|
SRAM macros.
|
||||||
views are written below the selected `build/<variant>/physical/librelane/chip/`
|
|
||||||
directory.
|
These are separate single-level runs. The chip flow does not consume the core
|
||||||
|
run as a hierarchical macro, so either artifact can be developed and reviewed
|
||||||
|
without a two-level integration dependency.
|
||||||
|
|
||||||
|
The flow consumes the committed `configs/ci/ihp130.mk` profile, canonical SoC
|
||||||
|
pin map, and IHP-Open-PDK revision in `dependencies/dependencies.lock.json`.
|
||||||
|
LibreLane reads the upstream `ihp-sg13g2/libs.tech/librelane` configuration
|
||||||
|
directly through manual-PDK mode. Generated RTL, SDC, configuration, run
|
||||||
|
databases, reports, and final views are written below the selected
|
||||||
|
`build/<variant>/physical/librelane/{core,chip}/` directory.
|
||||||
|
|
||||||
Use the LibreLane 3.0.5 environment supplied by the development shell:
|
Use the LibreLane 3.0.5 environment supplied by the development shell:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-doctor
|
make CONFIG=configs/ci/ihp130.mk librelane-doctor
|
||||||
|
make CONFIG=configs/ci/ihp130.mk librelane-core
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-chip
|
make CONFIG=configs/ci/ihp130.mk librelane-chip
|
||||||
|
make CONFIG=configs/ci/ihp130.mk librelane-core-package
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-package
|
make CONFIG=configs/ci/ihp130.mk librelane-package
|
||||||
```
|
```
|
||||||
|
|
||||||
The initial bare-die plan contains all 109 signal pads, 80 core/IO power and
|
The chip target places all 109 signal pads, 80 core/IO power and ground pads,
|
||||||
ground pads, four corners, I/O fillers, 70 um bondpads, and a seal ring. It is
|
four corners, I/O fillers, and 70 um bondpads. It is a bare-die plan, not a
|
||||||
not a QFN/BGA package definition. `PDK=GF180`, `PDK=SKY130`, and `PDK=ICS55`
|
QFN/BGA package definition. The SDC constrains the external clock at 72 MHz;
|
||||||
remain explicit future adapter work and are rejected by this flow.
|
mid-PnR timing report passes are skipped because LibreLane 3.0.5 emits 1000
|
||||||
|
expanded paths per group, while final post-RCX multi-corner STA remains enabled.
|
||||||
|
|
||||||
|
`PDK=GF180`, `PDK=SKY130`, and `PDK=ICS55` are intentionally rejected until
|
||||||
|
their technology-specific LibreLane adapters, pad cells, macro collateral, and
|
||||||
|
signoff decks are qualified. The target split is technology-neutral so those
|
||||||
|
adapters can reuse the core/chip interface when they are ready.
|
||||||
|
|
||||||
IHP SG13G2 does not currently provide pad cells implementing retroSoC's
|
IHP SG13G2 does not currently provide pad cells implementing retroSoC's
|
||||||
programmable pull-up, pull-down, or Schmitt controls. The IHP implementation
|
programmable pull-up, pull-down, or Schmitt controls. The IHP implementation
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# IHP SG13G2 supply pads are physical-only cells and are not retained by Yosys.
|
||||||
|
foreach {kind master count} {
|
||||||
|
vdd sg13g2_IOPadVdd 24
|
||||||
|
vss sg13g2_IOPadVss 24
|
||||||
|
iovdd sg13g2_IOPadIOVdd 16
|
||||||
|
iovss sg13g2_IOPadIOVss 16
|
||||||
|
} {
|
||||||
|
for {set index 0} {$index < $count} {incr index} {
|
||||||
|
set instance_name [format {%s_pads[%d].%s_pad} $kind $index $kind]
|
||||||
|
set db_block [ord::get_db_block]
|
||||||
|
if {[$db_block findInst $instance_name] == "NULL"} {
|
||||||
|
make_instance $instance_name $master
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source $::env(SCRIPTS_DIR)/openroad/common/pad_cfg.tcl
|
||||||
@@ -28,6 +28,16 @@ foreach vdd $::env(VDD_NETS) gnd $::env(GND_NETS) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# I/O rails are pad-ring internal and are absent from the synthesized top-level.
|
||||||
|
foreach {net signal_type} {IOVDD POWER IOVSS GROUND} {
|
||||||
|
set db_net [[ord::get_db_block] findNet $net]
|
||||||
|
if {$db_net == "NULL"} {
|
||||||
|
set db_net [odb::dbNet_create [ord::get_db_block] $net]
|
||||||
|
$db_net setSpecial
|
||||||
|
$db_net setSigType $signal_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# IHP IO cells carry both core and external-IO power rails on lower-case pins.
|
# IHP IO cells carry both core and external-IO power rails on lower-case pins.
|
||||||
add_global_connection -net $::env(VDD_NET) -inst_pattern .* -pin_pattern vdd -power
|
add_global_connection -net $::env(VDD_NET) -inst_pattern .* -pin_pattern vdd -power
|
||||||
add_global_connection -net $::env(GND_NET) -inst_pattern .* -pin_pattern vss -ground
|
add_global_connection -net $::env(GND_NET) -inst_pattern .* -pin_pattern vss -ground
|
||||||
@@ -35,13 +45,47 @@ add_global_connection -net IOVDD -inst_pattern .* -pin_pattern iovdd -power
|
|||||||
add_global_connection -net IOVSS -inst_pattern .* -pin_pattern iovss -ground
|
add_global_connection -net IOVSS -inst_pattern .* -pin_pattern iovss -ground
|
||||||
global_connect -verbose
|
global_connect -verbose
|
||||||
|
|
||||||
|
# connect_by_abutment assigns IHP pad rails to corner-local *_RING nets. Rebind
|
||||||
|
# those physical pad and filler pins so the PDN knows they are VDD/VSS terminals.
|
||||||
|
proc reconnect_ihp_padring_rails {block} {
|
||||||
|
foreach {net_name pin_name} {
|
||||||
|
VDD vdd
|
||||||
|
VSS vss
|
||||||
|
IOVDD iovdd
|
||||||
|
IOVSS iovss
|
||||||
|
} {
|
||||||
|
set net [$block findNet $net_name]
|
||||||
|
foreach inst [$block getInsts] {
|
||||||
|
set master_name [[$inst getMaster] getName]
|
||||||
|
if {![string match "sg13g2_IOPad*" $master_name] &&
|
||||||
|
![string match "sg13g2_Filler*" $master_name] &&
|
||||||
|
![string match "sg13g2_Corner*" $master_name]} {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foreach iterm [$inst getITerms] {
|
||||||
|
if {[[$iterm getMTerm] getName] != $pin_name} {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
odb::dbITerm_disconnect $iterm
|
||||||
|
odb::dbITerm_connect $iterm $net
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reconnect_ihp_padring_rails [ord::get_db_block]
|
||||||
|
|
||||||
set_voltage_domain -name CORE -power $::env(VDD_NET) -ground $::env(GND_NET) \
|
set_voltage_domain -name CORE -power $::env(VDD_NET) -ground $::env(GND_NET) \
|
||||||
-secondary_power $secondary_supplies
|
-secondary_power $secondary_supplies
|
||||||
|
|
||||||
|
set stdcell_grid_args [list]
|
||||||
|
if {$::env(PDN_ENABLE_PINS) == 1} {
|
||||||
|
lappend stdcell_grid_args -pins "$::env(PDN_VERTICAL_LAYER) $::env(PDN_HORIZONTAL_LAYER)"
|
||||||
|
}
|
||||||
define_pdn_grid \
|
define_pdn_grid \
|
||||||
-name stdcell_grid \
|
-name stdcell_grid \
|
||||||
-starts_with POWER \
|
-starts_with POWER \
|
||||||
-voltage_domain CORE
|
-voltage_domain CORE \
|
||||||
|
{*}$stdcell_grid_args
|
||||||
|
|
||||||
add_pdn_stripe \
|
add_pdn_stripe \
|
||||||
-grid stdcell_grid \
|
-grid stdcell_grid \
|
||||||
|
|||||||
@@ -73,9 +73,23 @@ def main() -> int:
|
|||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
errors.append(f"required LibreLane/IHP file is missing: {path}")
|
errors.append(f"required LibreLane/IHP file is missing: {path}")
|
||||||
|
|
||||||
|
config: dict[str, object] = {}
|
||||||
|
flow_name = ""
|
||||||
|
if args.config.is_file():
|
||||||
|
try:
|
||||||
|
config = json.loads(args.config.read_text(encoding="utf-8"))
|
||||||
|
meta = config.get("meta", {})
|
||||||
|
if not isinstance(meta, dict) or not isinstance(meta.get("flow"), str):
|
||||||
|
raise ValueError("config meta.flow must name a LibreLane flow")
|
||||||
|
flow_name = meta["flow"]
|
||||||
|
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||||
|
errors.append(f"failed to read LibreLane configuration: {error}")
|
||||||
|
|
||||||
if args.config.is_file() and not errors:
|
if args.config.is_file() and not errors:
|
||||||
try:
|
try:
|
||||||
flow_class = Flow.factory.get("Chip")
|
flow_class = Flow.factory.get(flow_name)
|
||||||
|
if flow_class is None:
|
||||||
|
raise ValueError(f"unknown LibreLane flow: {flow_name}")
|
||||||
flow = flow_class(
|
flow = flow_class(
|
||||||
str(args.config.resolve()),
|
str(args.config.resolve()),
|
||||||
pdk="ihp-sg13g2",
|
pdk="ihp-sg13g2",
|
||||||
@@ -84,26 +98,29 @@ def main() -> int:
|
|||||||
pad="sg13g2_io",
|
pad="sg13g2_io",
|
||||||
design_dir=str(args.config.resolve().parent),
|
design_dir=str(args.config.resolve().parent),
|
||||||
)
|
)
|
||||||
|
details["flow"] = flow_name
|
||||||
details["config_design"] = flow.config["DESIGN_NAME"]
|
details["config_design"] = flow.config["DESIGN_NAME"]
|
||||||
details["drc_runset"] = str(flow.config["KLAYOUT_DRC_RUNSET"])
|
details["drc_runset"] = str(flow.config["KLAYOUT_DRC_RUNSET"])
|
||||||
details["lvs_setup"] = str(flow.config["NETGEN_SETUP"])
|
details["lvs_setup"] = str(flow.config["NETGEN_SETUP"])
|
||||||
details["pad_spice"] = [str(path) for path in flow.config["PAD_SPICE_MODELS"]]
|
details["pad_spice"] = [str(path) for path in flow.config["PAD_SPICE_MODELS"]]
|
||||||
except Exception as error: # LibreLane reports structured configuration exceptions.
|
except Exception as error: # LibreLane reports structured configuration exceptions.
|
||||||
errors.append(f"LibreLane Chip configuration failed to load: {error}")
|
errors.append(f"LibreLane {flow_name or 'unknown'} configuration failed to load: {error}")
|
||||||
|
|
||||||
if args.config.is_file():
|
if config:
|
||||||
config = json.loads(args.config.read_text(encoding="utf-8"))
|
|
||||||
placed = [
|
placed = [
|
||||||
item
|
item
|
||||||
for side in ("PAD_SOUTH", "PAD_EAST", "PAD_NORTH", "PAD_WEST")
|
for side in ("PAD_SOUTH", "PAD_EAST", "PAD_NORTH", "PAD_WEST")
|
||||||
for item in config.get(side, [])
|
for item in config.get(side, [])
|
||||||
]
|
]
|
||||||
expected = EXPECTED_SIGNAL_PADS + sum(IHP130_POWER_PAD_COUNTS.values())
|
|
||||||
details["placed_pad_count"] = len(placed)
|
details["placed_pad_count"] = len(placed)
|
||||||
if len(placed) != expected or len(placed) != len(set(placed)):
|
if flow_name == "Chip":
|
||||||
errors.append(
|
expected = EXPECTED_SIGNAL_PADS + sum(IHP130_POWER_PAD_COUNTS.values())
|
||||||
f"expected {expected} unique placed signal/power PADs, found {len(placed)}"
|
if len(placed) != expected or len(placed) != len(set(placed)):
|
||||||
)
|
errors.append(
|
||||||
|
f"expected {expected} unique placed signal/power PADs, found {len(placed)}"
|
||||||
|
)
|
||||||
|
elif flow_name == "Classic" and placed:
|
||||||
|
errors.append("core hardening configuration must not define pad-ring placement")
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
|
|||||||
@@ -64,11 +64,11 @@ def power_instances(side_index: int) -> list[str]:
|
|||||||
for local_index in range(6):
|
for local_index in range(6):
|
||||||
for kind in ("vdd", "vss"):
|
for kind in ("vdd", "vss"):
|
||||||
index = (side_index * 6) + local_index
|
index = (side_index * 6) + local_index
|
||||||
result.append(f"{kind}_pads\\[{index}\\].{kind}_pad")
|
result.append(f"{kind}_pads[{index}].{kind}_pad")
|
||||||
if local_index < 4:
|
if local_index < 4:
|
||||||
for kind in ("iovdd", "iovss"):
|
for kind in ("iovdd", "iovss"):
|
||||||
index = (side_index * 4) + local_index
|
index = (side_index * 4) + local_index
|
||||||
result.append(f"{kind}_pads\\[{index}\\].{kind}_pad")
|
result.append(f"{kind}_pads[{index}].{kind}_pad")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@@ -106,6 +106,9 @@ def macro_config(
|
|||||||
|
|
||||||
|
|
||||||
def build_config(args: argparse.Namespace) -> dict[str, object]:
|
def build_config(args: argparse.Namespace) -> dict[str, object]:
|
||||||
|
target = getattr(args, "target", "chip")
|
||||||
|
if target not in {"chip", "core"}:
|
||||||
|
raise ValueError("target must be chip or core")
|
||||||
pads, _ = read_map(args.pin_map)
|
pads, _ = read_map(args.pin_map)
|
||||||
active_pads = [pad for pad in pads if pad.feature is None or args.have_pll]
|
active_pads = [pad for pad in pads if pad.feature is None or args.have_pll]
|
||||||
signal_instances: dict[str, list[str]] = {side: [] for side in SIDE_ORDER}
|
signal_instances: dict[str, list[str]] = {side: [] for side in SIDE_ORDER}
|
||||||
@@ -152,44 +155,31 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
common = {
|
||||||
"meta": {"version": 3, "flow": "Chip"},
|
|
||||||
"DESIGN_NAME": "retrosoc_asic",
|
|
||||||
"VERILOG_FILES": [config_path(args.rtl, config_dir)],
|
"VERILOG_FILES": [config_path(args.rtl, config_dir)],
|
||||||
"USE_SLANG": True,
|
"USE_SLANG": True,
|
||||||
"SLANG_ARGUMENTS": ["--keep-hierarchy"],
|
"SLANG_ARGUMENTS": ["--keep-hierarchy"],
|
||||||
|
"VERILOG_POWER_DEFINE": None,
|
||||||
"SYNTH_SHARE_RESOURCES": False,
|
"SYNTH_SHARE_RESOURCES": False,
|
||||||
"SYNTH_HIERARCHY_MODE": "deferred_flatten",
|
"SYNTH_HIERARCHY_MODE": "deferred_flatten",
|
||||||
"YOSYS_LOG_LEVEL": "WARNING",
|
"YOSYS_LOG_LEVEL": "WARNING",
|
||||||
|
"SYNTH_STRATEGY": "AREA 3",
|
||||||
"PRIMARY_GDSII_STREAMOUT_TOOL": "klayout",
|
"PRIMARY_GDSII_STREAMOUT_TOOL": "klayout",
|
||||||
"PAD_SOUTH": pad_sides["SOUTH"],
|
|
||||||
"PAD_EAST": pad_sides["EAST"],
|
|
||||||
"PAD_NORTH": pad_sides["NORTH"],
|
|
||||||
"PAD_WEST": pad_sides["WEST"],
|
|
||||||
"PNR_SDC_FILE": config_path(args.sdc, config_dir),
|
"PNR_SDC_FILE": config_path(args.sdc, config_dir),
|
||||||
"SIGNOFF_SDC_FILE": config_path(args.sdc, config_dir),
|
"SIGNOFF_SDC_FILE": config_path(args.sdc, config_dir),
|
||||||
"FALLBACK_SDC": config_path(args.sdc, config_dir),
|
"FALLBACK_SDC": config_path(args.sdc, config_dir),
|
||||||
"CLOCK_PORT": list(CLOCK_PORTS),
|
"CLOCK_PORT": list(CLOCK_PORTS),
|
||||||
"CLOCK_PERIOD": 1_000_000_000 / args.ext_clk_hz,
|
"CLOCK_PERIOD": 1_000_000_000 / args.ext_clk_hz,
|
||||||
"VDD_NETS": ["VDD", "IOVDD"],
|
"VDD_NETS": ["VDD"],
|
||||||
"GND_NETS": ["VSS", "IOVSS"],
|
"GND_NETS": ["VSS"],
|
||||||
"FP_SIZING": "absolute",
|
"FP_SIZING": "absolute",
|
||||||
"DIE_AREA": [0, 0, 8000, 8000],
|
|
||||||
"CORE_AREA": [365, 365, 7635, 7635],
|
|
||||||
"PL_TARGET_DENSITY_PCT": 30,
|
|
||||||
"PDN_CORE_RING": True,
|
"PDN_CORE_RING": True,
|
||||||
"PDN_CORE_RING_VWIDTH": 15,
|
"PDN_CORE_RING_VWIDTH": 15,
|
||||||
"PDN_CORE_RING_HWIDTH": 15,
|
"PDN_CORE_RING_HWIDTH": 15,
|
||||||
"PDN_CORE_RING_VSPACING": 5,
|
"PDN_CORE_RING_VSPACING": 5,
|
||||||
"PDN_CORE_RING_HSPACING": 5,
|
"PDN_CORE_RING_HSPACING": 5,
|
||||||
"PDN_CORE_RING_CONNECT_TO_PADS": True,
|
"PDN_ENABLE_PINS": True,
|
||||||
"PDN_ENABLE_PINS": False,
|
|
||||||
"PDN_CFG": config_path(args.pdn, config_dir),
|
"PDN_CFG": config_path(args.pdn, config_dir),
|
||||||
"PAD_BONDPAD_NAME": "bondpad_70x70",
|
|
||||||
"EXTRA_GDS": [config_path(args.bondpad_gds, config_dir)],
|
|
||||||
"EXTRA_LEFS": [config_path(args.bondpad_lef, config_dir)],
|
|
||||||
"IGNORE_DISCONNECTED_MODULES": ["bondpad_70x70"],
|
|
||||||
"MAGIC_EXT_UNIQUE": "notopports",
|
|
||||||
"MACROS": macros,
|
"MACROS": macros,
|
||||||
"PDN_MACRO_CONNECTIONS": macro_hooks,
|
"PDN_MACRO_CONNECTIONS": macro_hooks,
|
||||||
"MAGIC_GDS_FLATGLOB": [
|
"MAGIC_GDS_FLATGLOB": [
|
||||||
@@ -201,6 +191,41 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
|
|||||||
"*_CELL_SUB",
|
"*_CELL_SUB",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
if target == "core":
|
||||||
|
return {
|
||||||
|
"meta": {"version": 3, "flow": "Classic"},
|
||||||
|
"DESIGN_NAME": "retrosoc_core",
|
||||||
|
"DIE_AREA": [0, 0, 6000, 6000],
|
||||||
|
"CORE_AREA": [120, 120, 5880, 5880],
|
||||||
|
"PL_TARGET_DENSITY_PCT": 45,
|
||||||
|
"PDN_CORE_RING_CONNECT_TO_PADS": False,
|
||||||
|
**common,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"meta": {"version": 3, "flow": "Chip"},
|
||||||
|
"DESIGN_NAME": "retrosoc_asic",
|
||||||
|
"SYNTH_KEEP_HIERARCHY_MODULES": [
|
||||||
|
"sg13g2_IOPadVdd",
|
||||||
|
"sg13g2_IOPadVss",
|
||||||
|
"sg13g2_IOPadIOVdd",
|
||||||
|
"sg13g2_IOPadIOVss",
|
||||||
|
],
|
||||||
|
"PAD_SOUTH": pad_sides["SOUTH"],
|
||||||
|
"PAD_EAST": pad_sides["EAST"],
|
||||||
|
"PAD_NORTH": pad_sides["NORTH"],
|
||||||
|
"PAD_WEST": pad_sides["WEST"],
|
||||||
|
"DIE_AREA": [0, 0, 8000, 8000],
|
||||||
|
"CORE_AREA": [365, 365, 7635, 7635],
|
||||||
|
"PL_TARGET_DENSITY_PCT": 30,
|
||||||
|
"PDN_CORE_RING_CONNECT_TO_PADS": True,
|
||||||
|
"PAD_CFG": config_path(ROOT / "physical/librelane/pad_cfg.tcl", config_dir),
|
||||||
|
"PAD_BONDPAD_NAME": "bondpad_70x70",
|
||||||
|
"EXTRA_GDS": [config_path(args.bondpad_gds, config_dir)],
|
||||||
|
"EXTRA_LEFS": [config_path(args.bondpad_lef, config_dir)],
|
||||||
|
"IGNORE_DISCONNECTED_MODULES": ["bondpad_70x70"],
|
||||||
|
"MAGIC_EXT_UNIQUE": "notopports",
|
||||||
|
**common,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def positive_integer(value: str) -> int:
|
def positive_integer(value: str) -> int:
|
||||||
@@ -222,6 +247,7 @@ def main() -> int:
|
|||||||
parser.add_argument("--ext-clk-hz", type=positive_integer, required=True)
|
parser.add_argument("--ext-clk-hz", type=positive_integer, required=True)
|
||||||
parser.add_argument("--aud-clk-hz", type=positive_integer, required=True)
|
parser.add_argument("--aud-clk-hz", type=positive_integer, required=True)
|
||||||
parser.add_argument("--have-pll", action="store_true")
|
parser.add_argument("--have-pll", action="store_true")
|
||||||
|
parser.add_argument("--target", choices=("chip", "core"), default="chip")
|
||||||
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:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ CLOCK_PADS = {
|
|||||||
"usb2_ulpi": "usb2_ulpi_clk_i_pad",
|
"usb2_ulpi": "usb2_ulpi_clk_i_pad",
|
||||||
}
|
}
|
||||||
RESET_PADS = {"ext_rst_n_i_pad", "jtag_trst_n_i_pad"}
|
RESET_PADS = {"ext_rst_n_i_pad", "jtag_trst_n_i_pad"}
|
||||||
|
IHP130_SYSTEM_CLOCK_PIN = "u_rcu.u_sys_clk_buf.u_sg13g2_buf_1/X"
|
||||||
|
|
||||||
|
|
||||||
def positive_integer(value: str) -> int:
|
def positive_integer(value: str) -> int:
|
||||||
@@ -58,7 +59,9 @@ def logical_directions(pad: Pad) -> tuple[bool, bool]:
|
|||||||
|
|
||||||
def render(args: argparse.Namespace) -> str:
|
def render(args: argparse.Namespace) -> str:
|
||||||
validate(args.domains, ROOT)
|
validate(args.domains, ROOT)
|
||||||
document = json.loads(args.domains.read_text(encoding="utf-8"))
|
target = getattr(args, "target", "chip")
|
||||||
|
if target not in {"chip", "core"}:
|
||||||
|
raise ValueError("target must be chip or core")
|
||||||
pads, _ = read_map(args.pin_map)
|
pads, _ = read_map(args.pin_map)
|
||||||
active = [pad for pad in pads if pad.feature is None or args.have_pll]
|
active = [pad for pad in pads if pad.feature is None or args.have_pll]
|
||||||
by_name = {pad.name: pad for pad in active}
|
by_name = {pad.name: pad for pad in active}
|
||||||
@@ -106,19 +109,21 @@ def render(args: argparse.Namespace) -> str:
|
|||||||
]
|
]
|
||||||
clock_vars: dict[str, str] = {}
|
clock_vars: dict[str, str] = {}
|
||||||
for name, pad_name in CLOCK_PADS.items():
|
for name, pad_name in CLOCK_PADS.items():
|
||||||
instance = ihp130_pad_instance(by_name[pad_name])
|
|
||||||
if instance is None:
|
|
||||||
raise ValueError(f"clock PAD has no IHP130 physical instance: {pad_name}")
|
|
||||||
variable = f"clk_{name}_pin"
|
variable = f"clk_{name}_pin"
|
||||||
lines.append(f'set {variable} [require_pins "clock {name}" {{{instance}/p2c}}]')
|
if target == "chip":
|
||||||
|
instance = ihp130_pad_instance(by_name[pad_name])
|
||||||
|
if instance is None:
|
||||||
|
raise ValueError(f"clock PAD has no IHP130 physical instance: {pad_name}")
|
||||||
|
lines.append(f'set {variable} [require_pins "clock {name}" {{{instance}/p2c}}]')
|
||||||
|
else:
|
||||||
|
lines.append(f'set {variable} [require_ports "clock {name}" {{{pad_name}}}]')
|
||||||
lines.append(f"create_clock -name clk_{name} -period {periods[name]:.12g} ${variable}")
|
lines.append(f"create_clock -name clk_{name} -period {periods[name]:.12g} ${variable}")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
clock_vars[name] = variable
|
clock_vars[name] = variable
|
||||||
|
|
||||||
system_pin = document["domains"][1]["sta"]["pin"]
|
|
||||||
lines.extend(
|
lines.extend(
|
||||||
[
|
[
|
||||||
f'set clk_system_pin [require_pins "clock system" {{{system_pin}}}]',
|
f'set clk_system_pin [require_pins "clock system" {{{IHP130_SYSTEM_CLOCK_PIN}}}]',
|
||||||
"create_generated_clock -name clk_system -source $clk_external_pin "
|
"create_generated_clock -name clk_system -source $clk_external_pin "
|
||||||
"-divide_by 1 $clk_system_pin",
|
"-divide_by 1 $clk_system_pin",
|
||||||
"",
|
"",
|
||||||
@@ -175,6 +180,7 @@ def main() -> int:
|
|||||||
parser.add_argument("--ext-clk-hz", type=positive_integer, required=True)
|
parser.add_argument("--ext-clk-hz", type=positive_integer, required=True)
|
||||||
parser.add_argument("--aud-clk-hz", type=positive_integer, required=True)
|
parser.add_argument("--aud-clk-hz", type=positive_integer, required=True)
|
||||||
parser.add_argument("--have-pll", action="store_true")
|
parser.add_argument("--have-pll", action="store_true")
|
||||||
|
parser.add_argument("--target", choices=("chip", "core"), default="chip")
|
||||||
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:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Create a checksummed LibreLane full-chip delivery archive."""
|
"""Create a checksummed LibreLane implementation delivery archive."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ def main() -> int:
|
|||||||
parser.add_argument("--root", type=Path, required=True)
|
parser.add_argument("--root", type=Path, required=True)
|
||||||
parser.add_argument("--variant-root", type=Path, required=True)
|
parser.add_argument("--variant-root", type=Path, required=True)
|
||||||
parser.add_argument("--run-root", type=Path, required=True)
|
parser.add_argument("--run-root", type=Path, required=True)
|
||||||
|
parser.add_argument("--target", choices=("chip", "core"), 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()
|
||||||
root = args.root.resolve()
|
root = args.root.resolve()
|
||||||
@@ -35,8 +36,8 @@ def main() -> int:
|
|||||||
run_root / "final",
|
run_root / "final",
|
||||||
run_root / "runs/current",
|
run_root / "runs/current",
|
||||||
variant_root / "meta/manifest.json",
|
variant_root / "meta/manifest.json",
|
||||||
variant_root / "meta/librelane-doctor.json",
|
variant_root / f"meta/librelane-{args.target}-doctor.json",
|
||||||
variant_root / "meta/librelane-chip.json",
|
variant_root / f"meta/librelane-{args.target}.json",
|
||||||
root / "dependencies/dependencies.lock.json",
|
root / "dependencies/dependencies.lock.json",
|
||||||
)
|
)
|
||||||
missing = [str(path) for path in required if not path.exists()]
|
missing = [str(path) for path in required if not path.exists()]
|
||||||
@@ -45,19 +46,19 @@ def main() -> int:
|
|||||||
|
|
||||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with tempfile.TemporaryDirectory(prefix="retrosoc-librelane-package-") as temporary:
|
with tempfile.TemporaryDirectory(prefix="retrosoc-librelane-package-") as temporary:
|
||||||
staging = Path(temporary) / "retrosoc-ihp130-chip"
|
staging = Path(temporary) / f"retrosoc-ihp130-{args.target}"
|
||||||
shutil.copytree(run_root / "final", staging / "final")
|
shutil.copytree(run_root / "final", staging / "final")
|
||||||
shutil.copytree(run_root / "runs/current", staging / "evidence/run")
|
shutil.copytree(run_root / "runs/current", staging / "evidence/run")
|
||||||
shutil.copy2(run_root / "config.json", staging / "evidence/config.json")
|
shutil.copy2(run_root / "config.json", staging / "evidence/config.json")
|
||||||
shutil.copy2(run_root / "result.json", staging / "evidence/result.json")
|
shutil.copy2(run_root / "result.json", staging / "evidence/result.json")
|
||||||
shutil.copy2(variant_root / "meta/manifest.json", staging / "evidence/manifest.json")
|
shutil.copy2(variant_root / "meta/manifest.json", staging / "evidence/manifest.json")
|
||||||
shutil.copy2(
|
shutil.copy2(
|
||||||
variant_root / "meta/librelane-doctor.json",
|
variant_root / f"meta/librelane-{args.target}-doctor.json",
|
||||||
staging / "evidence/librelane-doctor.json",
|
staging / "evidence/librelane-doctor.json",
|
||||||
)
|
)
|
||||||
shutil.copy2(
|
shutil.copy2(
|
||||||
variant_root / "meta/librelane-chip.json",
|
variant_root / f"meta/librelane-{args.target}.json",
|
||||||
staging / "evidence/librelane-chip.json",
|
staging / f"evidence/librelane-{args.target}.json",
|
||||||
)
|
)
|
||||||
shutil.copy2(
|
shutil.copy2(
|
||||||
root / "dependencies/dependencies.lock.json",
|
root / "dependencies/dependencies.lock.json",
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ at TT and slow corners. Derived Liberty files are stored below
|
|||||||
the locked HD library submodule; the remaining upstream libraries are outside
|
the locked HD library submodule; the remaining upstream libraries are outside
|
||||||
the SoC integration boundary.
|
the SoC integration boundary.
|
||||||
|
|
||||||
The IHP130 dependency is also the technology source for the single-level
|
The IHP130 dependency is also the technology source for the independent
|
||||||
`physical/librelane/` full-chip flow. The locked checkout includes the upstream
|
`physical/librelane/` core-hardening and full-chip pad-ring flows. The locked
|
||||||
`libs.tech/librelane` standard-cell, pad, extraction, DRC, LVS, filler, bondpad,
|
checkout includes the upstream `libs.tech/librelane` standard-cell, pad,
|
||||||
and seal-ring configuration; LibreLane consumes it directly in manual-PDK mode.
|
extraction, DRC, LVS, filler, bondpad, and seal-ring configuration; LibreLane
|
||||||
The other three open PDKs do not yet have project-owned LibreLane adapters.
|
consumes it directly in manual-PDK mode. The other three open PDKs do not yet
|
||||||
|
have project-owned LibreLane adapters.
|
||||||
|
|
||||||
All four PDK profiles run slow-corner OpenSTA core timing in CI. The analysis
|
All four PDK profiles run slow-corner OpenSTA core timing in CI. The analysis
|
||||||
uses the PDK standard-cell Liberty plus the linked IO-cell model required to
|
uses the PDK standard-cell Liberty plus the linked IO-cell model required to
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Tests for the single-level IHP130 LibreLane pad-ring flow."""
|
"""Tests for IHP130 LibreLane core and pad-ring flows."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -52,10 +52,10 @@ def test_chip_config_places_every_signal_and_power_pad_once(tmp_path: Path) -> N
|
|||||||
"north": 48,
|
"north": 48,
|
||||||
"west": 59,
|
"west": 59,
|
||||||
}
|
}
|
||||||
assert sum(item.startswith("vdd_pads\\[") for item in placed) == 24
|
assert sum(item.startswith("vdd_pads[") for item in placed) == 24
|
||||||
assert sum(item.startswith("vss_pads\\[") for item in placed) == 24
|
assert sum(item.startswith("vss_pads[") for item in placed) == 24
|
||||||
assert sum(item.startswith("iovdd_pads\\[") for item in placed) == 16
|
assert sum(item.startswith("iovdd_pads[") for item in placed) == 16
|
||||||
assert sum(item.startswith("iovss_pads\\[") for item in placed) == 16
|
assert sum(item.startswith("iovss_pads[") for item in placed) == 16
|
||||||
assert "u_extclk_i_pad.u_sg13g2_IOPadIn" in sides["south"]
|
assert "u_extclk_i_pad.u_sg13g2_IOPadIn" in sides["south"]
|
||||||
assert "u_gpio_31_io_pad.u_sg13g2_IOPadInOut4mA" in sides["east"]
|
assert "u_gpio_31_io_pad.u_sg13g2_IOPadInOut4mA" in sides["east"]
|
||||||
assert "u_sdram_dq15_io_pad.u_sg13g2_IOPadInOut4mA" in sides["west"]
|
assert "u_sdram_dq15_io_pad.u_sg13g2_IOPadInOut4mA" in sides["west"]
|
||||||
@@ -63,11 +63,21 @@ def test_chip_config_places_every_signal_and_power_pad_once(tmp_path: Path) -> N
|
|||||||
assert config["CORE_AREA"] == [365, 365, 7635, 7635]
|
assert config["CORE_AREA"] == [365, 365, 7635, 7635]
|
||||||
assert config["USE_SLANG"] is True
|
assert config["USE_SLANG"] is True
|
||||||
assert config["SLANG_ARGUMENTS"] == ["--keep-hierarchy"]
|
assert config["SLANG_ARGUMENTS"] == ["--keep-hierarchy"]
|
||||||
|
assert config["VERILOG_POWER_DEFINE"] is None
|
||||||
assert config["SYNTH_SHARE_RESOURCES"] is False
|
assert config["SYNTH_SHARE_RESOURCES"] is False
|
||||||
assert config["SYNTH_HIERARCHY_MODE"] == "deferred_flatten"
|
assert config["SYNTH_HIERARCHY_MODE"] == "deferred_flatten"
|
||||||
|
assert config["SYNTH_KEEP_HIERARCHY_MODULES"] == [
|
||||||
|
"sg13g2_IOPadVdd",
|
||||||
|
"sg13g2_IOPadVss",
|
||||||
|
"sg13g2_IOPadIOVdd",
|
||||||
|
"sg13g2_IOPadIOVss",
|
||||||
|
]
|
||||||
assert config["YOSYS_LOG_LEVEL"] == "WARNING"
|
assert config["YOSYS_LOG_LEVEL"] == "WARNING"
|
||||||
assert config["VDD_NETS"] == ["VDD", "IOVDD"]
|
assert config["SYNTH_STRATEGY"] == "AREA 3"
|
||||||
assert config["GND_NETS"] == ["VSS", "IOVSS"]
|
assert config["VDD_NETS"] == ["VDD"]
|
||||||
|
assert config["GND_NETS"] == ["VSS"]
|
||||||
|
assert config["PAD_CFG"].endswith("physical/librelane/pad_cfg.tcl")
|
||||||
|
assert config["PDN_ENABLE_PINS"] is True
|
||||||
assert set(config["MACROS"]) == {
|
assert set(config["MACROS"]) == {
|
||||||
"RM_IHPSG13_1P_4096x16_c3_bm_bist",
|
"RM_IHPSG13_1P_4096x16_c3_bm_bist",
|
||||||
"RM_IHPSG13_1P_4096x8_c3_bm_bist",
|
"RM_IHPSG13_1P_4096x8_c3_bm_bist",
|
||||||
@@ -75,6 +85,28 @@ def test_chip_config_places_every_signal_and_power_pad_once(tmp_path: Path) -> N
|
|||||||
assert len(config["PDN_MACRO_CONNECTIONS"]) == 6
|
assert len(config["PDN_MACRO_CONNECTIONS"]) == 6
|
||||||
|
|
||||||
|
|
||||||
|
def test_core_config_uses_a_padless_classic_flow(tmp_path: Path) -> None:
|
||||||
|
module = load_module(
|
||||||
|
"retrosoc_librelane_core_config", FLOW_ROOT / "scripts/generate_chip_config.py"
|
||||||
|
)
|
||||||
|
arguments = chip_arguments(tmp_path)
|
||||||
|
arguments.target = "core"
|
||||||
|
config = module.build_config(arguments)
|
||||||
|
|
||||||
|
assert config["meta"] == {"version": 3, "flow": "Classic"}
|
||||||
|
assert config["DESIGN_NAME"] == "retrosoc_core"
|
||||||
|
assert config["DIE_AREA"] == [0, 0, 6000, 6000]
|
||||||
|
assert config["CORE_AREA"] == [120, 120, 5880, 5880]
|
||||||
|
assert config["PDN_CORE_RING_CONNECT_TO_PADS"] is False
|
||||||
|
assert "PAD_CFG" not in config
|
||||||
|
assert not any(key.startswith("PAD_") for key in config)
|
||||||
|
assert "PAD_BONDPAD_NAME" not in config
|
||||||
|
assert set(config["MACROS"]) == {
|
||||||
|
"RM_IHPSG13_1P_4096x16_c3_bm_bist",
|
||||||
|
"RM_IHPSG13_1P_4096x8_c3_bm_bist",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_chip_sdc_is_pad_aware_and_does_not_false_path_all_io(tmp_path: Path) -> None:
|
def test_chip_sdc_is_pad_aware_and_does_not_false_path_all_io(tmp_path: Path) -> None:
|
||||||
module = load_module("retrosoc_librelane_sdc", FLOW_ROOT / "scripts/generate_sdc.py")
|
module = load_module("retrosoc_librelane_sdc", FLOW_ROOT / "scripts/generate_sdc.py")
|
||||||
arguments = SimpleNamespace(
|
arguments = SimpleNamespace(
|
||||||
@@ -88,6 +120,7 @@ def test_chip_sdc_is_pad_aware_and_does_not_false_path_all_io(tmp_path: Path) ->
|
|||||||
|
|
||||||
assert "u_extclk_i_pad.u_sg13g2_IOPadIn/p2c" in sdc
|
assert "u_extclk_i_pad.u_sg13g2_IOPadIn/p2c" in sdc
|
||||||
assert "u_usb2_ulpi_clk_i_pad.u_sg13g2_IOPadIn/p2c" in sdc
|
assert "u_usb2_ulpi_clk_i_pad.u_sg13g2_IOPadIn/p2c" in sdc
|
||||||
|
assert "u_rcu.u_sys_clk_buf.u_sg13g2_buf_1/X" in sdc
|
||||||
assert "create_generated_clock -name clk_system" in sdc
|
assert "create_generated_clock -name clk_system" in sdc
|
||||||
assert "set_clock_groups -name retrosoc_async -asynchronous" in sdc
|
assert "set_clock_groups -name retrosoc_async -asynchronous" in sdc
|
||||||
assert "set_input_delay -max" in sdc
|
assert "set_input_delay -max" in sdc
|
||||||
@@ -97,14 +130,35 @@ def test_chip_sdc_is_pad_aware_and_does_not_false_path_all_io(tmp_path: Path) ->
|
|||||||
assert "set_false_path -to [all_outputs]" not in sdc
|
assert "set_false_path -to [all_outputs]" not in sdc
|
||||||
|
|
||||||
|
|
||||||
def test_librelane_flow_is_single_level_chip_only() -> None:
|
def test_core_sdc_constrains_logical_top_ports(tmp_path: Path) -> None:
|
||||||
|
module = load_module("retrosoc_librelane_core_sdc", FLOW_ROOT / "scripts/generate_sdc.py")
|
||||||
|
arguments = SimpleNamespace(
|
||||||
|
domains=DOMAINS,
|
||||||
|
pin_map=PIN_MAP,
|
||||||
|
ext_clk_hz=72_000_000,
|
||||||
|
aud_clk_hz=18_432_000,
|
||||||
|
have_pll=False,
|
||||||
|
target="core",
|
||||||
|
)
|
||||||
|
sdc = module.render(arguments)
|
||||||
|
|
||||||
|
assert 'require_ports "clock external" {extclk_i_pad}' in sdc
|
||||||
|
assert 'require_ports "clock usb2_ulpi" {usb2_ulpi_clk_i_pad}' in sdc
|
||||||
|
assert "u_extclk_i_pad.u_sg13g2_IOPadIn/p2c" not in sdc
|
||||||
|
assert "u_rcu.u_sys_clk_buf.u_sg13g2_buf_1/X" in sdc
|
||||||
|
|
||||||
|
|
||||||
|
def test_librelane_flow_exposes_core_and_chip_targets() -> None:
|
||||||
makefile = (FLOW_ROOT / "Makefile").read_text(encoding="utf-8")
|
makefile = (FLOW_ROOT / "Makefile").read_text(encoding="utf-8")
|
||||||
top_makefile = (ROOT / "Makefile").read_text(encoding="utf-8")
|
top_makefile = (ROOT / "Makefile").read_text(encoding="utf-8")
|
||||||
lock = json.loads((ROOT / "dependencies/dependencies.lock.json").read_text())
|
lock = json.loads((ROOT / "dependencies/dependencies.lock.json").read_text())
|
||||||
|
|
||||||
assert "librelane-chip:" in makefile
|
assert "librelane-chip:" in makefile
|
||||||
|
assert "librelane-core:" in makefile
|
||||||
|
assert "librelane-core-package:" in makefile
|
||||||
|
assert "LIBRELANE_TARGET ?= chip" in makefile
|
||||||
|
assert "--skip OpenROAD.STAMidPNR" in makefile
|
||||||
assert "--librelane-safe" in makefile
|
assert "--librelane-safe" in makefile
|
||||||
assert "librelane-core" not in makefile
|
|
||||||
assert "librelane-all" not in makefile
|
assert "librelane-all" not in makefile
|
||||||
assert "include physical/librelane/Makefile" in top_makefile
|
assert "include physical/librelane/Makefile" in top_makefile
|
||||||
ihp_filelist = (ROOT / "rtl/filelist/pdk_ihp130.fl").read_text(encoding="utf-8")
|
ihp_filelist = (ROOT / "rtl/filelist/pdk_ihp130.fl").read_text(encoding="utf-8")
|
||||||
@@ -126,6 +180,13 @@ def test_pdn_connects_all_ihp_pad_power_rails() -> None:
|
|||||||
|
|
||||||
assert "findNet $vdd" in pdn
|
assert "findNet $vdd" in pdn
|
||||||
assert "findNet $gnd" in pdn
|
assert "findNet $gnd" in pdn
|
||||||
|
assert "foreach {net signal_type} {IOVDD POWER IOVSS GROUND}" in pdn
|
||||||
|
pad_cfg = (FLOW_ROOT / "pad_cfg.tcl").read_text(encoding="utf-8")
|
||||||
|
assert "make_instance $instance_name $master" in pad_cfg
|
||||||
|
assert "proc reconnect_ihp_padring_rails" in pdn
|
||||||
|
assert "odb::dbITerm_connect $iterm $net" in pdn
|
||||||
|
assert "set stdcell_grid_args [list]" in pdn
|
||||||
|
assert '-pins "$::env(PDN_VERTICAL_LAYER) $::env(PDN_HORIZONTAL_LAYER)"' in pdn
|
||||||
assert "-net $::env(VDD_NET) -inst_pattern .* -pin_pattern vdd -power" in pdn
|
assert "-net $::env(VDD_NET) -inst_pattern .* -pin_pattern vdd -power" in pdn
|
||||||
assert "-net $::env(GND_NET) -inst_pattern .* -pin_pattern vss -ground" in pdn
|
assert "-net $::env(GND_NET) -inst_pattern .* -pin_pattern vss -ground" in pdn
|
||||||
assert "-net IOVDD -inst_pattern .* -pin_pattern iovdd -power" in pdn
|
assert "-net IOVDD -inst_pattern .* -pin_pattern iovdd -power" in pdn
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
|
|||||||
|
|
||||||
ports = (tmp_path / "rtl/retrosoc_asic_ports.svh").read_text(encoding="utf-8")
|
ports = (tmp_path / "rtl/retrosoc_asic_ports.svh").read_text(encoding="utf-8")
|
||||||
pads = (tmp_path / "rtl/retrosoc_asic_pad_bindings.svh").read_text(encoding="utf-8")
|
pads = (tmp_path / "rtl/retrosoc_asic_pad_bindings.svh").read_text(encoding="utf-8")
|
||||||
|
core_ports = (tmp_path / "rtl/retrosoc_core_ports.svh").read_text(encoding="utf-8")
|
||||||
|
core_bindings = (tmp_path / "rtl/retrosoc_core_bindings.svh").read_text(encoding="utf-8")
|
||||||
fpga = (tmp_path / "rtl/retrosoc_asic_fpga_mini_bindings.svh").read_text(encoding="utf-8")
|
fpga = (tmp_path / "rtl/retrosoc_asic_fpga_mini_bindings.svh").read_text(encoding="utf-8")
|
||||||
testbench = (tmp_path / "rtl/retrosoc_asic_tb_bindings.svh").read_text(encoding="utf-8")
|
testbench = (tmp_path / "rtl/retrosoc_asic_tb_bindings.svh").read_text(encoding="utf-8")
|
||||||
verilator = (tmp_path / "rtl/retrosoc_asic_verilator_bindings.svh").read_text(encoding="utf-8")
|
verilator = (tmp_path / "rtl/retrosoc_asic_verilator_bindings.svh").read_text(encoding="utf-8")
|
||||||
@@ -52,6 +54,11 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
|
|||||||
assert "inout wire IOVSS," in ports
|
assert "inout wire IOVSS," in ports
|
||||||
assert "inout wire VDD," in ports
|
assert "inout wire VDD," in ports
|
||||||
assert "inout wire VSS," in ports
|
assert "inout wire VSS," in ports
|
||||||
|
assert "inout wire VDD," not in core_ports
|
||||||
|
assert "inout extclk_i_pad," in core_ports
|
||||||
|
assert "assign s_ext_clk = extclk_i_pad;" in core_bindings
|
||||||
|
assert "assign gpio_0_io_pad = u_gpio_if.oe_o[0] ? u_gpio_if.do_o[0] : 1'bz;" in core_bindings
|
||||||
|
assert "assign u_sdram_if.dq_i[15] = sdram_dq15_io_pad;" in core_bindings
|
||||||
assert "inout extclk_i_pad," in ports
|
assert "inout extclk_i_pad," in ports
|
||||||
assert "input xi_i_pad," in ports
|
assert "input xi_i_pad," in ports
|
||||||
assert "inout sdram_dq15_io_pad" in ports
|
assert "inout sdram_dq15_io_pad" in ports
|
||||||
@@ -78,6 +85,7 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
|
|||||||
assert "tc_io_in_pad u_usb2_ulpi_clk_i_pad" in pads
|
assert "tc_io_in_pad u_usb2_ulpi_clk_i_pad" in pads
|
||||||
assert ".p2c(s_usb2_ulpi_clk)" in pads
|
assert ".p2c(s_usb2_ulpi_clk)" in pads
|
||||||
assert "`define RETROSOC_PAD_POWER_CONNECTIONS" in pads
|
assert "`define RETROSOC_PAD_POWER_CONNECTIONS" in pads
|
||||||
|
assert "sg13g2_IOPadIOVdd iovdd_pad" in pads
|
||||||
assert "for (genvar i = 0; i < 24; i++) begin : vdd_pads" in pads
|
assert "for (genvar i = 0; i < 24; i++) begin : vdd_pads" in pads
|
||||||
assert "for (genvar i = 0; i < 24; i++) begin : vss_pads" in pads
|
assert "for (genvar i = 0; i < 24; i++) begin : vss_pads" in pads
|
||||||
assert "for (genvar i = 0; i < 16; i++) begin : iovdd_pads" in pads
|
assert "for (genvar i = 0; i < 16; i++) begin : iovdd_pads" in pads
|
||||||
|
|||||||
Reference in new issue
Block a user