mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
- Added support for optional files in GDS generation to handle legacy layouts. - Introduced macro-level LVS/ERC processing to improve verification of hard macros. - Updated STA scripts to adopt PrimeTime analysis settings from legacy flows. - Enhanced synthesis settings to align with CX55 legacy flow, including clock gating and area optimization. - Improved reporting functions to include detailed synthesis summaries and area metrics. - Updated package generation script to support both TINY and MINI SOC configurations. - Added comprehensive tests for ECO translation, timing contract generation, and constraint handling. - Ensured compatibility with new timing contract formats and enhanced error handling for domain drift.
234 lines
9.0 KiB
Python
234 lines
9.0 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
from scripts.dependency_lock import DEFAULT_LOCK, load_lock # noqa: E402
|
|
|
|
|
|
def component(name: str, kind: str, spec: dict[str, object]) -> dict[str, object]:
|
|
license_name = str(spec.get("license", "NOASSERTION"))
|
|
license_value = (
|
|
{"name": license_name}
|
|
if license_name == "NOASSERTION" or license_name.startswith("LicenseRef-")
|
|
else {"id": license_name}
|
|
)
|
|
item: dict[str, object] = {
|
|
"type": "library" if kind != "toolchain" else "application",
|
|
"name": name,
|
|
"licenses": [{"license": license_value}],
|
|
"externalReferences": [{"type": "distribution", "url": spec["url"]}],
|
|
}
|
|
if "revision" in spec:
|
|
item["version"] = spec["revision"]
|
|
item["properties"] = [{"name": "vcs:revision", "value": spec["revision"]}]
|
|
if "nar_hash" in spec:
|
|
item["properties"].append({"name": "nix:narHash", "value": spec["nar_hash"]})
|
|
else:
|
|
item["version"] = spec.get("version", spec.get("sha256", "unknown"))
|
|
item["hashes"] = [{"alg": "SHA-256", "content": spec["sha256"]}]
|
|
return item
|
|
|
|
|
|
def container_component(name: str, spec: dict[str, object]) -> dict[str, object]:
|
|
license_name = str(spec.get("license", "NOASSERTION"))
|
|
license_value = (
|
|
{"name": license_name}
|
|
if license_name == "NOASSERTION" or license_name.startswith("LicenseRef-")
|
|
else {"id": license_name}
|
|
)
|
|
digest = str(spec["digest"])
|
|
return {
|
|
"type": "container",
|
|
"name": f"container/{name}",
|
|
"version": digest.removeprefix("sha256:"),
|
|
"licenses": [{"license": license_value}],
|
|
"externalReferences": [{"type": "distribution", "url": spec["url"]}],
|
|
"hashes": [{"alg": "SHA-256", "content": digest.removeprefix("sha256:")}],
|
|
"properties": [{"name": "oci:image", "value": spec["image"]}],
|
|
}
|
|
|
|
|
|
def make_sbom(lock: dict[str, object]) -> dict[str, object]:
|
|
components = [component(name, "source", spec) for name, spec in lock["sources"].items()]
|
|
components.extend(component(name, "archive", spec) for name, spec in lock["archives"].items())
|
|
components.extend(
|
|
container_component(name, spec) for name, spec in lock["container_images"].items()
|
|
)
|
|
components.extend(
|
|
component(f"nix/{name}", "source", spec) for name, spec in lock["nix_inputs"].items()
|
|
)
|
|
for platform, tools in lock["toolchains"].items():
|
|
components.extend(
|
|
component(f"{platform}/{name}", "toolchain", spec) for name, spec in tools.items()
|
|
)
|
|
return {
|
|
"bomFormat": "CycloneDX",
|
|
"specVersion": "1.5",
|
|
"version": 1,
|
|
"components": components,
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Create retroSoC source deliverables")
|
|
parser.add_argument("--root", type=Path, required=True)
|
|
parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK)
|
|
parser.add_argument("--variant-root", type=Path, required=True)
|
|
parser.add_argument("--output-dir", type=Path, required=True)
|
|
args = parser.parse_args()
|
|
root = args.root.resolve()
|
|
manifest_path = args.variant_root.resolve() / "meta/manifest.json"
|
|
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
config = manifest["configuration"]
|
|
lock = load_lock(args.lock)
|
|
soc = config.get("SOC", "MINI")
|
|
product_root = root / "rtl" / soc.lower()
|
|
args.output_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
with tempfile.TemporaryDirectory(
|
|
prefix="retrosoc-package-", dir=args.output_dir.parent
|
|
) as temp:
|
|
staging = Path(temp) / "package"
|
|
staging.mkdir()
|
|
commercial_contract = Path(temp) / "commercial_timing_contract.tcl"
|
|
subprocess.run(
|
|
[
|
|
"python3",
|
|
str(root / "physical/commercial/scripts/generate_timing_contract.py"),
|
|
"--soc",
|
|
soc,
|
|
"--domains",
|
|
str(product_root / "integration/clock_reset_domains.json"),
|
|
"--pin-map",
|
|
str(product_root / "pin_map/pin_map.json"),
|
|
"--output",
|
|
str(commercial_contract),
|
|
],
|
|
cwd=root,
|
|
check=True,
|
|
)
|
|
metadata_files = [
|
|
f"contracts/{commercial_contract.name}={commercial_contract}",
|
|
]
|
|
if soc == "TINY":
|
|
tiny_sdc = Path(temp) / "tiny_core.sdc"
|
|
subprocess.run(
|
|
[
|
|
"python3",
|
|
str(root / "physical/smoke/sta/opensta/generate_sdc.py"),
|
|
"--domains",
|
|
str(product_root / "integration/clock_reset_domains.json"),
|
|
"--pin-map",
|
|
str(product_root / "pin_map/pin_map.json"),
|
|
"--output",
|
|
str(tiny_sdc),
|
|
],
|
|
cwd=root,
|
|
check=True,
|
|
)
|
|
metadata_files.append(f"contracts/{tiny_sdc.name}={tiny_sdc}")
|
|
if config.get("MINI_MODE", "PRODUCT") == "MPW":
|
|
dynamic_core_filelist = (
|
|
args.variant_root
|
|
/ "generated/mpw"
|
|
/ config["SIMU"].lower()
|
|
/ "core/core.fl"
|
|
)
|
|
dynamic_ip_filelist = (
|
|
args.variant_root
|
|
/ "generated/mpw"
|
|
/ config["SIMU"].lower()
|
|
/ "ip/ip.fl"
|
|
)
|
|
else:
|
|
dynamic_core_filelist = (
|
|
args.variant_root / "generated/user_extensions/legacy_core.fl"
|
|
)
|
|
dynamic_ip_filelist = (
|
|
args.variant_root / "generated/user_extensions/legacy_ip.fl"
|
|
)
|
|
command = [
|
|
"python3",
|
|
str(root / "physical/smoke/syn/tools/export_soc_sources.py"),
|
|
"sv",
|
|
"--output-dir",
|
|
str(staging),
|
|
"--pdk",
|
|
config["PDK"],
|
|
"--simu",
|
|
config["SIMU"],
|
|
"--jtag-idcode",
|
|
config.get("JTAG_IDCODE", "DEADBEEF"),
|
|
"--ext-clk-hz",
|
|
str(config.get("EXT_CLK_HZ", 72_000_000)),
|
|
"--aud-clk-hz",
|
|
str(config.get("AUD_CLK_HZ", 18_432_000)),
|
|
"--clint-timebase-hz",
|
|
str(config.get("CLINT_TIMEBASE_HZ", 1_000_000)),
|
|
"--dynamic-core-filelist",
|
|
str(dynamic_core_filelist),
|
|
"--dynamic-ip-filelist",
|
|
str(dynamic_ip_filelist),
|
|
"--memory-map-filelist",
|
|
str(args.variant_root / "generated/memory_map/memory_map.fl"),
|
|
"--soc-topology-filelist",
|
|
str(args.variant_root / "generated/soc_topology/soc_topology.fl"),
|
|
"--user-extensions-filelist",
|
|
str(args.variant_root / "generated/user_extensions/user_extensions.fl"),
|
|
"--pin-map-filelist",
|
|
str(args.variant_root / "generated/pin_map/pin_map.fl"),
|
|
"--archinfo-incdir",
|
|
str(args.variant_root / "generated/archinfo"),
|
|
]
|
|
for metadata in metadata_files:
|
|
command.extend(("--metadata-file", metadata))
|
|
if soc == "TINY":
|
|
if config.get("SYNTH") == "YOSYS":
|
|
recipe = config.get("SYNTH_RECIPE", "balanced")
|
|
folder = "yosys" if recipe == "balanced" else f"yosys-{recipe}"
|
|
source_dir = args.variant_root / "syn" / folder / "filelists"
|
|
else:
|
|
source_dir = args.variant_root / "sim" / config["SIMU"].lower() / "filelists"
|
|
command.extend(("--soc", "TINY", "--source-filelist-dir", str(source_dir)))
|
|
for key, flag in (
|
|
("HAVE_PLL", "--have-pll"),
|
|
("HAVE_SRAM_IF", "--have-sram-if"),
|
|
("HAVE_SRAM_MACRO", "--have-sram-macro"),
|
|
("HAVE_SVA", "--have-sva"),
|
|
):
|
|
if config.get(key) == "YES":
|
|
command.append(flag)
|
|
subprocess.run(command, cwd=root, check=True)
|
|
command[2] = "tar"
|
|
subprocess.run(command, cwd=root, check=True)
|
|
shutil.copy2(args.lock, staging / "dependencies.lock.json")
|
|
shutil.copy2(manifest_path, staging / "manifest.json")
|
|
(staging / "sbom.cdx.json").write_text(
|
|
json.dumps(make_sbom(lock), indent=2, sort_keys=True) + "\n", encoding="utf-8"
|
|
)
|
|
checksums = []
|
|
for path in sorted(item for item in staging.iterdir() if item.is_file()):
|
|
checksums.append(f"{hashlib.sha256(path.read_bytes()).hexdigest()} {path.name}")
|
|
(staging / "SHA256SUMS").write_text("\n".join(checksums) + "\n", encoding="utf-8")
|
|
if args.output_dir.exists():
|
|
shutil.rmtree(args.output_dir)
|
|
shutil.copytree(staging, args.output_dir)
|
|
print(f"package: {args.output_dir.resolve()}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|