feat: enhance LibreLane flow with on-chip SRAM support and OpenSTA integration

This commit is contained in:
maksyuki committed 2026-08-26 08:32:54 +08:00
1 parent 87df13fb46
commit f953ef7137
8 files changed
+239 -43

No files matched your search

+10 -5
View File
@@ -22,6 +22,7 @@ LIBRELANE_KLAYOUT_PATH := $(LIBRELANE_PDK_ROOT)/$(LIBRELANE_PDK)/libs.tech/kla
LIBRELANE_BONDPAD_SCRIPT := $(LIBRELANE_PDK_ROOT)/$(LIBRELANE_PDK)/libs.tech/klayout/tech/scripts/bondpad.py
LIBRELANE_PDN := $(LIBRELANE_FLOW_ROOT)/pdn_cfg.tcl
LIBRELANE_SRAM_VH := $(LIBRELANE_FLOW_ROOT)/sram_blackboxes.vh
LIBRELANE_STA_REPORT_LIMIT := $(LIBRELANE_FLOW_ROOT)/sta_report_limit.tcl
LIBRELANE_CLOCK_DOMAINS := $(ROOT_PATH)/rtl/mini/integration/clock_reset_domains.json
LIBRELANE_PIN_MAP := $(ROOT_PATH)/rtl/mini/pin_map/pin_map.json
LIBRELANE_TRACKED_RTL := $(shell git -C $(ROOT_PATH) ls-files \
@@ -29,6 +30,7 @@ LIBRELANE_TRACKED_RTL := $(shell git -C $(ROOT_PATH) ls-files \
LIBRELANE_SOURCE_PYTHON ?= /usr/bin/python3
LIBRELANE_PYTHON ?= python3
LIBRELANE_COMMAND := $(LIBRELANE_PYTHON) -m librelane
# 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
@@ -51,6 +53,7 @@ LIBRELANE_FEATURE_ARGS := \
LIBRELANE_CONFIG_DEPS := $(LIBRELANE_RTL) $(LIBRELANE_SDC) \
$(LIBRELANE_PDN) $(LIBRELANE_SRAM_VH) \
$(LIBRELANE_STA_REPORT_LIMIT) \
$(LIBRELANE_FLOW_ROOT)/scripts/generate_chip_config.py
ifeq ($(LIBRELANE_TARGET),chip)
LIBRELANE_CONFIG_DEPS += $(LIBRELANE_BONDPAD_GDS) $(LIBRELANE_BONDPAD_LEF)
@@ -106,8 +109,10 @@ $(LIBRELANE_CONFIG): $(LIBRELANE_CONFIG_DEPS)
--pdn $(LIBRELANE_PDN) --bondpad-gds $(LIBRELANE_BONDPAD_GDS) \
--bondpad-lef $(LIBRELANE_BONDPAD_LEF) --sram-vh $(LIBRELANE_SRAM_VH) \
--ext-clk-hz $(EXT_CLK_HZ) --aud-clk-hz $(AUD_CLK_HZ) \
$(if $(filter YES,$(HAVE_PLL)),--have-pll) --target $(LIBRELANE_TARGET) \
--output $@
--sram-size-kib $(SRAM_SIZE_KIB) \
$(if $(filter YES,$(HAVE_PLL)),--have-pll) \
$(if $(filter YES,$(HAVE_SRAM_MACRO)),--have-sram-macro) \
--target $(LIBRELANE_TARGET) --output $@
librelane-input: $(LIBRELANE_CONFIG)
@@ -123,7 +128,7 @@ librelane-doctor: $(LIBRELANE_DOCTOR_RESULT)
librelane-run: librelane-doctor manifest
$(FLOW_PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool librelane \
--log $(LIBRELANE_LOG) --result $(LIBRELANE_RESULT) --cwd $(LIBRELANE_RUN_ROOT) -- \
librelane $(LIBRELANE_CONFIG) --manual-pdk --pdk $(LIBRELANE_PDK) \
$(LIBRELANE_COMMAND) $(LIBRELANE_CONFIG) --manual-pdk --pdk $(LIBRELANE_PDK) \
--pdk-root $(LIBRELANE_PDK_ROOT) --scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
--run-tag $(LIBRELANE_RUN_TAG) --overwrite --jobs $(JOBS) $(LIBRELANE_MID_PNR_SKIP) \
--condensed --hide-progress-bar --save-views-to $(LIBRELANE_FINAL_DIR)
@@ -137,13 +142,13 @@ librelane-core:
+$(MAKE) LIBRELANE_TARGET=core librelane-run
librelane-openroad: librelane-doctor
cd $(LIBRELANE_RUN_ROOT) && librelane $(LIBRELANE_CONFIG) --manual-pdk \
cd $(LIBRELANE_RUN_ROOT) && $(LIBRELANE_COMMAND) $(LIBRELANE_CONFIG) --manual-pdk \
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInOpenROAD
librelane-klayout: librelane-doctor
cd $(LIBRELANE_RUN_ROOT) && librelane $(LIBRELANE_CONFIG) --manual-pdk \
cd $(LIBRELANE_RUN_ROOT) && $(LIBRELANE_COMMAND) $(LIBRELANE_CONFIG) --manual-pdk \
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInKLayout
+56 -30
View File
@@ -6,6 +6,7 @@ source $::env(SCRIPTS_DIR)/openroad/common/io.tcl
source $::env(SCRIPTS_DIR)/openroad/common/set_global_connections.tcl
set_global_connections
# Use the PDK-native alternating routing directions (H/V/H/V/H).
set secondary_supplies []
foreach vdd $::env(VDD_NETS) gnd $::env(GND_NETS) {
if {$vdd != $::env(VDD_NET)} {
@@ -94,7 +95,8 @@ add_pdn_stripe \
-pitch $::env(PDN_VPITCH) \
-offset $::env(PDN_VOFFSET) \
-spacing $::env(PDN_VSPACING) \
-starts_with POWER
-starts_with POWER \
-extend_to_core_ring
add_pdn_stripe \
-grid stdcell_grid \
@@ -103,21 +105,31 @@ add_pdn_stripe \
-pitch $::env(PDN_HPITCH) \
-offset $::env(PDN_HOFFSET) \
-spacing $::env(PDN_HSPACING) \
-starts_with POWER
-starts_with POWER \
-extend_to_core_ring
add_pdn_connect \
# The top-metal ring uses -add_connect below, which owns the TopMetal1/TopMetal2 vias.
# Basilisk's IHP130 topology uses a low-metal ring to collect standard-cell
# rails, plus a separate top-metal ring for the pad-facing mesh.
add_pdn_ring \
-grid stdcell_grid \
-layers "$::env(PDN_VERTICAL_LAYER) $::env(PDN_HORIZONTAL_LAYER)"
-layers "Metal2 Metal3" \
-widths "0.52 0.52" \
-spacings "0.8 0.8" \
-core_offsets "1.6 1.6" \
-starts_with POWER \
-add_connect
if {$::env(PDN_ENABLE_RAILS) == 1} {
add_pdn_stripe \
-grid stdcell_grid \
-layer $::env(PDN_RAIL_LAYER) \
-width $::env(PDN_RAIL_WIDTH) \
-followpins
add_pdn_connect \
-grid stdcell_grid \
-layers "$::env(PDN_RAIL_LAYER) $::env(PDN_VERTICAL_LAYER)"
-followpins \
-extend_to_core_ring
add_pdn_connect -grid stdcell_grid -layers "Metal1 Metal2"
add_pdn_connect -grid stdcell_grid -layers "Metal1 Metal3"
}
if {$::env(PDN_CORE_RING) == 1} {
@@ -126,41 +138,55 @@ if {$::env(PDN_CORE_RING) == 1} {
-layers "$::env(PDN_VERTICAL_LAYER) $::env(PDN_HORIZONTAL_LAYER)" \
-widths "$::env(PDN_CORE_RING_VWIDTH) $::env(PDN_CORE_RING_HWIDTH)" \
-spacings "$::env(PDN_CORE_RING_VSPACING) $::env(PDN_CORE_RING_HSPACING)" \
-core_offset "$::env(PDN_CORE_RING_VOFFSET) $::env(PDN_CORE_RING_HOFFSET)" \
-connect_to_pads
-core_offsets "$::env(PDN_CORE_RING_VOFFSET) $::env(PDN_CORE_RING_HOFFSET)" \
-connect_to_pads \
-connect_to_pad_layers TopMetal2 \
-add_connect
}
define_pdn_grid \
-macro \
-default \
-name macro_default \
-starts_with POWER \
-halo "$::env(PDN_HORIZONTAL_HALO) $::env(PDN_VERTICAL_HALO)"
add_pdn_connect \
-grid macro_default \
-layers "$::env(PDN_VERTICAL_LAYER) $::env(PDN_HORIZONTAL_LAYER)"
set packet_ram_instances [list \
u_retrosoc.u_apb4_periph.u_apb4_usb2.u_link_domain.u_packet_store.u_packet_ram.u_packet_ram.u_data_high \
u_retrosoc.u_apb4_periph.u_apb4_usb2.u_link_domain.u_packet_store.u_packet_ram.u_packet_ram.u_data_low \
u_retrosoc.u_apb4_periph.u_apb4_usb2.u_link_domain.u_packet_store.u_packet_ram.u_packet_ram.u_ecc]
set sram_cells [list \
RM_IHPSG13_1P_4096x16_c3_bm_bist \
RM_IHPSG13_1P_4096x8_c3_bm_bist \
RM_IHPSG13_1P_1024x32_c2_bm_bist]
define_pdn_grid \
-macro \
-instances $packet_ram_instances \
-cells $sram_cells \
-name packet_ram_grid \
-grid_over_boundary \
-voltage_domains CORE \
-starts_with POWER
add_pdn_ring \
-grid packet_ram_grid \
-layers "Metal2 Metal3" \
-widths "0.52 0.52" \
-spacings "0.8 0.8" \
-core_offsets "1.6 1.6" \
-starts_with POWER \
-add_connect
add_pdn_stripe \
-grid packet_ram_grid \
-layer Metal4 \
-width 0.52 \
-pitch 20.0 \
-offset 2.0 \
-spacing 0.8 \
-starts_with POWER
add_pdn_stripe \
-grid packet_ram_grid \
-layer Metal5 \
-width 2.81 \
-layer TopMetal1 \
-width 2.5 \
-pitch 11.24 \
-offset 2.81 \
-spacing 2.81 \
-nets "VSS VDD" \
-starts_with POWER
add_pdn_connect -grid packet_ram_grid -layers "Metal4 Metal5"
add_pdn_connect -grid packet_ram_grid -layers "Metal2 TopMetal1"
add_pdn_connect -grid packet_ram_grid -layers "Metal3 TopMetal1"
add_pdn_connect -grid packet_ram_grid -layers "Metal3 Metal4"
add_pdn_connect -grid packet_ram_grid -layers "Metal4 TopMetal1"
add_pdn_connect -grid packet_ram_grid -layers "Metal5 TopMetal1"
add_pdn_connect -grid packet_ram_grid -layers "TopMetal1 TopMetal2"
+14 -2
View File
@@ -19,6 +19,7 @@ from scripts.setup_helpers import atomic_write # noqa: E402
EXPECTED_LIBRELANE_VERSION = "LibreLane v3.0.5"
EXPECTED_OPENSTA_VERSION = "3.0.0"
EXPECTED_SIGNAL_PADS = 109
@@ -37,18 +38,29 @@ def main() -> int:
errors: list[str] = []
details: dict[str, object] = {}
for tool in ("librelane", "klayout"):
for tool in ("librelane", "klayout", "sta"):
path = shutil.which(tool)
details[f"{tool}_path"] = path
if path is None:
errors.append(f"required tool is missing: {tool}")
if shutil.which("librelane") is not None:
version = command_output(["librelane", "--version"]).splitlines()[0]
version = command_output(
[sys.executable, "-m", "librelane", "--version"]
).splitlines()[0]
details["librelane_version"] = version
if version != EXPECTED_LIBRELANE_VERSION:
errors.append(f"expected {EXPECTED_LIBRELANE_VERSION}, found {version or '<empty>'}")
if shutil.which("sta") is not None:
version = command_output(["sta", "-version"])
details["opensta_version"] = version
if version != EXPECTED_OPENSTA_VERSION:
errors.append(
f"expected OpenSTA {EXPECTED_OPENSTA_VERSION}, "
f"found {version or '<empty>'}"
)
lock = json.loads(args.lock.read_text(encoding="utf-8"))
expected_pdk_revision = lock["sources"]["pdk_ihp130"]["revision"]
details["locked_pdk_revision"] = expected_pdk_revision
@@ -6,6 +6,7 @@ from __future__ import annotations
import argparse
import json
import os
import re
import sys
from pathlib import Path
@@ -44,6 +45,19 @@ SRAM_INSTANCES = {
}
def onchip_sram_instances(capacity_kib: int) -> dict[str, tuple[list[int], str]]:
if capacity_kib <= 0 or capacity_kib % 4 != 0:
raise ValueError("SRAM_SIZE_KIB must be a positive multiple of 4")
count = capacity_kib // 4
return {
f"u_retrosoc.u_onchip_ram.gen_memory.gen_bank[{index}].u_ram.u_mem": (
[2000 + (index % 4) * 500, 500 + (index // 4) * 750],
"N",
)
for index in range(count)
}
def config_path(path: Path, base: Path) -> str:
return f"dir::{os.path.relpath(path.resolve(), base.resolve())}"
@@ -141,17 +155,34 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
config_dir = args.output.resolve().parent
sram_header = config_path(args.sram_vh, config_dir)
active_sram_instances = dict(SRAM_INSTANCES)
if getattr(args, "have_sram_macro", False):
active_sram_instances["RM_IHPSG13_1P_1024x32_c2_bm_bist"] = onchip_sram_instances(
getattr(args, "sram_size_kib", 32)
)
macros = {
master: macro_config(master, instances, sram_header)
for master, instances in SRAM_INSTANCES.items()
for master, instances in active_sram_instances.items()
}
macro_hooks: list[str] = []
for instances in SRAM_INSTANCES.values():
for instances in active_sram_instances.values():
for instance in instances:
bracket_start = instance.find("[")
if bracket_start >= 0:
bracket_end = instance.index("]", bracket_start)
instance_pattern = (
re.escape(instance[:bracket_start])
+ ".*"
+ instance[bracket_start + 1 : bracket_end]
+ ".*"
+ re.escape(instance[bracket_end + 1 :])
)
else:
instance_pattern = re.escape(instance)
macro_hooks.extend(
[
f"{instance} VDD VSS VDDARRAY! VSS!",
f"{instance} VDD VSS VDD! VSS!",
f"{instance_pattern} VDD VSS VDDARRAY! VSS!",
f"{instance_pattern} VDD VSS VDD! VSS!",
]
)
@@ -164,10 +195,17 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
"SYNTH_HIERARCHY_MODE": "deferred_flatten",
"YOSYS_LOG_LEVEL": "WARNING",
"SYNTH_STRATEGY": "AREA 3",
"RUN_POST_GPL_DESIGN_REPAIR": False,
"RUN_CTS": False,
"RUN_POST_CTS_RESIZER_TIMING": False,
"EXTRA_EXCLUDED_CELLS": ["sg13g2_IOPad*"],
"PRIMARY_GDSII_STREAMOUT_TOOL": "klayout",
"PNR_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),
"STA_EXTRA_CORNER_TCL_FILE": config_path(
ROOT / "physical/librelane/sta_report_limit.tcl", config_dir
),
"CLOCK_PORT": list(CLOCK_PORTS),
"CLOCK_PERIOD": 1_000_000_000 / args.ext_clk_hz,
"VDD_NETS": ["VDD"],
@@ -179,6 +217,7 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
"PDN_CORE_RING_VSPACING": 5,
"PDN_CORE_RING_HSPACING": 5,
"PDN_ENABLE_PINS": True,
"ERROR_ON_PDN_VIOLATIONS": True,
"PDN_CFG": config_path(args.pdn, config_dir),
"MACROS": macros,
"PDN_MACRO_CONNECTIONS": macro_hooks,
@@ -216,7 +255,7 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
"PAD_WEST": pad_sides["WEST"],
"DIE_AREA": [0, 0, 8000, 8000],
"CORE_AREA": [365, 365, 7635, 7635],
"PL_TARGET_DENSITY_PCT": 30,
"PL_TARGET_DENSITY_PCT": 45,
"PDN_CORE_RING_CONNECT_TO_PADS": True,
"PAD_CFG": config_path(ROOT / "physical/librelane/pad_cfg.tcl", config_dir),
"PAD_BONDPAD_NAME": "bondpad_70x70",
@@ -247,6 +286,8 @@ def main() -> int:
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("--have-pll", action="store_true")
parser.add_argument("--have-sram-macro", action="store_true")
parser.add_argument("--sram-size-kib", type=positive_integer, default=32)
parser.add_argument("--target", choices=("chip", "core"), default="chip")
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
@@ -138,7 +138,6 @@ def render(args: argparse.Namespace) -> str:
"set_clock_uncertainty -setup 0.2 $all_retrosoc_clocks",
"set_clock_uncertainty -hold 0.1 $all_retrosoc_clocks",
"set_clock_transition 0.1 $all_retrosoc_clocks",
"set_propagated_clock $all_retrosoc_clocks",
"set_timing_derate -early 0.95",
"set_timing_derate -late 1.05",
"set_max_fanout 10 [current_design]",
+23
View File
@@ -45,3 +45,26 @@ module RM_IHPSG13_1P_4096x8_c3_bm_bist (
input wire [ 7:0] A_BIST_BM
);
endmodule
(* blackbox *)
module RM_IHPSG13_1P_1024x32_c2_bm_bist (
input wire A_CLK,
input wire A_MEN,
input wire A_WEN,
input wire A_REN,
input wire [ 9:0] A_ADDR,
input wire [31:0] A_DIN,
input wire A_DLY,
output wire [31:0] A_DOUT,
input wire [31:0] A_BM,
input wire A_BIST_CLK,
input wire A_BIST_EN,
input wire A_BIST_MEN,
input wire A_BIST_WEN,
input wire A_BIST_REN,
input wire [ 9:0] A_BIST_ADDR,
input wire [31:0] A_BIST_DIN,
input wire [31:0] A_BIST_BM
);
endmodule
+35
View File
@@ -0,0 +1,35 @@
# LibreLane 3.0.5 hard-codes 1000 expanded paths per clock group. Cap only
# report enumeration; worst-slack/TNS analysis still covers the full graph.
set ::retrosoc_sta_group_path_count 10
puts "\[INFO\] OpenSTA executable: [info nameofexecutable]"
rename report_checks ::retrosoc_report_checks
proc report_checks {args} {
set count_index [lsearch -exact $args -group_path_count]
if {$count_index >= 0} {
set value_index [expr {$count_index + 1}]
if {$value_index < [llength $args]} {
lset args $value_index $::retrosoc_sta_group_path_count
}
} else {
lappend args -group_path_count $::retrosoc_sta_group_path_count
}
uplevel 1 [list ::retrosoc_report_checks {*}$args]
}
rename report_check_types ::retrosoc_report_check_types
proc report_check_types {args} {
set violator_index [lsearch -exact $args -violators]
if {$violator_index >= 0} {
set args [lreplace $args $violator_index $violator_index]
}
uplevel 1 [list ::retrosoc_report_check_types {*}$args]
}
rename report_parasitic_annotation ::retrosoc_report_parasitic_annotation
proc report_parasitic_annotation {args} {
set detail_index [lsearch -exact $args -report_unannotated]
if {$detail_index >= 0} {
set args [lreplace $args $detail_index $detail_index]
}
uplevel 1 [list ::retrosoc_report_parasitic_annotation {*}$args]
}
+55
View File
@@ -61,6 +61,7 @@ def test_chip_config_places_every_signal_and_power_pad_once(tmp_path: Path) -> N
assert "u_sdram_dq15_io_pad.u_sg13g2_IOPadInOut4mA" in sides["west"]
assert config["DIE_AREA"] == [0, 0, 8000, 8000]
assert config["CORE_AREA"] == [365, 365, 7635, 7635]
assert config["PL_TARGET_DENSITY_PCT"] == 45
assert config["USE_SLANG"] is True
assert config["SLANG_ARGUMENTS"] == ["--keep-hierarchy"]
assert config["VERILOG_POWER_DEFINE"] is None
@@ -74,10 +75,18 @@ def test_chip_config_places_every_signal_and_power_pad_once(tmp_path: Path) -> N
]
assert config["YOSYS_LOG_LEVEL"] == "WARNING"
assert config["SYNTH_STRATEGY"] == "AREA 3"
assert config["RUN_POST_GPL_DESIGN_REPAIR"] is False
assert config["RUN_CTS"] is False
assert config["RUN_POST_CTS_RESIZER_TIMING"] is False
assert config["EXTRA_EXCLUDED_CELLS"] == ["sg13g2_IOPad*"]
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 config["ERROR_ON_PDN_VIOLATIONS"] is True
assert config["STA_EXTRA_CORNER_TCL_FILE"].endswith(
"physical/librelane/sta_report_limit.tcl"
)
assert set(config["MACROS"]) == {
"RM_IHPSG13_1P_4096x16_c3_bm_bist",
"RM_IHPSG13_1P_4096x8_c3_bm_bist",
@@ -107,6 +116,27 @@ def test_core_config_uses_a_padless_classic_flow(tmp_path: Path) -> None:
}
def test_chip_config_includes_configured_onchip_sram_banks(tmp_path: Path) -> None:
module = load_module(
"retrosoc_librelane_sram_config", FLOW_ROOT / "scripts/generate_chip_config.py"
)
arguments = chip_arguments(tmp_path)
arguments.have_sram_macro = True
arguments.sram_size_kib = 32
config = module.build_config(arguments)
macro = config["MACROS"]["RM_IHPSG13_1P_1024x32_c2_bm_bist"]
assert len(macro["instances"]) == 8
assert "u_retrosoc.u_onchip_ram.gen_memory.gen_bank[0].u_ram.u_mem" in macro["instances"]
assert "u_retrosoc.u_onchip_ram.gen_memory.gen_bank[7].u_ram.u_mem" in macro["instances"]
assert any(
"gen_bank.*0.*\\.u_ram" in connection
for connection in config["PDN_MACRO_CONNECTIONS"]
)
blackboxes = (FLOW_ROOT / "sram_blackboxes.vh").read_text(encoding="utf-8")
assert "module RM_IHPSG13_1P_1024x32_c2_bm_bist" in blackboxes
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")
arguments = SimpleNamespace(
@@ -123,6 +153,7 @@ def test_chip_sdc_is_pad_aware_and_does_not_false_path_all_io(tmp_path: Path) ->
assert "u_rcu.u_sys_clk_buf.u_sg13g2_buf_1/X" in sdc
assert "create_generated_clock -name clk_system" in sdc
assert "set_clock_groups -name retrosoc_async -asynchronous" in sdc
assert "set_propagated_clock" not in sdc
assert "set_input_delay -max" in sdc
assert "set_output_delay -max" in sdc
assert "set_false_path -from $reset_ext_rst_n_i_pad" in sdc
@@ -157,6 +188,7 @@ def test_librelane_flow_exposes_core_and_chip_targets() -> None:
assert "librelane-core:" in makefile
assert "librelane-core-package:" in makefile
assert "LIBRELANE_TARGET ?= chip" in makefile
assert "LIBRELANE_COMMAND := $(LIBRELANE_PYTHON) -m librelane" in makefile
assert "--skip OpenROAD.STAMidPNR" in makefile
assert "--librelane-safe" in makefile
assert "librelane-all" not in makefile
@@ -164,6 +196,8 @@ def test_librelane_flow_exposes_core_and_chip_targets() -> None:
ihp_filelist = (ROOT / "rtl/filelist/pdk_ihp130.fl").read_text(encoding="utf-8")
assert ihp_filelist.index("sg13g2_udp.v") < ihp_filelist.index("sg13g2_stdcell.v")
assert lock["sources"]["pdk_ihp130"]["revision"] == ("970a7688e7dcce2a6172797df9ef47bde2f60f9f")
doctor = (FLOW_ROOT / "scripts/doctor.py").read_text(encoding="utf-8")
assert 'EXPECTED_OPENSTA_VERSION = "3.0.0"' in doctor
def test_bondpad_lef_matches_the_generated_master_contract() -> None:
@@ -175,6 +209,20 @@ def test_bondpad_lef_matches_the_generated_master_contract() -> None:
assert "SITE sg13g2_ioSite" in lef
def test_sta_report_wrapper_caps_only_expanded_path_enumeration() -> None:
wrapper = (FLOW_ROOT / "sta_report_limit.tcl").read_text(encoding="utf-8")
assert "set ::retrosoc_sta_group_path_count 10" in wrapper
assert "OpenSTA executable" in wrapper
assert "rename report_checks ::retrosoc_report_checks" in wrapper
assert "lsearch -exact $args -group_path_count" in wrapper
assert "lappend args -group_path_count" in wrapper
assert "rename report_check_types ::retrosoc_report_check_types" in wrapper
assert "lsearch -exact $args -violators" in wrapper
assert "rename report_parasitic_annotation" in wrapper
assert "lsearch -exact $args -report_unannotated" in wrapper
assert "::retrosoc_report_checks" in wrapper
def test_pdn_connects_all_ihp_pad_power_rails() -> None:
pdn = (FLOW_ROOT / "pdn_cfg.tcl").read_text(encoding="utf-8")
@@ -187,6 +235,13 @@ def test_pdn_connects_all_ihp_pad_power_rails() -> None:
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 "odb::dbTechLayer_setDirection" not in pdn
assert "Metal4 HORIZONTAL" not in pdn
assert '-layers "Metal2 Metal3"' in pdn
assert "-extend_to_core_ring" in pdn
assert "-connect_to_pad_layers TopMetal2" in pdn
assert '-layers "Metal3 Metal4"' in pdn
assert "-layer Metal4" 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 IOVDD -inst_pattern .* -pin_pattern iovdd -power" in pdn