feat: add support for ICS55 PDK in various components and tools

- Introduced Verilator and Yosys warning configurations for ICS55.
- Updated RTL filelists to include functional models for ICS55.
- Modified memory modules to use wire instead of logic for clock and reset signals.
- Enhanced Makefile and Iverilog configurations to support ICS55.
- Implemented new simulation cells for ICS55 in the tech library.
- Updated regression scripts and tests to include ICS55 support.
- Added functionality to prepare ICS55 liberty and simulation models.
- Enhanced testbench to validate ICS55 specific components and behaviors.
This commit is contained in:
maksyuki committed 2026-08-01 11:55:49 +08:00
1 parent efac07b29c
commit ed19891bf5
35 files changed
+5336 -43

No files matched your search

+2 -1
View File
@@ -6,7 +6,8 @@ workflows.
`workflows/quality.yml` runs the fast quality gate. `workflows/regression-smoke.yml`,
`workflows/regression-ihp130.yml`, `workflows/regression-gf180.yml`,
`workflows/regression-sky130.yml`, and `workflows/nightly.yml` call the reusable regression workflow;
`workflows/regression-ics55.yml`, `workflows/regression-sky130.yml`, and
`workflows/nightly.yml` call the reusable regression workflow;
`workflows/release.yml` packages tagged releases. `actions/locked-tools/`
installs the locked open-source tools used by CI.
+1
View File
@@ -62,6 +62,7 @@ jobs:
app/fatfs/ff16
pdk/IHP-Open-PDK
pdk/gf180mcu-pdk
pdk/icsprout55-pdk
pdk/skywater-pdk
rtl/managed/clusterip/archinfo
rtl/managed/clusterip/clint
+1
View File
@@ -79,6 +79,7 @@ jobs:
run: |
python3 scripts/regress.py --root . --suite pr --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite pr --pdk GF180 --dry-run
python3 scripts/regress.py --root . --suite pr --pdk ICS55 --dry-run
python3 scripts/regress.py --root . --suite pr --pdk SKY130 --dry-run
python3 scripts/regress.py --root . --suite nightly --pdk IHP130 --dry-run
python3 scripts/regress.py --root . --suite smoke --pdk IHP130 --dry-run
+24
View File
@@ -0,0 +1,24 @@
name: regression-ics55
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: regression-ics55-${{ github.ref }}
cancel-in-progress: true
jobs:
verified-open-source-flows:
uses: ./.github/workflows/_regression.yml
with:
suite: pr
pdk: ICS55
profile: configs/ci/ics55.mk
retention_days: 30
+1
View File
@@ -341,6 +341,7 @@ regress-pr:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk IHP130
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk GF180
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk SKY130
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk ICS55
regress-nightly:
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly
+6 -4
View File
@@ -3,6 +3,7 @@
[![quality](https://github.com/retroSoC/retroSoC/actions/workflows/quality.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/quality.yml)
[![IHP130 regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression-ihp130.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression-ihp130.yml)
[![GF180 regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression-gf180.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression-gf180.yml)
[![ICS55 regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression-ics55.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression-ics55.yml)
[![SKY130 regression](https://github.com/retroSoC/retroSoC/actions/workflows/regression-sky130.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/regression-sky130.yml)
[![nightly](https://github.com/retroSoC/retroSoC/actions/workflows/nightly.yml/badge.svg)](https://github.com/retroSoC/retroSoC/actions/workflows/nightly.yml)
@@ -16,8 +17,8 @@ under the [Mulan Permissive Software License, Version 2](LICENSE).
- A fixed architecture with a Hazard3 management core by default (PicoRV32 is
an explicit build option) and software-selected
user-core and user-IP extension slots.
- Configurable GF180, SKY130, IHP130, and ICS55 implementation targets. GF180, SKY130,
and IHP130 have open-source CI coverage; ICS55 remains a configured cluster target.
- Configurable GF180, SKY130, IHP130, and ICS55 implementation targets with
open-source CI coverage.
- A memory-mapped peripheral subsystem with GPIO, UART, timers, PWM, I2C, I2S, PS2,
1-Wire, SPI/QSPI, SDIO, PSRAM/OPI-PSRAM, SDRAM, DMA, LCD, RTC, watchdog, RNG, and CRC
support. Available interfaces depend on the selected SoC configuration.
@@ -53,9 +54,10 @@ core and defaults to `HAZARD3`.
| --- | --- | --- | --- |
| [`configs/ci/ihp130.mk`](configs/ci/ihp130.mk) | RV32IM | `bringup` | Pull-request open-source regression: firmware, Verilator, Icarus, Yosys, Icarus netlist simulation, and OpenSTA. |
| [`configs/ci/gf180.mk`](configs/ci/gf180.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, and Icarus netlist coverage. |
| [`configs/ci/ics55.mk`](configs/ci/ics55.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist simulation, and OpenSTA core timing coverage. |
| [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, and Icarus netlist coverage. |
| [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | RV32IM | `bringup` | Cluster-only configuration; requires the site PDK and licensed-tool environment. |
| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | RV32IM | `bringup` | Compatibility profile for site-specific ICS55 runs. |
## Prerequisites
@@ -145,7 +147,7 @@ make CONFIG=configs/ci/ihp130.mk SIMU=IVERILOG \
configuration, and runs the Icarus assembly self-test. It runs strict RTL lint
before those flows and omits synthesis,
timing, and netlist simulation for fast feedback. `make regress-pr` runs the
supported IHP130, GF180, and SKY130 PR matrices in sequence, including
supported IHP130, GF180, ICS55, and SKY130 PR matrices in sequence, including
slow-corner OpenSTA core timing analysis for each PDK.
Build outputs are isolated below `build/<profile>-<YYYY-MM-DD-HH-MM>-<config-hash>/`. Each variant keeps its
+11 -5
View File
@@ -43,7 +43,7 @@
"revision": "e696e093129ca2212487aa169af74d06ebd86eb6",
"destination": "pdk/icsprout55-pdk",
"recursive": true,
"license": "NOASSERTION"
"license": "Apache-2.0"
},
"pdk_gf180": {
"url": "https://github.com/google/gf180mcu-pdk.git",
@@ -83,6 +83,12 @@
"destination": "app/fatfs/ff16/ff16.zip",
"license": "LicenseRef-FatFs"
},
"pdk_ics55_h7cr_liberty": {
"url": "https://github.com/openecos-projects/icsprout55-pdk/releases/download/v1.10.100/ics55_LLSC_H7CR_liberty.tar.bz2",
"sha256": "faa3fc2f3ef8560c2d7cafc7800ecee39b69b8d177b14d098ed94062ba3f6e5c",
"destination": ".cache/retrosoc/downloads/pdk/ics55/ics55_LLSC_H7CR_liberty.tar.bz2",
"license": "Apache-2.0"
},
"serv": {
"url": "https://github.com/olofk/serv/archive/refs/tags/1.4.0.tar.gz",
"sha256": "f81c37b9f9d548c658e23bb7eb90fbed8b54e2e9e51fa42b8cd2f34cecc472ab",
@@ -121,10 +127,10 @@
"path": "bin"
},
"yosys": {
"version": "0.58",
"url": "https://github.com/retroSoC/artifact/releases/download/yosys-v0.58/yosys-v0.58-ubuntu-22.04-gcc.tar.gz",
"sha256": "54468eb1fe474b11b8014fcabb068bf1398eaa072fa724b26e09dd2df58c3386",
"archive": "yosys-v0.58-ubuntu-22.04-gcc.tar.gz",
"version": "0.67",
"url": "https://github.com/retroSoC/artifact/releases/download/yosys-v0.67/yosys-v0.67-ubuntu-22.04-gcc.tar.gz",
"sha256": "272b5060e5c5d8c8830e2d4efd8d88f388fe39e152136bd99c293e718a91c1a7",
"archive": "yosys-v0.67-ubuntu-22.04-gcc.tar.gz",
"path": "bin"
},
"sby": {
+10
View File
@@ -0,0 +1,10 @@
SOC := MINI
PDK := ICS55
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
HAVE_SVA := NO
ISA := RV32IM
HAVE_CSR := NO
APP := bringup
LINK_TYPE := ld2_psram
+8 -6
View File
@@ -7,21 +7,23 @@
| Smoke | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator SVA compilation, Icarus assembly self-test |
| Pull request | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Pull request | `configs/ci/gf180.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Pull request | `configs/ci/ics55.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Pull request | `configs/ci/sky130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus, Yosys, netlist Icarus, OpenSTA |
| Nightly | `configs/ci/ihp130.mk` | repeated full IHP130 regression |
| Cluster | `configs/cluster/ics55.mk` | configuration only; run with site PDK/tool access |
| Cluster | `configs/cluster/ics55.mk` | compatibility profile for site-specific ICS55 runs |
OpenSTA runs a reproducible core-STA baseline for every CI PDK: IHP130 uses
`slow_1p08V_125C`, GF180 uses `ss_125C_4v50`, and SKY130 uses `ss_100C_1v40`.
`slow_1p08V_125C`, GF180 uses `ss_125C_4v50`, ICS55 uses the H7CR
`ss_1p08_125C` view, and SKY130 uses `ss_100C_1v40`.
The SDC is generated from `rtl/mini/integration/clock_reset_domains.json`, applies
the external, audio, and DVP clocks at their technology-buffer observation pins,
and groups unrelated clocks asynchronously. It intentionally excludes package,
board, pad-ring, extraction, and PLL timing, so it is a regression-quality core
analysis rather than physical signoff.
VCS and ICS55 flows remain cluster-only because public runners do not have the licensed simulator or
the validated site environment. They use the same variant layout, manifests, result JSON, warning
scanner, and cleanup rules.
VCS flows remain cluster-only because public runners do not have the licensed
simulator. ICS55 uses the same variant layout, manifests, result JSON, warning
scanner, and cleanup rules as the other open-source CI PDKs.
## Reproducible Inputs
@@ -177,7 +179,7 @@ for diagnosis but is initially non-blocking.
`quality.yml` validates C, Makefile, and self-owned RTL formatting as well as Python, YAML, GitHub
Actions, the dependency lock, and script tests. `regression-smoke.yml` provides fast IHP130 feedback;
the three full PDK regression workflows remain required PR coverage and do not repeat the format checks.
the four full PDK regression workflows remain required PR coverage and do not repeat the format checks.
`nightly.yml` repeats the fixed IHP130 architecture. Source dependencies, locked tool archives, and Verilator `ccache` use
separate cache keys.
+13 -4
View File
@@ -10,7 +10,13 @@ inputs. Do not reformat, patch, or delete vendor content as part of unrelated
work. PDK changes require the affected synthesis, timing, physical-design, or
FPGA validation flow and the ownership review defined by `.github/CODEOWNERS`.
The supported CI PDKs are IHP130, GF180, and SKY130. GF180 assembles its
The supported CI PDKs are IHP130, GF180, ICS55, and SKY130. ICS55 obtains its
locked H7CR standard-cell Liberty archive through
`config/dependencies.lock.json` and materializes normalized TT and SS views
below `.cache/retrosoc/pdk/ics55/`; its IO Liberty is part of the locked
source checkout. PDK setup also generates a cached copy of the H7CR
functional standard-cell model that corrects the seven upstream `MUXI2`
self-feedback models without modifying the vendor checkout. GF180 assembles its
locked `gf180mcu_fd_sc_mcu7t5v0` Liberty fragments and the two SoC-used IO
cells (`in_c` and `bi_t`) at both its TT and core-STA slow corners. SKY130
generates its `sky130_fd_sc_hd` Liberty model from locked upstream JSON files
@@ -19,12 +25,12 @@ at TT and slow corners. Derived Liberty files are stored below
the locked HD library submodule; the remaining upstream libraries are outside
the SoC integration boundary.
All three PDK profiles run slow-corner OpenSTA core timing in CI. The analysis
All four PDK profiles run slow-corner OpenSTA core timing in CI. The analysis
uses the PDK standard-cell Liberty plus the linked IO-cell model required to
read the synthesized top-level wrapper. It excludes board, pad-ring,
extraction, and PLL timing and is therefore not a physical signoff result.
The GF180 and SKY130 CI flows synth the digital core and run functional
The GF180, ICS55, and SKY130 CI flows synth the digital core and run functional
standard-cell netlist simulation. Their generic SoC wrappers explicitly use
the locked PDK GPIO and clock cells for RTL and netlist simulation: GF180 uses
`gf180mcu_fd_io__in_c`/`gf180mcu_fd_io__bi_t` and MCU7T5V0 clock cells, while
@@ -34,7 +40,10 @@ collateral; physical pad-ring integration remains a technology-specific
signoff task. GF180/SKY130 PLL configurations are rejected because no
qualified crystal-pad implementation is selected. SKY130 maps the generic
Schmitt-control input to its threshold-select control and therefore does not
provide hysteresis. Verilator uses a pin-compatible local functional model
provide hysteresis. ICS55 uses its cached corrected standard-cell functional
model with timing blocks disabled and a pin-compatible local PBMUX/PWE model
only for Verilator, because the upstream IO model uses unsupported gate
strengths and transmission primitives. Verilator uses a pin-compatible local functional model
for the GF180 IO cells because the upstream model contains unsupported `rnmos`
primitives. SKY130 uses the same approach because its upstream IO model uses
unsupported switch primitives and drive strengths. Synthesis still preserves
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Prepare the locked ICS55 H7CR Liberty timing views."""
from __future__ import annotations
import argparse
import os
import shutil
import sys
import tempfile
from pathlib import Path
from typing import Iterable
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from scripts.install_toolchain import safe_extract # noqa: E402
from scripts.setup_helpers import atomic_write, sha256 # noqa: E402
TT_OUTPUT = "ics55_h7cr_tt.lib"
SS_OUTPUT = "ics55_h7cr_ss.lib"
MARKER = ".complete"
def find_corner(root: Path, tokens: Iterable[str]) -> Path:
normalized_tokens = tuple(token.lower() for token in tokens)
matches = [
path
for path in sorted(root.rglob("*.lib"))
if all(token in path.name.lower() for token in normalized_tokens)
]
if len(matches) != 1:
description = ", ".join(normalized_tokens)
raise FileNotFoundError(
f"expected one ICS55 Liberty corner matching {description}, found {len(matches)}"
)
return matches[0]
def prepare(archive: Path, output_dir: Path, revision: str) -> None:
archive = archive.resolve()
if not archive.is_file():
raise FileNotFoundError(f"ICS55 Liberty archive not found: {archive}")
output_dir = output_dir.resolve()
marker_content = f"{revision}\n{sha256(archive)}\n"
marker = output_dir / MARKER
if (
marker.is_file()
and marker.read_text(encoding="utf-8") == marker_content
and (output_dir / TT_OUTPUT).is_file()
and (output_dir / SS_OUTPUT).is_file()
):
print(f"ICS55 Liberty is ready: {output_dir}")
return
output_dir.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix=f".{output_dir.name}.", dir=output_dir.parent) as temp:
candidate = Path(temp) / "content"
extracted = Path(temp) / "extracted"
candidate.mkdir()
extracted.mkdir()
safe_extract(archive, extracted)
shutil.copy2(find_corner(extracted, ("tt", "1p2", "25")), candidate / TT_OUTPUT)
shutil.copy2(find_corner(extracted, ("ss", "1p08", "125")), candidate / SS_OUTPUT)
atomic_write(candidate / MARKER, marker_content)
if output_dir.exists():
shutil.rmtree(output_dir)
os.replace(candidate, output_dir)
print(f"prepared ICS55 Liberty: {output_dir}")
def main() -> int:
parser = argparse.ArgumentParser(
description="Extract locked ICS55 H7CR Liberty timing views into the cache"
)
parser.add_argument("--archive", type=Path, required=True)
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--revision", required=True)
args = parser.parse_args()
prepare(args.archive, args.output_dir, args.revision)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""Prepare the ICS55 H7CR functional standard-cell model for simulation."""
from __future__ import annotations
import argparse
import re
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, sha256 # noqa: E402
EXPECTED_MUXI2_CELLS = {
"MUXI2X0P5H7R",
"MUXI2X0P7H7R",
"MUXI2X1H7R",
"MUXI2X1P4H7R",
"MUXI2X2H7R",
"MUXI2X3H7R",
"MUXI2X4H7R",
}
BROKEN_IMPLEMENTATION = " udp_mux2 u0(Y, A, B, S0);\n not u1(Y, Y);"
FIXED_IMPLEMENTATION = (
" wire muxi2_y;\n\n"
" udp_mux2 u0(muxi2_y, A, B, S0);\n"
" not u1(Y, muxi2_y);"
)
MUXI2_MODULE = re.compile(
r"(?P<header>\bmodule\s+(?P<name>MUXI2X[A-Za-z0-9]+H7R)"
r"\s*\(Y,\s*A,\s*B,\s*S0\);)"
r"(?P<body>.*?)"
r"(?P<footer>\bendmodule\s*//\s*(?P=name))",
re.DOTALL,
)
def patch_muxi2_models(content: str) -> tuple[str, tuple[str, ...]]:
"""Replace the self-referential MUXI2 model with an intermediate-net form."""
cells: list[str] = []
def patch(match: re.Match[str]) -> str:
name = match["name"]
body = match["body"]
broken_count = body.count(BROKEN_IMPLEMENTATION)
fixed_count = body.count(FIXED_IMPLEMENTATION)
if broken_count == 1 and fixed_count == 0:
body = body.replace(BROKEN_IMPLEMENTATION, FIXED_IMPLEMENTATION)
elif broken_count != 0 or fixed_count != 1:
raise ValueError(f"unexpected {name} functional implementation")
cells.append(name)
return f"{match['header']}{body}{match['footer']}"
patched = MUXI2_MODULE.sub(patch, content)
found = set(cells)
if found != EXPECTED_MUXI2_CELLS:
missing = ", ".join(sorted(EXPECTED_MUXI2_CELLS - found))
unexpected = ", ".join(sorted(found - EXPECTED_MUXI2_CELLS))
details = []
if missing:
details.append(f"missing {missing}")
if unexpected:
details.append(f"unexpected {unexpected}")
raise ValueError("unexpected ICS55 H7CR MUXI2 cell set: " + "; ".join(details))
return patched, tuple(sorted(cells))
def prepare(source: Path, output: Path, revision: str) -> None:
source = source.resolve()
if not source.is_file():
raise FileNotFoundError(f"ICS55 H7CR Verilog model not found: {source}")
patched, cells = patch_muxi2_models(source.read_text(encoding="utf-8"))
output = output.resolve()
metadata = f"{revision}\n{sha256(source)}\n{','.join(cells)}\n"
changed = atomic_write(output, patched)
atomic_write(output.with_suffix(output.suffix + ".revision"), metadata)
state = "prepared" if changed else "ready"
print(f"ICS55 H7CR functional model {state}: {output}")
def main() -> int:
parser = argparse.ArgumentParser(
description="Patch the locked ICS55 H7CR MUXI2 functional models in the cache"
)
parser.add_argument("--source", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--revision", required=True)
args = parser.parse_args()
prepare(args.source, args.output, args.revision)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+42 -2
View File
@@ -8,8 +8,8 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from scripts.dependency_lock import source # noqa: E402
from scripts.setup_helpers import ensure_git_repo # noqa: E402
from scripts.dependency_lock import archive, source # noqa: E402
from scripts.setup_helpers import download_file, ensure_git_repo # noqa: E402
def main() -> int:
@@ -116,6 +116,46 @@ def main() -> int:
),
check=True,
)
elif name == "pdk_ics55":
liberty = archive("pdk_ics55_h7cr_liberty")
archive_path = ROOT / liberty["destination"]
download_file(
liberty["url"],
archive_path,
liberty["sha256"],
update=args.update,
timeout=120,
)
subprocess.run(
(
sys.executable,
str(ROOT / "pdk/prepare_ics55_liberty.py"),
"--archive",
str(archive_path),
"--output-dir",
str(ROOT / ".cache/retrosoc/pdk/ics55"),
"--revision",
dependency["revision"],
),
check=True,
)
subprocess.run(
(
sys.executable,
str(ROOT / "pdk/prepare_ics55_sim_model.py"),
"--source",
str(
destination
/ "IP/STD_cell/ics55_LLSC_H7C_V1p10C100"
/ "ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v"
),
"--output",
str(ROOT / ".cache/retrosoc/pdk/ics55/ics55_h7cr_functional.v"),
"--revision",
dependency["revision"],
),
check=True,
)
return 0
+12
View File
@@ -0,0 +1,12 @@
{
"entries": [
{
"count": 1,
"example": "warning:Found both default and explicit timescale based delays. Use",
"signature": "aee22756a35889cc"
}
],
"profile": "ics55",
"schema_version": 1,
"tool": "iverilog"
}
+6
View File
@@ -0,0 +1,6 @@
{
"entries": [],
"profile": "ics55",
"schema_version": 1,
"tool": "opensta"
}
File diff suppressed because it is too large. Load diff
+77
View File
@@ -0,0 +1,77 @@
{
"entries": [
{
"count": 1,
"example": "UNOPTFLAT:$ROOT/rtl/managed/mpw/core/username1/kianV/kianv_harris_mc_edition.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.alu_valid'",
"signature": "1558b81c8599cb8f"
},
{
"count": 1,
"example": "UNOPTFLAT:$ROOT/rtl/ip/native/storage/xpi.sv:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_xpi_if.io_di_i'",
"signature": "411d9ab46fef9d7d"
},
{
"count": 1,
"example": "UNOPTFLAT:$ROOT/rtl/managed/mpw/core/username1/kianV/kianv_harris_mc_edition.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.ALUSrcA'",
"signature": "5b2c034f9f3213b7"
},
{
"count": 1,
"example": "PINMISSING:$ROOT/rtl/managed/mpw/core/username4/serv/serv_synth_wrapper.v:<line>: Instance has missing pin: 'o_ren'",
"signature": "704b76c39b529025"
},
{
"count": 2,
"example": "CMPCONST:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Comparison is constant due to limited range",
"signature": "813090adc9f1e8a8"
},
{
"count": 1,
"example": "UNOPTFLAT:$ROOT/rtl/managed/mpw/core/username1/kianV/kianv_harris_mc_edition.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.ALUSrcB'",
"signature": "8c87143c9aa63807"
},
{
"count": 1,
"example": "UNOPTFLAT:$ROOT/rtl/managed/mpw/core/username1/kianV/alu_decoder.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.control_unit_I.alu_decoder_I.ALUControl'",
"signature": "a7cc0b675cb3537c"
},
{
"count": 1,
"example": "UNOPTFLAT:$ROOT/rtl/managed/mpw/core/username1/kianV/alu.v:<line>: Signal unoptimizable: Circular combinational logic: 'retrosoc_top.u_retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username1.u_kianv_harris_mc_edition.datapath_unit_I.alu_I.is_sub'",
"signature": "b808f41166ecb8fb"
},
{
"count": 1,
"example": "WIDTHEXPAND:$ROOT/rtl/managed/mpw/core/username6/darkriscv/darkriscv.v:<line>: Operator COND expects 32 bits on the Conditional True, but Conditional True's LT generates 1 bits.",
"signature": "c1b20cb9d552ca20"
},
{
"count": 14,
"example": "WIDTHTRUNC:$ROOT/rtl/managed/mpw/core/username6/darkriscv/darkriscv.v:<line>: Logical operator COND expects 1 bit on the Conditional Test, but Conditional Test's VARREF 'FLUSH' generates 2 bits.",
"signature": "c7ca04782912d519"
},
{
"count": 1,
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_read.sv:<line>: Operator ASSIGNDLY expects 8 bits on the Assign RHS, but Assign RHS's REPLICATE generates 9 bits.",
"signature": "c963ef155f9a032c"
},
{
"count": 3,
"example": "WIDTHTRUNC:$ROOT/rtl/ip/native/storage/spisd_write.sv:<line>: Bit extraction of var[7:0] requires 3 bit index, not 4 bits.",
"signature": "ef9f314dab2ac31d"
},
{
"count": 1,
"example": "WIDTHEXPAND:$ROOT/rtl/managed/mpw/core/username6/darkriscv/darkriscv.v:<line>: Operator COND expects 32 bits on the Conditional True, but Conditional True's LTS generates 1 bits.",
"signature": "f505968343b665e3"
},
{
"count": 35,
"example": "SPECIFYIGN:$ROOT/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v:<line>: Ignoring unsupported: specify block construct",
"signature": "fda0ac7e720e6088"
}
],
"profile": "ics55",
"schema_version": 1,
"tool": "verilator"
}
+62
View File
@@ -0,0 +1,62 @@
{
"entries": [
{
"count": 1,
"example": "warning:Wire hazard3_csr_username3$retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username3.u_hazard3_cpu_1port.core.csr_u.\\dbg_req_resume_prev is used but has no driver.",
"signature": "0a782ef45c9a2826"
},
{
"count": 1,
"example": "warning:Wire hazard3_csr_username3$retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username3.u_hazard3_cpu_1port.core.csr_u.\\debug_mode is used but has no driver.",
"signature": "0b15e8e8ff1ec2e6"
},
{
"count": 1,
"example": "warning:Wire hazard3_csr_username3$retrosoc_asic.u_retrosoc.u_user_core_top.u_user_core_design_username3.u_hazard3_cpu_1port.core.csr_u.\\pending_dbg_resume_prev is used but has no driver.",
"signature": "1b869cf19bdd0fc1"
},
{
"count": 2,
"example": "warning:asynchronous load value missing for variable '\\update_irq_reg_arrays.i'",
"signature": "56c5f69398be8d7b"
},
{
"count": 10,
"example": "warning:The network is combinational (run \"fraig\" or \"fraig_sweep\").",
"signature": "62cc5ee554312205"
},
{
"count": 4,
"example": "warning:asynchronous load value missing for variable '\\update_nonconst.prev'",
"signature": "a30df20b98d49196"
},
{
"count": 1,
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_mgmt_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\debug_mode is used but has no driver.",
"signature": "abc4bf82285e7e27"
},
{
"count": 1,
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_mgmt_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\dbg_req_resume_prev is used but has no driver.",
"signature": "b53fced7cd12c426"
},
{
"count": 4,
"example": "warning:asynchronous load value missing for variable '\\update_nonconst.nonconst'",
"signature": "d35463a1de40c7b2"
},
{
"count": 1,
"example": "warning:Wire hazard3_csr$retrosoc_asic.u_retrosoc.u_core_wrapper.u_mgmt_core_wrapper.u_hazard3_cpu_1port.core.csr_u.\\pending_dbg_resume_prev is used but has no driver.",
"signature": "dcbf3e1cbb359ba9"
},
{
"count": 10,
"example": "warning:Detected 6 multi-output cells (for example, \"ADDFX1H7R\").",
"signature": "efa45c73f40f4fe6"
}
],
"profile": "ics55",
"schema_version": 1,
"tool": "yosys"
}
+2 -1
View File
@@ -1,2 +1,3 @@
/pdk/icsprout55-pdk/IP/STD_cell/ics55_LLSC_H7C_V1p10C100/ics55_LLSC_H7CR/verilog/ics55_LLSC_H7CR.v
+define+functional
/cache/retrosoc/pdk/ics55/ics55_h7cr_functional.v
/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/verilog/icsIOA_N55_3P3.v
+2 -2
View File
@@ -41,8 +41,8 @@ module nmi_opipsram (
// verilog_format: on
);
logic dummy0 = clk_i;
logic dummy1 = rst_n_i;
wire dummy0 = clk_i;
wire dummy1 = rst_n_i;
// nmi
assign nmi.rdata = '0;
+2 -2
View File
@@ -40,8 +40,8 @@ module nmi_sdio (
// verilog_format: on
);
logic dummy0 = clk_i;
logic dummy1 = rst_n_i;
wire dummy0 = clk_i;
wire dummy1 = rst_n_i;
// nmi
assign nmi.rdata = '0;
assign nmi.ready = '0;
+6
View File
@@ -112,7 +112,13 @@ ifeq ($(PDK), IHP130)
else ifeq ($(PDK), SKY130)
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_sky130.fl
else ifeq ($(PDK), ICS55)
ifeq ($(SYNTH), YOSYS)
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_ics55_yosys.fl
else ifeq ($(SIMU), VERILATOR)
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_ics55_verilator.fl
else
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_ics55.fl
endif
else ifeq ($(PDK), GF180)
PDK_FILELIST := $(GENERATED_FL_DIR)/pdk_gf180.fl
endif
+2 -2
View File
@@ -65,8 +65,8 @@ gen_iverilog_filelist: $(IVERILOG_BEHV_FLIST)
tech-cell-test: $(FILELIST_STAMP) $(TECH_CELL_TEST) $(ROOT_PATH)/rtl/tech/tc_io.sv \
$(ROOT_PATH)/rtl/tech/tc_clk.sv
@case "$(PDK)" in \
GF180|SKY130) ;; \
*) echo "tech-cell-test supports PDK=GF180 or PDK=SKY130" >&2; exit 2 ;; \
GF180|SKY130|ICS55) ;; \
*) echo "tech-cell-test supports PDK=GF180, SKY130, or ICS55" >&2; exit 2 ;; \
esac
@mkdir -p $(TECH_CELL_TEST_DIR)
python3 $(ROOT_PATH)/scripts/run_flow.py --tool iverilog-tech-cells \
+1
View File
@@ -16,6 +16,7 @@ ROOT_DIR = RTL_DIR.parent
DEFAULT_OUTPUT_DIR = MINI_DIR / ".generated_fl"
PATH_PREFIXES = {
"cache": ROOT_DIR / ".cache",
"pdk": ROOT_DIR / "pdk",
"clusterip": RTL_DIR / "managed" / "clusterip",
"ip": RTL_DIR / "ip",
+49
View File
@@ -0,0 +1,49 @@
// Functional ICS55 IO models for Verilator only.
//
// The native model uses transistor primitives, transmission gates, and drive
// strengths that Verilator does not support. These models retain the logical
// behavior used by the SoC wrappers without replacing native-cell instances in
// other flows.
`timescale 1ns / 1ps
// The native PDK module names and analog drive controls are fixed interfaces.
// They do not affect the digital behavior represented by these models.
/* verilator lint_off DECLFILENAME */
/* verilator lint_off UNUSED */
module P65_1233_PWE (
input logic E,
input logic XIN,
output logic XOUT,
output logic XC
);
assign XOUT = ~(E & XIN);
assign XC = E & XIN;
endmodule
module P65_1233_PBMUX (
output logic C,
inout wire A,
inout wire PAD,
input logic IE,
input logic CS,
input logic I,
input logic OE,
input logic OD,
input logic PU,
input logic PD,
input logic DS0,
input logic DS1
);
assign PAD = OE ? (OD ? (I ? 1'bz : 1'b0) : I) : 1'bz;
assign PAD = PU ? 1'b1 : 1'bz;
assign PAD = PD ? 1'b0 : 1'bz;
assign A = PAD;
assign C = IE ? PAD : 1'b0;
endmodule
/* verilator lint_on UNUSED */
/* verilator lint_on DECLFILENAME */
+8
View File
@@ -203,6 +203,14 @@ module tc_clk_xor2 (
.X(clk_o)
);
`elsif PDK_ICS55
(* keep *) (* dont_touch = "true" *)
XOR2X0P5H7R u_XOR2X0P5H7R (
.A(clk0_i),
.B(clk1_i),
.Y(clk_o)
);
`elsif PDK_GF180
(* keep *) (* dont_touch = "true" *)
gf180mcu_fd_sc_mcu7t5v0__xor2_1 u_gf180mcu_fd_sc_mcu7t5v0__xor2_1 (
+34
View File
@@ -188,6 +188,23 @@ module tc_io_in_pad (
.p2c(p2c)
);
`elsif PDK_ICS55
(* keep *) (* dont_touch = "true" *)
P65_1233_PBMUX u_P65_1233_PBMUX (
.C (p2c),
.A (),
.PAD(pad),
.IE (1'b1),
.CS (1'b1), // 1: CMOS 0: SCHMI
.I (1'b0),
.OE (1'b0),
.OD (1'b0),
.PU (1'b0),
.PD (1'b0),
.DS0(1'b0),
.DS1(1'b1) // 8mA
);
`endif
endmodule
@@ -229,6 +246,23 @@ module tc_io_out_pad (
.c2p(c2p)
);
`elsif PDK_ICS55
(* keep *) (* dont_touch = "true" *)
P65_1233_PBMUX u_P65_1233_PBMUX (
.C (),
.A (),
.PAD(pad),
.IE (1'b0),
.CS (1'b1), // 1: CMOS 0: SCHMI
.I (c2p),
.OE (1'b1),
.OD (1'b0),
.PU (1'b0),
.PD (1'b0),
.DS0(1'b0),
.DS1(1'b1) // 8mA
);
`endif
endmodule
+1
View File
@@ -58,6 +58,7 @@ NIGHTLY_PROFILES = PR_PROFILES
PDK_PR_PROFILES = {
"GF180": "configs/ci/gf180.mk",
"IHP130": "configs/ci/ihp130.mk",
"ICS55": "configs/ci/ics55.mk",
"SKY130": "configs/ci/sky130.mk",
}
+2
View File
@@ -12,6 +12,8 @@ make CONFIG=configs/ci/ihp130.mk SYNTH=YOSYS synth
make CONFIG=configs/ci/ihp130.mk STA=OPENSTA sta
make CONFIG=configs/ci/gf180.mk SYNTH=YOSYS synth
make CONFIG=configs/ci/gf180.mk STA=OPENSTA sta
make CONFIG=configs/ci/ics55.mk SYNTH=YOSYS synth
make CONFIG=configs/ci/ics55.mk STA=OPENSTA sta
make CONFIG=configs/ci/sky130.mk SYNTH=YOSYS synth
make CONFIG=configs/ci/sky130.mk STA=OPENSTA sta
```
+3
View File
@@ -12,6 +12,9 @@ OPENSTA_LINK_LIBS := $(ROOT_PATH)/.cache/retrosoc/pdk/gf180/gf180mcu_fd_io_retro
else ifeq ($(PDK),SKY130)
OPENSTA_LIBERTY := $(ROOT_PATH)/.cache/retrosoc/pdk/sky130/sky130_fd_sc_hd__ss_100C_1v40.lib
OPENSTA_LINK_LIBS := $(ROOT_PATH)/sta/opensta/sky130_io_blackbox.liberty
else ifeq ($(PDK),ICS55)
OPENSTA_LIBERTY := $(ROOT_PATH)/.cache/retrosoc/pdk/ics55/ics55_h7cr_ss.lib
OPENSTA_LINK_LIBS := $(ROOT_PATH)/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/liberty/ICSIOA_N55_3P3_ss_1p08_2p97_125c.lib
else
$(error OpenSTA core-STA does not support PDK=$(PDK))
endif
+7 -9
View File
@@ -34,16 +34,14 @@ if {$pdk == "IHP130"} {
set tech_cell_tiehi {sg13g2_tiehi L_HI}
set tech_cell_tielo {sg13g2_tielo L_LO}
} elseif {$pdk == "ICS55"} {
set pdk_dir "/nfs/share/home/zhuangchunan/proj/Flow_CX55/lib_data"
set pdk_cells_lib /nfs/share/home/qiming/yosys-flow
set pdk_sram_lib ${pdk_dir}/mem
set pdk_io_lib ${pdk_dir}/ccslib
set tech_cells [list "$pdk_cells_lib/ETSCA_N55_H7BL_DSS_PRCMAX_V1P0_T125.lib"]
set tech_macros [list "$pdk_sram_lib/S55NLLG1PH_X256Y4D32_BW_ss_1.08_125.lib"]
lappend tech_macros "$pdk_io_lib/ETIOA_N55_3P3_ss1p08v2p97v125c.lib"
set pdk_dir "$root_dir/pdk/icsprout55-pdk"
set pdk_cells_lib "$root_dir/.cache/retrosoc/pdk/ics55"
set pdk_io_lib "${pdk_dir}/IP/IO/ICsprout_55LLULP1233_IO_251013/liberty"
set tech_cells [list "$pdk_cells_lib/ics55_h7cr_tt.lib"]
set tech_macros [list "$pdk_io_lib/ICSIOA_N55_3P3_tt_1p2_3p3_25c.lib"]
# for hilomap
set tech_cell_tiehi {TIEHIH7H Y}
set tech_cell_tielo {TIELOH7H Y}
set tech_cell_tiehi {TIEHIH7R Z}
set tech_cell_tielo {TIELOH7R Z}
} elseif {$pdk == "GF180"} {
set pdk_cells_lib "$root_dir/.cache/retrosoc/pdk/gf180"
set tech_cells [list "$pdk_cells_lib/gf180mcu_fd_sc_mcu7t5v0__tt_025C_5v00.lib"]
+1 -4
View File
@@ -33,12 +33,9 @@ set abc_comb_script [processAbcScript $abc_combinational_script]
# read liberty files and prepare some variables
source $script_dir/init_tech.tcl
yosys plugin -i slang
# # read design
yosys read_slang --top $top_design -F $sv_flist \
--compat-mode --keep-hierarchy \
--allow-use-before-declare --ignore-timing --ignore-unknown-modules
--keep-hierarchy --allow-use-before-declare --ignore-initial --ignore-timing
# # blackbox requested modules
# if { [info exists ::env(YOSYS_BLACKBOX_MODULES)] } {
+72
View File
@@ -73,6 +73,39 @@ module tc_pdk_cells_tb;
);
`endif
`ifdef PDK_ICS55
logic ics55_od_i;
logic ics55_od_oe;
logic ics55_od_mode;
logic ics55_pu;
logic ics55_pd;
tri ics55_pad;
logic [2:0] ics55_mux_inputs;
logic ics55_mux_y;
P65_1233_PBMUX u_ics55_open_drain_pad (
.C (),
.A (),
.PAD(ics55_pad),
.IE (1'b0),
.CS (1'b1),
.I (ics55_od_i),
.OE (ics55_od_oe),
.OD (ics55_od_mode),
.PU (ics55_pu),
.PD (ics55_pd),
.DS0(1'b0),
.DS1(1'b1)
);
MUXI2X0P5H7R u_ics55_muxi2 (
.A (ics55_mux_inputs[0]),
.B (ics55_mux_inputs[1]),
.S0(ics55_mux_inputs[2]),
.Y (ics55_mux_y)
);
`endif
assign in_pad = in_drive_en ? in_drive : 1'bz;
assign tri_pad = tri_drive_en ? tri_drive : 1'bz;
assign schmitt_pad = schmitt_drive_en ? schmitt_drive : 1'bz;
@@ -168,6 +201,14 @@ module tc_pdk_cells_tb;
gf180_seq_d = 1'b0;
gf180_seq_reset_n = 1'b0;
`endif
`ifdef PDK_ICS55
ics55_od_i = 1'b1;
ics55_od_oe = 1'b0;
ics55_od_mode = 1'b1;
ics55_pu = 1'b0;
ics55_pd = 1'b0;
ics55_mux_inputs = 3'b000;
`endif
#5;
in_drive_en = 1'b1;
@@ -191,7 +232,11 @@ module tc_pdk_cells_tb;
tri_c2p_en = 1'b1;
#5;
expect_bit(tri_pad, 1'b1, "tri-state pad output");
`ifdef PDK_ICS55
expect_bit(tri_p2c, 1'b0, "ICS55 tri-state output disables input receiver");
`else
expect_bit(tri_p2c, 1'b1, "tri-state output readback");
`endif
tri_c2p_en = 1'b0;
#5;
if (tri_pad !== 1'bz) begin
@@ -244,6 +289,33 @@ module tc_pdk_cells_tb;
expect_bit(gf180_dffrnq_q, 1'b1, "GF180 reset-low DFF clock capture");
expect_bit(gf180_dffsnq_q, 1'b1, "GF180 reset-high DFF clock capture");
`endif
`ifdef PDK_ICS55
ics55_od_oe = 1'b1;
#5;
if (ics55_pad !== 1'bz) begin
$fatal(1, "ICS55 open-drain pad did not release high data");
end
ics55_od_i = 1'b0;
#5;
expect_bit(ics55_pad, 1'b0, "ICS55 open-drain low drive");
ics55_od_oe = 1'b0;
ics55_pu = 1'b1;
#5;
expect_bit(ics55_pad, 1'b1, "ICS55 pull-up");
ics55_pu = 1'b0;
ics55_pd = 1'b1;
#5;
expect_bit(ics55_pad, 1'b0, "ICS55 pull-down");
for (int mux_vector = 0; mux_vector < 8; mux_vector++) begin
ics55_mux_inputs = mux_vector[2:0];
#1;
expect_bit(
ics55_mux_y,
~(ics55_mux_inputs[2] ? ics55_mux_inputs[1] : ics55_mux_inputs[0]),
$sformatf("ICS55 MUXI2 vector %0d", mux_vector)
);
end
`endif
$display("technology IO and clock cell test passed");
$finish;
+1 -1
View File
@@ -664,7 +664,7 @@ def test_smoke_regression_uses_ihp130_behavioral_coverage_only() -> None:
def test_pdk_pr_regressions_cover_firmware_rtl_and_netlist() -> None:
assert set(PDK_PR_PROFILES) == {"GF180", "IHP130", "SKY130"}
assert set(PDK_PR_PROFILES) == {"GF180", "IHP130", "ICS55", "SKY130"}
for profile in PDK_PR_PROFILES.values():
commands = pdk_pr_commands(profile)
command_values = [values for _, values in commands]
+104
View File
@@ -2,9 +2,12 @@
from __future__ import annotations
import hashlib
import io
import json
import subprocess
import sys
import tarfile
from pathlib import Path
@@ -13,6 +16,8 @@ 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"
ICS55_PREPARER = ROOT / "pdk/prepare_ics55_liberty.py"
ICS55_SIM_MODEL_PREPARER = ROOT / "pdk/prepare_ics55_sim_model.py"
def generate(domains: Path, output: Path) -> subprocess.CompletedProcess[str]:
@@ -113,3 +118,102 @@ def test_gf180_liberty_generator_assembles_requested_io_cells(tmp_path: Path) ->
assert (output.with_suffix(".lib.revision")).read_text(encoding="utf-8") == (
"locked-revision\ngf180mcu_fd_io\nbi_t,in_c\n"
)
def test_ics55_liberty_preparer_selects_stable_tt_and_ss_views(tmp_path: Path) -> None:
archive = tmp_path / "ics55_LLSC_H7CR_liberty.tar.bz2"
members = {
"liberty/ics55_LLSC_H7CR_tt_1p2_25c.lib": "library (ics55_tt) {}\n",
"liberty/ics55_LLSC_H7CR_ss_1p08_125c.lib": "library (ics55_ss) {}\n",
}
with tarfile.open(archive, "w:bz2") as bundle:
for name, content in members.items():
payload = content.encode("utf-8")
member = tarfile.TarInfo(name)
member.size = len(payload)
bundle.addfile(member, io.BytesIO(payload))
output = tmp_path / "output"
command = [
sys.executable,
str(ICS55_PREPARER),
"--archive",
str(archive),
"--output-dir",
str(output),
"--revision",
"locked-revision",
]
first = subprocess.run(command, text=True, capture_output=True)
assert first.returncode == 0, first.stderr
assert (output / "ics55_h7cr_tt.lib").read_text(encoding="utf-8") == members[
"liberty/ics55_LLSC_H7CR_tt_1p2_25c.lib"
]
assert (output / "ics55_h7cr_ss.lib").read_text(encoding="utf-8") == members[
"liberty/ics55_LLSC_H7CR_ss_1p08_125c.lib"
]
assert (output / ".complete").read_text(encoding="utf-8") == (
"locked-revision\n" + hashlib.sha256(archive.read_bytes()).hexdigest() + "\n"
)
first_mtime = (output / "ics55_h7cr_tt.lib").stat().st_mtime_ns
second = subprocess.run(command, text=True, capture_output=True)
assert second.returncode == 0, second.stderr
assert (output / "ics55_h7cr_tt.lib").stat().st_mtime_ns == first_mtime
def test_ics55_sim_model_preparer_patches_every_h7cr_muxi2_variant(tmp_path: Path) -> None:
cells = (
"MUXI2X0P5H7R",
"MUXI2X0P7H7R",
"MUXI2X1H7R",
"MUXI2X1P4H7R",
"MUXI2X2H7R",
"MUXI2X3H7R",
"MUXI2X4H7R",
)
source = tmp_path / "ics55_LLSC_H7CR.v"
source.write_text(
"".join(
f"module {cell} (Y, A, B, S0);\n"
"output Y;\n"
"input A, B, S0;\n\n"
" udp_mux2 u0(Y, A, B, S0);\n"
" not u1(Y, Y);\n"
f"endmodule //{cell}\n"
for cell in cells
),
encoding="utf-8",
)
output = tmp_path / "ics55_h7cr_functional.v"
command = [
sys.executable,
str(ICS55_SIM_MODEL_PREPARER),
"--source",
str(source),
"--output",
str(output),
"--revision",
"locked-revision",
]
first = subprocess.run(command, text=True, capture_output=True)
assert first.returncode == 0, first.stderr
prepared = output.read_text(encoding="utf-8")
assert prepared.count("wire muxi2_y;") == len(cells)
assert prepared.count("udp_mux2 u0(muxi2_y, A, B, S0);") == len(cells)
assert "not u1(Y, Y);" not in prepared
assert (output.with_suffix(".v.revision")).read_text(encoding="utf-8") == (
"locked-revision\n"
+ hashlib.sha256(source.read_bytes()).hexdigest()
+ "\n"
+ ",".join(cells)
+ "\n"
)
first_mtime = output.stat().st_mtime_ns
second = subprocess.run(command, text=True, capture_output=True)
assert second.returncode == 0, second.stderr
assert output.stat().st_mtime_ns == first_mtime