mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
- 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.
76 lines
2.5 KiB
Python
76 lines
2.5 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
|
|
|
|
|
DEFAULT_CORNER = "tt_025C_5v00"
|
|
DEFAULT_LIBRARY = "gf180mcu_fd_sc_mcu7t5v0"
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
description="Assemble a GF180 Liberty model from locked PDK fragments"
|
|
)
|
|
parser.add_argument("--source", type=Path, required=True)
|
|
parser.add_argument("--output-dir", type=Path, required=True)
|
|
parser.add_argument("--revision", required=True)
|
|
parser.add_argument("--library", default=DEFAULT_LIBRARY)
|
|
parser.add_argument("--cell", action="append")
|
|
parser.add_argument("--output-name")
|
|
parser.add_argument("--corner", default=DEFAULT_CORNER)
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> int:
|
|
args = parse_args()
|
|
source = args.source.resolve()
|
|
library_dir = source / "libraries" / args.library / "latest"
|
|
header = library_dir / "liberty" / f"{args.library}__{args.corner}.lib"
|
|
cells_dir = library_dir / "cells"
|
|
if args.cell:
|
|
cell_libraries = [
|
|
cells_dir / cell / f"{args.library}__{cell}__{args.corner}.lib"
|
|
for cell in sorted(set(args.cell))
|
|
]
|
|
else:
|
|
cell_libraries = sorted(cells_dir.glob(f"*/*__{args.corner}.lib"))
|
|
if not header.is_file() or not cell_libraries:
|
|
raise FileNotFoundError(f"missing GF180 Liberty inputs below {library_dir}")
|
|
|
|
output_dir = args.output_dir.resolve()
|
|
output_name = args.output_name or f"{args.library}__{args.corner}.lib"
|
|
output = output_dir / output_name
|
|
revision = output.with_suffix(output.suffix + ".revision")
|
|
revision_contents = (
|
|
f"{args.revision}\n{args.library}\n{','.join(sorted(set(args.cell or ())))}\n"
|
|
)
|
|
if (
|
|
output.is_file()
|
|
and revision.is_file()
|
|
and revision.read_text(encoding="utf-8") == revision_contents
|
|
):
|
|
print(f"GF180 Liberty is ready: {output}")
|
|
return 0
|
|
|
|
prefix, separator, _ = header.read_text(encoding="utf-8").rpartition("\n}")
|
|
if not separator:
|
|
raise RuntimeError(f"GF180 Liberty header is malformed: {header}")
|
|
cells = "\n\n".join(path.read_text(encoding="utf-8") for path in cell_libraries)
|
|
atomic_write(output, f"{prefix}\n\n{cells}\n}}\n")
|
|
atomic_write(revision, revision_contents)
|
|
print(f"generated GF180 Liberty: {output}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|