mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
feat: enhance core-STA integration and timing analysis
- Updated soc_topology.json to include new fabric links for core, DMA, native, and APB interfaces. - Refactored retrosoc.sv to utilize generated fabric links and included necessary SVH files for better modularity. - Improved check_clock_reset_domains.py to validate hierarchical pins and added support for positive number checks. - Modified regress.py to include OpenSTA in the regression commands. - Enhanced README.md in the STA directory to clarify the timing analysis flow and supported PDK profiles. - Removed outdated gen2.sdc file and replaced it with a dynamic SDC generation approach. - Updated opensta.mk to generate SDC files dynamically based on the current domain map and pin map. - Created generate_sdc.py to automate SDC generation from clock/reset domain inventory. - Added tests for generated core-STA constraints to ensure coverage of current clock domains and validate pin map integrity. - Introduced new timing metrics and warnings for the sky130 PDK in the quality checks.
This commit is contained in:
30 files changed
+719
-149
No files matched your search
@@ -30,6 +30,14 @@ def write_invalid_map(tmp_path: Path, document: dict[str, object]) -> Path:
|
||||
def test_clock_reset_domain_inventory_matches_the_rcu() -> None:
|
||||
result = check(DOMAIN_MAP)
|
||||
assert result.returncode == 0, result.stderr
|
||||
document = json.loads(DOMAIN_MAP.read_text(encoding="utf-8"))
|
||||
assert document["schema_version"] == 2
|
||||
assert {domain["name"] for domain in document["domains"]} == {
|
||||
"external",
|
||||
"system",
|
||||
"audio",
|
||||
"dvp",
|
||||
}
|
||||
|
||||
|
||||
def test_clock_reset_domain_inventory_rejects_unknown_domain_and_instance(tmp_path: Path) -> None:
|
||||
@@ -44,3 +52,9 @@ def test_clock_reset_domain_inventory_rejects_unknown_domain_and_instance(tmp_pa
|
||||
result = check(write_invalid_map(tmp_path, document))
|
||||
assert result.returncode != 0
|
||||
assert "does not declare rst_sync instance" in result.stderr
|
||||
|
||||
document = json.loads(DOMAIN_MAP.read_text(encoding="utf-8"))
|
||||
document["domains"][1]["sta"]["source_domain"] = "unknown"
|
||||
result = check(write_invalid_map(tmp_path, document))
|
||||
assert result.returncode != 0
|
||||
assert "STA source references unknown domain" in result.stderr
|
||||
@@ -376,6 +376,7 @@ def test_pdk_pr_regressions_cover_firmware_rtl_and_netlist() -> None:
|
||||
assert any("SIMU=VERILATOR" in values and "firmware" in values for values in command_values)
|
||||
assert any("SIMU=IVERILOG" in values and "sim-asm" in values for values in command_values)
|
||||
assert any("SYNTH=YOSYS" in values and "synth" in values for values in command_values)
|
||||
assert ("STA=OPENSTA", "sta") in command_values
|
||||
assert any("SIMU=IVERILOG" in values and "netsim" in values for values in command_values)
|
||||
|
||||
|
||||
|
||||
@@ -68,6 +68,8 @@ def test_topology_generates_complete_native_apb_and_gpio_bindings(tmp_path: Path
|
||||
apb_declarations = (tmp_path / "rtl/soc_apb_declarations.svh").read_text(encoding="utf-8")
|
||||
apb_routes = (tmp_path / "rtl/soc_apb_request_routes.svh").read_text(encoding="utf-8")
|
||||
apb_response = (tmp_path / "rtl/soc_apb_response_mux.svh").read_text(encoding="utf-8")
|
||||
fabric = (tmp_path / "rtl/soc_fabric_interfaces.svh").read_text(encoding="utf-8")
|
||||
bus_fabric = (tmp_path / "rtl/soc_bus_fabric.svh").read_text(encoding="utf-8")
|
||||
filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8")
|
||||
|
||||
assert interfaces.count("nmi_if u_") == 18
|
||||
@@ -82,6 +84,9 @@ def test_topology_generates_complete_native_apb_and_gpio_bindings(tmp_path: Path
|
||||
assert "assign tmr.paddr = nmi.addr;" in apb_routes
|
||||
assert "({32{s_psel_q[8]}} & tmr.prdata)" in apb_response
|
||||
assert "localparam int NSLV = 9;" in apb_declarations
|
||||
assert fabric.count("nmi_if u_") == 4
|
||||
assert ".core_nmi(u_core_nmi_if)" in bus_fabric
|
||||
assert ".apb_nmi(u_apb_nmi_if)" in bus_fabric
|
||||
assert filelist.startswith("+incdir+")
|
||||
|
||||
|
||||
@@ -129,6 +134,12 @@ def test_topology_rejects_duplicate_region_and_disabled_owner(tmp_path: Path) ->
|
||||
assert result.returncode != 0
|
||||
assert "disabled but declares regions" in result.stderr
|
||||
|
||||
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
|
||||
document["fabric_links"][1]["name"] = "core"
|
||||
result = validate(write_invalid_topology(tmp_path, document))
|
||||
assert result.returncode != 0
|
||||
assert "fabric role core is duplicated" in result.stderr
|
||||
|
||||
|
||||
def test_topology_rejects_invalid_apb_target_ownership(tmp_path: Path) -> None:
|
||||
document = json.loads(TOPOLOGY.read_text(encoding="utf-8"))
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
"""Tests for generated core-STA constraints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DOMAINS = ROOT / "rtl/mini/integration/clock_reset_domains.json"
|
||||
PIN_MAP = ROOT / "rtl/mini/pin_map/pin_map.json"
|
||||
GENERATOR = ROOT / "sta/opensta/generate_sdc.py"
|
||||
GF180_GENERATOR = ROOT / "pdk/generate_gf180_liberty.py"
|
||||
|
||||
|
||||
def generate(domains: Path, output: Path) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(GENERATOR),
|
||||
"--domains",
|
||||
str(domains),
|
||||
"--pin-map",
|
||||
str(PIN_MAP),
|
||||
"--output",
|
||||
str(output),
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
|
||||
def test_core_sdc_covers_current_clock_domains(tmp_path: Path) -> None:
|
||||
output = tmp_path / "retrosoc_core.sdc"
|
||||
result = generate(DOMAINS, output)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
sdc = output.read_text(encoding="utf-8")
|
||||
assert "create_clock -name clk_external -period 13.888888889" in sdc
|
||||
assert "create_generated_clock -name clk_system" in sdc
|
||||
assert "create_clock -name clk_audio -period 54.253472222" in sdc
|
||||
assert "create_clock -name clk_dvp -period 41.666666667" in sdc
|
||||
assert "u_dvp_pclk_clk_buf/clk_o" in sdc
|
||||
assert "-group [get_clocks {clk_external clk_system}]" in sdc
|
||||
assert "set_clock_transition 0.1 [get_clocks {clk_dvp}]" in sdc
|
||||
assert "set_input_transition" not in sdc
|
||||
assert "set_false_path -from $reset_ext_rst_n_i_pad" in sdc
|
||||
|
||||
|
||||
def test_core_sdc_rejects_pads_missing_from_the_pin_map(tmp_path: Path) -> None:
|
||||
document = json.loads(DOMAINS.read_text(encoding="utf-8"))
|
||||
document["domains"][3]["sta"]["source_port"] = "missing_pad"
|
||||
invalid = tmp_path / "clock_reset_domains.json"
|
||||
invalid.write_text(json.dumps(document), encoding="utf-8")
|
||||
|
||||
result = generate(invalid, tmp_path / "retrosoc_core.sdc")
|
||||
|
||||
assert result.returncode != 0
|
||||
assert "source port is not declared in pin map" in result.stderr
|
||||
|
||||
|
||||
def test_gf180_liberty_generator_assembles_requested_io_cells(tmp_path: Path) -> None:
|
||||
library = "gf180mcu_fd_io"
|
||||
corner = "ss_125C_4v50"
|
||||
source = tmp_path / "source"
|
||||
library_dir = source / "libraries" / library / "latest"
|
||||
liberty_dir = library_dir / "liberty"
|
||||
cells_dir = library_dir / "cells"
|
||||
liberty_dir.mkdir(parents=True)
|
||||
(liberty_dir / f"{library}__{corner}.lib").write_text(
|
||||
"library (gf180_io) {\n}\n", encoding="utf-8"
|
||||
)
|
||||
for cell in ("bi_t", "in_c"):
|
||||
cell_dir = cells_dir / cell
|
||||
cell_dir.mkdir(parents=True)
|
||||
(cell_dir / f"{library}__{cell}__{corner}.lib").write_text(
|
||||
f"cell ({cell}) {{}}\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
output_dir = tmp_path / "output"
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(GF180_GENERATOR),
|
||||
"--source",
|
||||
str(source),
|
||||
"--output-dir",
|
||||
str(output_dir),
|
||||
"--revision",
|
||||
"locked-revision",
|
||||
"--library",
|
||||
library,
|
||||
"--cell",
|
||||
"bi_t",
|
||||
"--cell",
|
||||
"in_c",
|
||||
"--corner",
|
||||
corner,
|
||||
"--output-name",
|
||||
"retrosoc_io.lib",
|
||||
],
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
output = output_dir / "retrosoc_io.lib"
|
||||
assert "library (gf180_io)" in output.read_text(encoding="utf-8")
|
||||
assert "cell (bi_t) {}" in output.read_text(encoding="utf-8")
|
||||
assert "cell (in_c) {}" in output.read_text(encoding="utf-8")
|
||||
assert (output.with_suffix(".lib.revision")).read_text(encoding="utf-8") == (
|
||||
"locked-revision\ngf180mcu_fd_io\nbi_t,in_c\n"
|
||||
)
|
||||
Reference in new issue
Block a user