mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
Merge branch 'feat/sw' into dev
# Conflicts: # Makefile # README.md # app/README.md # app/apps/hp_boot/main.c # configs/README.md # dependencies/dependencies.lock.json # publications/datasheets/sections/architecture.typ # publications/datasheets/sections/boot-bundle-format.typ # publications/datasheets/sections/linux-runtime.typ # publications/datasheets/sections/overview.typ # publications/datasheets/system-reference.json # publications/software_reference.py # scripts/build_hp_smoke.py # scripts/run_flow.py
This commit is contained in:
81 files changed
+2423
-974
No files matched your search
+9
-2
@@ -127,11 +127,18 @@ than replace, the common `SIM_TEST_PASS` simulation verdict.
|
||||
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
|
||||
`build_hp_linux.py` builds the RV64 `lp64d` userspace/OpenSBI image set with the repo-owned
|
||||
external OpenSBI platform. `package_hp_boot.py` creates the V2 typed LP/HP flash
|
||||
bundle and its SHA-256/CRC manifest. `check_lp_hp_performance.py` applies the
|
||||
measured 2.5x CoreMark/MHz gate.
|
||||
|
||||
`setup_hp_rtthread.py` installs the locked kernel, RV64 bare-metal compiler and
|
||||
isolated SCons environment. `build_hp_rtthread.py` stages only the repository BSP
|
||||
under the variant and records the source/compiler/configuration inputs.
|
||||
`hp_tools.py` checks source revisions and RV64 ELF class. `run_hp_sim.py` keeps
|
||||
Linux, smoke and RT-Thread logs/verdicts separate and checks every required
|
||||
workload marker together with a successful process exit.
|
||||
|
||||
`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
|
||||
|
||||
+125
-14
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the pinned RV32 HP Buildroot, Linux, DTB, and OpenSBI images."""
|
||||
"""Build pinned RV64 Linux images with a small, static acceptance initramfs."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -11,21 +11,45 @@ import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from scripts.hp_tools import require_revision, require_rv64_elf
|
||||
except ModuleNotFoundError:
|
||||
from hp_tools import require_revision, require_rv64_elf
|
||||
|
||||
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),
|
||||
"rootfs.cpio": (0x39000000, 8 * 1024 * 1024),
|
||||
}
|
||||
|
||||
REQUIRED_LINUX_CONFIG = (
|
||||
"CONFIG_ARCH_RV64I",
|
||||
"CONFIG_64BIT",
|
||||
"CONFIG_MMU",
|
||||
"CONFIG_OF_RESERVED_MEM",
|
||||
"CONFIG_BINFMT_ELF",
|
||||
"CONFIG_BINFMT_SCRIPT",
|
||||
"CONFIG_PRINTK",
|
||||
"CONFIG_TTY",
|
||||
"CONFIG_HVC_RISCV_SBI",
|
||||
"CONFIG_SERIAL_EARLYCON_RISCV_SBI",
|
||||
"CONFIG_PROC_FS",
|
||||
"CONFIG_SYSFS",
|
||||
"CONFIG_SHMEM",
|
||||
"CONFIG_TMPFS",
|
||||
"CONFIG_DEVTMPFS",
|
||||
"CONFIG_DEVMEM",
|
||||
)
|
||||
|
||||
def command(arguments: list[str], cwd: Path) -> None:
|
||||
|
||||
def command(arguments: list[str], cwd: Path, extra_env: dict[str, str] | None = None) -> None:
|
||||
environment = dict(os.environ)
|
||||
environment.pop("CONFIG", None)
|
||||
environment.pop("MAKEFLAGS", None)
|
||||
environment.pop("MAKEOVERRIDES", None)
|
||||
environment.pop("MFLAGS", None)
|
||||
environment.update(extra_env or {})
|
||||
subprocess.run(arguments, cwd=cwd, check=True, env=environment)
|
||||
|
||||
|
||||
@@ -51,12 +75,65 @@ def source_revision(path: Path) -> str:
|
||||
|
||||
|
||||
def find_toolchain(buildroot_output: Path) -> str:
|
||||
matches = sorted((buildroot_output / "host/bin").glob("riscv32*-linux-*-gcc"))
|
||||
matches = sorted((buildroot_output / "host/bin").glob("riscv64*-linux-*-gcc"))
|
||||
if len(matches) != 1:
|
||||
raise RuntimeError(f"expected one RV32 Linux GCC in {buildroot_output / 'host/bin'}")
|
||||
raise RuntimeError(f"expected one RV64 Linux GCC in {buildroot_output / 'host/bin'}")
|
||||
return str(matches[0])[: -len("gcc")]
|
||||
|
||||
|
||||
def minimal_busybox(buildroot: Path, output: Path, external: Path, jobs: int) -> str:
|
||||
"""Expand an allowlist using BusyBox's own allnoconfig, not default-y oldconfig."""
|
||||
make = ["make", f"O={output}", f"BR2_EXTERNAL={external}", f"-j{jobs}"]
|
||||
command([*make, "busybox-configure"], buildroot)
|
||||
sources = [path for path in (output / "build").glob("busybox-*")
|
||||
if (path / "scripts/kconfig/conf").is_file()]
|
||||
if len(sources) != 1:
|
||||
raise RuntimeError("expected one configured locked BusyBox source")
|
||||
source = sources[0]
|
||||
config_dir = output.parent / "busybox-config"
|
||||
(config_dir / "include").mkdir(parents=True, exist_ok=True)
|
||||
command([str(source / "scripts/kconfig/conf"), "-n", str(source / "Config.in")], config_dir,
|
||||
{"srctree": str(source), "KCONFIG_NOTIMESTAMP": "1",
|
||||
"KCONFIG_ALLCONFIG": str(external / "busybox/retrosoc_hp.config")})
|
||||
# This BusyBox conf implementation resets booleans even when ALLCONFIG
|
||||
# specified y. Overlay our explicit choices after its all-no expansion;
|
||||
# Buildroot's ordinary oldconfig then resolves the real dependencies.
|
||||
selected = dict(line.split("=", 1) for line in
|
||||
(external / "busybox/retrosoc_hp.config").read_text().splitlines()
|
||||
if line.startswith("CONFIG_") and "=" in line)
|
||||
config = config_dir / ".config"
|
||||
lines = []
|
||||
for line in config.read_text().splitlines():
|
||||
key = line[2:].split(" ", 1)[0] if line.startswith("# CONFIG_") else line.split("=", 1)[0]
|
||||
lines.append(f"{key}={selected.pop(key)}" if key in selected else line)
|
||||
lines.extend(f"{key}={value}" for key, value in selected.items())
|
||||
config.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
config_arg = f"BUSYBOX_CONFIG_FILE={config_dir / '.config'}"
|
||||
command([*make, config_arg, "busybox-reconfigure"], buildroot)
|
||||
return config_arg
|
||||
|
||||
|
||||
def validate_linux_config(config: Path) -> None:
|
||||
values: dict[str, str] = {}
|
||||
for line in config.read_text(encoding="utf-8").splitlines():
|
||||
if line.startswith("CONFIG_") and "=" in line:
|
||||
symbol, value = line.split("=", 1)
|
||||
values[symbol] = value
|
||||
elif line.startswith("# CONFIG_") and line.endswith(" is not set"):
|
||||
symbol = line[len("# ") : -len(" is not set")]
|
||||
values[symbol] = "n"
|
||||
|
||||
invalid = [
|
||||
f"{symbol}={values.get(symbol, 'missing')}"
|
||||
for symbol in REQUIRED_LINUX_CONFIG
|
||||
if values.get(symbol) != "y"
|
||||
]
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
"invalid HP Linux effective configuration: " + ", ".join(invalid)
|
||||
)
|
||||
|
||||
|
||||
def build(args: argparse.Namespace) -> None:
|
||||
root = args.root.resolve()
|
||||
output = args.output.resolve()
|
||||
@@ -64,6 +141,9 @@ def build(args: argparse.Namespace) -> None:
|
||||
buildroot = require_source(args.buildroot, "Buildroot")
|
||||
linux = require_source(args.linux, "Linux")
|
||||
opensbi = require_source(args.opensbi, "OpenSBI")
|
||||
for name, path in (("buildroot_hp", buildroot), ("linux_hp", linux), ("opensbi_hp", opensbi)):
|
||||
if path != require_revision(root, name).resolve():
|
||||
raise ValueError(f"{name} must use its locked source directory")
|
||||
external = root / "app/ports/linux"
|
||||
|
||||
buildroot_output = output / "buildroot"
|
||||
@@ -71,7 +151,16 @@ def build(args: argparse.Namespace) -> None:
|
||||
["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)
|
||||
busybox_config = minimal_busybox(buildroot, buildroot_output, external, args.jobs)
|
||||
command(["make", f"O={buildroot_output}", f"BR2_EXTERNAL={external}",
|
||||
busybox_config, f"-j{args.jobs}"], buildroot)
|
||||
busybox_sources = sorted((buildroot_output / "build").glob("busybox-*/.config"))
|
||||
if len(busybox_sources) != 1:
|
||||
raise RuntimeError("missing effective BusyBox configuration")
|
||||
busybox_effective = busybox_sources[0].read_text().splitlines()
|
||||
for symbol in ("STATIC", "BUSYBOX", "ASH", "MOUNT", "TEST", "ECHO", "CAT", "TRUE"):
|
||||
if f"CONFIG_{symbol}=y" not in busybox_effective:
|
||||
raise RuntimeError(f"HP BusyBox requires CONFIG_{symbol}=y")
|
||||
cross_compile = find_toolchain(buildroot_output)
|
||||
|
||||
linux_output = output / "linux"
|
||||
@@ -94,6 +183,7 @@ def build(args: argparse.Namespace) -> None:
|
||||
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}", "olddefconfig"],
|
||||
linux,
|
||||
)
|
||||
validate_linux_config(linux_output / ".config")
|
||||
command(
|
||||
["make", f"O={linux_output}", "ARCH=riscv", f"CROSS_COMPILE={cross_compile}",
|
||||
f"-j{args.jobs}", "Image"],
|
||||
@@ -102,10 +192,28 @@ def build(args: argparse.Namespace) -> None:
|
||||
|
||||
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
|
||||
require_rv64_elf(linux_output / "vmlinux")
|
||||
busybox = buildroot_output / "target/bin/busybox"
|
||||
require_rv64_elf(busybox)
|
||||
if "INTERP" in subprocess.check_output([cross_compile + "readelf", "-l", str(busybox)], text=True):
|
||||
raise ValueError("HP acceptance BusyBox must be statically linked")
|
||||
helper = output / "hp-ready"
|
||||
command([cross_compile + "gcc", "-Os", "-static", "-Wall", "-Wextra", "-Werror",
|
||||
"-o", str(helper), str(external / "hp_ready.c")], root)
|
||||
require_rv64_elf(helper)
|
||||
archive_spec = output / "initramfs.list"
|
||||
entries = [f"dir /{name} 755 0 0" for name in ("bin", "dev", "proc", "sys", "tmp")]
|
||||
entries += [f"file /bin/busybox {busybox} 755 0 0",
|
||||
f"file /bin/hp-ready {helper} 755 0 0", f"file /init {external / 'init'} 755 0 0",
|
||||
"nod /dev/console 600 0 0 c 5 1", "nod /dev/mem 600 0 0 c 1 1"]
|
||||
entries += [f"slink /bin/{name} busybox 777 0 0" for name in
|
||||
("sh", "mount", "mkdir", "echo", "test", "cat", "sleep")]
|
||||
archive_spec.write_text("\n".join(entries) + "\n", encoding="utf-8")
|
||||
rootfs_source = image_dir / "rootfs.cpio"
|
||||
with rootfs_source.open("wb") as archive:
|
||||
subprocess.run([str(linux_output / "usr/gen_init_cpio"), "-t", "0", str(archive_spec)],
|
||||
stdout=archive, check=True)
|
||||
initrd_end = LAYOUT["rootfs.cpio"][0] + rootfs_source.stat().st_size
|
||||
command(
|
||||
[
|
||||
"dtc",
|
||||
@@ -140,9 +248,9 @@ def build(args: argparse.Namespace) -> None:
|
||||
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_zicbom_zicsr_zifencei",
|
||||
"PLATFORM_RISCV_XLEN=64",
|
||||
"PLATFORM_RISCV_ABI=lp64d",
|
||||
"PLATFORM_RISCV_ISA=rv64imafdc_zicbom_zicsr_zifencei",
|
||||
"FW_TEXT_START=0x38000000",
|
||||
"FW_JUMP=y",
|
||||
"FW_JUMP_ADDR=0x38400000",
|
||||
@@ -155,15 +263,18 @@ def build(args: argparse.Namespace) -> None:
|
||||
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)
|
||||
|
||||
require_rv64_elf(opensbi_output / "platform/retrosoc_hp/firmware/fw_jump.elf")
|
||||
manifest: dict[str, object] = {
|
||||
"schema_version": 1,
|
||||
"xlen": 64,
|
||||
"abi": "lp64d",
|
||||
"initramfs": "minimal-static-uncompressed",
|
||||
"boot_flow": "opensbi-fw_jump",
|
||||
"hart_id": 1,
|
||||
"sources": {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the repository BSP against the unmodified, locked RT-Thread kernel."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from scripts.hp_tools import cross_prefix, require_revision, require_rv64_elf
|
||||
except ModuleNotFoundError:
|
||||
from hp_tools import cross_prefix, require_revision, require_rv64_elf
|
||||
|
||||
|
||||
def build(root: Path, output: Path, jobs: int) -> None:
|
||||
source = require_revision(root, "rtthread_hp")
|
||||
bsp = output / "bsp"
|
||||
bsp.mkdir(parents=True, exist_ok=True)
|
||||
for path in (root / "app/ports/rtthread").iterdir():
|
||||
if path.is_file():
|
||||
shutil.copy2(path, bsp / path.name)
|
||||
environment = dict(os.environ)
|
||||
for key in ("CONFIG", "MAKEFLAGS", "MAKEOVERRIDES", "MFLAGS"):
|
||||
environment.pop(key, None)
|
||||
environment.update(RTT_ROOT=str(source), RTT_EXEC_PATH=str(Path(cross_prefix(root)).parent),
|
||||
RETROSOC_ROOT=str(root))
|
||||
expected = re.search(r"^scons==([\d.]+)", (root / "requirements/rtthread.txt").read_text(), re.M)
|
||||
actual = subprocess.check_output([str(root / ".cache/retrosoc/rtthread-venv/bin/python"),
|
||||
"-c", "import SCons; print(SCons.__version__)"], text=True).strip()
|
||||
if expected is None or actual != expected[1]:
|
||||
raise ValueError(f"RT-Thread SCons version does not match requirements: {actual}")
|
||||
subprocess.run([str(root / ".cache/retrosoc/rtthread-venv/bin/scons"), f"-j{jobs}"],
|
||||
cwd=bsp, env=environment, check=True)
|
||||
require_rv64_elf(bsp / "rtthread.elf")
|
||||
images = output / "images"
|
||||
images.mkdir(parents=True, exist_ok=True)
|
||||
for name in ("rtthread.elf", "rtthread.bin", "rtthread.map"):
|
||||
shutil.copy2(bsp / name, images / name)
|
||||
revision = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD"],
|
||||
text=True).strip()
|
||||
manifest = {"schema_version": 1, "xlen": 64, "abi": "lp64d", "hart_id": 1,
|
||||
"rtthread_revision": revision,
|
||||
"scons_version": actual,
|
||||
"compiler": subprocess.check_output([cross_prefix(root) + "gcc", "--version"],
|
||||
text=True).splitlines()[0],
|
||||
"sha256": hashlib.sha256((images / "rtthread.bin").read_bytes()).hexdigest()}
|
||||
manifest["inputs"] = {str(path.relative_to(root)): hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
for path in sorted((root / "app/ports/rtthread").iterdir()) if path.is_file()}
|
||||
manifest["inputs"]["requirements/rtthread.txt"] = hashlib.sha256(
|
||||
(root / "requirements/rtthread.txt").read_bytes()).hexdigest()
|
||||
(images / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--jobs", type=int, default=1)
|
||||
args = parser.parse_args()
|
||||
build(args.root.resolve(), args.output.resolve(), args.jobs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -86,7 +86,10 @@ def generate(args: argparse.Namespace) -> None:
|
||||
manifest = {
|
||||
"schema_version": 1,
|
||||
"module": GENERATED_MODULE,
|
||||
"configuration": "rv32imafdc_zicbom_max",
|
||||
"configuration": "rv64imafdc_zicbom_max",
|
||||
"xlen": 64,
|
||||
"physical_address_width": 32,
|
||||
"mmu": "sv39",
|
||||
"vexiiriscv_revision": actual,
|
||||
"source_status": git_status(source),
|
||||
"submodules": submodules,
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"""Shared locked RV64 build helpers for HP payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import struct
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from scripts.dependency_lock import load_lock
|
||||
except ModuleNotFoundError:
|
||||
from dependency_lock import load_lock
|
||||
|
||||
|
||||
def cross_prefix(root: Path) -> str:
|
||||
spec = load_lock(root / "dependencies/dependencies.lock.json")["toolchains"]["ubuntu-22.04"][
|
||||
"riscv_gnu_hp"
|
||||
]
|
||||
return str(root / ".cache/retrosoc/development/toolchains" /
|
||||
f"riscv_gnu_hp-{spec['version']}" / spec["path"] / "riscv64-unknown-elf-")
|
||||
|
||||
|
||||
def require_rv64_elf(path: Path) -> None:
|
||||
header = path.read_bytes()[:64]
|
||||
if (len(header) < 64 or header[:6] != b"\x7fELF\x02\x01" or
|
||||
struct.unpack_from("<H", header, 18)[0] != 243):
|
||||
raise ValueError(f"expected a little-endian RV64 ELF: {path}")
|
||||
|
||||
|
||||
def require_revision(root: Path, name: str) -> Path:
|
||||
spec = load_lock(root / "dependencies/dependencies.lock.json")["sources"][name]
|
||||
source = root / spec["destination"]
|
||||
actual = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD"], text=True).strip()
|
||||
if actual != spec["revision"]:
|
||||
raise ValueError(f"{name} revision mismatch: {actual} != {spec['revision']}")
|
||||
if subprocess.run(["git", "-C", str(source), "diff", "--quiet", "HEAD", "--"],
|
||||
check=False).returncode:
|
||||
raise ValueError(f"{name} has modified tracked source files: {source}")
|
||||
return source
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--root", type=Path, required=True)
|
||||
print(cross_prefix(parser.parse_args().root.resolve()))
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Package LP firmware and the fixed HP Linux image set into one flash image."""
|
||||
"""Package LP firmware and a typed RV64 HP workload into a V2 flash image."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -15,7 +15,7 @@ FLASH_SIZE = 16 * 1024 * 1024
|
||||
BUNDLE_OFFSET = 1 * 1024 * 1024
|
||||
PAYLOAD_ALIGNMENT = 4096
|
||||
MAGIC = 0x50485352
|
||||
VERSION = 1
|
||||
VERSION = 2
|
||||
REQUIRED = 1
|
||||
HEADER = struct.Struct("<8I")
|
||||
ENTRY = struct.Struct("<6I")
|
||||
@@ -23,9 +23,19 @@ 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),
|
||||
(4, "rootfs.cpio", 0x39000000, 8 * 1024 * 1024),
|
||||
)
|
||||
HEADER_SIZE = HEADER.size + (ENTRY.size * len(ARTIFACTS))
|
||||
WORKLOADS = {"linux": 1, "smoke": 2, "rtthread": 3}
|
||||
|
||||
|
||||
def artifacts_for(workload: str) -> tuple[tuple[int, str, int, int], ...]:
|
||||
if workload == "linux":
|
||||
return ARTIFACTS
|
||||
if workload not in WORKLOADS:
|
||||
raise ValueError(f"unsupported HP workload: {workload}")
|
||||
name = "hp_smoke.bin" if workload == "smoke" else "rtthread.bin"
|
||||
return ((5, name, 0x38000000, 512 * 1024),)
|
||||
|
||||
|
||||
def align_up(value: int, alignment: int) -> int:
|
||||
@@ -37,6 +47,8 @@ def sha256(data: bytes) -> str:
|
||||
|
||||
|
||||
def package(args: argparse.Namespace) -> None:
|
||||
workload = getattr(args, "workload", "linux")
|
||||
artifacts = artifacts_for(workload)
|
||||
firmware = args.firmware.read_bytes()
|
||||
if len(firmware) > BUNDLE_OFFSET:
|
||||
raise ValueError(
|
||||
@@ -48,7 +60,7 @@ def package(args: argparse.Namespace) -> None:
|
||||
payloads: list[tuple[int, bytes]] = []
|
||||
artifact_manifest: dict[str, object] = {}
|
||||
cursor = payload_offset
|
||||
for kind, name, load_address, maximum in ARTIFACTS:
|
||||
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")
|
||||
@@ -75,8 +87,8 @@ def package(args: argparse.Namespace) -> None:
|
||||
total_size,
|
||||
0,
|
||||
REQUIRED,
|
||||
0,
|
||||
) + b"".join(entries)
|
||||
WORKLOADS[workload],
|
||||
) + b"".join(entries).ljust(HEADER_SIZE - HEADER.size, b"\0")
|
||||
header_crc32 = zlib.crc32(header) & 0xFFFFFFFF
|
||||
header = HEADER.pack(
|
||||
MAGIC,
|
||||
@@ -86,8 +98,8 @@ def package(args: argparse.Namespace) -> None:
|
||||
total_size,
|
||||
header_crc32,
|
||||
REQUIRED,
|
||||
0,
|
||||
) + b"".join(entries)
|
||||
WORKLOADS[workload],
|
||||
) + b"".join(entries).ljust(HEADER_SIZE - HEADER.size, b"\0")
|
||||
|
||||
image = bytearray(b"\xFF" * cursor)
|
||||
image[: len(firmware)] = firmware
|
||||
@@ -99,7 +111,9 @@ def package(args: argparse.Namespace) -> None:
|
||||
args.output.write_bytes(image)
|
||||
manifest = {
|
||||
"schema_version": 1,
|
||||
"format": "retrosoc-hp-boot-bundle-v1",
|
||||
"format": "retrosoc-hp-boot-bundle-v2",
|
||||
"workload": workload,
|
||||
"xlen": 64,
|
||||
"bundle_offset": f"0x{BUNDLE_OFFSET:08X}",
|
||||
"flash_capacity_bytes": FLASH_SIZE,
|
||||
"header_crc32": f"0x{header_crc32:08X}",
|
||||
@@ -120,6 +134,7 @@ def main() -> None:
|
||||
parser.add_argument("--images", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--manifest", type=Path, required=True)
|
||||
parser.add_argument("--workload", choices=WORKLOADS, default="linux")
|
||||
package(parser.parse_args())
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -210,7 +210,7 @@ def main() -> int:
|
||||
"duration_seconds": round(time.monotonic() - start_clock, 3),
|
||||
"peak_rss_kib": resource.getrusage(resource.RUSAGE_CHILDREN).ru_maxrss,
|
||||
"exit_code": returncode,
|
||||
"status": "passed" if returncode == 0 else "failed",
|
||||
"status": "interrupted" if returncode == 130 else ("passed" if returncode == 0 else "failed"),
|
||||
"log": str(args.log.resolve()),
|
||||
}
|
||||
if error_message:
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run and validate one HP workload without reusing another workload's verdict."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
MARKERS = {
|
||||
"smoke": ("HP_LINUX_READY", "HP_GA2D_PASS", "HP_GA2D_CACHE_CLEAN"),
|
||||
"linux": ("HP_LINUX_INIT_PASS", "retroSoC HP Linux ready", "HP_LINUX_READY"),
|
||||
"rtthread": ("RTTHREAD_RV64_PASS", "RTTHREAD_TIMEOUT_PASS", "RTTHREAD_PREEMPT_PASS",
|
||||
"RTTHREAD_CONTEXT_PASS", "RTTHREAD_IPC_PASS", "RTTHREAD_MAILBOX_IRQ_PASS",
|
||||
"RTTHREAD_TEST_PASS", "HP_RTTHREAD_PASS"),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--emulator", type=Path, required=True)
|
||||
parser.add_argument("--image", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--workload", choices=MARKERS, required=True)
|
||||
parser.add_argument("--timeout", type=int, required=True)
|
||||
args = parser.parse_args()
|
||||
output = args.output.resolve()
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
result = output / "result-sim-check.json"
|
||||
result.write_text(json.dumps({"status": "running", "workload": args.workload}) + "\n",
|
||||
encoding="utf-8")
|
||||
scripts = Path(__file__).resolve().parent
|
||||
try:
|
||||
process = subprocess.run([
|
||||
sys.executable, str(scripts / "run_flow.py"), "--tool", "verilator-sim",
|
||||
"--stream-bytes", "--log", str(output / "sim.log"), "--result",
|
||||
str(output / "result-sim.json"), "--cwd", str(output), "--",
|
||||
str(args.emulator.resolve()), "-i", str(args.image.resolve()),
|
||||
"--fast-flash", "-t", str(args.timeout),
|
||||
], check=False)
|
||||
if process.returncode:
|
||||
result.write_text(json.dumps({"status": "failed", "exit_code": process.returncode})
|
||||
+ "\n", encoding="utf-8")
|
||||
return process.returncode
|
||||
command = [sys.executable, str(scripts / "check_simulation.py"), "--log",
|
||||
str(output / "sim.log"), "--result", str(result)]
|
||||
for marker in ("VERILATOR_FAST_FLASH=enabled", "SIM_TEST_PASS code=0",
|
||||
*MARKERS[args.workload]):
|
||||
command.extend(("--require", marker))
|
||||
return subprocess.run(command, check=False).returncode
|
||||
except KeyboardInterrupt:
|
||||
result.write_text('{"status": "interrupted"}\n', encoding="utf-8")
|
||||
return 130
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Install the locked RT-Thread source, RV64 compiler and SCons environment."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import venv
|
||||
from pathlib import Path
|
||||
|
||||
from dependency_lock import load_lock
|
||||
from install_toolchain import install
|
||||
from setup_helpers import ensure_git_repo, run
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--update", action="store_true")
|
||||
args = parser.parse_args()
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
lock = load_lock()
|
||||
spec = lock["sources"]["rtthread_hp"]
|
||||
ensure_git_repo(spec["url"], root / spec["destination"], spec["revision"], update=args.update)
|
||||
install("riscv_gnu_hp", lock["toolchains"]["ubuntu-22.04"]["riscv_gnu_hp"],
|
||||
root / ".cache/retrosoc/development", args.update)
|
||||
environment = root / ".cache/retrosoc/rtthread-venv"
|
||||
if not (environment / "bin/python").is_file():
|
||||
venv.EnvBuilder(with_pip=True).create(environment)
|
||||
run((str(environment / "bin/python"), "-m", "pip", "install", "--require-hashes",
|
||||
"--cache-dir", str(root / ".cache/retrosoc/pip"),
|
||||
"-r", str(root / "requirements/rtthread.txt")))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -8,7 +8,7 @@ import vexiiriscv.misc.EmbeddedRiscvJtag
|
||||
|
||||
import java.nio.file.{Files, Paths}
|
||||
|
||||
/** Generates the fixed retroSoC RV32 Linux application core. */
|
||||
/** Generates the fixed retroSoC RV64 application core. */
|
||||
object GenerateRetroSocHp extends App {
|
||||
require(args.length == 1, "usage: GenerateRetroSocHp <output-directory>")
|
||||
|
||||
@@ -16,7 +16,7 @@ object GenerateRetroSocHp extends App {
|
||||
Files.createDirectories(output)
|
||||
|
||||
val param = new ParamSimple()
|
||||
param.xlen = 32
|
||||
param.xlen = 64
|
||||
param.physicalWidth = 32
|
||||
param.resetVector = 0x38000000L
|
||||
param.asidWidth = 9
|
||||
|
||||
Reference in new issue
Block a user