feat: add syn and floorplan design for librelane

This commit is contained in:
maksyuki committed 2026-08-24 20:11:14 +08:00
1 parent 46a3a90be6
commit fe347ed583
13 files changed
+329 -102

No files matched your search

+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+31 -15
View File
@@ -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
+16
View File
@@ -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
+45 -1
View File
@@ -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 \
+26 -9
View File
@@ -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:
+13 -7
View File
@@ -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:
+8 -7
View File
@@ -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",
+6 -5
View File
@@ -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
+70 -9
View File
@@ -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
+8
View File
@@ -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