Merge branch 'feat/mini-hp-core' into dev

This commit is contained in:
maksyuki committed 2026-10-23 08:19:21 +08:00
commit 5c0a3aa280
163 files changed
+13498 -2457

No files matched your search

+21 -2
View File
@@ -135,9 +135,28 @@ SRAM CoreMark quick report into `meta/coremark.json`; it requires one valid
`COREMARK_RESULT` record and `COREMARK_PASS`. Both reports complement, rather
than replace, the common `SIM_TEST_PASS` simulation verdict.
`setup_openc906.py` installs or updates (`--update`) the locked OpenC906
source used by Mini product profiles and verifies the checked-out revision;
the vendored checkout is never modified. `generate_openc906.py` resolves the
upstream `C906_RTL_FACTORY/gen_rtl` filelists from that checkout, substitutes
the reviewed overrides from `rtl/mini/ip_overrides/` (the hart-1
`aq_sysio_kid.v`, the strong-order MMIO-window `sysmap.h`, the eight
`aq_spsram_*.v` SRAM wrappers whose PDK-selectable macro path — stacked
IHP130 `RM_IHPSG13_1P_*`, exact-geometry ICS55 `ics55_ecos_sram_*`, or tiled
GF180 `gf180mcu_fd_ip_sram__sram*x8m8wm1` — is
gated on `PDK_IHP130`/`PDK_ICS55`/`PDK_GF180`+`HAVE_SRAM_MACRO` without
`PDK_BEHAV`, and
the `gated_clk_cell.v`/`tdt_gated_clk_cell.v`/`BUFGCE.v` clock-gate overrides
that select the process ICG cell under a `PDK_*` define without `PDK_BEHAV`),
each pinned to
the upstream file's SHA-256 so a locked-revision move forces review), and
emits the single compilation unit `openc906_combined.v` below `build/`;
generated RTL is never tracked.
`generate_vexiiriscv.py` verifies the locked VexiiRiscv revision, rejects local
source changes before and after generation, and generates the fixed HP core
below `build/`; generated RTL is never tracked.
source changes before and after generation, and generates the fixed Std-series
core (preserved from the retired Mini HP configuration) below `build/`; generated
RTL is never tracked.
`setup_hp_linux.py` installs the locked OpenSBI, Linux, and Buildroot revisions
and can resume a checkout left without `HEAD` by an interrupted fetch.
`build_hp_linux.py` builds the RV64 `lp64d` userspace/OpenSBI image set with the repo-owned
+1 -1
View File
@@ -25,7 +25,7 @@ def build(args: argparse.Namespace) -> None:
subprocess.run(
[
compiler,
"-march=rv64imafdc_zicbom_zicsr_zifencei",
"-march=rv64imafdc_zicsr_zifencei",
"-mabi=lp64d",
"-nostdlib",
"-nostartfiles",
+1 -1
View File
@@ -250,7 +250,7 @@ def build(args: argparse.Namespace) -> None:
f"PLATFORM_DIR={external / 'opensbi'}",
"PLATFORM_RISCV_XLEN=64",
"PLATFORM_RISCV_ABI=lp64d",
"PLATFORM_RISCV_ISA=rv64imafdc_zicbom_zicsr_zifencei",
"PLATFORM_RISCV_ISA=rv64imafdc_zicsr_zifencei",
"FW_TEXT_START=0x38000000",
"FW_JUMP=y",
"FW_JUMP_ADDR=0x38400000",
+1 -1
View File
@@ -22,7 +22,7 @@ def build(args: argparse.Namespace) -> None:
objcopy = f"{args.cross}objcopy"
command = [
compiler,
"-march=rv64imafdc_zicbom_zicsr_zifencei",
"-march=rv64imafdc_zicsr_zifencei",
"-mabi=lp64d",
"-nostdlib",
"-nostartfiles",
+7 -1
View File
@@ -21,6 +21,10 @@ SELF_OWNED_RTL_ROOTS = {
("rtl", "tech"),
("tests", "rtl"),
}
# Vendored upstream sources carried with a reviewed patch (e.g. the OpenC906
# hart-ID override) keep upstream formatting so they stay diffable against
# the locked revision.
VENDORED_RTL_DIRS = ("ip_overrides",)
def tracked_files(root: Path) -> list[Path]:
@@ -41,7 +45,9 @@ def format_files(paths: Iterable[Path], kind: str) -> list[Path]:
return sorted(
path
for path in paths
if path.suffix in RTL_SUFFIXES and tuple(path.parts[:2]) in SELF_OWNED_RTL_ROOTS
if path.suffix in RTL_SUFFIXES
and tuple(path.parts[:2]) in SELF_OWNED_RTL_ROOTS
and not any(part in VENDORED_RTL_DIRS for part in path.parts)
)
raise ValueError(f"unknown format kind: {kind}")
+245
View File
@@ -0,0 +1,245 @@
#!/usr/bin/env python3
"""Emit the Mini HP OpenC906 filelist from the locked vendored source.
The OpenC906 repository ships pre-generated Verilog below
``C906_RTL_FACTORY/gen_rtl`` together with upstream filelists that use a
``${CODE_BASE_PATH}`` placeholder and mix compilation-unit macro headers with
sources. This script resolves the placeholder to the locked checkout and
substitutes the reviewed retroSoC overrides from ``rtl/mini/ip_overrides/``
for their vendored counterparts:
- ``aq_sysio_kid.v``: upstream hardwires mhartid to 0; the Mini LP/HP
contract requires the HP core to report hart 1.
- ``aq_biu_write_channel.v``: the vendored BIU streams a write's W data as
soon as the LSU offers it, even while the AW still waits in ``awbuf`` for a
busy downstream slave (AXI-legal W-before-AW). The Mini HP fabric behind the
pad is single-outstanding (serializing 128->64 downsizer + MMIO demux +
target guard) and cannot attribute early W beats without a WID, so the
beats land in the prior write's burst, the queued AW is orphaned, and the
channel wedges (observed as a permanent guard WriteStream stall with the
SDRAM controller parked waiting for write data). The override gates the W
stream on the burst's AW having been issued on the pad.
- ``sysmap.h``: the upstream default region table marks the Mini peripheral
window cacheable/bufferable; the override maps the Mini address map with
the MMIO window (including the core-internal CLINT/PLIC window) as
strong-order non-cacheable, as the OpenC906 user manual requires.
- ``aq_spsram_*.v``: the eight single-port SRAM wrappers (OpenC906
integration manual chapter 3 memory swap). Each override keeps the upstream
behavioral ``aq_f_spsram_*`` instance by default and substitutes IHP130
``RM_IHPSG13_1P_*`` macro combinations (depth/width stacking per manual
section 3.1.5) or exact-geometry ICS55 ``ics55_ecos_sram_*`` macros only
when the build defines ``PDK_IHP130``/``PDK_ICS55`` and
``HAVE_SRAM_MACRO`` without ``PDK_BEHAV``.
- ``gated_clk_cell.v`` / ``tdt_gated_clk_cell.v`` / ``BUFGCE.v``: the
clock-gating cells (manual chapter 3 ICG swap). Each override keeps the
upstream default (pass-through for the two ``*_gated_clk_cell`` wrappers,
behavioral latch gate for ``BUFGCE``) unless a PDK define is set without
``PDK_BEHAV``, in which case the process ICG cell is instantiated
(``sg13g2_slgcp_1`` / ``ICGX1H7R`` / ``sky130_fd_sc_hd__sdlclkp_1`` /
``gf180mcu_fd_sc_mcu7t5v0__icgtp_1``).
All sources are concatenated into a single compilation unit
(``openc906_combined.v``) because gen_rtl relies on header macros being
visible in every source; one file preserves that semantics for tools with
per-file compilation units (slang/Yosys) and is equivalent for
Verilator/Icarus/VCS. The vendored checkout itself is never modified.
"""
from __future__ import annotations
import argparse
import hashlib
import json
import subprocess
from pathlib import Path
from typing import Any
GENERATED_MODULE = "openC906"
UPSTREAM_FILELISTS = (
"C906_RTL_FACTORY/gen_rtl/filelists/C906_asic_rtl.fl",
"C906_RTL_FACTORY/gen_rtl/filelists/tdt_dmi_top_rtl.fl",
)
# vendored relpath -> (override relpath below rtl/mini/ip_overrides/, sha256 of
# the vendored file at the locked revision). The sha256 pins force a review of
# the override whenever the locked OpenC906 revision moves.
OVERRIDE_DIR = Path("rtl/mini/ip_overrides")
OVERRIDES = {
"C906_RTL_FACTORY/gen_rtl/cpu/rtl/aq_sysio_kid.v": (
"aq_sysio_kid.v",
"afb4d8da4c9fb01edf9286c69111c9799ccf0cc66c339dadad1705efdd35dd8c",
),
"C906_RTL_FACTORY/gen_rtl/mmu/rtl/sysmap.h": (
"sysmap.h",
"0389089a42ef762768e57bbb4fdcbe6b95f33ab650b5bacc0de8122e0d8711d5",
),
"C906_RTL_FACTORY/gen_rtl/biu/rtl/aq_biu_write_channel.v": (
"aq_biu_write_channel.v",
"7d98d231287f4a8807593990411d96879deb68cfda764c2fb92bc93c2b10e204",
),
"C906_RTL_FACTORY/gen_rtl/ifu/rtl/aq_spsram_1024x16.v": (
"aq_spsram_1024x16.v",
"8969cdfd73933ff4ca105d562e31283e16433f90e4ceaa4339759e6df7ba34d9",
),
"C906_RTL_FACTORY/gen_rtl/ifu/rtl/aq_spsram_2048x32.v": (
"aq_spsram_2048x32.v",
"3f4aa927be7d16a0bcd0d3a0f973d93e5cd1b0912f310981bf24656711964abd",
),
"C906_RTL_FACTORY/gen_rtl/ifu/rtl/aq_spsram_256x59.v": (
"aq_spsram_256x59.v",
"6760517965692f1c94c31be295696d7c016f138d697c3495184a6bb14ce482f3",
),
"C906_RTL_FACTORY/gen_rtl/lsu/rtl/aq_spsram_1024x64.v": (
"aq_spsram_1024x64.v",
"695c054d66f673cc863e2eaea0ec53845c45f83797acb7abe9d08e59dfc9d0bb",
),
"C906_RTL_FACTORY/gen_rtl/lsu/rtl/aq_spsram_128x8.v": (
"aq_spsram_128x8.v",
"7a8e963c4539f5ecfb29b147e5bc1b1e66fffd3cb5ee72923db7199092a15f38",
),
"C906_RTL_FACTORY/gen_rtl/lsu/rtl/aq_spsram_64x58.v": (
"aq_spsram_64x58.v",
"f4b5457d06d2e97ae158432cb7c265242c05c9f96ffeb8c68083d8cee941bd96",
),
"C906_RTL_FACTORY/gen_rtl/mmu/rtl/aq_spsram_64x88.v": (
"aq_spsram_64x88.v",
"0a38b7ffc18c2b837e7a253d7ba7b40fc88a172f1b94ed552470614927d7fee8",
),
"C906_RTL_FACTORY/gen_rtl/mmu/rtl/aq_spsram_64x98.v": (
"aq_spsram_64x98.v",
"99680fb11271eee6cfe6ff7aaa95cbdd04ef4f78d7b0e23a18c17ef064a8bedf",
),
"C906_RTL_FACTORY/gen_rtl/clk/rtl/gated_clk_cell.v": (
"gated_clk_cell.v",
"22a4a5f41c7733bd4935adc8a036870a110a8e2f2a70f1f3cf64476b7a49eb9e",
),
"C906_RTL_FACTORY/gen_rtl/tdt/rtl/common/tdt_gated_clk_cell.v": (
"tdt_gated_clk_cell.v",
"9c38f1d71a3aaa64e00d2aa7d5a43f5836080c604f245dd414a85935241d26bd",
),
"C906_RTL_FACTORY/gen_rtl/common/rtl/BUFGCE.v": (
"BUFGCE.v",
"b0907982b81c64494756d7f62a3a35bf961f6cbf7d54241460a476dde36a333a",
),
}
def run(command: list[str], cwd: Path) -> str:
result = subprocess.run(command, cwd=cwd, check=True, capture_output=True, text=True)
return result.stdout.strip()
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as source:
for chunk in iter(lambda: source.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def locked_revision(lock_path: Path) -> str:
document: dict[str, Any] = json.loads(lock_path.read_text(encoding="utf-8"))
source = document.get("sources", {}).get("openc906")
if not isinstance(source, dict) or not isinstance(source.get("revision"), str):
raise ValueError("dependency lock has no openc906 source revision")
return source["revision"]
def validate_locked_source(source: Path, expected: str) -> str:
actual = run(["git", "rev-parse", "HEAD"], source)
if actual != expected:
raise ValueError(f"OpenC906 revision mismatch: expected {expected}, found {actual}")
status = run(["git", "status", "--short"], source)
if status:
raise ValueError("OpenC906 source has local changes:\n" + status)
return actual
def collect_sources(root: Path, source: Path) -> tuple[list[Path], dict[str, str]]:
"""Resolve upstream filelists to absolute source paths with overrides."""
files: list[Path] = []
used: dict[str, str] = {}
for filelist in UPSTREAM_FILELISTS:
for line in (source / filelist).read_text(encoding="utf-8").splitlines():
entry = line.strip()
if not entry or entry.startswith("#"):
continue
resolved = Path(entry.replace("${CODE_BASE_PATH}", str(source / "C906_RTL_FACTORY")))
if not resolved.is_file():
raise FileNotFoundError(f"upstream filelist entry not found: {resolved}")
if resolved.suffix != ".h" and resolved.suffix != ".v":
raise ValueError(f"unexpected upstream filelist entry kind: {resolved}")
relative = resolved.relative_to(source).as_posix()
if relative in OVERRIDES:
override_name, expected_sha256 = OVERRIDES[relative]
if sha256(resolved) != expected_sha256:
raise ValueError(
f"vendored {Path(relative).name} digest changed; re-review the "
"override before updating OVERRIDES"
)
resolved = (root / OVERRIDE_DIR / override_name).resolve()
if not resolved.is_file():
raise FileNotFoundError(f"override not found: {resolved}")
used[relative] = override_name
files.append(resolved)
missing = set(OVERRIDES) - set(used)
if missing:
raise ValueError(f"override target(s) not present in upstream filelists: {missing}")
return files, used
def generate(args: argparse.Namespace) -> None:
root = args.root.resolve()
source = args.source.resolve()
output = args.output.resolve()
expected = locked_revision(args.lock.resolve())
actual = validate_locked_source(source, expected)
files, used = collect_sources(root, source)
output.mkdir(parents=True, exist_ok=True)
combined = output / "openc906_combined.v"
chunks = [
"// retroSoC build-time concatenation of the locked OpenC906 RTL.\n",
"// Do not edit; regenerate with `make openc906-prepare`.\n",
]
for path in files:
chunks.append(f"\n// ---- begin: {path.name} ----\n")
chunks.append(path.read_text(encoding="utf-8"))
chunks.append("\n")
combined.write_text("".join(chunks), encoding="utf-8")
(output / "openc906.fl").write_text(str(combined) + "\n", encoding="utf-8")
manifest = {
"schema_version": 1,
"module": GENERATED_MODULE,
"configuration": "openc906_default_rv64gc",
"openc906_revision": actual,
"source_status": [],
"overrides": {
target: {
"override": str(OVERRIDE_DIR / override_name),
"expected_upstream_sha256": OVERRIDES[target][1],
}
for target, override_name in sorted(used.items())
},
"file_count": len(files),
}
args.manifest.resolve().parent.mkdir(parents=True, exist_ok=True)
args.manifest.resolve().write_text(
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=Path, required=True)
parser.add_argument("--source", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--manifest", type=Path, required=True)
parser.add_argument("--lock", type=Path, required=True)
generate(parser.parse_args())
if __name__ == "__main__":
main()
+10 -5
View File
@@ -1,5 +1,9 @@
#!/usr/bin/env python3
"""Generate the locked VexiiRiscv HP core below the selected build variant."""
"""Generate the locked VexiiRiscv Std-series core below the selected build variant.
The generator is a Std-series asset preserved from the retired Mini HP
configuration; Mini no longer consumes it.
"""
from __future__ import annotations
@@ -12,8 +16,8 @@ from pathlib import Path
from typing import Any
GENERATOR_CLASS = "vexiiriscv.GenerateRetroSocHp"
GENERATED_MODULE = "vexii_riscv_hp_generated"
GENERATOR_CLASS = "vexiiriscv.GenerateRetroSocStd"
GENERATED_MODULE = "vexiiriscv_std_generated"
def run(command: list[str], cwd: Path) -> str:
@@ -86,7 +90,7 @@ def generate(args: argparse.Namespace) -> None:
environment["COURSIER_CACHE"] = str(sbt_cache / "coursier")
subprocess.run(command, cwd=source, check=True, env=environment)
generated = output / "vexii_riscv_hp_generated.v"
generated = output / "vexiiriscv_std_generated.v"
if not generated.is_file():
raise FileNotFoundError(f"VexiiRiscv generator did not create {generated}")
validate_locked_source(source, expected)
@@ -95,6 +99,7 @@ def generate(args: argparse.Namespace) -> None:
manifest = {
"schema_version": 1,
"module": GENERATED_MODULE,
"product": "std",
"configuration": "rv64imafdc_zicbom_max",
"xlen": 64,
"physical_address_width": 32,
@@ -102,7 +107,7 @@ def generate(args: argparse.Namespace) -> None:
"vexiiriscv_revision": actual,
"source_status": [],
"submodules": submodules,
"generator": str((scala_dir / "GenerateRetroSocHp.scala").relative_to(root)),
"generator": str((scala_dir / "GenerateRetroSocStd.scala").relative_to(root)),
"files": {generated.name: sha256(generated)},
}
args.manifest.resolve().parent.mkdir(parents=True, exist_ok=True)
+16
View File
@@ -287,6 +287,14 @@ static void rs_{prefix}_cache_clean(const void *pointer, uint32_t bytes) {{
uintptr_t cursor = (uintptr_t)pointer & ~(uintptr_t)UINT32_C(63);
uintptr_t end = ((uintptr_t)pointer + bytes + UINT32_C(63)) & ~(uintptr_t)UINT32_C(63);
while (cursor < end) {{ __asm__ volatile("cbo.clean 0(%0)" :: "r"(cursor) : "memory"); cursor += 64U; }}
#elif defined(__riscv)
/* OpenC906 (T-Head C906): custom-0 dcache.cva, 0x0250000b | rs1<<15 */
uintptr_t cursor = (uintptr_t)pointer & ~(uintptr_t)UINT32_C(63);
uintptr_t end = ((uintptr_t)pointer + bytes + UINT32_C(63)) & ~(uintptr_t)UINT32_C(63);
while (cursor < end) {{
__asm__ volatile(".insn r 0x0b, 0x0, 0x01, x0, %0, x5" :: "r"(cursor) : "memory");
cursor += 64U;
}}
#else
(void)pointer; (void)bytes;
#endif
@@ -297,6 +305,14 @@ static void rs_{prefix}_cache_invalidate(const void *pointer, uint32_t bytes) {{
uintptr_t cursor = (uintptr_t)pointer & ~(uintptr_t)UINT32_C(63);
uintptr_t end = ((uintptr_t)pointer + bytes + UINT32_C(63)) & ~(uintptr_t)UINT32_C(63);
while (cursor < end) {{ __asm__ volatile("cbo.inval 0(%0)" :: "r"(cursor) : "memory"); cursor += 64U; }}
#elif defined(__riscv)
/* OpenC906 (T-Head C906): custom-0 dcache.iva, 0x0260000b | rs1<<15 */
uintptr_t cursor = (uintptr_t)pointer & ~(uintptr_t)UINT32_C(63);
uintptr_t end = ((uintptr_t)pointer + bytes + UINT32_C(63)) & ~(uintptr_t)UINT32_C(63);
while (cursor < end) {{
__asm__ volatile(".insn r 0x0b, 0x0, 0x01, x0, %0, x6" :: "r"(cursor) : "memory");
cursor += 64U;
}}
#else
(void)pointer; (void)bytes;
#endif
+1 -1
View File
@@ -450,7 +450,7 @@ def main() -> int:
"softmax_cycles": int(marker.group(4)),
**lp_evidence},
"hp": {"result": str(args.hp_result), "check": str(args.hp_check),
"command": hp["command"], "mode": "polling-zicbom",
"command": hp["command"], "mode": "polling-xtheadcmo",
**hp_evidence},
"summary": {
"required_checks": 47,
+1 -2
View File
@@ -111,7 +111,7 @@ def build_variants(timestamp: str, log_dir: Path) -> dict[str, Path]:
def qualification_configuration(variants: dict[str, Path]) -> dict[str, object]:
selected = ("MINI_MODE", "APP", "SIMU", "HP_CONFIG", "EXT_CLK_HZ")
selected = ("MINI_MODE", "APP", "SIMU", "EXT_CLK_HZ")
configurations: dict[str, dict[str, str]] = {}
for workload, variant in variants.items():
manifest = json.loads((variant / "meta/manifest.json").read_text(encoding="utf-8"))
@@ -128,7 +128,6 @@ def qualification_configuration(variants: dict[str, Path]) -> dict[str, object]:
"MINI_MODE": "PRODUCT",
"APP": "hp_boot",
"SIMU": "VERILATOR",
"HP_CONFIG": "rv64imafdc_zicbom_max",
"EXT_CLK_HZ": "72000000",
}
if configuration != expected:
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""Install the locked OpenC906 source used by Mini product profiles."""
from __future__ import annotations
import argparse
from pathlib import Path
from dependency_lock import source
from setup_helpers import ensure_git_repo
ROOT = Path(__file__).resolve().parents[1]
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--update", action="store_true")
args = parser.parse_args()
dependency = source("openc906")
destination = ROOT / dependency["destination"]
ensure_git_repo(
dependency["url"],
destination,
dependency["revision"],
recursive=dependency.get("recursive", False),
update=args.update,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -8,9 +8,11 @@ import vexiiriscv.misc.EmbeddedRiscvJtag
import java.nio.file.{Files, Paths}
/** Generates the fixed retroSoC RV64 application core. */
object GenerateRetroSocHp extends App {
require(args.length == 1, "usage: GenerateRetroSocHp <output-directory>")
/** Generates the fixed retroSoC Std-series RV64 application core configuration
* (preserved from the retired Mini HP configuration).
*/
object GenerateRetroSocStd extends App {
require(args.length == 1, "usage: GenerateRetroSocStd <output-directory>")
val output = Paths.get(args(0)).toAbsolutePath.normalize
Files.createDirectories(output)
@@ -120,6 +122,6 @@ object GenerateRetroSocHp extends App {
)
)
)
VexiiRiscv(plugins).setDefinitionName("vexii_riscv_hp_generated")
VexiiRiscv(plugins).setDefinitionName("vexiiriscv_std_generated")
}
}