mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: add HP mailbox and PLIC tests; enhance existing test coverage
- Updated axi4_interconnect_tb to include an additional master and issue high-priority access tests. - Introduced hp_mailbox_tb for testing the HP mailbox functionality, including register and doorbell operations. - Added plic_tb to validate PLIC priority, claim, and completion mechanisms. - Enhanced pll_ctrl_tb to initialize additional system control signals. - Updated soc_irq_topology_tb to account for new IRQ sources and validate IRQ signal behavior. - Improved sysctrl_tb to include tests for HP lifecycle control and status registers. - Added tests for HP boot bundle layout, CRC, and payload validation. - Introduced test_hp_mailbox.py for RTL regression testing of the HP mailbox. - Added test_hp_platform.py to validate HP platform configuration and generated-source boundaries. - Enhanced test_librelane_flow.py to ensure correct signal placement in chip configuration. - Added performance log tests to validate LP and HP coremark ratios. - Updated test_soc_topology.py to reflect changes in IRQ and peripheral mappings.
This commit is contained in:
101 files changed
+4880
-111
No files matched your search
@@ -25,6 +25,15 @@ 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.
|
||||
|
||||
`generate_vexiiriscv.py` verifies the locked VexiiRiscv revision and generates
|
||||
the fixed HP core 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 RV32 `ilp32d` image set with the repo-owned
|
||||
external OpenSBI platform. `package_hp_boot.py` creates the v1 LP/HP flash
|
||||
bundle and its SHA-256/CRC manifest. `check_lp_hp_performance.py` applies the
|
||||
measured 2.5x CoreMark/MHz gate.
|
||||
|
||||
`run_debug_session.py` is the local Hazard3 debug acceptance driver. It starts
|
||||
the Verilator remote-bitbang endpoint, the lock-pinned OpenOCD binary, and
|
||||
RISC-V GDB, then records their logs and a structured result. Invoke it through
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the pinned RV32 HP Buildroot, Linux, DTB, and OpenSBI images."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
LAYOUT = {
|
||||
"fw_jump.bin": (0x38000000, 512 * 1024),
|
||||
"retrosoc_hp.dtb": (0x38080000, 64 * 1024),
|
||||
"Image": (0x38400000, 12 * 1024 * 1024),
|
||||
"rootfs.cpio.gz": (0x39000000, 8 * 1024 * 1024),
|
||||
}
|
||||
|
||||
|
||||
def command(arguments: list[str], cwd: Path) -> None:
|
||||
environment = dict(os.environ)
|
||||
environment.pop("CONFIG", None)
|
||||
environment.pop("MAKEFLAGS", None)
|
||||
environment.pop("MAKEOVERRIDES", None)
|
||||
environment.pop("MFLAGS", None)
|
||||
subprocess.run(arguments, cwd=cwd, check=True, env=environment)
|
||||
|
||||
|
||||
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 require_source(path: Path, name: str) -> Path:
|
||||
resolved = path.resolve()
|
||||
if not (resolved / ".git").exists():
|
||||
raise FileNotFoundError(f"missing locked {name} source: {resolved}; run setup-hp-linux")
|
||||
return resolved
|
||||
|
||||
|
||||
def source_revision(path: Path) -> str:
|
||||
return subprocess.check_output(
|
||||
["git", "-C", str(path), "rev-parse", "HEAD"], text=True
|
||||
).strip()
|
||||
|
||||
|
||||
def find_toolchain(buildroot_output: Path) -> str:
|
||||
matches = sorted((buildroot_output / "host/bin").glob("riscv32*-linux-*-gcc"))
|
||||
if len(matches) != 1:
|
||||
raise RuntimeError(f"expected one RV32 Linux GCC in {buildroot_output / 'host/bin'}")
|
||||
return str(matches[0])[: -len("gcc")]
|
||||
|
||||
|
||||
def build(args: argparse.Namespace) -> None:
|
||||
root = args.root.resolve()
|
||||
output = args.output.resolve()
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
buildroot = require_source(args.buildroot, "Buildroot")
|
||||
linux = require_source(args.linux, "Linux")
|
||||
opensbi = require_source(args.opensbi, "OpenSBI")
|
||||
external = root / "app/ports/linux"
|
||||
|
||||
buildroot_output = output / "buildroot"
|
||||
command(
|
||||
["make", f"O={buildroot_output}", f"BR2_EXTERNAL={external}", "retrosoc_hp_defconfig"],
|
||||
buildroot,
|
||||
)
|
||||
command(["make", f"O={buildroot_output}", f"BR2_EXTERNAL={external}", f"-j{args.jobs}"], buildroot)
|
||||
cross_compile = find_toolchain(buildroot_output)
|
||||
|
||||
linux_output = output / "linux"
|
||||
command(
|
||||
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}", "tinyconfig"],
|
||||
linux,
|
||||
)
|
||||
command(
|
||||
[
|
||||
str(linux / "scripts/kconfig/merge_config.sh"),
|
||||
"-m",
|
||||
"-O",
|
||||
str(linux_output),
|
||||
str(linux_output / ".config"),
|
||||
str(external / "linux/retrosoc_hp.config"),
|
||||
],
|
||||
linux,
|
||||
)
|
||||
command(
|
||||
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}", "olddefconfig"],
|
||||
linux,
|
||||
)
|
||||
command(
|
||||
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}",
|
||||
f"-j{args.jobs}", "Image"],
|
||||
linux,
|
||||
)
|
||||
|
||||
image_dir = output / "images"
|
||||
image_dir.mkdir(parents=True, exist_ok=True)
|
||||
rootfs_source = buildroot_output / "images/rootfs.cpio.gz"
|
||||
if not rootfs_source.is_file():
|
||||
raise FileNotFoundError(f"HP Linux build output is missing: {rootfs_source}")
|
||||
initrd_end = LAYOUT["rootfs.cpio.gz"][0] + rootfs_source.stat().st_size
|
||||
command(
|
||||
[
|
||||
"dtc",
|
||||
"-I",
|
||||
"dts",
|
||||
"-O",
|
||||
"dtb",
|
||||
"-o",
|
||||
str(image_dir / "retrosoc_hp.dtb"),
|
||||
str(external / "linux/retrosoc_hp.dts"),
|
||||
],
|
||||
root,
|
||||
)
|
||||
command(
|
||||
[
|
||||
"fdtput",
|
||||
"-t",
|
||||
"x",
|
||||
str(image_dir / "retrosoc_hp.dtb"),
|
||||
"/chosen",
|
||||
"linux,initrd-end",
|
||||
f"0x{initrd_end:08x}",
|
||||
],
|
||||
root,
|
||||
)
|
||||
|
||||
opensbi_output = output / "opensbi"
|
||||
command(
|
||||
[
|
||||
"make",
|
||||
f"O={opensbi_output}",
|
||||
f"CROSS_COMPILE={cross_compile}",
|
||||
"PLATFORM=retrosoc_hp",
|
||||
f"PLATFORM_DIR={external / 'opensbi'}",
|
||||
"PLATFORM_RISCV_XLEN=32",
|
||||
"PLATFORM_RISCV_ABI=ilp32d",
|
||||
"PLATFORM_RISCV_ISA=rv32imafdc_zicsr_zifencei",
|
||||
"FW_TEXT_START=0x38000000",
|
||||
"FW_JUMP=y",
|
||||
"FW_JUMP_ADDR=0x38400000",
|
||||
"FW_JUMP_FDT_ADDR=0x38080000",
|
||||
f"-j{args.jobs}",
|
||||
],
|
||||
opensbi,
|
||||
)
|
||||
|
||||
copies = {
|
||||
opensbi_output / "platform/retrosoc_hp/firmware/fw_jump.bin": image_dir / "fw_jump.bin",
|
||||
linux_output / "arch/riscv/boot/Image": image_dir / "Image",
|
||||
rootfs_source: image_dir / "rootfs.cpio.gz",
|
||||
}
|
||||
for source, destination in copies.items():
|
||||
if not source.is_file():
|
||||
raise FileNotFoundError(f"HP Linux build output is missing: {source}")
|
||||
shutil.copy2(source, destination)
|
||||
|
||||
manifest: dict[str, object] = {
|
||||
"schema_version": 1,
|
||||
"boot_flow": "opensbi-fw_jump",
|
||||
"hart_id": 1,
|
||||
"sources": {
|
||||
"buildroot": source_revision(buildroot),
|
||||
"linux": source_revision(linux),
|
||||
"opensbi": source_revision(opensbi),
|
||||
},
|
||||
"artifacts": {},
|
||||
}
|
||||
artifacts = manifest["artifacts"]
|
||||
assert isinstance(artifacts, dict)
|
||||
for name, (address, maximum) in LAYOUT.items():
|
||||
path = image_dir / name
|
||||
size = path.stat().st_size
|
||||
if size > maximum:
|
||||
raise ValueError(f"{name} is {size} bytes; maximum is {maximum}")
|
||||
artifacts[name] = {
|
||||
"address": f"0x{address:08X}",
|
||||
"maximum_bytes": maximum,
|
||||
"size_bytes": size,
|
||||
"sha256": sha256(path),
|
||||
}
|
||||
(image_dir / "manifest.json").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("--buildroot", type=Path, required=True)
|
||||
parser.add_argument("--linux", type=Path, required=True)
|
||||
parser.add_argument("--opensbi", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--jobs", type=int, default=1)
|
||||
build(parser.parse_args())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the minimal hart-1 smoke payload used by HP RTL simulation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def build(args: argparse.Namespace) -> None:
|
||||
output = args.output.resolve()
|
||||
images = output / "images"
|
||||
images.mkdir(parents=True, exist_ok=True)
|
||||
elf = output / "hp_smoke.elf"
|
||||
compiler = f"{args.cross}gcc"
|
||||
objcopy = f"{args.cross}objcopy"
|
||||
subprocess.run(
|
||||
[
|
||||
compiler,
|
||||
"-march=rv32imafdc_zicsr_zifencei",
|
||||
"-mabi=ilp32d",
|
||||
"-nostdlib",
|
||||
"-nostartfiles",
|
||||
"-Wl,--build-id=none",
|
||||
f"-Wl,-T,{args.linker.resolve()}",
|
||||
"-o",
|
||||
str(elf),
|
||||
str(args.source.resolve()),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
subprocess.run(
|
||||
[objcopy, "-O", "binary", str(elf), str(images / "fw_jump.bin")],
|
||||
check=True,
|
||||
)
|
||||
(images / "retrosoc_hp.dtb").write_bytes(b"SMOK")
|
||||
(images / "Image").write_bytes(b"SMOK")
|
||||
(images / "rootfs.cpio.gz").write_bytes(b"SMOK")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--source", type=Path, required=True)
|
||||
parser.add_argument("--linker", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--cross", default="riscv32-unknown-elf-")
|
||||
build(parser.parse_args())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Enforce the measured HP-to-LP CoreMark/MHz performance ratio."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def result(report: dict[str, object], label: str) -> dict[str, object]:
|
||||
if report.get("status") != "passed":
|
||||
raise ValueError(f"{label} CoreMark report did not pass")
|
||||
results = report.get("results")
|
||||
if not isinstance(results, list) or len(results) != 1 or not isinstance(results[0], dict):
|
||||
raise ValueError(f"{label} CoreMark report must contain exactly one result")
|
||||
return results[0]
|
||||
|
||||
|
||||
def compare(
|
||||
lp_report: dict[str, object], hp_report: dict[str, object], threshold: Decimal
|
||||
) -> dict[str, object]:
|
||||
if threshold <= 0:
|
||||
raise ValueError("minimum performance ratio must be positive")
|
||||
lp = result(lp_report, "LP")
|
||||
hp = result(hp_report, "HP")
|
||||
if lp.get("mode") != hp.get("mode"):
|
||||
raise ValueError("LP and HP CoreMark reports must use the same mode")
|
||||
if lp.get("cpu_hz") != hp.get("cpu_hz"):
|
||||
raise ValueError("LP and HP CoreMark reports must use the same fixed frequency")
|
||||
try:
|
||||
lp_score = Decimal(str(lp["coremark_per_mhz"]))
|
||||
hp_score = Decimal(str(hp["coremark_per_mhz"]))
|
||||
ratio = hp_score / lp_score
|
||||
except (InvalidOperation, KeyError, ZeroDivisionError) as error:
|
||||
raise ValueError("invalid CoreMark/MHz values") from error
|
||||
passed = ratio >= threshold
|
||||
return {
|
||||
"schema_version": 1,
|
||||
"status": "passed" if passed else "failed",
|
||||
"frequency_hz": lp["cpu_hz"],
|
||||
"mode": lp["mode"],
|
||||
"lp_coremark_per_mhz": str(lp_score),
|
||||
"hp_coremark_per_mhz": str(hp_score),
|
||||
"minimum_ratio": str(threshold),
|
||||
"measured_ratio": str(ratio.quantize(Decimal("0.001"))),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--lp", type=Path, required=True)
|
||||
parser.add_argument("--hp", type=Path, required=True)
|
||||
parser.add_argument("--minimum-ratio", type=Decimal, default=Decimal("2.5"))
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
report = compare(
|
||||
json.loads(args.lp.read_text(encoding="utf-8")),
|
||||
json.loads(args.hp.read_text(encoding="utf-8")),
|
||||
args.minimum_ratio,
|
||||
)
|
||||
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||
print(f"performance comparison failed: {error}", file=sys.stderr)
|
||||
return 1
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(
|
||||
json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8"
|
||||
)
|
||||
if report["status"] != "passed":
|
||||
print(
|
||||
f"HP/LP CoreMark/MHz ratio {report['measured_ratio']} is below "
|
||||
f"{report['minimum_ratio']}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the locked VexiiRiscv HP core below the selected build variant."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
GENERATOR_CLASS = "vexiiriscv.GenerateRetroSocHp"
|
||||
GENERATED_MODULE = "vexii_riscv_hp_generated"
|
||||
|
||||
|
||||
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 git_revision(source: Path) -> str:
|
||||
return run(["git", "rev-parse", "HEAD"], source)
|
||||
|
||||
|
||||
def git_status(source: Path) -> list[str]:
|
||||
output = run(["git", "status", "--short"], source)
|
||||
return output.splitlines() if output else []
|
||||
|
||||
|
||||
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("vexiiriscv")
|
||||
if not isinstance(source, dict) or not isinstance(source.get("revision"), str):
|
||||
raise ValueError("dependency lock has no vexiiriscv source revision")
|
||||
return source["revision"]
|
||||
|
||||
|
||||
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 = git_revision(source)
|
||||
if actual != expected:
|
||||
raise ValueError(f"VexiiRiscv revision mismatch: expected {expected}, found {actual}")
|
||||
|
||||
scala_dir = root / "scripts" / "vexiiriscv"
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
command = [
|
||||
str(args.sbt),
|
||||
f'set Compile / unmanagedSourceDirectories += file("{scala_dir}")',
|
||||
f"runMain {GENERATOR_CLASS} {output}",
|
||||
]
|
||||
environment = os.environ.copy()
|
||||
java_home = environment.get("HOME")
|
||||
if java_home:
|
||||
inherited = environment.get("JAVA_TOOL_OPTIONS", "").strip()
|
||||
environment["JAVA_TOOL_OPTIONS"] = (
|
||||
f"{inherited} -Duser.home={java_home}".strip()
|
||||
)
|
||||
subprocess.run(command, cwd=source, check=True, env=environment)
|
||||
|
||||
generated = output / "vexii_riscv_hp_generated.v"
|
||||
if not generated.is_file():
|
||||
raise FileNotFoundError(f"VexiiRiscv generator did not create {generated}")
|
||||
|
||||
submodules = run(["git", "submodule", "status", "--recursive"], source).splitlines()
|
||||
manifest = {
|
||||
"schema_version": 1,
|
||||
"module": GENERATED_MODULE,
|
||||
"configuration": "rv32imafdc_max",
|
||||
"vexiiriscv_revision": actual,
|
||||
"source_status": git_status(source),
|
||||
"submodules": submodules,
|
||||
"generator": str((scala_dir / "GenerateRetroSocHp.scala").relative_to(root)),
|
||||
"files": {generated.name: sha256(generated)},
|
||||
}
|
||||
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"
|
||||
)
|
||||
(output / "vexiiriscv.fl").write_text(str(generated) + "\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)
|
||||
parser.add_argument("--sbt", type=Path, required=True)
|
||||
generate(parser.parse_args())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Package LP firmware and the fixed HP Linux image set into one flash image."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import struct
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
FLASH_SIZE = 16 * 1024 * 1024
|
||||
BUNDLE_OFFSET = 1 * 1024 * 1024
|
||||
PAYLOAD_ALIGNMENT = 4096
|
||||
MAGIC = 0x50485352
|
||||
VERSION = 1
|
||||
REQUIRED = 1
|
||||
HEADER = struct.Struct("<8I")
|
||||
ENTRY = struct.Struct("<6I")
|
||||
ARTIFACTS = (
|
||||
(1, "fw_jump.bin", 0x38000000, 512 * 1024),
|
||||
(2, "retrosoc_hp.dtb", 0x38080000, 64 * 1024),
|
||||
(3, "Image", 0x38400000, 12 * 1024 * 1024),
|
||||
(4, "rootfs.cpio.gz", 0x39000000, 8 * 1024 * 1024),
|
||||
)
|
||||
HEADER_SIZE = HEADER.size + (ENTRY.size * len(ARTIFACTS))
|
||||
|
||||
|
||||
def align_up(value: int, alignment: int) -> int:
|
||||
return (value + alignment - 1) & ~(alignment - 1)
|
||||
|
||||
|
||||
def sha256(data: bytes) -> str:
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
|
||||
|
||||
def package(args: argparse.Namespace) -> None:
|
||||
firmware = args.firmware.read_bytes()
|
||||
if len(firmware) > BUNDLE_OFFSET:
|
||||
raise ValueError(
|
||||
f"LP firmware is {len(firmware)} bytes; bundle starts at {BUNDLE_OFFSET}"
|
||||
)
|
||||
|
||||
payload_offset = align_up(BUNDLE_OFFSET + HEADER_SIZE, PAYLOAD_ALIGNMENT)
|
||||
entries: list[bytes] = []
|
||||
payloads: list[tuple[int, bytes]] = []
|
||||
artifact_manifest: dict[str, object] = {}
|
||||
cursor = payload_offset
|
||||
for kind, name, load_address, maximum in ARTIFACTS:
|
||||
data = (args.images / name).read_bytes()
|
||||
if not data or len(data) > maximum:
|
||||
raise ValueError(f"{name} size {len(data)} is outside 1..{maximum} bytes")
|
||||
crc32 = zlib.crc32(data) & 0xFFFFFFFF
|
||||
entries.append(ENTRY.pack(kind, cursor, load_address, len(data), crc32, REQUIRED))
|
||||
payloads.append((cursor, data))
|
||||
artifact_manifest[name] = {
|
||||
"crc32": f"0x{crc32:08X}",
|
||||
"flash_offset": f"0x{cursor:08X}",
|
||||
"load_address": f"0x{load_address:08X}",
|
||||
"sha256": sha256(data),
|
||||
"size_bytes": len(data),
|
||||
}
|
||||
cursor = align_up(cursor + len(data), PAYLOAD_ALIGNMENT)
|
||||
|
||||
if cursor > FLASH_SIZE:
|
||||
raise ValueError(f"HP boot image needs {cursor} bytes; flash capacity is {FLASH_SIZE}")
|
||||
total_size = cursor - BUNDLE_OFFSET
|
||||
header = HEADER.pack(
|
||||
MAGIC,
|
||||
VERSION,
|
||||
HEADER_SIZE,
|
||||
len(entries),
|
||||
total_size,
|
||||
0,
|
||||
REQUIRED,
|
||||
0,
|
||||
) + b"".join(entries)
|
||||
header_crc32 = zlib.crc32(header) & 0xFFFFFFFF
|
||||
header = HEADER.pack(
|
||||
MAGIC,
|
||||
VERSION,
|
||||
HEADER_SIZE,
|
||||
len(entries),
|
||||
total_size,
|
||||
header_crc32,
|
||||
REQUIRED,
|
||||
0,
|
||||
) + b"".join(entries)
|
||||
|
||||
image = bytearray(b"\xFF" * cursor)
|
||||
image[: len(firmware)] = firmware
|
||||
image[BUNDLE_OFFSET : BUNDLE_OFFSET + len(header)] = header
|
||||
for offset, data in payloads:
|
||||
image[offset : offset + len(data)] = data
|
||||
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_bytes(image)
|
||||
manifest = {
|
||||
"schema_version": 1,
|
||||
"format": "retrosoc-hp-boot-bundle-v1",
|
||||
"bundle_offset": f"0x{BUNDLE_OFFSET:08X}",
|
||||
"flash_capacity_bytes": FLASH_SIZE,
|
||||
"header_crc32": f"0x{header_crc32:08X}",
|
||||
"image_sha256": sha256(image),
|
||||
"image_size_bytes": len(image),
|
||||
"lp_firmware_size_bytes": len(firmware),
|
||||
"artifacts": artifact_manifest,
|
||||
}
|
||||
args.manifest.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.manifest.write_text(
|
||||
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--firmware", type=Path, required=True)
|
||||
parser.add_argument("--images", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--manifest", type=Path, required=True)
|
||||
package(parser.parse_args())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Install the pinned HP Linux, OpenSBI, and Buildroot source trees."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from dependency_lock import source
|
||||
from setup_helpers import ensure_git_repo, run
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def repair_incomplete_checkout(
|
||||
destination: Path, url: str, revision: str
|
||||
) -> None:
|
||||
if not (destination / ".git").is_dir():
|
||||
return
|
||||
result = subprocess.run(
|
||||
["git", "-C", str(destination), "rev-parse", "--verify", "HEAD"],
|
||||
check=False,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return
|
||||
run(("git", "remote", "set-url", "origin", url), cwd=destination)
|
||||
run(("git", "fetch", "--depth", "1", "origin", revision), cwd=destination)
|
||||
run(("git", "checkout", "--detach", "FETCH_HEAD"), cwd=destination)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--update", action="store_true")
|
||||
args = parser.parse_args()
|
||||
# The stable Linux mirror is large enough to expose HTTP/2 proxy framing failures.
|
||||
os.environ.setdefault("GIT_CONFIG_COUNT", "1")
|
||||
os.environ.setdefault("GIT_CONFIG_KEY_0", "http.version")
|
||||
os.environ.setdefault("GIT_CONFIG_VALUE_0", "HTTP/1.1")
|
||||
for name in ("opensbi_hp", "linux_hp", "buildroot_hp"):
|
||||
dependency = source(name)
|
||||
destination = ROOT / dependency["destination"]
|
||||
repair_incomplete_checkout(
|
||||
destination, dependency["url"], dependency["revision"]
|
||||
)
|
||||
ensure_git_repo(
|
||||
dependency["url"],
|
||||
destination,
|
||||
dependency["revision"],
|
||||
recursive=dependency.get("recursive", False),
|
||||
update=args.update,
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,125 @@
|
||||
package vexiiriscv
|
||||
|
||||
import spinal.core._
|
||||
import spinal.lib.bus.misc.SizeMapping
|
||||
import spinal.lib.bus.tilelink.M2sTransfers
|
||||
import spinal.lib.system.tag.PmaRegionImpl
|
||||
import vexiiriscv.misc.EmbeddedRiscvJtag
|
||||
|
||||
import java.nio.file.{Files, Paths}
|
||||
|
||||
/** Generates the fixed retroSoC RV32 Linux application core. */
|
||||
object GenerateRetroSocHp extends App {
|
||||
require(args.length == 1, "usage: GenerateRetroSocHp <output-directory>")
|
||||
|
||||
val output = Paths.get(args(0)).toAbsolutePath.normalize
|
||||
Files.createDirectories(output)
|
||||
|
||||
val param = new ParamSimple()
|
||||
param.xlen = 32
|
||||
param.physicalWidth = 32
|
||||
param.resetVector = 0x38000000L
|
||||
param.asidWidth = 9
|
||||
param.addISA("m", "a", "f", "d", "c", "s", "u", "zicntr", "zihpm")
|
||||
param.fixIsaParams()
|
||||
|
||||
param.decoders = 2
|
||||
param.lanes = 2
|
||||
param.withBtb = true
|
||||
param.withGShare = true
|
||||
param.withRas = true
|
||||
param.withLateAlu = true
|
||||
param.withAlignerBuffer = true
|
||||
param.withDispatcherBuffer = true
|
||||
param.allowBypassFrom = 0
|
||||
param.storeRs2Late = true
|
||||
param.divRadix = 4
|
||||
param.divArea = false
|
||||
|
||||
param.fetchL1Enable = true
|
||||
param.fetchL1Sets = 64
|
||||
param.fetchL1Ways = 4
|
||||
param.fetchMemDataWidthMin = 64
|
||||
param.fetchL1RefillCount = 2
|
||||
param.fetchL1Prefetch = "nl"
|
||||
param.fetchBus = FetchBusEnum.axi4
|
||||
|
||||
param.lsuL1Enable = true
|
||||
param.lsuL1Sets = 64
|
||||
param.lsuL1Ways = 4
|
||||
param.lsuMemDataWidthMin = 64
|
||||
param.lsuL1RefillCount = 8
|
||||
param.lsuL1WritebackCount = 8
|
||||
param.lsuStoreBufferSlots = 4
|
||||
param.lsuStoreBufferOps = 32
|
||||
param.lsuSoftwarePrefetch = true
|
||||
param.lsuHardwarePrefetch = "rpt"
|
||||
param.withLsuBypass = true
|
||||
param.lsuBus = LsuBusEnum.axi4
|
||||
param.lsuL1Bus = LsuL1BusEnum.axi4
|
||||
|
||||
param.pmpParam.pmpSize = 16
|
||||
param.pmpParam.granularity = 4096
|
||||
param.additionalPerformanceCounters = 8
|
||||
param.privParam.withRdTime = true
|
||||
param.privParam.withDebug = true
|
||||
param.privParam.debugTriggers = 4
|
||||
param.embeddedJtagTap = true
|
||||
|
||||
val config = SpinalConfig(
|
||||
targetDirectory = output.toString,
|
||||
inlineRom = true
|
||||
)
|
||||
config.generateVerilog {
|
||||
val plugins = param.plugins(hartId = 1)
|
||||
plugins.foreach {
|
||||
case plugin: EmbeddedRiscvJtag =>
|
||||
plugin.debugCd = ClockDomain.current.copy(
|
||||
reset = Bool().setName("EmbeddedRiscvJtag_logic_debug_reset")
|
||||
)
|
||||
case _ =>
|
||||
}
|
||||
ParamSimple.setPma(
|
||||
plugins,
|
||||
Seq(
|
||||
new PmaRegionImpl(
|
||||
mapping = SizeMapping(0x00000000L, 0x01000000L),
|
||||
isMain = false,
|
||||
isExecutable = true,
|
||||
transfers = M2sTransfers.all
|
||||
),
|
||||
new PmaRegionImpl(
|
||||
mapping = SizeMapping(0x02000000L, 0x2E000000L),
|
||||
isMain = false,
|
||||
isExecutable = false,
|
||||
transfers = M2sTransfers.all
|
||||
),
|
||||
new PmaRegionImpl(
|
||||
mapping = SizeMapping(0x30000000L, 0x00020000L),
|
||||
isMain = true,
|
||||
isExecutable = true,
|
||||
transfers = M2sTransfers.all
|
||||
),
|
||||
new PmaRegionImpl(
|
||||
mapping = SizeMapping(0x38000000L, 0x04000000L),
|
||||
isMain = true,
|
||||
isExecutable = true,
|
||||
transfers = M2sTransfers.all
|
||||
),
|
||||
new PmaRegionImpl(
|
||||
mapping = SizeMapping(0x40000000L, 0x02000000L),
|
||||
isMain = true,
|
||||
isExecutable = true,
|
||||
transfers = M2sTransfers.all
|
||||
),
|
||||
new PmaRegionImpl(
|
||||
mapping = SizeMapping(0x48000000L, 0x08000000L),
|
||||
isMain = true,
|
||||
isExecutable = true,
|
||||
transfers = M2sTransfers.all
|
||||
)
|
||||
)
|
||||
)
|
||||
VexiiRiscv(plugins).setDefinitionName("vexii_riscv_hp_generated")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user