mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: enhance LibreLane flow with safety features and update power rail connections
This commit is contained in:
8 files changed
+114
-22
No files matched your search
@@ -63,6 +63,7 @@ $(LIBRELANE_RTL): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP) $(LIBRELANE_TRACKED_RTL
|
||||
--user-extensions-filelist $(USER_EXTENSIONS_FILELIST) \
|
||||
--pin-map-filelist $(PIN_MAP_FILELIST) \
|
||||
--archinfo-incdir $(ARCHINFO_METADATA_DIR) \
|
||||
--librelane-safe \
|
||||
$(LIBRELANE_FEATURE_ARGS)
|
||||
|
||||
$(LIBRELANE_SDC): $(LIBRELANE_FLOW_ROOT)/scripts/generate_sdc.py \
|
||||
|
||||
@@ -10,18 +10,31 @@ set secondary_supplies []
|
||||
foreach vdd $::env(VDD_NETS) gnd $::env(GND_NETS) {
|
||||
if {$vdd != $::env(VDD_NET)} {
|
||||
lappend secondary_supplies $vdd
|
||||
set db_net [[ord::get_db_block] findNet $vdd]
|
||||
if {$db_net == "NULL"} {
|
||||
set net [odb::dbNet_create [ord::get_db_block] $vdd]
|
||||
$net setSpecial
|
||||
$net setSigType POWER
|
||||
$net setSigType "POWER"
|
||||
}
|
||||
}
|
||||
if {$gnd != $::env(GND_NET)} {
|
||||
lappend secondary_supplies $gnd
|
||||
set db_net [[ord::get_db_block] findNet $gnd]
|
||||
if {$db_net == "NULL"} {
|
||||
set net [odb::dbNet_create [ord::get_db_block] $gnd]
|
||||
$net setSpecial
|
||||
$net setSigType GROUND
|
||||
$net setSigType "GROUND"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 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(GND_NET) -inst_pattern .* -pin_pattern vss -ground
|
||||
add_global_connection -net IOVDD -inst_pattern .* -pin_pattern iovdd -power
|
||||
add_global_connection -net IOVSS -inst_pattern .* -pin_pattern iovss -ground
|
||||
global_connect -verbose
|
||||
|
||||
set_voltage_domain -name CORE -power $::env(VDD_NET) -ground $::env(GND_NET) \
|
||||
-secondary_power $secondary_supplies
|
||||
|
||||
|
||||
@@ -158,6 +158,9 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
|
||||
"VERILOG_FILES": [config_path(args.rtl, config_dir)],
|
||||
"USE_SLANG": True,
|
||||
"SLANG_ARGUMENTS": ["--keep-hierarchy"],
|
||||
"SYNTH_SHARE_RESOURCES": False,
|
||||
"SYNTH_HIERARCHY_MODE": "deferred_flatten",
|
||||
"YOSYS_LOG_LEVEL": "WARNING",
|
||||
"PRIMARY_GDSII_STREAMOUT_TOOL": "klayout",
|
||||
"PAD_SOUTH": pad_sides["SOUTH"],
|
||||
"PAD_EAST": pad_sides["EAST"],
|
||||
|
||||
@@ -23,6 +23,19 @@ from generate_user_extensions import generate as generate_user_extensions # noq
|
||||
|
||||
|
||||
INCLUDE_RE = re.compile(r'^\s*`include\s+"([^"]+)"')
|
||||
HAZARD3_UPDATE_FUNCTION_RE = re.compile(
|
||||
r"function\s+\[XLEN-1:0\]\s+update_nonconst\s*;\s*"
|
||||
r"input\s+\[XLEN-1:0\]\s+prev\s*;\s*"
|
||||
r"input\s+\[XLEN-1:0\]\s+nonconst\s*;\s*"
|
||||
r"begin\s*"
|
||||
r"update_nonconst\s*=\s*\(wdata_update\s*&\s*nonconst\)\s*\|\s*"
|
||||
r"\(prev\s*&\s*~nonconst\)\s*;\s*"
|
||||
r"end\s*endfunction"
|
||||
)
|
||||
HAZARD3_UPDATE_CALL_RE = re.compile(
|
||||
r"update_nonconst\(\s*(?P<previous>[A-Za-z_][A-Za-z0-9_$]*)\s*,\s*"
|
||||
r"(?P<writable>[A-Za-z_][A-Za-z0-9_$]*)\s*\)"
|
||||
)
|
||||
|
||||
|
||||
def build_defines(args: argparse.Namespace) -> list[str]:
|
||||
@@ -160,7 +173,24 @@ def defines_to_sv(defines: list[str]) -> list[str]:
|
||||
return output
|
||||
|
||||
|
||||
def write_single_sv(filelist: FileList, output: Path) -> None:
|
||||
def sanitize_librelane_source(source: str) -> str:
|
||||
"""Inline the Hazard3 CSR helper that yosys-slang mis-models with hierarchy."""
|
||||
source, definitions = HAZARD3_UPDATE_FUNCTION_RE.subn("", source, count=1)
|
||||
if definitions != 1:
|
||||
raise RuntimeError("expected exactly one Hazard3 update_nonconst definition")
|
||||
|
||||
def inline_update(match: re.Match[str]) -> str:
|
||||
previous = match.group("previous")
|
||||
writable = match.group("writable")
|
||||
return f"((wdata_update & {writable}) | ({previous} & ~{writable}))"
|
||||
|
||||
source, calls = HAZARD3_UPDATE_CALL_RE.subn(inline_update, source)
|
||||
if calls != 2 or "update_nonconst" in source:
|
||||
raise RuntimeError("expected exactly two Hazard3 update_nonconst calls")
|
||||
return source
|
||||
|
||||
|
||||
def write_single_sv(filelist: FileList, output: Path, *, librelane_safe: bool = False) -> None:
|
||||
lines = [
|
||||
"// Generated by physical/smoke/syn/tools/export_soc_sources.py",
|
||||
*defines_to_sv(filelist.defines),
|
||||
@@ -170,7 +200,10 @@ def write_single_sv(filelist: FileList, output: Path) -> None:
|
||||
lines.append(f"// ===== file: {source.relative_to(REPO_ROOT)} =====")
|
||||
lines.extend(inline_file(source, filelist.incdirs))
|
||||
lines.append("")
|
||||
atomic_write(output, "\n".join(lines) + "\n")
|
||||
source = "\n".join(lines) + "\n"
|
||||
if librelane_safe:
|
||||
source = sanitize_librelane_source(source)
|
||||
atomic_write(output, source)
|
||||
|
||||
|
||||
def bundle_relative(path: Path) -> Path:
|
||||
@@ -231,9 +264,7 @@ def metadata_file(value: str) -> tuple[Path, Path]:
|
||||
or ".." in relative.parts
|
||||
or not source.is_file()
|
||||
):
|
||||
raise argparse.ArgumentTypeError(
|
||||
"metadata must use a safe DESTINATION=SOURCE_FILE mapping"
|
||||
)
|
||||
raise argparse.ArgumentTypeError("metadata must use a safe DESTINATION=SOURCE_FILE mapping")
|
||||
return relative, source.resolve()
|
||||
|
||||
|
||||
@@ -266,6 +297,7 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--user-extensions-filelist", type=Path)
|
||||
parser.add_argument("--pin-map-filelist", type=Path)
|
||||
parser.add_argument("--archinfo-incdir", type=Path)
|
||||
parser.add_argument("--librelane-safe", action="store_true")
|
||||
parser.add_argument("--metadata-file", type=metadata_file, action="append", default=[])
|
||||
parser.add_argument("--output-dir", type=Path, default=REPO_ROOT / "export")
|
||||
return parser.parse_args()
|
||||
@@ -289,7 +321,7 @@ def main() -> int:
|
||||
filelist = configured_filelist(args, generated_dir, user_extensions_dir)
|
||||
if args.mode == "sv":
|
||||
output = export_dir / "retrosoc_asic_sources.sv"
|
||||
write_single_sv(filelist, output)
|
||||
write_single_sv(filelist, output, librelane_safe=args.librelane_safe)
|
||||
else:
|
||||
output = write_tar(filelist, export_dir, args.soc, dict(args.metadata_file))
|
||||
print(f"generated: {output}")
|
||||
|
||||
@@ -247,5 +247,6 @@ module dvp_core (
|
||||
end
|
||||
end
|
||||
|
||||
assign error_flags_o = s_err_flags_q;
|
||||
// SYNC, SIZE and PARTIAL are reserved until their capture paths are implemented.
|
||||
assign error_flags_o = {s_err_flags_q[5:4], 3'b000, s_err_flags_q[0]};
|
||||
endmodule
|
||||
@@ -148,7 +148,7 @@ module sdio_reg (
|
||||
`APB4_SDIO__IP_ID: s_read_data = 32'h5344_494F;
|
||||
`APB4_SDIO__IP_VERSION: s_read_data = 32'h0001_0000;
|
||||
`APB4_SDIO__CAPABILITY: s_read_data = 32'h0000_01FF;
|
||||
`APB4_SDIO__HOST_CTRL: s_read_data = {29'd0, s_host_ctrl_q};
|
||||
`APB4_SDIO__HOST_CTRL: s_read_data = {29'd0, s_host_ctrl_q[2], 1'b0, s_host_ctrl_q[0]};
|
||||
`APB4_SDIO__CLOCK_CTRL: s_read_data = {8'd0, s_half_period_q, 7'd0, s_clock_en_q};
|
||||
`APB4_SDIO__CLOCK_ACTUAL: s_read_data = clock_actual_i;
|
||||
`APB4_SDIO__BUS_CTRL: s_read_data = {30'd0, s_bus_width_q};
|
||||
|
||||
@@ -61,6 +61,11 @@ 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["USE_SLANG"] is True
|
||||
assert config["SLANG_ARGUMENTS"] == ["--keep-hierarchy"]
|
||||
assert config["SYNTH_SHARE_RESOURCES"] is False
|
||||
assert config["SYNTH_HIERARCHY_MODE"] == "deferred_flatten"
|
||||
assert config["YOSYS_LOG_LEVEL"] == "WARNING"
|
||||
assert config["VDD_NETS"] == ["VDD", "IOVDD"]
|
||||
assert config["GND_NETS"] == ["VSS", "IOVSS"]
|
||||
assert set(config["MACROS"]) == {
|
||||
@@ -98,6 +103,7 @@ def test_librelane_flow_is_single_level_chip_only() -> None:
|
||||
lock = json.loads((ROOT / "dependencies/dependencies.lock.json").read_text())
|
||||
|
||||
assert "librelane-chip:" in makefile
|
||||
assert "--librelane-safe" in makefile
|
||||
assert "librelane-core" not in makefile
|
||||
assert "librelane-all" not in makefile
|
||||
assert "include physical/librelane/Makefile" in top_makefile
|
||||
@@ -113,3 +119,14 @@ def test_bondpad_lef_matches_the_generated_master_contract() -> None:
|
||||
assert "CLASS COVER" in lef
|
||||
assert "SIZE 70.0 BY 70.0" in lef
|
||||
assert "SITE sg13g2_ioSite" in lef
|
||||
|
||||
|
||||
def test_pdn_connects_all_ihp_pad_power_rails() -> None:
|
||||
pdn = (FLOW_ROOT / "pdn_cfg.tcl").read_text(encoding="utf-8")
|
||||
|
||||
assert "findNet $vdd" in pdn
|
||||
assert "findNet $gnd" 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
|
||||
assert "-net IOVSS -inst_pattern .* -pin_pattern iovss -ground" in pdn
|
||||
+37
-12
@@ -192,6 +192,30 @@ def test_source_export_uses_the_fixed_hazard3_management_core(tmp_path: Path) ->
|
||||
assert any(path.name == "hazard3_cpu_1port.v" for path in filelist.files)
|
||||
|
||||
|
||||
def test_librelane_source_export_inlines_hazard3_update_helper() -> None:
|
||||
module_path = ROOT / "physical/smoke/syn/tools/export_soc_sources.py"
|
||||
spec = importlib.util.spec_from_file_location("retrosoc_librelane_export", module_path)
|
||||
assert spec is not None and spec.loader is not None
|
||||
source_export = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(source_export)
|
||||
|
||||
source = """function [XLEN-1:0] update_nonconst;
|
||||
input [XLEN-1:0] prev;
|
||||
input [XLEN-1:0] nonconst;
|
||||
begin
|
||||
update_nonconst = (wdata_update & nonconst) | (prev & ~nonconst);
|
||||
end
|
||||
endfunction
|
||||
mtvec_reg <= update_nonconst(mtvec_reg, MTVEC_WMASK);
|
||||
mie <= update_nonconst(mie, MIE_WMASK);
|
||||
"""
|
||||
sanitized = source_export.sanitize_librelane_source(source)
|
||||
|
||||
assert "update_nonconst" not in sanitized
|
||||
assert "mtvec_reg <= ((wdata_update & MTVEC_WMASK) | (mtvec_reg & ~MTVEC_WMASK));" in sanitized
|
||||
assert "mie <= ((wdata_update & MIE_WMASK) | (mie & ~MIE_WMASK));" in sanitized
|
||||
|
||||
|
||||
def test_package_forwards_manifest_jtag_idcode() -> None:
|
||||
package_source = (ROOT / "scripts/package.py").read_text(encoding="utf-8")
|
||||
|
||||
@@ -306,16 +330,12 @@ def test_formal_filelists_are_scoped_to_the_protocol_duts(tmp_path: Path) -> Non
|
||||
assert generate_formal_filelist(
|
||||
"pll_rcu", pll_rcu_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
assert generate_formal_filelist(
|
||||
"gpio", gpio_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
assert generate_formal_filelist("gpio", gpio_filelist, memory_map, topology, user_extensions)
|
||||
assert generate_formal_filelist(
|
||||
"ws2812", ws2812_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
assert generate_formal_filelist("i2c", i2c_filelist, memory_map, topology, user_extensions)
|
||||
assert generate_formal_filelist(
|
||||
"clint", clint_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
assert generate_formal_filelist("clint", clint_filelist, memory_map, topology, user_extensions)
|
||||
assert generate_formal_filelist(
|
||||
"opipsram", opipsram_filelist, memory_map, topology, user_extensions
|
||||
)
|
||||
@@ -1034,9 +1054,7 @@ def test_smoke_regression_uses_ihp130_behavioral_coverage_only() -> None:
|
||||
"IHP130",
|
||||
"--dry-run",
|
||||
)
|
||||
assert (
|
||||
"+ make CONFIG=configs/ci/ihp130.mk APP=ci_smoke firmware" in dry_run.stdout
|
||||
)
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk APP=ci_smoke firmware" in dry_run.stdout
|
||||
assert "netsim" not in dry_run.stdout
|
||||
|
||||
invalid = subprocess.run(
|
||||
@@ -1122,10 +1140,14 @@ def test_nightly_regression_runs_optional_yosys_recipes() -> None:
|
||||
"IHP130",
|
||||
"--dry-run",
|
||||
)
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS SYNTH_RECIPE=area synth" in nightly.stdout
|
||||
assert (
|
||||
"+ make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS SYNTH_RECIPE=area synth" in nightly.stdout
|
||||
)
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk STA=OPENSTA SYNTH_RECIPE=area sta" in nightly.stdout
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk SYNTH_RECIPE=area metrics" in nightly.stdout
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS SYNTH_RECIPE=speed synth" in nightly.stdout
|
||||
assert (
|
||||
"+ make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS SYNTH_RECIPE=speed synth" in nightly.stdout
|
||||
)
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk STA=OPENSTA SYNTH_RECIPE=speed sta" in nightly.stdout
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk SYNTH_RECIPE=speed metrics" in nightly.stdout
|
||||
|
||||
@@ -1164,7 +1186,10 @@ def test_nightly_extra_regression_skips_pr_netsim() -> None:
|
||||
"IHP130",
|
||||
"--dry-run",
|
||||
)
|
||||
assert "+ make CONFIG=configs/benchmark/ihp130-hazard3-coremark.mk SIMU=VERILATOR HAVE_SVA=YES coremark-report" in extra.stdout
|
||||
assert (
|
||||
"+ make CONFIG=configs/benchmark/ihp130-hazard3-coremark.mk SIMU=VERILATOR HAVE_SVA=YES coremark-report"
|
||||
in extra.stdout
|
||||
)
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS SYNTH_RECIPE=area synth" in extra.stdout
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk STA=OPENSTA SYNTH_RECIPE=area sta" in extra.stdout
|
||||
assert "+ make CONFIG=configs/ci/ihp130.mk SYNTH_RECIPE=area metrics" in extra.stdout
|
||||
|
||||
Reference in new issue
Block a user