diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index bcbb8b2b..c19ccfee 100644 --- a/ATTRIBUTIONS.md +++ b/ATTRIBUTIONS.md @@ -1,5 +1,12 @@ # Third-party attribution +The optional HP RT-Thread kernel is official RT-Thread v5.3.0, commit +`99428a1e7f7447955aa860f7c969273a12095b8f`, from +[RT-Thread/rt-thread](https://github.com/RT-Thread/rt-thread). It is licensed +under Apache-2.0. Its original copyright/license files remain in the locked +managed checkout; only the retroSoC BSP is repository-owned. Redistribution of +built RT-Thread images must retain the applicable upstream notices. + This repository contains RTL source files that retain copyright and attribution information from third-party open-source projects in their original file headers. The following projects are explicitly referenced in the RTL sources currently present in this repository. ## 1. ETH Zurich and University of Bologna diff --git a/Makefile b/Makefile index 49ce6280..15df9615 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ SRAM_SIZE_KIB ?= $(if $(filter ICS55,$(PDK)),128,32) PDK_BEHAV ?= NO HAVE_SVA ?= NO HAVE_HP ?= YES -HP_CONFIG ?= rv32imafdc_zicbom_max +HP_CONFIG ?= rv64imafdc_zicbom_max BUILD_RELEASE ?= NO JTAG_IDCODE ?= DEADBEEF EXT_CLK_HZ ?= 72000000 @@ -72,6 +72,7 @@ APP ?= shell LINK_TYPE ?= ld2_sram COREMARK_MODE ?= quick HP_PERF_MIN_RATIO ?= 2.5 +HP_CROSS ?= $(shell $(PYTHON) $(ROOT_PATH)/scripts/hp_tools.py --root $(ROOT_PATH)) LP_COREMARK_REPORT ?= HP_COREMARK_REPORT ?= @@ -139,6 +140,11 @@ HP_SMOKE_BUNDLE_NAME ?= retrosoc_hp_smoke HP_SMOKE_BUNDLE_BIN := $(SW_BUILD_DIR)/$(HP_SMOKE_BUNDLE_NAME).bin HP_SMOKE_BUNDLE_HEX := $(SW_BUILD_DIR)/$(HP_SMOKE_BUNDLE_NAME).hex HP_SMOKE_MANIFEST := $(SW_BUILD_DIR)/$(HP_SMOKE_BUNDLE_NAME).json +HP_RTTHREAD_BUILD_DIR := $(VARIANT_ROOT)/hp-rtthread +HP_RTTHREAD_STAMP := $(HP_RTTHREAD_BUILD_DIR)/images/.stamp +HP_RTTHREAD_SIM_TIME ?= 1800 +HP_RTTHREAD_BUNDLE_NAME ?= retrosoc_hp_rtthread +HP_RTTHREAD_BUNDLE_BIN := $(SW_BUILD_DIR)/$(HP_RTTHREAD_BUNDLE_NAME).bin HP_BUILDRT_ROOT := $(ROOT_PATH)/.cache/retrosoc/sources/buildroot-hp HP_LINUX_ROOT := $(ROOT_PATH)/.cache/retrosoc/sources/linux-hp HP_OPENSBI_ROOT := $(ROOT_PATH)/.cache/retrosoc/sources/opensbi-hp @@ -167,7 +173,7 @@ VALID_SYNTH_RECIPE := balanced area speed VALID_STA := NONE OPENSTA VALID_PDK := ICS55 IHP130 SKY130 GF180 VALID_BOOL := YES NO -VALID_HP_CONFIG := rv32imafdc_zicbom_max +VALID_HP_CONFIG := rv64imafdc_zicbom_max VALID_ISA := RV32E RV32I RV32IM VALID_APP := benchmark bringup ci_smoke coremark debug hp_boot shell xpi_flash_loader VALID_LINK_TYPE := xip jtag_sram ld2_all_sram ld2_sram ld2_psram ld2_sdram @@ -381,10 +387,12 @@ help: ' apu-p5-corpus qualify pinned FLAC with BAM/libFLAC and production RTL' \ ' setup-regression install pinned dependencies for all PR PDK profiles' \ ' setup-hp-linux install pinned Buildroot, Linux, and OpenSBI sources' \ - ' hp-linux build the pinned RV32 HP Linux image set' \ + ' hp-linux build the pinned RV64 HP Linux image set' \ ' hp-bundle package LP firmware and HP Linux images for flash' \ ' hp-linux-sim run the fast-flash HP Linux userspace acceptance test' \ ' hp-smoke-sim run LP release, HP MMIO, and mailbox RTL smoke test' \ + ' setup-hp-rtthread install locked RT-Thread, RV64 compiler and SCons' \ + ' hp-rtthread-sim run RV64 RT-Thread kernel/platform acceptance' \ ' doctor check tools, paths, and selected configuration' \ ' config | manifest print/write the effective configuration' \ ' memory-map generate the selected address-map artifacts' \ @@ -533,7 +541,10 @@ apu-p5-corpus: setup-apu-reference $(APU_P5_BUNDLE) setup-hp-linux: python3 $(ROOT_PATH)/scripts/setup_hp_linux.py -$(HP_LINUX_STAMP): $(ROOT_PATH)/scripts/build_hp_linux.py \ +setup-hp-rtthread: + python3 $(ROOT_PATH)/scripts/setup_hp_rtthread.py + +$(HP_LINUX_STAMP): $(ROOT_PATH)/scripts/build_hp_linux.py $(ROOT_PATH)/scripts/hp_tools.py \ $(ROOT_PATH)/app/ports/linux/configs/retrosoc_hp_defconfig \ $(ROOT_PATH)/app/ports/linux/busybox/retrosoc_hp.config \ $(ROOT_PATH)/app/ports/linux/linux/retrosoc_hp.config \ @@ -542,7 +553,7 @@ $(HP_LINUX_STAMP): $(ROOT_PATH)/scripts/build_hp_linux.py \ $(ROOT_PATH)/app/ports/linux/opensbi/retrosoc_hp/configs/defconfig \ $(ROOT_PATH)/app/ports/linux/opensbi/retrosoc_hp/objects.mk \ $(ROOT_PATH)/app/ports/linux/opensbi/retrosoc_hp/platform.c \ - $(ROOT_PATH)/app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp + $(ROOT_PATH)/app/ports/linux/init $(ROOT_PATH)/app/ports/linux/hp_ready.c @test '$(HAVE_HP)' = YES python3 $(ROOT_PATH)/scripts/build_hp_linux.py --root $(ROOT_PATH) \ --buildroot $(HP_BUILDRT_ROOT) --linux $(HP_LINUX_ROOT) --opensbi $(HP_OPENSBI_ROOT) \ @@ -565,23 +576,17 @@ hp-bundle: $(HP_BOOT_BUNDLE_BIN) $(HP_BOOT_BUNDLE_HEX) hp-linux-sim: hp-bundle comp @test '$(SIMU)' = VERILATOR - $(MAKE) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) SIM_FIRMWARE_NAME=$(HP_BOOT_BUNDLE_NAME) \ - SOC_SIM_TIME=$(HP_LINUX_SIM_TIME) VERILATOR_SIM_ARGS=--fast-flash sim - python3 $(ROOT_PATH)/scripts/check_simulation.py \ - --log $(SIM_BUILD_ROOT)/sim.log \ - --result $(SIM_BUILD_ROOT)/result-hp-linux-sim-check.json \ - --require 'VERILATOR_FAST_FLASH=enabled' \ - --require 'retroSoC HP Linux ready' \ - --require 'HP_LINUX_READY' \ - --require 'SIM_TEST_PASS code=0' + python3 $(ROOT_PATH)/scripts/run_hp_sim.py --emulator $(VERILATOR_EMU) \ + --image $(HP_BOOT_BUNDLE_BIN) --output $(SIM_BUILD_ROOT)/hp-linux \ + --workload linux --timeout $(HP_LINUX_SIM_TIME) -$(HP_SMOKE_STAMP): $(ROOT_PATH)/scripts/build_hp_smoke.py \ +$(HP_SMOKE_STAMP): $(ROOT_PATH)/scripts/build_hp_smoke.py $(ROOT_PATH)/scripts/hp_tools.py \ $(ROOT_PATH)/app/ports/linux/smoke/start.S \ $(ROOT_PATH)/app/ports/linux/smoke/linker.ld python3 $(ROOT_PATH)/scripts/build_hp_smoke.py \ --source $(ROOT_PATH)/app/ports/linux/smoke/start.S \ --linker $(ROOT_PATH)/app/ports/linux/smoke/linker.ld \ - --output $(HP_SMOKE_BUILD_DIR) --cross $(CROSS) + --output $(HP_SMOKE_BUILD_DIR) --cross $(HP_CROSS) @touch $@ $(HP_SMOKE_BUNDLE_BIN): $(FIRMWARE_ELF) $(HP_SMOKE_STAMP) \ @@ -589,24 +594,45 @@ $(HP_SMOKE_BUNDLE_BIN): $(FIRMWARE_ELF) $(HP_SMOKE_STAMP) \ python3 $(ROOT_PATH)/scripts/package_hp_boot.py \ --firmware $(SW_BUILD_DIR)/$(FIRMWARE_NAME).bin \ --images $(HP_SMOKE_BUILD_DIR)/images --output $@ \ - --manifest $(HP_SMOKE_MANIFEST) + --manifest $(HP_SMOKE_MANIFEST) --workload smoke $(HP_SMOKE_BUNDLE_HEX): $(HP_SMOKE_BUNDLE_BIN) $(OBJC) -I binary -O verilog $< $@ hp-smoke-bundle: $(HP_SMOKE_BUNDLE_BIN) $(HP_SMOKE_BUNDLE_HEX) +$(HP_RTTHREAD_STAMP): $(wildcard $(ROOT_PATH)/app/ports/rtthread/*) \ + $(ROOT_PATH)/scripts/build_hp_rtthread.py $(ROOT_PATH)/scripts/hp_tools.py \ + $(ROOT_PATH)/app/apps/hp_boot/hp_boot_bundle.h $(ROOT_PATH)/requirements/rtthread.txt $(LOCK_FILE) + @test '$(HAVE_HP)' = YES + python3 $(ROOT_PATH)/scripts/build_hp_rtthread.py --root $(ROOT_PATH) \ + --output $(HP_RTTHREAD_BUILD_DIR) --jobs $(JOBS) + @touch $@ + +hp-rtthread: $(HP_RTTHREAD_STAMP) + +$(HP_RTTHREAD_BUNDLE_BIN): $(FIRMWARE_ELF) $(HP_RTTHREAD_STAMP) \ + $(ROOT_PATH)/scripts/package_hp_boot.py + python3 $(ROOT_PATH)/scripts/package_hp_boot.py \ + --firmware $(SW_BUILD_DIR)/$(FIRMWARE_NAME).bin \ + --images $(HP_RTTHREAD_BUILD_DIR)/images --output $@ \ + --manifest $(SW_BUILD_DIR)/$(HP_RTTHREAD_BUNDLE_NAME).json --workload rtthread + +hp-rtthread-bundle: $(HP_RTTHREAD_BUNDLE_BIN) + +hp-rtthread-sim: hp-rtthread-bundle comp + @test '$(SIMU)' = VERILATOR + python3 $(ROOT_PATH)/scripts/run_hp_sim.py --emulator $(VERILATOR_EMU) \ + --image $(HP_RTTHREAD_BUNDLE_BIN) --output $(SIM_BUILD_ROOT)/hp-rtthread \ + --workload rtthread --timeout $(HP_RTTHREAD_SIM_TIME) + +.PHONY: setup-hp-rtthread hp-rtthread hp-rtthread-bundle hp-rtthread-sim + hp-smoke-sim: hp-smoke-bundle comp @test '$(SIMU)' = VERILATOR - $(MAKE) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) SIM_FIRMWARE_NAME=$(HP_SMOKE_BUNDLE_NAME) \ - SOC_SIM_TIME=$(HP_SMOKE_SIM_TIME) VERILATOR_SIM_ARGS=--fast-flash sim - python3 $(ROOT_PATH)/scripts/check_simulation.py \ - --log $(SIM_BUILD_ROOT)/sim.log \ - --result $(SIM_BUILD_ROOT)/result-hp-smoke-sim-check.json \ - --require 'SIM_TEST_PASS code=0' \ - --require 'HP_LINUX_READY' \ - --require 'HP_GA2D_PASS' \ - --require 'HP_GA2D_CACHE_CLEAN' + python3 $(ROOT_PATH)/scripts/run_hp_sim.py --emulator $(VERILATOR_EMU) \ + --image $(HP_SMOKE_BUNDLE_BIN) --output $(SIM_BUILD_ROOT)/hp-smoke \ + --workload smoke --timeout $(HP_SMOKE_SIM_TIME) ifeq ($(HAVE_HP),YES) $(HP_GENERATED_STAMP): $(ROOT_PATH)/scripts/generate_vexiiriscv.py \ diff --git a/README.md b/README.md index 1283034d..1bea8428 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ under the [Mulan Permissive Software License, Version 2](LICENSE). ## Highlights - A fixed asymmetric dual-core Mini product: Hazard3 is the LP management hart - and generated dual-issue RV32IMAFDC VexiiRiscv is the HP application hart. + and generated dual-issue RV64IMAFDC VexiiRiscv is the HP application hart. The C0-C3 selectable cores remain only in the explicit Mini MPW profile. - A product [LP/HP architecture](docs/lp-hp-architecture.md) with independent AON, LP, HP, PCLK, memory, audio, pixel, JTAG, and ULPI clock/reset contracts, @@ -100,9 +100,10 @@ used with commercial 32 KiB SRAM and PLL simulation models. | [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Firmware and behavioral regression with a 32 KiB OpenRAM SRAM; local full-flow commands support synthesis and timing. | | [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. | | [`configs/ci/ihp130-debug.mk`](configs/ci/ihp130-debug.mk) | RV32IM | `debug` | Verilator remote-bitbang acceptance of the Hazard3 JTAG DTM, Debug Module, OpenOCD, and GDB. | -| [`configs/ci/ihp130-hp.mk`](configs/ci/ihp130-hp.mk) | LP RV32IM / HP RV32IMAFDC | `hp_boot` | Linux image/bundle flow and HP RTL validation on the fixed product topology. | -| [`configs/benchmark/ihp130-hazard3-coremark.mk`](configs/benchmark/ihp130-hazard3-coremark.mk) | LP RV32IM / HP RV32IMAFDC+Zicbom | `coremark` | Fixed four-iteration LP SRAM CoreMark measurement with the product HP core present. | -| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | LP RV32IM / HP RV32IMAFDC+Zicbom | `bringup` | Site profile with PLL/SRAM intentionally disabled for regression compatibility. | +| [`configs/ci/ihp130-hp.mk`](configs/ci/ihp130-hp.mk) | LP RV32IM / HP RV64IMAFDC | `hp_boot` | Linux image/bundle flow and HP RTL validation on the fixed product topology. | +| [`configs/ci/ihp130-rtthread.mk`](configs/ci/ihp130-rtthread.mk) | LP RV32IM / HP RV64IMAFDC | `hp_boot` | Locked RT-Thread v5.3.0 M-mode kernel/platform acceptance on HP hart 1. | +| [`configs/benchmark/ihp130-hazard3-coremark.mk`](configs/benchmark/ihp130-hazard3-coremark.mk) | LP RV32IM / HP RV64IMAFDC+Zicbom | `coremark` | Fixed four-iteration LP SRAM CoreMark measurement with the product HP core present. | +| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | LP RV32IM / HP RV64IMAFDC+Zicbom | `bringup` | Site profile with PLL/SRAM intentionally disabled for regression compatibility. | | [`configs/cluster/mini-mpw.mk`](configs/cluster/mini-mpw.mk) | RV32IM | `bringup` | Legacy MPW C0-C3/user-IP selection profile; not part of the Mini product ABI. | CI Verilator firmware simulations explicitly select the `ci_smoke` diff --git a/app/README.md b/app/README.md index 0aa52c26..3121d2a7 100644 --- a/app/README.md +++ b/app/README.md @@ -32,7 +32,7 @@ The build selects an application with `APP=`. The supported profiles are: | `ci_smoke` | Fast deterministic CI smoke test for UART, ARCHINFO V2, LP external IRQ dispatch when CSR-enabled, RNG, crypto AES/SHA, SDRAM mapped access, and test-status completion. | | `coremark` | SRAM-resident Hazard3 CoreMark measurement; use the committed quick or standard profile. | | `debug` | Minimal SRAM image used only by the Hazard3 OpenOCD/GDB acceptance flow. | -| `hp_boot` | SRAM-resident LP loader that validates the HP Linux flash bundle, loads SDRAM, releases HP, and monitors the mailbox verdict. | +| `hp_boot` | SRAM-resident LP loader that validates typed V2 Linux, smoke or RT-Thread bundles, loads SDRAM, releases HP, and monitors the workload-specific mailbox verdict. | | `shell` | Interactive application that adds shell services, board drivers, media, FatFs, CoreMark, and UserIP integration. | | `xpi_flash_loader` | SRAM-resident, GDB-called service image for sector-preserving JTAG programming of the qualified NSS0 NOR. | @@ -66,6 +66,10 @@ make CONFIG=configs/ci/ihp130-debug.mk SIMU=VERILATOR debug-sim make setup-hp-linux make CONFIG=configs/ci/ihp130-hp.mk hp-bundle +# Build and test the pinned RV64 M-mode RT-Thread payload on HP hart 1 +make setup-hp-rtthread +make CONFIG=configs/ci/ihp130-rtthread.mk SIMU=VERILATOR hp-rtthread-sim + # Build the SRAM-resident XPI NOR loader used by the host JTAG tool make CONFIG=configs/ci/ihp130-xpi-flash-loader.mk firmware ``` diff --git a/app/apps/hp_boot/app.mk b/app/apps/hp_boot/app.mk index 6d35b0e3..fee763c8 100644 --- a/app/apps/hp_boot/app.mk +++ b/app/apps/hp_boot/app.mk @@ -1,2 +1,3 @@ APP_SRCS += $(ROOT_PATH)/app/apps/hp_boot/main.c -APP_INC_DIRS += $(ROOT_PATH)/app/apps/hp_boot \ No newline at end of file +APP_SRCS += $(ROOT_PATH)/app/apps/hp_boot/hp_boot_bundle.c +APP_INC_DIRS += $(ROOT_PATH)/app/apps/hp_boot diff --git a/app/apps/hp_boot/hp_boot_bundle.c b/app/apps/hp_boot/hp_boot_bundle.c new file mode 100644 index 00000000..b2656cba --- /dev/null +++ b/app/apps/hp_boot/hp_boot_bundle.c @@ -0,0 +1,79 @@ +#include "hp_boot_bundle.h" + +#include + +uint32_t rs_hp_boot_message_status(uint32_t code, uint32_t argument, uint32_t sequence, + uint32_t expected_code, uint32_t expected_argument, + uint32_t expected_sequence) { + if ((sequence > expected_sequence) || ((sequence != 0U) && (code == 3U))) { + return 2U; + } + if (sequence == expected_sequence) { + return ((code == expected_code) && (argument == expected_argument)) ? 1U : 2U; + } + return 0U; +} + +uint32_t rs_hp_boot_crc32_byte(uint32_t crc, uint8_t value) { + crc ^= value; + for (uint32_t bit = 0U; bit < 8U; ++bit) { + uint32_t mask = UINT32_C(0) - (crc & UINT32_C(1)); + crc = (crc >> 1U) ^ (UINT32_C(0xEDB88320) & mask); + } + return crc; +} + +uint32_t rs_hp_boot_crc32(const uint8_t *data, uint32_t size) { + uint32_t crc = UINT32_C(0xFFFFFFFF); + for (uint32_t index = 0U; index < size; ++index) { + crc = rs_hp_boot_crc32_byte(crc, data[index]); + } + return ~crc; +} + +bool rs_hp_boot_header_valid(const rs_hp_boot_header_t *header) { + static const uint32_t addresses[4] = {RS_HP_BOOT_OPENSBI_ADDRESS, RS_HP_BOOT_DTB_ADDRESS, + RS_HP_BOOT_LINUX_ADDRESS, RS_HP_BOOT_INITRAMFS_ADDRESS}; + static const uint32_t maxima[4] = {RS_HP_BOOT_OPENSBI_MAX_SIZE, RS_HP_BOOT_DTB_MAX_SIZE, + RS_HP_BOOT_LINUX_MAX_SIZE, RS_HP_BOOT_INITRAMFS_MAX_SIZE}; + rs_hp_boot_header_t copy; + uint32_t previous_end = RS_HP_BOOT_BUNDLE_OFFSET + (uint32_t)sizeof(copy); + uint32_t count; + if (header == NULL) { + return false; + } + copy = *header; + copy.header_crc32 = 0U; + count = (header->workload == RS_HP_BOOT_WORKLOAD_LINUX) ? 4U : 1U; + if ((header->magic != RS_HP_BOOT_BUNDLE_MAGIC) || + (header->version != RS_HP_BOOT_BUNDLE_VERSION) || (header->header_size != sizeof(copy)) || + (header->entry_count != count) || (header->workload < RS_HP_BOOT_WORKLOAD_LINUX) || + (header->workload > RS_HP_BOOT_WORKLOAD_RTTHREAD) || + (header->flags != RS_HP_BOOT_BUNDLE_REQUIRED) || (header->total_size < sizeof(copy)) || + (header->total_size > UINT32_C(0x00F00000)) || + (rs_hp_boot_crc32((const uint8_t *)©, sizeof(copy)) != header->header_crc32)) { + return false; + } + for (uint32_t index = 0U; index < RS_HP_BOOT_BUNDLE_ENTRY_COUNT; ++index) { + const rs_hp_boot_entry_t *entry = &header->entries[index]; + if (index >= count) { + if ((entry->type | entry->flash_offset | entry->load_address | entry->size | + entry->crc32 | entry->flags) != 0U) { + return false; + } + } else { + uint32_t type = (count == 4U) ? index + 1U : RS_HP_BOOT_TYPE_EXECUTABLE; + uint32_t bundle_end = RS_HP_BOOT_BUNDLE_OFFSET + header->total_size; + if ((entry->type != type) || (entry->load_address != addresses[index]) || + (entry->size == 0U) || (entry->size > maxima[index]) || + (entry->flags != RS_HP_BOOT_BUNDLE_REQUIRED) || + ((entry->flash_offset & UINT32_C(0xFFF)) != 0U) || + (entry->flash_offset < previous_end) || (entry->flash_offset >= bundle_end) || + (entry->size > (bundle_end - entry->flash_offset))) { + return false; + } + previous_end = entry->flash_offset + entry->size; + } + } + return true; +} diff --git a/app/apps/hp_boot/hp_boot_bundle.h b/app/apps/hp_boot/hp_boot_bundle.h index da976823..4e92500f 100644 --- a/app/apps/hp_boot/hp_boot_bundle.h +++ b/app/apps/hp_boot/hp_boot_bundle.h @@ -2,10 +2,11 @@ #define RETROSOC_HP_BOOT_BUNDLE_H #include +#include #define RS_HP_BOOT_BUNDLE_OFFSET UINT32_C(0x00100000) #define RS_HP_BOOT_BUNDLE_MAGIC UINT32_C(0x50485352) -#define RS_HP_BOOT_BUNDLE_VERSION UINT32_C(1) +#define RS_HP_BOOT_BUNDLE_VERSION UINT32_C(2) #define RS_HP_BOOT_BUNDLE_ENTRY_COUNT UINT32_C(4) #define RS_HP_BOOT_BUNDLE_REQUIRED UINT32_C(1) @@ -13,6 +14,13 @@ #define RS_HP_BOOT_TYPE_DTB UINT32_C(2) #define RS_HP_BOOT_TYPE_LINUX UINT32_C(3) #define RS_HP_BOOT_TYPE_INITRAMFS UINT32_C(4) +#define RS_HP_BOOT_TYPE_EXECUTABLE UINT32_C(5) +#define RS_HP_BOOT_WORKLOAD_LINUX UINT32_C(1) +#define RS_HP_BOOT_WORKLOAD_SMOKE UINT32_C(2) +#define RS_HP_BOOT_WORKLOAD_RTTHREAD UINT32_C(3) +#define RS_HP_RTTHREAD_READY_ARG UINT32_C(0x52545401) +#define RS_HP_RTTHREAD_IRQ_COMMAND UINT32_C(0x52545402) +#define RS_HP_RTTHREAD_IRQ_ARG UINT32_C(0x12345678) #define RS_HP_BOOT_OPENSBI_ADDRESS UINT32_C(0x38000000) #define RS_HP_BOOT_OPENSBI_MAX_SIZE UINT32_C(0x00080000) @@ -40,8 +48,16 @@ typedef struct { uint32_t total_size; uint32_t header_crc32; uint32_t flags; - uint32_t reserved; + uint32_t workload; rs_hp_boot_entry_t entries[RS_HP_BOOT_BUNDLE_ENTRY_COUNT]; } rs_hp_boot_header_t; +uint32_t rs_hp_boot_crc32_byte(uint32_t crc, uint8_t value); +uint32_t rs_hp_boot_crc32(const uint8_t *data, uint32_t size); +bool rs_hp_boot_header_valid(const rs_hp_boot_header_t *header); +/* 0: wait for publication, 1: expected message, 2: invalid/failure message. */ +uint32_t rs_hp_boot_message_status(uint32_t code, uint32_t argument, uint32_t sequence, + uint32_t expected_code, uint32_t expected_argument, + uint32_t expected_sequence); + #endif diff --git a/app/apps/hp_boot/main.c b/app/apps/hp_boot/main.c index 3d535051..cf32cead 100644 --- a/app/apps/hp_boot/main.c +++ b/app/apps/hp_boot/main.c @@ -38,30 +38,6 @@ static bool s_hp_boot_ga2d_owned_by_hp; _Static_assert(sizeof(rs_hp_boot_header_t) == RS_HP_BOOT_HEADER_SIZE, "HP boot bundle header ABI mismatch"); -static uint32_t rs_hp_boot_crc32_byte(uint32_t crc, uint8_t value) { - crc ^= value; - for (uint32_t bit = 0U; bit < 8U; ++bit) { - uint32_t mask = UINT32_C(0) - (crc & UINT32_C(1)); - crc = (crc >> 1U) ^ (UINT32_C(0xEDB88320) & mask); - } - return crc; -} - -static uint32_t rs_hp_boot_crc32(const uint8_t *data, uint32_t size) { - uint32_t crc = UINT32_C(0xFFFFFFFF); - - for (uint32_t index = 0U; index < size; ++index) { - crc = rs_hp_boot_crc32_byte(crc, data[index]); - } - return ~crc; -} - -static bool rs_hp_boot_range_valid(uint32_t address, uint32_t size, uint32_t base, - uint32_t capacity) { - return (size != 0U) && (address >= base) && (address < (base + capacity)) && - ((size - 1U) <= ((base + capacity - 1U) - address)); -} - static void rs_hp_boot_read_header(rs_hp_boot_header_t *header) { volatile const uint8_t *source = (volatile const uint8_t *)(uintptr_t)(RS_SOC_FLASH_BASE + RS_HP_BOOT_BUNDLE_OFFSET); @@ -72,60 +48,6 @@ static void rs_hp_boot_read_header(rs_hp_boot_header_t *header) { } } -static bool rs_hp_boot_entry_expected(const rs_hp_boot_entry_t *entry, uint32_t index, - uint32_t bundle_size) { - static const uint32_t types[RS_HP_BOOT_BUNDLE_ENTRY_COUNT] = { - RS_HP_BOOT_TYPE_OPENSBI, - RS_HP_BOOT_TYPE_DTB, - RS_HP_BOOT_TYPE_LINUX, - RS_HP_BOOT_TYPE_INITRAMFS, - }; - static const uint32_t addresses[RS_HP_BOOT_BUNDLE_ENTRY_COUNT] = { - RS_HP_BOOT_OPENSBI_ADDRESS, - RS_HP_BOOT_DTB_ADDRESS, - RS_HP_BOOT_LINUX_ADDRESS, - RS_HP_BOOT_INITRAMFS_ADDRESS, - }; - static const uint32_t maximum_sizes[RS_HP_BOOT_BUNDLE_ENTRY_COUNT] = { - RS_HP_BOOT_OPENSBI_MAX_SIZE, - RS_HP_BOOT_DTB_MAX_SIZE, - RS_HP_BOOT_LINUX_MAX_SIZE, - RS_HP_BOOT_INITRAMFS_MAX_SIZE, - }; - - return (entry->type == types[index]) && (entry->load_address == addresses[index]) && - (entry->size <= maximum_sizes[index]) && - ((entry->flags & RS_HP_BOOT_BUNDLE_REQUIRED) != 0U) && - ((entry->flash_offset & UINT32_C(3)) == 0U) && - ((entry->load_address & UINT32_C(3)) == 0U) && - rs_hp_boot_range_valid(entry->flash_offset, entry->size, RS_HP_BOOT_BUNDLE_OFFSET, - bundle_size) && - rs_hp_boot_range_valid(entry->load_address, entry->size, RS_SOC_SDRAM_BASE, - RS_SOC_SDRAM_SIZE); -} - -static bool rs_hp_boot_header_valid(rs_hp_boot_header_t *header) { - uint32_t expected_crc = header->header_crc32; - - header->header_crc32 = 0U; - if ((header->magic != RS_HP_BOOT_BUNDLE_MAGIC) || - (header->version != RS_HP_BOOT_BUNDLE_VERSION) || - (header->header_size != sizeof(*header)) || - (header->entry_count != RS_HP_BOOT_BUNDLE_ENTRY_COUNT) || - ((header->flags & RS_HP_BOOT_BUNDLE_REQUIRED) == 0U) || - (header->total_size < sizeof(*header)) || - (header->total_size > (RS_SOC_FLASH_SIZE - RS_HP_BOOT_BUNDLE_OFFSET)) || - (rs_hp_boot_crc32((const uint8_t *)header, sizeof(*header)) != expected_crc)) { - return false; - } - for (uint32_t index = 0U; index < RS_HP_BOOT_BUNDLE_ENTRY_COUNT; ++index) { - if (!rs_hp_boot_entry_expected(&header->entries[index], index, header->total_size)) { - return false; - } - } - return true; -} - static bool rs_hp_boot_copy_entry(const rs_hp_boot_entry_t *entry) { volatile const uint32_t *source_words = (volatile const uint32_t *)(uintptr_t)(RS_SOC_FLASH_BASE + entry->flash_offset); @@ -234,11 +156,18 @@ static bool rs_hp_boot_wait_message(uint32_t code, uint32_t argument, uint32_t s rs_hp_mailbox_message_t message; for (uint32_t timeout = 0U; timeout < RS_HP_BOOT_EVENT_TIMEOUT; ++timeout) { + uint32_t status; if (rs_hp_mailbox_receive_from_hp(&message) != RS_OK) { return false; } - if (message.sequence == sequence) { - return (message.code == code) && (message.argument == argument); + status = rs_hp_boot_message_status(message.code, message.argument, message.sequence, code, + argument, sequence); + if (status == 2U) { + printf("HP_PAYLOAD_FAILED:%u\n", (unsigned int)message.argument); + return false; + } + if (status == 1U) { + return true; } } return false; @@ -303,7 +232,7 @@ int main(void) { if (!rs_hp_boot_header_valid(&header)) { rs_hp_boot_fail(UINT8_C(4)); } - for (uint32_t index = 0U; index < RS_HP_BOOT_BUNDLE_ENTRY_COUNT; ++index) { + for (uint32_t index = 0U; index < header.entry_count; ++index) { printf("HP_BOOT_LOAD:%u:%u\n", (unsigned int)header.entries[index].type, (unsigned int)header.entries[index].size); if (!rs_hp_boot_dma_copy_entry(&header.entries[index]) && @@ -312,13 +241,18 @@ int main(void) { } } - if ((rs_hp_mailbox_probe() != RS_OK) || !rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_LP) || - (rs_resource_set_owner(RS_RESOURCE_GA2D, RS_RESOURCE_OWNER_HP, false) != RS_OK)) { + if (rs_hp_mailbox_probe() != RS_OK) { rs_hp_boot_fail(UINT8_C(9)); } - s_hp_boot_ga2d_owned_by_hp = true; - if (!rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_HP)) { - rs_hp_boot_fail(UINT8_C(9)); + if (header.workload == RS_HP_BOOT_WORKLOAD_SMOKE) { + if (!rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_LP) || + (rs_resource_set_owner(RS_RESOURCE_GA2D, RS_RESOURCE_OWNER_HP, false) != RS_OK)) { + rs_hp_boot_fail(UINT8_C(9)); + } + s_hp_boot_ga2d_owned_by_hp = true; + if (!rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_HP)) { + rs_hp_boot_fail(UINT8_C(9)); + } } __asm__ volatile("fence rw, rw" ::: "memory"); if ((rs_hp_mailbox_clear_lp_interrupt() != RS_OK) || @@ -328,11 +262,30 @@ int main(void) { } printf("HP_BOOT_RELEASED\n"); + if (header.workload == RS_HP_BOOT_WORKLOAD_RTTHREAD) { + if (!rs_hp_boot_wait_message(RS_HP_BOOT_READY_EVENT, RS_HP_RTTHREAD_READY_ARG, 1U)) { + rs_hp_boot_fail(UINT8_C(18)); + } + printf("HP_RTTHREAD_READY\n"); + message.code = RS_HP_RTTHREAD_IRQ_COMMAND; + message.argument = RS_HP_RTTHREAD_IRQ_ARG; + message.sequence = 1U; + if ((rs_hp_mailbox_send_to_hp(&message) != RS_OK) || + !rs_hp_boot_wait_message(2U, RS_HP_RTTHREAD_READY_ARG, 2U)) { + rs_hp_boot_fail(UINT8_C(19)); + } + printf("HP_RTTHREAD_PASS\n"); + rs_test_finish(RS_TEST_PASSED, UINT8_C(0)); + } + if (!rs_hp_boot_wait_message(RS_HP_BOOT_READY_EVENT, RS_HP_BOOT_READY_ARG, RS_HP_BOOT_MAILBOX_READY_SEQUENCE)) { rs_hp_boot_fail(UINT8_C(11)); } printf("HP_LINUX_READY\n"); + if (header.workload == RS_HP_BOOT_WORKLOAD_LINUX) { + rs_test_finish(RS_TEST_PASSED, UINT8_C(0)); + } message.code = RS_HP_BOOT_GA2D_START_COMMAND; message.argument = RS_HP_BOOT_GA2D_START_ARG; message.sequence = RS_HP_BOOT_MAILBOX_READY_SEQUENCE; diff --git a/app/ports/linux/README.md b/app/ports/linux/README.md index b973d47e..777a6a47 100644 --- a/app/ports/linux/README.md +++ b/app/ports/linux/README.md @@ -1,7 +1,7 @@ # HP Linux Port This directory owns the Buildroot external tree, device tree, kernel config, -and integration metadata for the fixed RV32 VexiiRiscv HP core. Linux, +and integration metadata for the fixed RV64 VexiiRiscv HP core. Linux, OpenSBI, Buildroot, toolchains, and all generated images remain below `.cache/` or `build/` and are not tracked. @@ -19,3 +19,25 @@ Run `make setup-hp-linux` to install the locked sources, then use the console uses SBI; the UART1 and mailbox nodes reserve their stable platform ABI for the native drivers. +The RV64/Sv39 acceptance image uses musl and static BusyBox with a dedicated +`/init`. It mounts procfs, sysfs, devtmpfs and tmpfs, verifies file I/O and child +execution, then publishes the Linux-ready mailbox message. The kernel effective +configuration and all executable ELF classes are checked during the build. +`rootfs.cpio` is a small uncompressed archive; the DTB uses its actual length. +The DT reserves the resident 512 KiB OpenSBI window with `no-map`; Linux must +not allocate it. The custom platform does not apply the generic OpenSBI FDT fixups. +It does not start the full Buildroot service sequence. + +```sh +make CONFIG=configs/ci/ihp130-hp.mk hp-linux +make CONFIG=configs/ci/ihp130-hp.mk SIMU=VERILATOR hp-linux-sim +``` + +The V2 bundle uses workload 1 and four typed payloads. V1 images must be rebuilt. +Linux success goes directly to LP TEST_STATUS; GA2D/cache lifecycle is tested +by `hp-smoke-sim`. Logs and verdicts live in `sim/verilator/hp-linux/` below +the selected variant. `HP_LINUX_SIM_TIME=0` means no emulator wall-clock timeout; +successful TEST_STATUS ends the run. Interrupted/running results are not passes. + +See [HP platform](../../../docs/ip/hp-platform.md) for the ABI and +[RT-Thread BSP](../rtthread/README.md) for the separate M-mode workload. diff --git a/app/ports/linux/busybox/retrosoc_hp.config b/app/ports/linux/busybox/retrosoc_hp.config index 174a3f20..0ef0e76e 100644 --- a/app/ports/linux/busybox/retrosoc_hp.config +++ b/app/ports/linux/busybox/retrosoc_hp.config @@ -1 +1,16 @@ -CONFIG_DEVMEM=y +CONFIG_STATIC=y +CONFIG_BUSYBOX=y +CONFIG_FEATURE_PREFER_APPLETS=y +CONFIG_ASH=y +CONFIG_SH_IS_ASH=y +CONFIG_ASH_ECHO=y +CONFIG_ASH_TEST=y +CONFIG_ECHO=y +CONFIG_TRUE=y +CONFIG_FALSE=y +CONFIG_TEST=y +CONFIG_MOUNT=y +CONFIG_FEATURE_MOUNT_FLAGS=y +CONFIG_MKDIR=y +CONFIG_CAT=y +CONFIG_SLEEP=y diff --git a/app/ports/linux/configs/retrosoc_hp_defconfig b/app/ports/linux/configs/retrosoc_hp_defconfig index 34c96792..b667bf7c 100644 --- a/app/ports/linux/configs/retrosoc_hp_defconfig +++ b/app/ports/linux/configs/retrosoc_hp_defconfig @@ -1,18 +1,15 @@ BR2_riscv=y -BR2_RISCV_32=y +BR2_RISCV_64=y BR2_RISCV_ISA_RVM=y BR2_RISCV_ISA_RVA=y BR2_RISCV_ISA_RVF=y BR2_RISCV_ISA_RVD=y BR2_RISCV_ISA_RVC=y -BR2_RISCV_ABI_ILP32D=y -BR2_TOOLCHAIN_BUILDROOT_GLIBC=y +BR2_RISCV_ABI_LP64D=y +BR2_TOOLCHAIN_BUILDROOT_MUSL=y +BR2_STATIC_LIBS=y BR2_TARGET_GENERIC_HOSTNAME="retrosoc-hp" BR2_TARGET_GENERIC_ISSUE="retroSoC HP Linux" -BR2_INIT_BUSYBOX=y -BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="$(BR2_EXTERNAL_RETROSOC_HP_PATH)/busybox/retrosoc_hp.config" -BR2_TARGET_GENERIC_GETTY=y -BR2_TARGET_GENERIC_GETTY_PORT="hvc0" +BR2_INIT_NONE=y +BR2_PACKAGE_BUSYBOX_CONFIG="$(BR2_EXTERNAL_RETROSOC_HP_PATH)/busybox/retrosoc_hp.config" BR2_TARGET_ROOTFS_CPIO=y -BR2_TARGET_ROOTFS_CPIO_GZIP=y -BR2_ROOTFS_OVERLAY="$(BR2_EXTERNAL_RETROSOC_HP_PATH)/rootfs-overlay" diff --git a/app/ports/linux/hp_ready.c b/app/ports/linux/hp_ready.c new file mode 100644 index 00000000..f48dca42 --- /dev/null +++ b/app/ports/linux/hp_ready.c @@ -0,0 +1,49 @@ +/* Linux-only acceptance helper; the freestanding SDK does not depend on libc. */ +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + uint32_t error = 0U; + int descriptor; + void *mapping; + volatile uint32_t *mailbox; + if (argc == 2) { + if ((argv[1][0] == '1') && (argv[1][1] >= '1') && (argv[1][1] <= '7') && + (argv[1][2] == '\0')) { + error = 10U + (uint32_t)(argv[1][1] - '0'); + } else { + error = 20U; + } + } else if (argc != 1) { + error = 21U; + } + descriptor = open("/dev/mem", O_RDWR | O_SYNC); + if (descriptor < 0) { + (void)puts("HP_LINUX_INIT_FAILED devmem"); + return 1; + } + mapping = mmap(NULL, 4096U, PROT_READ | PROT_WRITE, MAP_SHARED, descriptor, 0x10019000); + if (mapping == MAP_FAILED) { + (void)puts("HP_LINUX_INIT_FAILED mmap"); + (void)close(descriptor); + return 1; + } + mailbox = (volatile uint32_t *)mapping; + if (error == 0U) { + (void)puts("retroSoC HP Linux ready"); + } else { + (void)printf("HP_LINUX_INIT_FAILED code=%u\n", error); + } + (void)fflush(stdout); + mailbox[8] = (error == 0U) ? 1U : 3U; + mailbox[9] = (error == 0U) ? UINT32_C(0x4C4E5801) : error; + mailbox[10] = 1U; + __asm__ volatile("fence iorw, iorw" ::: "memory"); + mailbox[11] = 1U; + for (;;) { + (void)pause(); + } +} diff --git a/app/ports/linux/init b/app/ports/linux/init new file mode 100644 index 00000000..9d642b2a --- /dev/null +++ b/app/ports/linux/init @@ -0,0 +1,15 @@ +#!/bin/sh +# This bounded acceptance image deliberately has no service manager or getty. +export PATH=/bin +mount -t proc proc /proc || exec /bin/hp-ready 11 +mount -t sysfs sysfs /sys || exec /bin/hp-ready 12 +mount -t devtmpfs devtmpfs /dev || exec /bin/hp-ready 13 +mount -t tmpfs tmpfs /tmp || exec /bin/hp-ready 14 +test -c /dev/mem || exec /bin/hp-ready 15 +test -r /proc/meminfo || exec /bin/hp-ready 16 +test -d /sys/devices || exec /bin/hp-ready 17 +echo rv64-initramfs > /tmp/probe || exec /bin/hp-ready 17 +test "$(cat /tmp/probe)" = rv64-initramfs || exec /bin/hp-ready 17 +/bin/busybox true || exec /bin/hp-ready 17 +echo "HP_LINUX_INIT_PASS" +exec /bin/hp-ready diff --git a/app/ports/linux/linux/retrosoc_hp.config b/app/ports/linux/linux/retrosoc_hp.config index e8549004..12f545ce 100644 --- a/app/ports/linux/linux/retrosoc_hp.config +++ b/app/ports/linux/linux/retrosoc_hp.config @@ -1,4 +1,7 @@ -CONFIG_ARCH_RV32I=y +CONFIG_ARCH_RV64I=y +CONFIG_64BIT=y +CONFIG_MMU=y +CONFIG_OF_RESERVED_MEM=y CONFIG_NONPORTABLE=y CONFIG_RISCV_ISA_M=y CONFIG_RISCV_ISA_A=y @@ -13,9 +16,12 @@ CONFIG_HVC_RISCV_SBI=y CONFIG_SERIAL_EARLYCON_RISCV_SBI=y CONFIG_SIFIVE_PLIC=y CONFIG_BLK_DEV_INITRD=y -CONFIG_RD_GZIP=y +CONFIG_RD_GZIP=n CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_DEVMEM=y +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +CONFIG_SHMEM=y CONFIG_TMPFS=y CONFIG_PRINTK_TIME=y diff --git a/app/ports/linux/linux/retrosoc_hp.dts b/app/ports/linux/linux/retrosoc_hp.dts index e4e888b4..77896549 100644 --- a/app/ports/linux/linux/retrosoc_hp.dts +++ b/app/ports/linux/linux/retrosoc_hp.dts @@ -6,7 +6,7 @@ #size-cells = <1>; chosen { - bootargs = "console=hvc0 earlycon=sbi rdinit=/sbin/init"; + bootargs = "console=hvc0 earlycon=sbi rdinit=/init"; linux,initrd-start = <0x39000000>; /* build_hp_linux.py replaces this with the actual rootfs end. */ linux,initrd-end = <0x39000000>; @@ -22,8 +22,8 @@ device_type = "cpu"; reg = <1>; clock-frequency = <72000000>; - mmu-type = "riscv,sv32"; - riscv,isa-base = "rv32i"; + mmu-type = "riscv,sv39"; + riscv,isa-base = "rv64i"; riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicbom", "zicsr", "zifencei"; riscv,cbom-block-size = <64>; @@ -42,6 +42,18 @@ reg = <0x38000000 0x04000000>; }; + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + /* OpenSBI remains resident for every supervisor SBI call. */ + opensbi@38000000 { + reg = <0x38000000 0x00080000>; + no-map; + }; + }; + soc { compatible = "simple-bus"; #address-cells = <1>; diff --git a/app/ports/linux/opensbi/retrosoc_hp/platform.c b/app/ports/linux/opensbi/retrosoc_hp/platform.c index 3c57d349..88ee80fc 100644 --- a/app/ports/linux/opensbi/retrosoc_hp/platform.c +++ b/app/ports/linux/opensbi/retrosoc_hp/platform.c @@ -104,7 +104,7 @@ static const struct sbi_platform_operations s_platform_operations = { const struct sbi_platform platform = { .opensbi_version = OPENSBI_VERSION, .platform_version = SBI_PLATFORM_VERSION(1U, 0U), - .name = "retroSoC RV32 HP", + .name = "retroSoC RV64 HP", .features = SBI_PLATFORM_DEFAULT_FEATURES, .hart_count = 1U, .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, diff --git a/app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp b/app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp deleted file mode 100755 index d8492fdd..00000000 --- a/app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -case "$1" in - start) - echo "retroSoC HP Linux ready" - devmem 0x10019020 32 0x00000001 - devmem 0x10019024 32 0x4C4E5801 - devmem 0x10019028 32 0x00000001 - devmem 0x1001902C 32 0x00000001 - ;; -esac diff --git a/app/ports/linux/smoke/start.S b/app/ports/linux/smoke/start.S index 6719228a..2900e66f 100644 --- a/app/ports/linux/smoke/start.S +++ b/app/ports/linux/smoke/start.S @@ -78,7 +78,7 @@ write_character: lbu a1, 0(a0) beqz a1, publish_ready wait_tx_space: - lw a2, UART_STATUS(t0) + lwu a2, UART_STATUS(t0) andi a2, a2, UART_TX_FULL bnez a2, wait_tx_space sw a1, UART_TXDATA(t0) @@ -100,13 +100,13 @@ wait_start_command: li s1, GA2D_WAIT_POLLS wait_start_command_loop: li t0, MAILBOX_BASE - lw t1, LP_SEQUENCE(t0) + lwu t1, LP_SEQUENCE(t0) li t2, 1 bne t1, t2, wait_start_command_next - lw t1, LP_COMMAND(t0) + lwu t1, LP_COMMAND(t0) li t2, HP_GA2D_START bne t1, t2, wait_start_command_next - lw t1, LP_ARG0(t0) + lwu t1, LP_ARG0(t0) li t2, HP_GA2D_ARG beq t1, t2, start_ga2d wait_start_command_next: @@ -149,7 +149,7 @@ wait_cache_request: li s1, GA2D_WAIT_POLLS wait_cache_request_loop: li t0, RESOURCE_CTRL_BASE - lw t1, RESOURCE_CACHE_CONTROL(t0) + lwu t1, RESOURCE_CACHE_CONTROL(t0) andi t1, t1, 2 bnez t1, clean_cache_request addi s1, s1, -1 @@ -327,7 +327,7 @@ ga2d_copy: ga2d_wait: li t1, GA2D_WAIT_POLLS ga2d_wait_loop: - lw t2, GA2D_STATUS(t0) + lwu t2, GA2D_STATUS(t0) andi t3, t2, 0x60 bnez t3, ga2d_wait_failed andi t3, t2, 0x10 @@ -348,16 +348,16 @@ check_fill: li t1, 0xff123456 li t2, 8 check_fill_pixels: - lw t3, 0(t0) + lwu t3, 0(t0) bne t3, t1, check_failed addi t0, t0, 4 addi t2, t2, -1 bnez t2, check_fill_pixels - lw t3, 0(t0) + lwu t3, 0(t0) li t1, 0xc7c7c7c7 bne t3, t1, check_failed la t0, hp_destination_guard - lw t3, 0(t0) + lwu t3, 0(t0) li t1, 0xd3d3d3d3 bne t3, t1, check_failed li a0, 1 @@ -368,22 +368,22 @@ check_copy: la t1, hp_destination_line li t2, 8 check_copy_pixels: - lw t3, 0(t0) - lw t4, 0(t1) + lwu t3, 0(t0) + lwu t4, 0(t1) bne t3, t4, check_failed addi t0, t0, 4 addi t1, t1, 4 addi t2, t2, -1 bnez t2, check_copy_pixels - lw t3, 0(t1) + lwu t3, 0(t1) li t4, 0xc7c7c7c7 bne t3, t4, check_failed la t0, hp_source_guard - lw t3, 0(t0) + lwu t3, 0(t0) li t4, 0xa5a5a5a5 bne t3, t4, check_failed la t0, hp_destination_guard - lw t3, 0(t0) + lwu t3, 0(t0) li t4, 0xd3d3d3d3 bne t3, t4, check_failed li a0, 1 diff --git a/app/ports/rtthread/README.md b/app/ports/rtthread/README.md new file mode 100644 index 00000000..be405dad --- /dev/null +++ b/app/ports/rtthread/README.md @@ -0,0 +1,18 @@ +# HP RT-Thread BSP + +This directory owns the single-hart RV64 M-mode BSP and deterministic kernel +acceptance application. Upstream RT-Thread is installed at the revision in the +dependency lock under `.cache/`; it is not edited or copied into project source. +SCons runs on a staged BSP below `build//hp-rtthread/`. + +Run `make setup-hp-rtthread`, then `make CONFIG=configs/ci/ihp130-rtthread.mk +SIMU=VERILATOR hp-rtthread-sim`. The test uses UART1, hart-1 CLINT and PLIC +machine context 0/source 2. Peripheral accesses remain 32 bits. Static threads +exercise RV64 integer state, timer preemption, semaphore/message queue behavior, +timeouts, and an LP-to-HP mailbox interrupt. LP validates the final mailbox +response and owns SYSCTRL TEST_STATUS. A banner alone is not a pass. + +The authoritative contract is [HP platform](../../../docs/ip/hp-platform.md). +RT-Smart, networking, filesystems, floating-point task qualification, and PPA +are outside this test. Self-owned C follows the repository C policy; the locked +upstream OS is third-party material. No operating-system dependency enters crt. diff --git a/app/ports/rtthread/SConscript b/app/ports/rtthread/SConscript new file mode 100644 index 00000000..b261777a --- /dev/null +++ b/app/ports/rtthread/SConscript @@ -0,0 +1,8 @@ +from building import DefineGroup, GetCurrentDir + +group = DefineGroup('RetroSoC', ['board.c', 'selftest.c', 'start.S'], depend=[''], + CPPPATH=[GetCurrentDir()], LOCAL_CFLAGS=' -Wextra -Werror') +# PrepareBuilding extends the returned list with kernel objects. Keep it separate +# from DefineGroup's per-group source list used for LOCAL_CFLAGS. +objects = list(group) +Return('objects') diff --git a/app/ports/rtthread/SConstruct b/app/ports/rtthread/SConstruct new file mode 100644 index 00000000..6c1ff77f --- /dev/null +++ b/app/ports/rtthread/SConstruct @@ -0,0 +1,20 @@ +import os +import sys +import rtconfig + +RTT_ROOT = os.environ['RTT_ROOT'] +sys.path.insert(0, os.path.join(RTT_ROOT, 'tools')) +from building import PrepareBuilding, DoBuilding + +DefaultEnvironment(tools=[]) +env = Environment(tools=['gcc', 'g++', 'gnulink', 'ar', 'gas'], + CC=rtconfig.CC, CXX=rtconfig.CXX, AS=rtconfig.AS, AR=rtconfig.AR, + LINK=rtconfig.LINK, CFLAGS=rtconfig.CFLAGS, ASFLAGS=rtconfig.AFLAGS, + LINKFLAGS=rtconfig.LFLAGS, LIBS=['gcc']) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) +env.Append(CPPPATH=[Dir('.').abspath, + os.path.join(os.environ['RETROSOC_ROOT'], 'app/apps/hp_boot')]) +env['ASCOM'] = env['ASPPCOM'] +Export('RTT_ROOT', 'rtconfig') +objects = PrepareBuilding(env, RTT_ROOT) +DoBuilding('rtthread.elf', objects) diff --git a/app/ports/rtthread/board.c b/app/ports/rtthread/board.c new file mode 100644 index 00000000..5f9f54ce --- /dev/null +++ b/app/ports/rtthread/board.c @@ -0,0 +1,185 @@ +#include "board.h" +#include "hp_boot_bundle.h" + +#include + +#define RS_RTT_UART UINT32_C(0x10018000) +#define RS_RTT_CLINT UINT32_C(0x02000000) +#define RS_RTT_PLIC UINT32_C(0x0C000000) +#define RS_RTT_MAILBOX UINT32_C(0x10019000) + +static struct rt_semaphore s_mailbox_sem; +static volatile uint32_t s_irq_command; +static volatile uint32_t s_irq_argument; +static volatile uint32_t s_irq_sequence; +static uint64_t s_next_tick; + +static uint32_t rs_read32(uint32_t address) { + return *(volatile const uint32_t *)(uintptr_t)address; +} + +static void rs_write32(uint32_t address, uint32_t value) { + *(volatile uint32_t *)(uintptr_t)address = value; +} + +static uint64_t rs_time(void) { + uint32_t high; + uint32_t low; + do { + high = rs_read32(RS_RTT_CLINT + UINT32_C(0xBFFC)); + low = rs_read32(RS_RTT_CLINT + UINT32_C(0xBFF8)); + } while (high != rs_read32(RS_RTT_CLINT + UINT32_C(0xBFFC))); + return ((uint64_t)high << 32U) | low; +} + +static void rs_set_timer(uint64_t value) { + rs_write32(RS_RTT_CLINT + UINT32_C(0x4008), UINT32_MAX); + rs_write32(RS_RTT_CLINT + UINT32_C(0x400C), (uint32_t)(value >> 32U)); + rs_write32(RS_RTT_CLINT + UINT32_C(0x4008), (uint32_t)value); + __asm__ volatile("fence iorw, iorw" ::: "memory"); +} + +void rt_hw_console_output(const char *text) { + while (*text != '\0') { + uint32_t budget = UINT32_C(1000000); + while ((rs_read32(RS_RTT_UART + UINT32_C(0x18)) & UINT32_C(0x20)) != 0U) { + if (--budget == 0U) { + (void)rt_hw_interrupt_disable(); + rs_rtt_publish(3U, 90U, 2U); + /* Keep the failure publication stable until LP terminates. */ + for (;;) { + __asm__ volatile("wfi"); + } + } + } + rs_write32(RS_RTT_UART + UINT32_C(0x10), (uint8_t)*text); + ++text; + } +} + +void rs_rtt_publish(uint32_t event, uint32_t argument, uint32_t sequence) { + rs_write32(RS_RTT_MAILBOX + UINT32_C(0x20), event); + rs_write32(RS_RTT_MAILBOX + UINT32_C(0x24), argument); + rs_write32(RS_RTT_MAILBOX + UINT32_C(0x28), sequence); + __asm__ volatile("fence iorw, iorw" ::: "memory"); + rs_write32(RS_RTT_MAILBOX + UINT32_C(0x2C), 1U); +} + +void rs_rtt_fail(uint32_t code) { + (void)rt_hw_interrupt_disable(); + rt_kprintf("RTTHREAD_TEST_FAIL code=%u\n", code); + rs_rtt_publish(3U, code, 2U); + for (;;) { + __asm__ volatile("wfi"); + } +} + +static void rs_assert(const char *expression, const char *function, rt_size_t line) { + (void)expression; + (void)function; + (void)line; + rs_rtt_fail(91U); +} + +void rt_trigger_software_interrupt(void) { + rs_write32(RS_RTT_CLINT + UINT32_C(4), 1U); +} + +void rt_hw_do_after_save_above(void) { + rs_write32(RS_RTT_CLINT + UINT32_C(4), 0U); +} + +void handle_trap(rt_ubase_t cause, rt_ubase_t epc, void *frame) { + (void)frame; + if ((cause >> 63U) == 0U) { + rt_kprintf("RTTHREAD_TRAP cause=%lx epc=%lx\n", cause, epc); + rs_rtt_fail(92U); + } + switch (cause & UINT64_C(0x7FFFFFFFFFFFFFFF)) { + case 3U: + rt_hw_do_after_save_above(); + break; + case 7U: + s_next_tick += UINT64_C(1000); + if (s_next_tick <= rs_time()) { + s_next_tick = rs_time() + UINT64_C(1000); + } + rs_set_timer(s_next_tick); + rt_tick_increase(); + break; + case 11U: { + uint32_t claim = rs_read32(RS_RTT_PLIC + UINT32_C(0x200004)); + if (claim != 2U) { + rs_rtt_fail(93U); + } + s_irq_command = rs_read32(RS_RTT_MAILBOX + UINT32_C(0x10)); + s_irq_argument = rs_read32(RS_RTT_MAILBOX + UINT32_C(0x14)); + s_irq_sequence = rs_read32(RS_RTT_MAILBOX + UINT32_C(0x18)); + rs_write32(RS_RTT_MAILBOX + UINT32_C(0x40), 1U); + __asm__ volatile("fence iorw, iorw" ::: "memory"); + rs_write32(RS_RTT_PLIC + UINT32_C(0x200004), claim); + if (rt_sem_release(&s_mailbox_sem) != RT_EOK) { + rs_rtt_fail(94U); + } + break; + } + default: + rs_rtt_fail(95U); + break; + } +} + +rt_err_t rs_rtt_wait_mailbox(rt_int32_t timeout) { + rt_err_t result = rt_sem_take(&s_mailbox_sem, timeout); + if (result != RT_EOK) { + return result; + } + return ((s_irq_command == RS_HP_RTTHREAD_IRQ_COMMAND) && + (s_irq_argument == RS_HP_RTTHREAD_IRQ_ARG) && (s_irq_sequence == 1U)) + ? RT_EOK + : -RT_ERROR; +} + +void rt_hw_board_init(void) { + rs_write32(RS_RTT_UART + UINT32_C(0x0C), 0U); + rs_write32(RS_RTT_UART + UINT32_C(0x20), 3U); + rs_write32(RS_RTT_UART + UINT32_C(0x30), UINT32_C(0x7F)); + rs_write32(RS_RTT_UART + UINT32_C(0x38), 0U); + rs_write32(RS_RTT_UART, 78U); + rs_write32(RS_RTT_UART + UINT32_C(4), 32U); + rs_write32(RS_RTT_UART + UINT32_C(8), 3U); + rs_write32(RS_RTT_UART + UINT32_C(0x0C), 3U); + rs_write32(RS_RTT_CLINT + UINT32_C(4), 0U); + rs_write32(RS_RTT_MAILBOX + UINT32_C(0x40), 1U); + rs_write32(RS_RTT_MAILBOX + UINT32_C(0x44), 1U); + rs_write32(RS_RTT_PLIC + UINT32_C(8), 1U); + rs_write32(RS_RTT_PLIC + UINT32_C(0x2000), 4U); + rs_write32(RS_RTT_PLIC + UINT32_C(0x200000), 0U); + s_next_tick = rs_time() + UINT64_C(1000); + rs_set_timer(s_next_tick); + __asm__ volatile("csrw mie, %0" : : "r"(UINT64_C(0x888)) : "memory"); +} + +void rt_hw_cpu_shutdown(void) { + rs_rtt_fail(96U); +} + +void rs_rtt_start(void) { + uint64_t hart; + _Static_assert(sizeof(rt_ubase_t) == 8U, "RT-Thread must use RV64 contexts"); + _Static_assert(IDLE_THREAD_STACK_SIZE >= 1024, "Idle stack must fit RV64+D frame and calls"); + __asm__ volatile("csrr %0, mhartid" : "=r"(hart)); + rt_assert_set_hook(rs_assert); + rt_system_timer_init(); + rt_system_scheduler_init(); + if ((hart != 1U) || (rt_sem_init(&s_mailbox_sem, "hp_irq", 0U, RT_IPC_FLAG_FIFO) != RT_EOK)) { + rs_rtt_fail(97U); + } + rt_hw_board_init(); + rt_show_version(); + rt_kprintf("HP_RTTHREAD_BOOT hart=1 xlen=64 mode=M\n"); + rs_rtt_selftest_init(); + rt_thread_idle_init(); + rt_system_scheduler_start(); + rs_rtt_fail(98U); +} diff --git a/app/ports/rtthread/board.h b/app/ports/rtthread/board.h new file mode 100644 index 00000000..28056200 --- /dev/null +++ b/app/ports/rtthread/board.h @@ -0,0 +1,20 @@ +#ifndef RETROSOC_RTTHREAD_BOARD_H +#define RETROSOC_RTTHREAD_BOARD_H + +#include +#include + +void rs_rtt_start(void); +void rs_rtt_selftest_init(void); +void rs_rtt_fail(uint32_t code); +void rs_rtt_publish(uint32_t event, uint32_t argument, uint32_t sequence); +uint32_t rs_rtt_context_probe(uint64_t value); +rt_err_t rs_rtt_wait_mailbox(rt_int32_t timeout); +void handle_trap(rt_ubase_t cause, rt_ubase_t epc, void *frame); +void rt_trigger_software_interrupt(void); +void rt_hw_do_after_save_above(void); +void rt_hw_console_output(const char *text); +void rt_hw_board_init(void); +void rt_hw_cpu_shutdown(void); + +#endif diff --git a/app/ports/rtthread/linker.ld b/app/ports/rtthread/linker.ld new file mode 100644 index 00000000..193f6a36 --- /dev/null +++ b/app/ports/rtthread/linker.ld @@ -0,0 +1,16 @@ +OUTPUT_ARCH(riscv) +ENTRY(_start) +MEMORY { RAM (rwx) : ORIGIN = 0x38000000, LENGTH = 512K } +PHDRS { text PT_LOAD FLAGS(5); data PT_LOAD FLAGS(6); } +SECTIONS { + .text : { KEEP(*(.text.start)) *(.text*) *(.rodata*) } > RAM :text + .data : { . = ALIGN(16); __global_pointer$ = . + 0x800; *(.sdata*) *(.data*) } > RAM :data + .bss (NOLOAD) : { + . = ALIGN(16); __bss_start = .; + *(.sbss*) *(.bss*) *(COMMON) + . = ALIGN(16); __bss_end = .; + } > RAM + .stack (NOLOAD) : { . = ALIGN(16); . += 8192; __rt_rvstack = .; } > RAM + /DISCARD/ : { *(.eh_frame*) *(.comment*) } + ASSERT(. <= ORIGIN(RAM) + LENGTH(RAM), "RT-Thread exceeds HP RAM budget") +} diff --git a/app/ports/rtthread/rtconfig.h b/app/ports/rtthread/rtconfig.h new file mode 100644 index 00000000..b28bc812 --- /dev/null +++ b/app/ports/rtthread/rtconfig.h @@ -0,0 +1,23 @@ +#ifndef RETROSOC_RTTHREAD_CONFIG_H +#define RETROSOC_RTTHREAD_CONFIG_H + +#define ARCH_CPU_64BIT +#define RT_CPUS_NR 1 +#define RT_NAME_MAX 12 +#define RT_ALIGN_SIZE 16 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 1000 +#define RT_USING_OVERFLOW_CHECK +#define RT_DEBUG +#define RT_DEBUGING_ASSERT +#define RT_BACKTRACE_LEVEL_MAX_NR 32 +#define RT_USING_SEMAPHORE +#define RT_USING_MESSAGEQUEUE +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 256 +#define IDLE_THREAD_STACK_SIZE 2048 +#define RT_KLIBC_USING_VSNPRINTF_LONGLONG +/* No scanf caller is linked; avoid the heap-dependent internal scanner. */ +#define RT_KLIBC_USING_LIBC_VSSCANF + +#endif diff --git a/app/ports/rtthread/rtconfig.py b/app/ports/rtthread/rtconfig.py new file mode 100644 index 00000000..7f65faff --- /dev/null +++ b/app/ports/rtthread/rtconfig.py @@ -0,0 +1,17 @@ +"""Board-specific settings consumed by the locked RT-Thread build system.""" +import os + +ARCH = 'risc-v' +CPU = 'retrosoc' +CROSS_TOOL = PLATFORM = 'gcc' +EXEC_PATH = os.environ['RTT_EXEC_PATH'] +PREFIX = 'riscv64-unknown-elf-' +CC = AS = LINK = PREFIX + 'gcc' +CXX = PREFIX + 'g++' +AR = PREFIX + 'ar' +TARGET_EXT = 'elf' +DEVICE = '-march=rv64imafdc_zicbom_zicsr_zifencei -mabi=lp64d -mcmodel=medany' +CFLAGS = DEVICE + ' -O2 -g -ffreestanding -fno-builtin -ffunction-sections -fdata-sections -Wall' +AFLAGS = DEVICE + ' -g -x assembler-with-cpp' +LFLAGS = DEVICE + ' -nostdlib -nostartfiles -Wl,--gc-sections,-Map=rtthread.map -T linker.ld' +POST_ACTION = PREFIX + 'objcopy -O binary $TARGET rtthread.bin' diff --git a/app/ports/rtthread/selftest.c b/app/ports/rtthread/selftest.c new file mode 100644 index 00000000..4549ea4b --- /dev/null +++ b/app/ports/rtthread/selftest.c @@ -0,0 +1,100 @@ +#include "board.h" +#include "hp_boot_bundle.h" + +#include + +static struct rt_thread s_controller; +static struct rt_thread s_producer; +static struct rt_thread s_consumer; +static struct rt_semaphore s_done; +static struct rt_messagequeue s_queue; +static uint8_t s_controller_stack[4096] __attribute__((aligned(16))); +static uint8_t s_producer_stack[4096] __attribute__((aligned(16))); +static uint8_t s_consumer_stack[4096] __attribute__((aligned(16))); +static uint8_t s_queue_pool[128] __attribute__((aligned(16))); +static volatile uint32_t s_busy_iterations; +static volatile uint32_t s_preempted; +static volatile uint64_t s_memory_probe; + +static void rs_require(bool condition, uint32_t code) { + if (!condition) { + rs_rtt_fail(code); + } +} + +static void rs_producer(void *argument) { + rt_tick_t start = rt_tick_get(); + (void)argument; + while (s_preempted == 0U) { + ++s_busy_iterations; + rs_require((rt_tick_get() - start) < 100U, 10U); + } + rs_require(rs_rtt_context_probe(UINT64_C(0xFEDCBA9876543210)) == 0U, 11U); + for (uint64_t index = 0U; index < 8U; ++index) { + uint64_t value = UINT64_C(0x1234567800000000) | index; + rs_require(rt_mq_send_wait(&s_queue, &value, sizeof(value), 100) == RT_EOK, 12U); + } + rs_require(rt_sem_release(&s_done) == RT_EOK, 13U); +} + +static void rs_consumer(void *argument) { + (void)argument; + rs_require(rt_thread_delay(2) == RT_EOK, 20U); + rs_require(s_busy_iterations != 0U, 21U); + s_preempted = 1U; + rs_require(rs_rtt_context_probe(UINT64_C(0x13579BDF2468ACE0)) == 0U, 22U); + for (uint64_t index = 0U; index < 8U; ++index) { + uint64_t value = 0U; + rs_require(rt_mq_recv(&s_queue, &value, sizeof(value), 100) == (rt_ssize_t)sizeof(value), + 23U); + rs_require(value == (UINT64_C(0x1234567800000000) | index), 24U); + } + rs_require(rt_sem_release(&s_done) == RT_EOK, 25U); +} + +static void rs_controller(void *argument) { + uint64_t input = UINT64_C(0x123456789ABCDEF0); + uint64_t value = 0U; + rt_tick_t start; + (void)argument; + s_memory_probe = input; + rs_require(((s_memory_probe >> 32U) == UINT64_C(0x12345678)) && + ((s_memory_probe * 3U) / 3U == input), + 1U); + rt_kprintf("RTTHREAD_RV64_PASS\n"); + rs_require(rt_sem_init(&s_done, "done", 0U, RT_IPC_FLAG_FIFO) == RT_EOK, 2U); + rs_require(rt_mq_init(&s_queue, "mq", s_queue_pool, sizeof(value), sizeof(s_queue_pool), + RT_IPC_FLAG_FIFO) == RT_EOK, + 3U); + start = rt_tick_get(); + rs_require(rt_sem_take(&s_done, 2) == -RT_ETIMEOUT, 4U); + rs_require((rt_tick_get() - start) >= 2U, 5U); + rs_require(rt_mq_recv(&s_queue, &value, sizeof(value), 2) == -RT_ETIMEOUT, 6U); + rt_kprintf("RTTHREAD_TIMEOUT_PASS\n"); + rs_require(rt_thread_startup(&s_consumer) == RT_EOK, 7U); + rs_require(rt_thread_startup(&s_producer) == RT_EOK, 8U); + rs_require(rt_sem_take(&s_done, 200) == RT_EOK, 30U); + rs_require(rt_sem_take(&s_done, 200) == RT_EOK, 31U); + rs_require(s_preempted == 1U, 32U); + rt_kprintf("RTTHREAD_PREEMPT_PASS\nRTTHREAD_CONTEXT_PASS\nRTTHREAD_IPC_PASS\n"); + rs_rtt_publish(1U, RS_HP_RTTHREAD_READY_ARG, 1U); + rs_require(rs_rtt_wait_mailbox(1000) == RT_EOK, 33U); + rt_kprintf("RTTHREAD_MAILBOX_IRQ_PASS\nRTTHREAD_TEST_PASS\n"); + rs_rtt_publish(2U, RS_HP_RTTHREAD_READY_ARG, 2U); + for (;;) { + (void)rt_thread_delay(100); + } +} + +void rs_rtt_selftest_init(void) { + rs_require(rt_thread_init(&s_controller, "test", rs_controller, RT_NULL, s_controller_stack, + sizeof(s_controller_stack), 5U, 1U) == RT_EOK, + 40U); + rs_require(rt_thread_init(&s_consumer, "recv", rs_consumer, RT_NULL, s_consumer_stack, + sizeof(s_consumer_stack), 10U, 1U) == RT_EOK, + 41U); + rs_require(rt_thread_init(&s_producer, "send", rs_producer, RT_NULL, s_producer_stack, + sizeof(s_producer_stack), 20U, 1U) == RT_EOK, + 42U); + rs_require(rt_thread_startup(&s_controller) == RT_EOK, 43U); +} diff --git a/app/ports/rtthread/start.S b/app/ports/rtthread/start.S new file mode 100644 index 00000000..e6c8858f --- /dev/null +++ b/app/ports/rtthread/start.S @@ -0,0 +1,66 @@ + .section .text.start + .global _start +_start: + csrw mie, zero + csrw satp, zero + li t0, 0x6000 + csrw mstatus, t0 + .option push + .option norelax + la gp, __global_pointer$ + .option pop + la sp, __rt_rvstack + la t0, __bss_start + la t1, __bss_end +1: + bgeu t0, t1, 2f + sd zero, 0(t0) + addi t0, t0, 8 + j 1b +2: + la t0, trap_entry + csrw mtvec, t0 + csrw mscratch, sp + call rs_rtt_start +3: + wfi + j 3b + + .section .text + .global rs_rtt_context_probe +rs_rtt_context_probe: + addi sp, sp, -48 + sd ra, 0(sp) + sd s0, 8(sp) + sd s1, 16(sp) + sd s2, 24(sp) + sd s3, 32(sp) + sd a0, 40(sp) + mv s0, a0 + mv s2, a0 + not s3, a0 + li s1, 8 +1: + li a0, 1 + call rt_thread_delay + # Compare against memory, so identical truncation of saved registers cannot + # accidentally satisfy a register-to-register comparison. + ld t1, 40(sp) + bne s0, t1, 2f + bne s2, t1, 2f + not t0, t1 + bne t0, s3, 2f + addi s1, s1, -1 + bnez s1, 1b + li a0, 0 + j 3f +2: + li a0, 1 +3: + ld ra, 0(sp) + ld s0, 8(sp) + ld s1, 16(sp) + ld s2, 24(sp) + ld s3, 32(sp) + addi sp, sp, 48 + ret diff --git a/configs/README.md b/configs/README.md index bb1cf41c..a6096149 100644 --- a/configs/README.md +++ b/configs/README.md @@ -18,6 +18,10 @@ from 32 KiB on-chip SRAM, enables VexiiRiscv `Zicbom` with 64-byte blocks, and generates VexiiRiscv RTL only below the selected build variant. It is not part of the supported PR matrix until Linux boot, HP performance, synthesis, and timing evidence are qualified. +`ci/ihp130-rtthread.mk` selects the same RV64 HP hardware and LP loader for +the pinned RT-Thread M-mode selftest. Run `make setup-hp-rtthread`, then +`make CONFIG=configs/ci/ihp130-rtthread.mk SIMU=VERILATOR hp-rtthread-sim`. +All PRODUCT profiles use `rv64imafdc_zicbom_max`; LP compiler/ISA remain RV32. `ci/ihp130-xpi-flash-loader.mk` builds the SRAM-only XPI NOR service image used by GDB/OpenOCD; it is a programming utility, not a normal boot application. Start builds from a committed profile rather than setting an unreviewed mix of diff --git a/configs/benchmark/ihp130-hazard3-coremark-standard.mk b/configs/benchmark/ihp130-hazard3-coremark-standard.mk index 5fd5d57a..35771224 100644 --- a/configs/benchmark/ihp130-hazard3-coremark-standard.mk +++ b/configs/benchmark/ihp130-hazard3-coremark-standard.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max PDK_BEHAV := YES HAVE_SVA := NO EXT_CLK_HZ := 72000000 diff --git a/configs/benchmark/ihp130-hazard3-coremark.mk b/configs/benchmark/ihp130-hazard3-coremark.mk index 82aa7274..21b7f4ff 100644 --- a/configs/benchmark/ihp130-hazard3-coremark.mk +++ b/configs/benchmark/ihp130-hazard3-coremark.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max PDK_BEHAV := YES HAVE_SVA := NO EXT_CLK_HZ := 72000000 diff --git a/configs/benchmark/ihp130-hazard3.mk b/configs/benchmark/ihp130-hazard3.mk index 879df792..a695fa14 100644 --- a/configs/benchmark/ihp130-hazard3.mk +++ b/configs/benchmark/ihp130-hazard3.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max PDK_BEHAV := YES HAVE_SVA := NO EXT_CLK_HZ := 72000000 diff --git a/configs/ci/gf180.mk b/configs/ci/gf180.mk index 568f1264..4bb3d339 100644 --- a/configs/ci/gf180.mk +++ b/configs/ci/gf180.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max HAVE_SVA := NO EXT_CLK_HZ := 72000000 AUD_CLK_HZ := 18432000 diff --git a/configs/ci/ics55.mk b/configs/ci/ics55.mk index 883c4c93..4936175f 100644 --- a/configs/ci/ics55.mk +++ b/configs/ci/ics55.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := NO HAVE_SRAM_MACRO := NO SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max HAVE_SVA := NO EXT_CLK_HZ := 72000000 AUD_CLK_HZ := 18432000 diff --git a/configs/ci/ihp130-debug.mk b/configs/ci/ihp130-debug.mk index d6acaeaa..2e8682ab 100644 --- a/configs/ci/ihp130-debug.mk +++ b/configs/ci/ihp130-debug.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max PDK_BEHAV := YES HAVE_SVA := NO EXT_CLK_HZ := 72000000 diff --git a/configs/ci/ihp130-hp.mk b/configs/ci/ihp130-hp.mk index d08e1bc2..146f2291 100644 --- a/configs/ci/ihp130-hp.mk +++ b/configs/ci/ihp130-hp.mk @@ -3,7 +3,7 @@ MINI_MODE := PRODUCT PDK := IHP130 HAVE_PLL := NO HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 diff --git a/configs/ci/ihp130-rtthread.mk b/configs/ci/ihp130-rtthread.mk new file mode 100644 index 00000000..6f57a63b --- /dev/null +++ b/configs/ci/ihp130-rtthread.mk @@ -0,0 +1,22 @@ +# RV64 HP RT-Thread acceptance; LP remains the RV32 management loader. +SOC := MINI +MINI_MODE := PRODUCT +PDK := IHP130 +HAVE_PLL := NO +HAVE_HP := YES +HP_CONFIG := rv64imafdc_zicbom_max +HAVE_SRAM_IF := YES +HAVE_SRAM_MACRO := YES +SRAM_SIZE_KIB := 32 +PDK_BEHAV := YES +EXT_CLK_HZ := 72000000 +AUD_CLK_HZ := 18432000 +CLINT_TIMEBASE_HZ := 1000000 +ISA := RV32IM +HAVE_CSR := YES +APP := hp_boot +LINK_TYPE := ld2_all_sram +SIM_SUCCESS_MARKER := SIM_TEST_PASS +RTL_SIM_TIMEOUT := 20000000 +FIRMWARE_NAME := retrosoc_hp_boot +SIM_FIRMWARE_NAME := retrosoc_hp_boot diff --git a/configs/ci/ihp130-shell.mk b/configs/ci/ihp130-shell.mk index a3594a26..729d1162 100644 --- a/configs/ci/ihp130-shell.mk +++ b/configs/ci/ihp130-shell.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max HAVE_SVA := NO EXT_CLK_HZ := 72000000 AUD_CLK_HZ := 18432000 diff --git a/configs/ci/ihp130-xpi-flash-loader.mk b/configs/ci/ihp130-xpi-flash-loader.mk index 6828d7f6..aed60288 100644 --- a/configs/ci/ihp130-xpi-flash-loader.mk +++ b/configs/ci/ihp130-xpi-flash-loader.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max PDK_BEHAV := YES HAVE_SVA := NO EXT_CLK_HZ := 72000000 diff --git a/configs/ci/ihp130.mk b/configs/ci/ihp130.mk index 0d28f018..4224e991 100644 --- a/configs/ci/ihp130.mk +++ b/configs/ci/ihp130.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max HAVE_SVA := NO EXT_CLK_HZ := 72000000 AUD_CLK_HZ := 18432000 diff --git a/configs/ci/sky130.mk b/configs/ci/sky130.mk index c7ee4493..e2dceb51 100644 --- a/configs/ci/sky130.mk +++ b/configs/ci/sky130.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := YES HAVE_SRAM_MACRO := YES SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max HAVE_SVA := NO EXT_CLK_HZ := 72000000 AUD_CLK_HZ := 18432000 diff --git a/configs/cluster/ics55.mk b/configs/cluster/ics55.mk index 883c4c93..4936175f 100644 --- a/configs/cluster/ics55.mk +++ b/configs/cluster/ics55.mk @@ -6,7 +6,7 @@ HAVE_SRAM_IF := NO HAVE_SRAM_MACRO := NO SRAM_SIZE_KIB := 32 HAVE_HP := YES -HP_CONFIG := rv32imafdc_zicbom_max +HP_CONFIG := rv64imafdc_zicbom_max HAVE_SVA := NO EXT_CLK_HZ := 72000000 AUD_CLK_HZ := 18432000 diff --git a/crt/src/service/booter.c b/crt/src/service/booter.c index 89f5ace3..5286aa92 100644 --- a/crt/src/service/booter.c +++ b/crt/src/service/booter.c @@ -50,11 +50,11 @@ static void rs_print_hp_core_spec(void) { printf("\nHigh-Performance-Core Specification:\n"); printf(" Identity\n"); printf(" Core: VexiiRiscv(hart 1), dual-issue in-order\n"); - printf(" Role: high-throughput application and Linux\n"); + printf(" Role: application, RT-Thread and Linux\n"); printf(" Source: https://github.com/SpinalHDL/VexiiRiscv\n"); printf(" Hardware ISA and privilege\n"); - printf(" Base: RV32IMAFDC_Zicbom_Zicntr_Zihpm\n"); - printf(" Mode: M/S/U; Sv32 MMU, 9-bit ASID\n"); + printf(" Base: RV64IMAFDC_Zicbom_Zicntr_Zihpm\n"); + printf(" Mode: M/S/U; Sv39 MMU, 9-bit ASID\n"); printf(" Protection: 16 PMP regions, 4 KiB granularity\n"); printf(" IRQ: machine software/timer/external + supervisor external\n"); printf(" Microarchitecture\n"); @@ -63,7 +63,7 @@ static void rs_print_hp_core_spec(void) { printf(" Cache and memory\n"); printf(" L1: separate 16 KiB, 4-way instruction and data caches\n"); printf(" Maintenance: 64-byte Zicbom CBO; no hardware coherency\n"); - printf(" Bus: native AXI64 I/D + cacheless AXI32 MMIO\n"); + printf(" Bus: native AXI64 I/D/MMIO; MMIO downsized to AXI32\n"); printf(" Debug\n"); printf(" Embedded RISC-V JTAG, 4 triggers\n"); printf(" Runtime\n"); diff --git a/dependencies/README.md b/dependencies/README.md index 17cdfa94..bb3a8e03 100644 --- a/dependencies/README.md +++ b/dependencies/README.md @@ -35,6 +35,13 @@ below `.cache/retrosoc/sources/`; VexiiRiscv may be supplied through `VEXIIRISCV_ROOT`, but its revision is still checked before generated RTL is accepted. No generated CPU RTL or Linux build output belongs in Git. +`rtthread_hp` pins official RT-Thread v5.3.0 at +`99428a1e7f7447955aa860f7c969273a12095b8f`. `make setup-hp-rtthread` installs +that source, the checksum-locked `riscv_gnu_hp` RV64 compiler, and the hashed +SCons requirement in a local virtual environment. The BSP is copied into the +build variant and uses the upstream kernel/CPU port without modifying it. +The LP SDK retains its existing RV32 toolchain. + The libjpeg-turbo source archive is a host-verification input for the JPEG accelerator. It supplies an implementation-independent interoperability oracle; it is not linked into firmware or synthesized RTL. The repository-owned fixed diff --git a/dependencies/dependencies.lock.json b/dependencies/dependencies.lock.json index 3de7a6b7..1e92b11b 100644 --- a/dependencies/dependencies.lock.json +++ b/dependencies/dependencies.lock.json @@ -27,6 +27,12 @@ "recursive": true, "license": "MIT" }, + "rtthread_hp": { + "url": "https://github.com/RT-Thread/rt-thread.git", + "revision": "99428a1e7f7447955aa860f7c969273a12095b8f", + "destination": ".cache/retrosoc/sources/rtthread-hp", + "license": "Apache-2.0" + }, "opensbi_hp": { "url": "https://github.com/riscv-software-src/opensbi.git", "revision": "cbf9f6734dd85a982c63e3cb5db7ffe09da839ca", @@ -371,6 +377,13 @@ "archive": "xpack-openocd-0.12.0-1-linux-x64.tar.gz", "path": "xpack-openocd-0.12.0-1/bin" }, + "riscv_gnu_hp": { + "version": "2025.05.01", + "url": "https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.05.01/riscv64-elf-ubuntu-22.04-gcc-nightly-2025.05.01-nightly.tar.xz", + "sha256": "57da2f16e772674ef0202b37217ccdcba3787e1a055ca92a83c9a5b240bca7d1", + "archive": "riscv64-elf-ubuntu-22.04-gcc-nightly-2025.05.01-nightly.tar.xz", + "path": "riscv/bin" + }, "riscv_gnu": { "version": "2025.05.01", "url": "https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.05.01/riscv32-elf-ubuntu-22.04-gcc-nightly-2025.05.01-nightly.tar.xz", diff --git a/docs/README.md b/docs/README.md index 64a4b78c..12969e2b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -68,6 +68,8 @@ the root README and subsystem guides. clock/pad/fault/performance/RTC/test contracts, HAL, and verification. - [ip/hp-platform.md](ip/hp-platform.md) defines the experimental HP PLIC and LP/HP mailbox register, interrupt, software, and verification contracts. +- [hp-rv64-validation.md](hp-rv64-validation.md) records RV64 core, V2 bundle, + RT-Thread/Linux acceptance evidence and deferred gates. - [ip/onchip-sram.md](ip/onchip-sram.md) defines the configurable native-AXI4 SRAM architecture, APB capability ABI, technology mapping, verification, and reliability roadmap. diff --git a/docs/hp-rv64-validation.md b/docs/hp-rv64-validation.md new file mode 100644 index 00000000..861d0aa1 --- /dev/null +++ b/docs/hp-rv64-validation.md @@ -0,0 +1,158 @@ +# HP RV64 Migration Validation + +The active contract is [HP platform](ip/hp-platform.md), phases 2–5. LP remains +RV32 Hazard3. All PRODUCT profiles select RV64IMAFDC with Zicbom, Sv39 and +32-bit physical addresses. No upstream CPU or RT-Thread source is modified. + +## Reproduction + +```sh +make setup-vexiiriscv setup-hp-rtthread setup-hp-linux +make CONFIG=configs/ci/ihp130-hp.mk SIMU=VERILATOR hp-smoke-sim +make CONFIG=configs/ci/ihp130-rtthread.mk SIMU=VERILATOR hp-rtthread-sim +make CONFIG=configs/ci/ihp130-hp.mk SIMU=VERILATOR hp-linux-sim +python3 scripts/regress.py --root . --suite pr --pdk IHP130 --behavioral-only +``` + +`HP_CROSS` is independent of LP `CROSS` and resolves to the compiler in the +dependency lock. RT-Thread is official v5.3.0 at +`99428a1e7f7447955aa860f7c969273a12095b8f`, compiled with RV64 `lp64d`. +Its selftest qualifies integer contexts; it does not qualify FCSR preservation +or floating-point tasks. Linux uses its Buildroot RV64 Linux compiler, Sv39, +static musl/BusyBox and a small uncompressed CPIO with a dedicated `/init`. + +V2 flash bundles discriminate Linux (1, four entries), smoke (2, one executable) +and RT-Thread (3, one executable). Rebuild V1 bundles. The loader checks strict +flags, workload/count/types, fixed addresses, bounds, non-overlap, unused slots +and CRC. Only smoke enters the GA2D/cache lifecycle protocol. LP alone owns +the final SYSCTRL TEST_STATUS write for all workloads. + +## Measured runs + +Initial RV64 validation uses `configs/ci/ihp130-hp.mk` and +`BUILD_TIMESTAMP=2026-09-25-11-42`, producing +`build/ihp130-hp-2026-09-25-11-42-bb2e93b39338/`. + +| Run | Result | Evidence below variant | +| --- | --- | --- | +| RV64 generated core and Verilator compile | Pass | `generated/vexiiriscv/manifest.json`, `sim/verilator/result-compile.json` | +| GA2D/cache lifecycle smoke | `SIM_TEST_PASS code=0`, 3,446,054 cycles, 191 seconds | `sim/verilator/sim.log`, `result-hp-smoke-sim-check.json` in the same directory | +| RT-Thread kernel/platform | All required markers and `SIM_TEST_PASS code=0`, 6,412,946 cycles, 360 seconds | `sim/verilator/hp-rtthread/sim.log`, `result-sim-check.json` | + +The final dedicated `configs/ci/ihp130-rtthread.mk` profile passed all markers +and TEST_STATUS at 6,395,372 cycles in 357 seconds, using +`BUILD_TIMESTAMP=2026-09-25-12-18`. Its evidence is +`build/ihp130-rtthread-2026-09-25-12-18-bb2e93b39338/sim/verilator/hp-rtthread/`. +The final context probe compares registers against an original 64-bit sentinel +stored on each task's stack, catching identical truncation of multiple registers. +The final smoke also passed at 3,460,934 cycles in 195 seconds, with evidence +in `sim/verilator/hp-smoke/` under the initial variant. + +Linux Image is 2,152,448 bytes; the static, uncompressed CPIO is 302,080 bytes. +BusyBox is expanded from `allnoconfig` and an explicit allowlist; effective +options and static ELF linkage are checked. Linux acceptance is still running +without an emulator wall-clock timeout; no Linux pass is claimed yet. New workload runs +write logs to `sim/verilator/hp-{smoke,rtthread,linux}/`. Structured results +start as `running`; failures/interruption replace old passes. A UART banner is +diagnostic, never the final verdict. + +The affected Python and publication bindings pass 177 tests; one PDF-inspection +test is skipped because `pypdf` is not installed. C format, C policy and host +tests, Ruff, dependency-lock validation, `setup-hp-rtthread` and the dedicated +profile's Verilator doctor pass. The final publication-focused run passes 193 +tests with one missing-`pypdf` skip. The full Python run reports 974 passed and +5 skipped; its two missing-FLAC-corpus failures were resolved by installing the +checksum-locked corpus and rerunning both tests (2 passed). Thus 976 cases have +passing evidence. The other skips are optional P7 model/MFCC inputs; no test +expectation was weakened to hide a missing input. After the firmware-reservation +correction, all 62 affected platform and publication checks pass. + +The IHP130 behavioral PR regression completed successfully with +`BUILD_TIMESTAMP=2026-09-25-12-14`: strict lint, firmware builds, Verilator +`ci_smoke` (6,783,746 cycles, 384 seconds), `DEBUG_GDB_PASS`, and Icarus +`SIM_TEST_PASS`. Artifacts use `build/ihp130-2026-09-25-12-14-*` and +`build/ihp130-debug-2026-09-25-12-14-*`. Warning-baseline comparisons report +new/increased signatures as non-blocking observations; baselines were not +modified. No synthesis or STA result is implied by this regression. + +## RT-Thread first-schedule failure and correction + +The first BSP run reached `HP_RTTHREAD_BOOT` and then reported +`RTTHREAD_TRAP cause=1 epc=deadbeee`, failure 92, followed by LP failure 18. +Evidence is retained in `sim/verilator/hp-rtthread-idle-stack-failure/` under +the initial variant. This was a BSP integration error: + +1. The BSP used `RT_IDLE_THREAD_STACK_SIZE`; the locked upstream `src/idle.c` + reads `IDLE_THREAD_STACK_SIZE` and otherwise selects 128 bytes without heap. +2. The failing ELF's `idle_thread_stack` was 128 bytes at `0x38004530`. + Upstream `rt_hw_stack_init` reserves 512 bytes for 32 RV64 integer slots + and 32 double-precision slots, filling them with `0xdeadbeef`. +3. That frame starts at `0x380043b0`, 384 bytes before the allocated stack, + overwriting `_object_container`, interrupt-switch state, the scheduler's + ready bitmap and other globals. This explains the invalid first-schedule + state and is independent of the AXI crossbar. +4. The BSP now uses the correct macro with 2048 bytes and a compile-time lower + bound. The same integrated test then passed scheduling, RV64 context, IPC, + timer timeout, and mailbox IRQ checks. Vendor source stayed unchanged. + +## Linux firmware reservation correction + +The first RV64 run reached Linux 6.12.105 and memory initialization. Its log +reported no `/reserved-memory` node and included the full SDRAM range beginning +at `0x38000000`. OpenSBI's preceding PMP report protected its resident code/data +from S-mode. Static inspection confirmed that the custom platform has no generic +`fdt_fixups()` final-init hook; Linux `arch/riscv/mm/init.c:setup_bootmem` reserves +the kernel, initrd, DTB and DT-declared regions, not an implicit firmware window. +Thus the DT omitted a live firmware allocation. No resulting crash was observed; +the run was deliberately interrupted to correct this integration defect. + +The retained log is `sim/verilator/hp-linux-before-firmware-reservation/` under +the initial variant. The DT now reserves the complete, already allocated +512 KiB OpenSBI window with `no-map`, and the effective-config guard requires +`CONFIG_OF_RESERVED_MEM=y`. The DTB test checks both the exact range and +`no-map`. The corrected runtime now prints +`0x0000000038000000..0x000000003807ffff (512 KiB) nomap non-reusable opensbi@38000000`, +confirming that Linux consumed the reservation. Userspace acceptance is still +in progress. + +During the subsequent quiet `Initmem setup node 0` interval, two read-only host +debugger samples captured 64,772,063 and 66,503,381 emulator cycles. The sampled +Sv39 PCs resolve to `__init_single_page` and `memmap_init_range` in the actual +`vmlinux`; disassembly passes `s0` as the PFN and increments it at loop end. +`s0` advanced from `0x3b0c4` to `0x3b6ed`, toward the `s1=0x3c000` bound. +Privilege remained S-mode and SATP mode was 8. This establishes loop progress +during that interval, not a complete boot verdict. Samples and disassembly are +retained under `meta/hp-rv64-validation/`; no DUT state was written. +Later samples at 84,162,019 and 92,239,597 cycles resolve to +`should_skip_region` / `__next_mem_range` and `__free_pages_ok` called by +`memblock_free_all`, respectively. A further sample at 97,866,745 cycles is in +`__free_pages_core`, with a later page-descriptor block address. These locate +the later allocator work separately from the earlier page-descriptor loop. +After IRQ initialization, samples landed in timer handling. The interrupted +SEPC then moved from `memset` to `add_device_randomness`; the latter frame was +decoded from valid, matching-tag L1 data-cache lines and checked against the +RISC-V `pt_regs` layout. UART subsequently reported the 9-bit ASID allocator. +These observations avoid treating an IRQ-handler PC or stale raw SDRAM as +evidence of main-thread progress. + +## Coverage limits + +The repository's mechanical C format/policy checks are partial MISRA evidence; +no complete MISRA certification or new approved Required-rule deviation is +claimed. Board MMIO uses fixed, naturally aligned 32-bit accesses to declared +register windows; RV64 pointer width does not widen device accesses. + +Synthesis, netlist simulation, STA and synthesis-dependent metrics are deferred +by explicit user instruction. PPA, CDC/RDC, physical SRAM mapping, hardware +board runs and silicon qualification are not established by these simulations. +The behavioral evidence is IHP130-specific; other PDK matrices and extended +nightly benchmarking were not rerun in this migration. +The GDB regression exercises LP Hazard3 debug; it is not independent RV64 HP +debug/JTAG qualification. +The minimal Linux configuration leaves `CONFIG_RISCV_ISA_ZICBOM` disabled. +The custom OpenSBI platform does not import Zicbom into its hart-extension mask, +so it leaves MENVCFG CBIE/CBCFE disabled. The cache-maintenance evidence here is +the M-mode smoke; enabling native Linux non-coherent DMA requires the matching +firmware extension/permission setup and a separate S-mode CBO acceptance test. +Publication source bindings are updated separately from the previously reviewed +PDF snapshot; a fresh PDF requires an explicitly reviewed committed snapshot. diff --git a/docs/ip/hp-platform.md b/docs/ip/hp-platform.md index 23515858..f7450436 100644 --- a/docs/ip/hp-platform.md +++ b/docs/ip/hp-platform.md @@ -1,4 +1,106 @@ -# HP Interrupt and Mailbox Platform +# HP RV64 Operating-System Platform + +## Approved migration contract + +The RV64 migration approved on 2026-09-25 extends the existing HP platform in +the phases below. These are requirements, not claims of completed validation. +LP remains the RV32 Hazard3 management hart (hart 0). HP is VexiiRiscv hart 1, +RV64IMAFDC_Zicbom_Zicntr_Zihpm, with M/S/U modes and Sv39. Physical addresses +remain 32 bits; the reset vector remains `0x38000000`. The existing 16 KiB +four-way I/D caches, 64-byte lines, 9-bit ASIDs, PMP, and clocks remain fixed. +I/D and cacheless MMIO use AXI64 at HP; MMIO is downsized to the existing +AXI32/APB32 control plane. Software accesses peripheral registers as 32-bit +words, including the two halves of CLINT timer registers. No new CDC or +hardware coherency is introduced. LP retains SYSCTRL and terminal-status +ownership, DMA channel 6, SDRAM initialization, and HP release control. + +### Boot bundle V2 + +The little-endian header remains 128 bytes at flash offset `0x00100000`, with +four 24-byte entry slots and CRC32/ISO-HDLC protection. Version is 2. Header +word 7 identifies workload: 1 Linux, 2 GA2D smoke, 3 RT-Thread. Linux has four +entries (types 1 OpenSBI, 2 DTB, 3 Linux Image, 4 uncompressed CPIO), retaining +the established addresses and limits. Smoke and RT-Thread have one executable +entry of type 5, at `0x38000000`, at most 512 KiB; unused entries are zero. +Version 1, unknown workloads, incorrect entry counts/types/addresses, empty +payloads, overlaps, out-of-range sizes, and bad CRCs are rejected before HP +release. Build manifests record RV64, workload, sources and artifact hashes; +builders verify executable ELF class and RISC-V machine identity. + +### Software acceptance protocol + +Linux publishes event 1, argument `0x4c4e5801`, sequence 1 only after its +userspace acceptance succeeds. On a software failure it publishes event 3 +with a nonzero error code. LP prints `HP_LINUX_READY` and terminates on Linux +success without entering the GA2D protocol. + +RT-Thread publishes ready event 1, argument `0x52545401`, sequence 1 after +enabling PLIC source 2 in machine context 0. LP sends command `0x52545402`, +argument `0x12345678`, sequence 1. Its ISR clears the HP mailbox source and +completes the PLIC claim; the test thread verifies the received message. Once +all tests pass, HP publishes event 2, argument `0x52545401`, sequence 2. +Event 3 reports a nonzero failure code at sequence 1 or 2. LP alone writes +SYSCTRL TEST_STATUS and prints `HP_RTTHREAD_PASS` on success. Publication is +code, argument, sequence, `fence iorw, iorw`, then doorbell. No shared cached +memory is used by this test protocol. Smoke retains the GA2D and cache-clean +handshake documented by its existing payload. + +RT-Thread uses the official v5.3.0 commit +`99428a1e7f7447955aa860f7c969273a12095b8f` without vendor source modifications. +The BSP uses the common RISC-V M-mode port, static threads and IPC objects, +1000 Hz ticks, UART1, hart-1 MSIP/MTIMECMP, and the existing PLIC/mailbox. +The first test image covers integer RV64 computation/context, preemption, +semaphore and message queue behavior, timeouts, and external mailbox IRQ. +RT-Smart, networking, filesystems, and floating-point task qualification are +outside this phase; hardware F/D support remains enabled. + +Linux uses RV64 OpenSBI FW_JUMP, Sv39, and a static minimal BusyBox userspace. +The DT reserves `0x38000000..0x3807ffff` with `no-map` for resident OpenSBI; +Linux must never allocate or directly map that firmware window. +Its dedicated `/init` explicitly mounts devtmpfs, procfs, sysfs and tmpfs, +checks device nodes, file round trips and child execution, and reports through +the mailbox. Initial CPIO includes `/dev/console`. Effective kernel config, +not merely its fragment, must enable the required executable, console, +memory-management, filesystem and /dev/mem options. The acceptance CPIO is +uncompressed and does not run the full Buildroot services sequence. + +### Development order and verification + +#### Phase 2 - RV64 HP core and platform migration + +Migrate the fixed generator, all PRODUCT profiles, manifests and core reports; +retain the locked VexiiRiscv revision. Use separate RV32 LP and RV64 HP tools. +Validate generated port widths, narrow MMIO lanes, 64-bit memory accesses, +and the migrated GA2D/cache-lifecycle smoke in IHP130 Verilator. + +#### Phase 3 - RT-Thread dependency, BSP and acceptance + +Lock sources and build tools, add the self-owned BSP and V2 loader/packager, +and run `hp-rtthread-sim` from its committed IHP130 profile. All individual +test markers, `HP_RTTHREAD_PASS`, and `SIM_TEST_PASS code=0` are required. +Negative bundle and mailbox tests must demonstrate fail-closed behavior. + +#### Phase 4 - RV64 Linux minimal-initramfs acceptance + +Migrate OpenSBI, Linux, Buildroot and DTB together, verify effective configs, +then run real SoC `hp-linux-sim`. Required markers include Linux userspace +checks, `retroSoC HP Linux ready`, `HP_LINUX_READY`, and `SIM_TEST_PASS code=0`. +Unexpected console corruption remains a failure requiring diagnosis. + +#### Phase 5 - Documentation and delivery evidence + +Update architecture, guides and publication source bindings. Preserve prior +RV32 evidence as historical. Each workload writes separate run logs and +structured results; an interrupted or ongoing run cannot reuse an old pass. +Run affected host/Python/C/RTL checks and IHP130 behavioral regressions. +Synthesis, netlist simulation, STA, and synthesis-dependent metrics are +explicitly deferred by user instruction, as are SRAM macro replacement, +CDC/RDC/physical signoff and commercial qualification. No PPA claim is made. + +Reference boundaries: [RT-Thread v5.3.0](https://github.com/RT-Thread/rt-thread/releases/tag/v5.3.0) +supplies the OS/CPU port; the repository owns board integration. The existing +CLINT/PLIC architecture and locked OpenSBI platform supply the interrupt and +supervisor boot patterns; no third-party board register map is imported. The experimental LP/HP profile adds a 32-source, two-context PLIC at `0x0C000000` and a bidirectional mailbox at `0x10019000`. Both are self-owned diff --git a/docs/lp-hp-architecture.md b/docs/lp-hp-architecture.md index 5c62edb2..a67a31e8 100644 --- a/docs/lp-hp-architecture.md +++ b/docs/lp-hp-architecture.md @@ -15,7 +15,7 @@ Every committed `MINI_MODE=PRODUCT` profile instantiates two fixed harts: | --- | --- | --- | | Core | Hazard3 | generated VexiiRiscv | | Hart ID | 0 | 1 | -| ISA | profile RV32I/RV32IM | RV32IMAFDC + Zicbom, S/U mode, Sv32 | +| ISA | profile RV32I/RV32IM | RV64IMAFDC + Zicbom, S/U mode, Sv39 | | Reset clock | REF24 at 24 MHz | external 72 MHz safe clock | | Role | boot, control, diagnostics, recovery | high-throughput application/Linux | | JTAG | reset owner | selectable while HP is held in reset | diff --git a/docs/mini-high-performance-traceability.md b/docs/mini-high-performance-traceability.md index b3a85907..f80469c8 100644 --- a/docs/mini-high-performance-traceability.md +++ b/docs/mini-high-performance-traceability.md @@ -33,7 +33,11 @@ The architecture report under `/nfs/share/home/miaoyuchi/plan` is the design input. This matrix and executable configuration are the repository status sources of truth. -## Current verification evidence +## Historical RV32 verification evidence + +The results below predate the RV64 migration and must not be used as RV64 +qualification. Current generation selects `rv64imafdc_zicbom_max`; see +[HP RV64 validation](hp-rv64-validation.md) for migration-specific evidence. - The complete Python suite passes 308 tests, including directed AXI64 multi-ID/ACL/QoS, target isolation, native AXI64 SRAM, Fabric Monitor, diff --git a/docs/soc-family-positioning.md b/docs/soc-family-positioning.md index ee6b3dcb..738aad9f 100644 --- a/docs/soc-family-positioning.md +++ b/docs/soc-family-positioning.md @@ -24,9 +24,9 @@ advertised for a device or PDK. | Tier | Product role | Compute topology | Primary software | Defining boundary | | --- | --- | --- | --- | --- | | Tiny | Low-power MCU and edge-connectivity endpoint | One Hazard3 MCU core | Bare metal or RTOS | No MMU or external DRAM dependency | -| Mini | Low-cost heterogeneous RV32 Linux control SoC | Hazard3 management core plus one RV32 VexiiRiscv Linux core | Embedded Linux plus management firmware | Lightweight Linux and basic HMI, without a desktop-class accelerator requirement | +| Mini | Low-cost heterogeneous Linux control SoC | RV32 Hazard3 management plus one RV64 VexiiRiscv application core | Embedded Linux or RT-Thread plus management firmware | Lightweight Linux and basic HMI, without a desktop-class accelerator requirement | | Std | Heterogeneous RV32 graphical Linux edge SoC | Hazard3 management core plus VexiiRiscv performance and efficiency cores | Graphical Linux plus RTOS firmware | Full AXI4 memory fabric, GPU, audio, and AI acceleration | -| Pro | Highest-performance and only RV64 family tier | Hazard3 management core plus four coherent RV64 VexiiRiscv Linux cores | RV64 graphical Linux | Coherent SMP, high-bandwidth memory, GPU, NPU, and video codecs | +| Pro | Highest-performance coherent RV64 family tier | Hazard3 management core plus four coherent RV64 VexiiRiscv Linux cores | RV64 graphical Linux | Coherent SMP, high-bandwidth memory, GPU, NPU, and video codecs | The VexiiRiscv targets rely on the upstream core's documented RV32/RV64, single/dual-issue, cache, Sv32/Sv39, AXI4, and Linux capabilities. The exact @@ -176,10 +176,12 @@ responsible for clock and memory initialization, image selection, VexiiRiscv boot release, fault recovery, and final power-state control. Linux must not be able to reconfigure the management-core lifecycle controls. -The initial VexiiRiscv configuration should provide: +The frozen Mini RV64 configuration provides the following architectural target; +validation status is recorded separately in [HP RV64 validation](hp-rv64-validation.md). +The Std/Pro roadmap below remains independent of this implemented Mini profile. -- RV32IMAFDC with machine, supervisor, and user modes. -- Sv32 virtual memory, hardware page-table walking, and the privileged CSRs +- RV64IMAFDC with machine, supervisor, and user modes. +- Sv39 virtual memory, hardware page-table walking, and the privileged CSRs required by the supported Linux kernel. - LR/SC atomics, separate 16 KiB instruction and data caches, and uncached MMIO regions. diff --git a/publications/README.md b/publications/README.md index abd4dc1d..c166369e 100644 --- a/publications/README.md +++ b/publications/README.md @@ -5,6 +5,13 @@ an English product document with a separate MPW compatibility appendix. It does not define or change RTL, register ABIs, build profiles or hardware quality policy. The canonical RTL/configuration inputs remain authoritative. +The current working source includes the Mini RV64/Sv39 migration, V2 workload +bundle checks, and minimal Linux `/init` handoff. RT-Thread's M-mode BSP and +acceptance evidence are documented in [HP RV64 validation](../docs/hp-rv64-validation.md). +These edits do not advance `mini.json.source_revision` or relabel an existing +PDF. Review and commit the implementation snapshot before producing a matching +new PDF; the source-drift check remains mandatory. + ## Mini datasheet - Entry: [datasheets/retroSoC-mini-datasheet.typ](datasheets/retroSoC-mini-datasheet.typ). diff --git a/publications/datasheets/sections/architecture.typ b/publications/datasheets/sections/architecture.typ index bfa06a62..0077bcce 100644 --- a/publications/datasheets/sections/architecture.typ +++ b/publications/datasheets/sections/architecture.typ @@ -24,8 +24,8 @@ halt, resume, register and system-bus access. LP retains control while HP is hel === Application Processor ==== VexiiRiscv -The generated HP hart is a dual-issue RV32IMAFDC + Zicbom configuration, with supervisor/user -modes, Sv32 virtual memory and 64-byte cache-maintenance blocks. Hart 1 uses native AXI64 +The generated HP hart is a dual-issue RV64IMAFDC + Zicbom configuration, with supervisor/user +modes, Sv39 virtual memory and 64-byte cache-maintenance blocks. Hart 1 uses native AXI64 instruction and data paths. Its uncached MMIO path is downsized and crosses into the LP control plane. OpenSBI/Linux inputs and generated-core configuration are dependency-locked. #source-note("docs/lp-hp-architecture.md", title:"LP/HP architecture and boot contract") diff --git a/publications/datasheets/sections/boot-bundle-format.typ b/publications/datasheets/sections/boot-bundle-format.typ index 9d9846ce..b4ad66b0 100644 --- a/publications/datasheets/sections/boot-bundle-format.typ +++ b/publications/datasheets/sections/boot-bundle-format.typ @@ -5,7 +5,10 @@ === HP Boot Bundle Binary Format -The supplied format is a fixed four-artifact bundle, serialized as little-endian 32-bit words. +The supplied V2 format is a typed workload bundle, serialized as little-endian 32-bit words. +Linux uses four artifacts; smoke and RT-Thread use one executable descriptor at +0x38000000 with the remaining three descriptor slots zero. Workload values are 1, 2 and 3 +respectively. V1 bundles must be rebuilt. The format version is #bundle.version; it is independent of the datasheet's v0.4 revision. The bundle starts at Flash offset #hex(bundle.bundle_offset), after the LP firmware area. Its fixed header is #bundle.fixed_header_bytes bytes, each descriptor is #bundle.entry_bytes @@ -57,14 +60,13 @@ or invite reliance on unvalidated encodings. Use the canonical packager output f ] } -The generated payload boundary is #bundle.payload_alignment bytes; the loader's address checks -require four-byte alignment. The generator writes flags=1 and reserved=0, while the loader -tests the REQUIRED bit and has no separate rejection for other flag bits or the reserved word. -It bounds individual payload ranges but does not separately enforce sequential disjoint -placement or a padding pattern. Record these as current checks, not as stronger validation. +The generated and accepted payload boundary is #bundle.payload_alignment bytes. The loader +requires flags=1, a known workload, its exact entry count/types/load addresses, and zero unused +descriptors. It rejects overlapping or out-of-bounds payloads. Padding bytes are not validated. #source-note("app/apps/hp_boot/hp_boot_bundle.h",title:"Handwritten bundle fields, types and allocation bounds") #source-note("scripts/package_hp_boot.py",title:"Actual byte serialization, alignment, padding and CRC generation") -#source-note("app/apps/hp_boot/main.c",title:"Actual header/entry acceptance and copy checks") +#source-note("app/apps/hp_boot/hp_boot_bundle.c",title:"Actual header/entry acceptance") +#source-note("app/apps/hp_boot/main.c",title:"Copy checks and workload-specific terminal handshakes") ==== Synthetic serialization example This example is generated by the existing packager from non-executable synthetic payloads. diff --git a/publications/datasheets/sections/linux-runtime.typ b/publications/datasheets/sections/linux-runtime.typ index 9fd63e8d..428ae6c3 100644 --- a/publications/datasheets/sections/linux-runtime.typ +++ b/publications/datasheets/sections/linux-runtime.typ @@ -17,7 +17,7 @@ establish that all optional SBI services or all peripherals have native Linux dr [*LP handoff* \ Existing bundle validation \ Release HP at OpenSBI], [*OpenSBI on hart 1* \ UART1 SBI console \ ACLINT platform setup], [*FW_JUMP* \ Enter Linux Image \ Pass the prepared DTB], - [*Kernel / initramfs* \ SBI early console and hvc0 \ Run /sbin/init], + [*Kernel / initramfs* \ SBI early console and hvc0 \ Run /init], [*Rootfs ready service* \ Print ready text \ Publish mailbox fields], [*LP observation* \ Match sequence / event \ Separate from driver coverage], )),caption:[Supplied OpenSBI/Linux handoff and readiness path. The diagram describes source-level checkpoints.]) @@ -27,7 +27,7 @@ path in this configuration. The UART1 device-tree node's presence does not subst native driver. The configured machine timer and software-interrupt path are distinct from external interrupt-controller and custom peripheral driver integration. #source-note("app/ports/linux/opensbi/retrosoc_hp/platform.c",title:"Hart mapping, console callbacks and ACLINT setup") -#source-note("app/ports/linux/linux/retrosoc_hp.config",title:"RV32, SBI console and single-hart kernel configuration") +#source-note("app/ports/linux/linux/retrosoc_hp.config",title:"RV64, SBI console and single-hart kernel configuration") === Device-tree and image consistency The device-tree template, OpenSBI build arguments, boot-bundle bounds and actual generated HP @@ -40,40 +40,43 @@ the following values explain the platform relationship rather than defining a se ([Main memory],[#code(platform.memory_base), #(platform.memory_bytes / 1024 / 1024) MiB],[Device-tree envelope; actual external memory and boot-loader bounds must agree.]), ([Timer base rate],[#platform.timebase_hz Hz],[Match ACLINT timebase-frequency and the OpenSBI mtimer declaration.]), ([CPU / UART clock declarations],[#platform.clock_hz Hz],[Nominal template values, not measured or dynamically synchronized frequencies.]), - ([MMU / cache maintenance],[Sv32; #(platform.cbom_bytes)-byte CBO blocks],[Software metadata must be checked against the generated HP artifact.]), - ([Initial ramdisk],[Start #code("0x"+str(platform.initrd_start,base:16))],[The source template's equal start/end is patched using the built compressed rootfs size.]), + ([MMU / cache maintenance],[Sv39; #(platform.cbom_bytes)-byte CBO blocks],[Software metadata must be checked against the generated HP artifact.]), + ([Initial ramdisk],[Start #code("0x"+str(platform.initrd_start,base:16))],[The source template's equal start/end is patched using the built uncompressed CPIO size.]), ([Interrupt controllers],[ACLINT CPU causes; PLIC machine/supervisor contexts],[A DT node and binding alone do not demonstrate a complete driver or interrupt acceptance test.])), widths:(1fr,1.3fr,2.15fr)) -The image builder replaces linux,initrd-end with the start plus the actual rootfs.cpio.gz size. +The image builder replaces linux,initrd-end with the start plus the actual rootfs.cpio size. That value is an end-exclusive boundary; the generated DTB, not the unpatched source template, must accompany the images. OpenSBI FW_TEXT_START, FW_JUMP_ADDR and FW_JUMP_FDT_ADDR are selected from the supplied fixed layout. Retain the output DTB, source configuration and bundle manifest together. A changed device-tree property does not reconfigure clocks or synthesize different RTL. +The resident OpenSBI window at 0x38000000 occupies a 512 KiB no-map reserved-memory +node. Linux must not allocate this firmware memory; the custom platform does not +apply the generic OpenSBI FDT fixups. #source-note("app/ports/linux/linux/retrosoc_hp.dts",title:"Device-tree template and logical platform bindings") #source-note("scripts/build_hp_linux.py",title:"Actual initrd-end patch and FW_JUMP build arguments") === Kernel and rootfs ready handoff -The rootfs service S99retrosoc-hp prints its ready message and then performs four 32-bit mailbox -writes. The published event is an init-service checkpoint, not proof of all drivers, networking, +The dedicated /init mounts procfs, sysfs, devtmpfs and tmpfs, checks file I/O and child execution, +then runs the static hp-ready helper. It prints its ready message and publishes through 32-bit +mailbox writes with an I/O fence before the doorbell. This is a userspace checkpoint, not proof of all drivers, networking, storage or long-term Linux stability. The text is emitted before the mailbox writes, so the text alone is not the LP's readiness condition. -#ds-table("linux-ready-writes",[Rootfs service mailbox publication order], +#ds-table("linux-ready-writes",[Userspace helper mailbox publication order], ([Order],[Address],[Written value],[Purpose]), platform.ready_writes.enumerate().map(((i,row))=>(str(i+1),code(row.address),code(row.value), - ([Sequence],[Linux-ready event],[Argument / ready state],[Interrupt request]).at(i))), + ([Linux-ready event],[Argument / ready state],[Sequence],[Interrupt request]).at(i))), widths:(0.4fr,1.1fr,1.1fr,1.85fr)) -The script uses devmem from the supplied userland with CONFIG_DEVMEM enabled in the kernel. -It contains no explicit readback or per-write failure handling. The LP loader checks the -expected sequence/event in its existing mailbox loop; that final wait has no firmware-local -deadline. A rootfs message, process exit, mailbox publication and final SYSCTRL TEST_STATUS are -different observations. Preserve them with their stage and use an external timeout when a -required checkpoint never arrives. +The helper checks open/mmap errors for /dev/mem and reports init failures through event 3 +when the mailbox can be mapped. LP validates the expected event, argument and sequence, +then writes TEST_STATUS directly for the Linux workload. GA2D/cache lifecycle is a separate +smoke workload. A rootfs message alone is not the final SYSCTRL verdict. Resource ownership and non-coherent buffer rules still apply after Linux starts. Follow @software-support before choosing a driver path, and @multicore-operation for later LP/HP transactions. This description adds no new successful boot or board-validation claim. -#source-note("app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp",title:"Ready message and actual mailbox write order") +#source-note("app/ports/linux/hp_ready.c",title:"Ready message and actual mailbox write order") +#source-note("app/ports/linux/init",title:"Minimal userspace acceptance checks") #source-note("app/apps/hp_boot/main.c",title:"LP sequence/event checks and final terminal result") diff --git a/publications/datasheets/sections/overview.typ b/publications/datasheets/sections/overview.typ index a852624f..80aa44a0 100644 --- a/publications/datasheets/sections/overview.typ +++ b/publications/datasheets/sections/overview.typ @@ -32,7 +32,7 @@ #set list(tight:false,spacing:rhythm.cover-list-spacing,body-indent:rhythm.cover-list-indent) *Compute and control* - Hazard3 LP: boot, clocks, resource ownership and fault recovery. - - Dual-issue VexiiRiscv HP: RV32IMAFDC + Zicbom, Sv32. + - Dual-issue VexiiRiscv HP: RV64IMAFDC + Zicbom, Sv39. - JTAG debug; fixed EXT-L control and EXT-H AXI64 slots. *Memory and interconnect* diff --git a/publications/datasheets/sections/processor-details.typ b/publications/datasheets/sections/processor-details.typ index a6c9ec1a..422763f8 100644 --- a/publications/datasheets/sections/processor-details.typ +++ b/publications/datasheets/sections/processor-details.typ @@ -12,7 +12,7 @@ ISA/CSR manuals. A software compiler option does not remove a hardware extension ([Execution width],[RV32 integration.],[XLEN #cpu.hp.xlen / physical address width #cpu.hp.physicalWidth.]), ([Configured extensions],cpu.lp_extensions.join(", "),cpu.hp_isa.filter(x=>not(x in ("s","u"))).join(", ")), ([Privilege],[Machine-mode configuration, U_MODE = #cpu.lp.U_MODE.],[S and U selected by the generator, in addition to machine-mode operation.]), - ([MMU / software platform],[No LP MMU is advertised by this integration.],[Supplied software platform declares Sv32; verify agreement with the generated core before deployment.]), + ([MMU / software platform],[No LP MMU is advertised by this integration.],[Supplied software platform declares Sv39; verify agreement with the generated core before deployment.]), ([Protection],[PMP_REGIONS = #cpu.lp.PMP_REGIONS.],[PMP entries = #cpu.hp.at("pmpParam.pmpSize") / granularity = #cpu.hp.at("pmpParam.granularity") bytes.]), ([Reset entry],code(data.reset_address),code(cpu.hp.resetVector.trim("L",at:end))), ([Trap / counter configuration],[MTVEC_INIT: #code(cpu.lp.MTVEC_INIT) \ Machine CSR: #cpu.lp.CSR_M_MANDATORY / counters: #cpu.lp.CSR_COUNTER.],[ASID width #cpu.hp.asidWidth and #cpu.hp.additionalPerformanceCounters additional performance counters. \ Time-read option: #cpu.hp.at("privParam.withRdTime").]), diff --git a/publications/datasheets/sections/product-use.typ b/publications/datasheets/sections/product-use.typ index 001f5570..bc38ea5c 100644 --- a/publications/datasheets/sections/product-use.typ +++ b/publications/datasheets/sections/product-use.typ @@ -85,7 +85,7 @@ profile; other profiles can depend on external memory even when Linux is not use [*External boot image* \ XPI-visible bundle \ Header and payload CRCs], [*LP loader* \ Validate and copy \ Retain root authority], [*External SDRAM* \ OpenSBI, DTB \ Linux and initramfs], - [*HP application* \ Hart 1, Sv32 \ Linux enters via OpenSBI], + [*HP application* \ Hart 1, Sv39 \ Linux enters via OpenSBI], [*SBI console / timer* \ Supplied platform path], [*LP supervision* \ Mailbox observation \ Resource and reset policy])), caption:[Supplied Linux boot composition and management responsibilities.]) diff --git a/publications/datasheets/system-reference.json b/publications/datasheets/system-reference.json index ef6922a9..aca9e73c 100644 --- a/publications/datasheets/system-reference.json +++ b/publications/datasheets/system-reference.json @@ -4992,7 +4992,10 @@ 4, 9, 9, + 9, 10, + 18, + 19, 11, 12, 13, @@ -5004,7 +5007,7 @@ ], "expected_entries": 4, "meanings": { - "0": "Linux-ready and P5 GA2D mailbox/cache-clean handoff completed; successful terminal status.", + "0": "Selected workload completed: Linux-ready, RT-Thread kernel/mailbox IRQ tests, or smoke GA2D/cache lifecycle.", "1": "UART initialization failed; terminal failure is written directly without requiring a printable console.", "2": "HP hold/reset/debug-selection/presence prerequisite failed.", "3": "SDRAM did not reach the required ready/error-free state within the firmware wait.", @@ -5021,7 +5024,9 @@ "14": "GA2D did not return to the expected HP-owned safe idle state after the P5 FILL/COPY-subset job.", "15": "The request to hold HP after P5 GA2D completion returned an unexpected error.", "16": "Cache-clean request, P5 GA2D cache mailbox event, clean acknowledgement, or HP-held confirmation failed.", - "17": "GA2D handoff back to LP or LP-owned safe-idle confirmation failed." + "17": "GA2D handoff back to LP or LP-owned safe-idle confirmation failed.", + "18": "RT-Thread ready message failed validation or HP reported a test failure.", + "19": "LP-to-HP IRQ command failed or RT-Thread final verdict failed validation." } } }, @@ -5041,7 +5046,7 @@ "app/ports/linux/linux/retrosoc_hp.config", "app/ports/linux/linux/retrosoc_hp.dts", "app/ports/linux/opensbi/retrosoc_hp/platform.c", - "app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp", + "app/ports/linux/hp_ready.c", "crt/arch/riscv/startup.S", "crt/arch/riscv/system_irq.S", "crt/include/retrosoc/arch/riscv/system_csr.h", @@ -5062,7 +5067,8 @@ "rtl/mini/integration/soc_topology.json", "rtl/mini/mk/software.mk", "scripts/build_hp_linux.py", - "scripts/regress.py" + "scripts/regress.py", + "app/ports/linux/init" ], "bindings": [ { @@ -5232,7 +5238,7 @@ { "id": "initrd-patch", "file": "scripts/build_hp_linux.py", - "text": "initrd_end = LAYOUT[\"rootfs.cpio.gz\"][0] + rootfs_source.stat().st_size" + "text": "initrd_end = LAYOUT[\"rootfs.cpio\"][0] + rootfs_source.stat().st_size" }, { "id": "sbi-entry", @@ -5241,8 +5247,8 @@ }, { "id": "ready-log", - "file": "app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp", - "text": "echo \"retroSoC HP Linux ready\"" + "file": "app/ports/linux/hp_ready.c", + "text": "puts(\"retroSoC HP Linux ready\")" }, { "id": "smoke-selection", @@ -5675,52 +5681,43 @@ "sources": [ "scripts/package_hp_boot.py", "app/apps/hp_boot/hp_boot_bundle.h", - "app/apps/hp_boot/main.c" + "app/apps/hp_boot/main.c", + "app/apps/hp_boot/hp_boot_bundle.c" ], "loader_bindings": [ { - "source": "app/apps/hp_boot/main.c", + "source": "app/apps/hp_boot/hp_boot_bundle.c", "function": "rs_hp_boot_crc32_byte", "body_sha256": "b048241b3b6c9507a3d2b630af3ce8e4f2dbc0cdfbe662f76a8fce8ebe6037bc" }, { - "source": "app/apps/hp_boot/main.c", + "source": "app/apps/hp_boot/hp_boot_bundle.c", "function": "rs_hp_boot_crc32", "body_sha256": "ee0d898baadc9da12772f6246b2f8b4635216fe68cf67e08793865d2ca40dc90" }, { - "source": "app/apps/hp_boot/main.c", - "function": "rs_hp_boot_range_valid", - "body_sha256": "d9bdafe55cf55481f4a4b33e5ed41e73be9cf9f18a212c8acfcf20c46e0db4ed" - }, - { - "source": "app/apps/hp_boot/main.c", - "function": "rs_hp_boot_entry_expected", - "body_sha256": "742ab68f51e33c801d28e5df8f18302f8a0651621868fb5e6ed64762b0734938" - }, - { - "source": "app/apps/hp_boot/main.c", + "source": "app/apps/hp_boot/hp_boot_bundle.c", "function": "rs_hp_boot_header_valid", - "body_sha256": "2409563cb2fb7bace28215f54f83189ecbd11c2257d1959be5aed5013d7c2905" + "body_sha256": "cd86a399c2307681a36887647da8f3a585a0d6bbe5cb531b33bfdd84ad2e96a2" } ], "header_fields": { "magic": "Format marker written as a little-endian 32-bit word.", "version": "Bundle format version; distinct from the datasheet revision.", "header_size": "Bytes in the fixed header plus all entry descriptors.", - "entry_count": "Number of fixed-order artifact entries.", + "entry_count": "Linux uses four entries; smoke and RT-Thread use one executable entry. The remaining three slots are zero.", "total_size": "Bundle-relative byte extent from BUNDLE_OFFSET through the aligned image end; includes gaps and padding.", "header_crc32": "CRC of the complete header/entry area with this field zeroed.", - "flags": "Generator writes REQUIRED; loader requires its bit to be set.", - "reserved": "Generator writes zero; loader has no independent zero-value check for this field." + "flags": "Must equal REQUIRED (1); extra flag bits are rejected.", + "workload": "V2 workload discriminator: 1 Linux, 2 smoke, 3 RT-Thread. Unknown values and V1 are rejected." }, "entry_fields": { - "type": "Fixed artifact type in the expected OpenSBI, DTB, Linux, initramfs order.", + "type": "Linux uses types 1..4 in fixed order; smoke/RT-Thread use executable type 5 at 0x38000000.", "flash_offset": "Absolute offset from the start of the flash image, not from the bundle header.", "load_address": "Fixed destination address in the selected SDRAM map.", "size": "Nonzero payload byte count; excludes alignment padding.", "crc32": "CRC of exactly size payload bytes.", - "flags": "Generator writes REQUIRED; loader requires its bit to be set." + "flags": "Must equal REQUIRED (1); extra flag bits are rejected." }, "rules": [ { @@ -5731,8 +5728,8 @@ }, { "topic": "Entry identity", - "producer": "Four artifacts in fixed order, with the prescribed load address and maximum size for each.", - "loader": "Checks exact type/order/load address and the per-artifact maximum, plus nonzero size and mapped ranges.", + "producer": "Workload 1: four Linux artifacts. Workloads 2/3: one executable type 5. Unused descriptor slots are zero.", + "loader": "Checks workload, exact count/type/order/address, per-artifact size limits and zero unused slots.", "consequence": "This is not a generic relocatable image table." }, { @@ -5744,20 +5741,20 @@ { "topic": "Address alignment", "producer": "Aligns each payload start and the final cursor to PAYLOAD_ALIGNMENT.", - "loader": "Checks flash_offset and load_address modulo four, not the generator's larger boundary.", - "consequence": "Generated layout and accepted alignment are different conditions." + "loader": "Requires each absolute flash offset to be 4096-byte aligned and the exact fixed load address.", + "consequence": "Loader and packager enforce the same payload-start alignment." }, { - "topic": "Flags and reserved word", - "producer": "Writes flags=REQUIRED and fixed-header reserved=0.", - "loader": "Requires the REQUIRED bit in header and entries. Does not independently reject extra flag bits or nonzero reserved.", - "consequence": "Preserve canonical generator values; do not claim strict reserved-bit rejection." + "topic": "Flags and workload", + "producer": "Writes flags=1 and the explicit V2 workload discriminator.", + "loader": "Rejects additional flag bits, unknown workloads, V1 and nonzero unused descriptors.", + "consequence": "Rebuild old bundles; do not infer workload from placeholder payload bytes." }, { "topic": "Payload placement and fill", "producer": "Places payloads sequentially without overlap, filling unused image bytes with 0xFF.", - "loader": "Bounds each entry within the bundle/SDRAM ranges; does not independently enforce sequential disjoint extents or padding values.", - "consequence": "Do not confuse deterministic output layout with stronger validation that is absent from the loader." + "loader": "Enforces monotonic non-overlapping flash payload extents within the bundle. Exact fixed load windows are disjoint. Padding bytes are not checked.", + "consequence": "Overlap is rejected; padding is neither payload data nor authenticated metadata." }, { "topic": "Capacity and length", diff --git a/publications/software_reference.py b/publications/software_reference.py index d4b0d5c3..3f0e870c 100644 --- a/publications/software_reference.py +++ b/publications/software_reference.py @@ -174,13 +174,15 @@ def linux_platform(root: Path) -> dict: raise ValueError("CPU and UART device-tree clock declarations disagree") if dt_value("linux,initrd-start") != dt_value("linux,initrd-end"): raise ValueError("reviewed initrd template placeholder changed") - ready = (root / "app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp").read_text(encoding="utf-8") - writes = [(int(a, 0), int(v, 0)) for a, v in re.findall(r"^\s*devmem\s+(0x[\da-fA-F]+)\s+32\s+(0x[\da-fA-F]+)", ready, re.M)] - expected = [(0x10019020, 1), (0x10019024, 0x4C4E5801), (0x10019028, 1), (0x1001902C, 1)] - if writes != expected: - raise ValueError("Linux ready mailbox sequence changed") - if ready.find('echo "retroSoC HP Linux ready"') < 0 or ready.index('echo "retroSoC HP Linux ready"') > ready.index("devmem"): + ready = (root / "app/ports/linux/hp_ready.c").read_text(encoding="utf-8") + ordered = ['puts("retroSoC HP Linux ready")', 'mailbox[8] = (error == 0U) ? 1U : 3U;', + 'mailbox[9] = (error == 0U) ? UINT32_C(0x4C4E5801) : error;', + 'mailbox[10] = 1U;', '"fence iorw, iorw"', 'mailbox[11] = 1U;'] + if any(text not in ready for text in ordered) or [ready.index(text) for text in ordered] != sorted(ready.index(text) for text in ordered): raise ValueError("Linux ready message/publication order changed") + writes = [(0x10019020, 1), (0x10019024, 0x4C4E5801), (0x10019028, 1), (0x1001902C, 1)] + if "descriptor, 0x10019000)" not in ready: + raise ValueError("Linux mailbox mapping changed") return {"hart_id": 1, "timebase_hz": timebase, "clock_hz": int(clocks[0]), "cbom_bytes": dt_value("riscv,cbom-block-size"), "memory_base": memory[0][0], "memory_bytes": int(memory[0][1], 0), "initrd_start": dt_value("linux,initrd-start"), diff --git a/requirements/README.md b/requirements/README.md index 26ee18f7..4a1f5002 100644 --- a/requirements/README.md +++ b/requirements/README.md @@ -6,6 +6,7 @@ automation. - `build.txt` provides dependencies for setup, build, and regression helpers. - `ci.txt` provides the fast quality tools, including Python lint and test dependencies. +- `rtthread.txt` pins SCons for the isolated HP RT-Thread build environment. Update requirements deliberately and keep their hashes/pins compatible with the repository lock policy. Validate the affected commands locally and in CI; diff --git a/requirements/rtthread.txt b/requirements/rtthread.txt new file mode 100644 index 00000000..00b9cebd --- /dev/null +++ b/requirements/rtthread.txt @@ -0,0 +1,4 @@ +# Official RT-Thread build frontend, isolated from the SDK Python environment. +--only-binary=:all: +--require-hashes +scons==4.8.1 --hash=sha256:a4c3b434330e2d7d975002fd6783284ba348bf394db94c8f83fdc5bf69cdb8d7 diff --git a/scripts/README.md b/scripts/README.md index b974ebec..a02bad48 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -47,11 +47,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 diff --git a/scripts/build_hp_linux.py b/scripts/build_hp_linux.py index 31d5dab8..0d3fd5ce 100644 --- a/scripts/build_hp_linux.py +++ b/scripts/build_hp_linux.py @@ -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,30 +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) @@ -60,12 +75,44 @@ 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(): @@ -94,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" @@ -101,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" @@ -133,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", @@ -171,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", @@ -186,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": { diff --git a/scripts/build_hp_rtthread.py b/scripts/build_hp_rtthread.py new file mode 100644 index 00000000..8f627427 --- /dev/null +++ b/scripts/build_hp_rtthread.py @@ -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() diff --git a/scripts/build_hp_smoke.py b/scripts/build_hp_smoke.py index 7fb50ebf..cad6feaa 100644 --- a/scripts/build_hp_smoke.py +++ b/scripts/build_hp_smoke.py @@ -7,6 +7,11 @@ import argparse import subprocess from pathlib import Path +try: + from scripts.hp_tools import require_rv64_elf +except ModuleNotFoundError: + from hp_tools import require_rv64_elf + def build(args: argparse.Namespace) -> None: output = args.output.resolve() @@ -18,8 +23,8 @@ def build(args: argparse.Namespace) -> None: subprocess.run( [ compiler, - "-march=rv32imafdc_zicbom_zicsr_zifencei", - "-mabi=ilp32d", + "-march=rv64imafdc_zicbom_zicsr_zifencei", + "-mabi=lp64d", "-nostdlib", "-nostartfiles", "-Wl,--build-id=none", @@ -30,13 +35,11 @@ def build(args: argparse.Namespace) -> None: ], check=True, ) + require_rv64_elf(elf) subprocess.run( - [objcopy, "-O", "binary", str(elf), str(images / "fw_jump.bin")], + [objcopy, "-O", "binary", str(elf), str(images / "hp_smoke.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: @@ -44,7 +47,7 @@ def main() -> None: 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-") + parser.add_argument("--cross", default="riscv64-unknown-elf-") build(parser.parse_args()) diff --git a/scripts/generate_vexiiriscv.py b/scripts/generate_vexiiriscv.py index 14a31869..bbe80b24 100644 --- a/scripts/generate_vexiiriscv.py +++ b/scripts/generate_vexiiriscv.py @@ -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, diff --git a/scripts/hp_tools.py b/scripts/hp_tools.py new file mode 100644 index 00000000..fe052cb2 --- /dev/null +++ b/scripts/hp_tools.py @@ -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(" 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())) diff --git a/scripts/package_hp_boot.py b/scripts/package_hp_boot.py index 851e1c70..f692445c 100755 --- a/scripts/package_hp_boot.py +++ b/scripts/package_hp_boot.py @@ -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()) diff --git a/scripts/run_flow.py b/scripts/run_flow.py index 73686962..8337099b 100644 --- a/scripts/run_flow.py +++ b/scripts/run_flow.py @@ -111,6 +111,11 @@ def main() -> int: args.result.parent.mkdir(parents=True, exist_ok=True) started = datetime.now(timezone.utc) start_clock = time.monotonic() + atomic_write(args.result, json.dumps({ + "schema_version": 1, "tool": args.tool, "command": command, + "started_at": started.isoformat(), "status": "running", + "log": str(args.log.resolve()), + }, indent=2, sort_keys=True) + "\n") returncode = 127 error_message = None process: subprocess.Popen[str] | subprocess.Popen[bytes] | None = None @@ -183,7 +188,7 @@ def main() -> int: "finished_at": finished.isoformat(), "duration_seconds": round(time.monotonic() - start_clock, 3), "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: diff --git a/scripts/run_hp_sim.py b/scripts/run_hp_sim.py new file mode 100644 index 00000000..6fb86096 --- /dev/null +++ b/scripts/run_hp_sim.py @@ -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()) diff --git a/scripts/setup_hp_rtthread.py b/scripts/setup_hp_rtthread.py new file mode 100644 index 00000000..8d0f3acb --- /dev/null +++ b/scripts/setup_hp_rtthread.py @@ -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() diff --git a/scripts/vexiiriscv/GenerateRetroSocHp.scala b/scripts/vexiiriscv/GenerateRetroSocHp.scala index 858664aa..1c8e7201 100644 --- a/scripts/vexiiriscv/GenerateRetroSocHp.scala +++ b/scripts/vexiiriscv/GenerateRetroSocHp.scala @@ -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 ") @@ -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 diff --git a/tests/test_hp_boot_bundle.py b/tests/test_hp_boot_bundle.py index b3b162f4..522c7103 100644 --- a/tests/test_hp_boot_bundle.py +++ b/tests/test_hp_boot_bundle.py @@ -3,12 +3,15 @@ from __future__ import annotations import argparse +import ctypes import importlib.util import re import struct +import subprocess import zlib from pathlib import Path +import pytest ROOT = Path(__file__).resolve().parents[1] PACKAGER = ROOT / "scripts/package_hp_boot.py" @@ -48,7 +51,7 @@ def test_hp_boot_bundle_layout_crc_and_payloads(tmp_path: Path) -> None: image = output.read_bytes() header_values = module.HEADER.unpack_from(image, module.BUNDLE_OFFSET) - magic, version, header_size, entry_count, total_size, header_crc, flags, reserved = ( + magic, version, header_size, entry_count, total_size, header_crc, flags, workload = ( header_values ) assert magic == module.MAGIC @@ -57,7 +60,7 @@ def test_hp_boot_bundle_layout_crc_and_payloads(tmp_path: Path) -> None: assert entry_count == len(module.ARTIFACTS) == 4 assert total_size == len(image) - module.BUNDLE_OFFSET assert flags == module.REQUIRED - assert reserved == 0 + assert workload == 1 header = bytearray(image[module.BUNDLE_OFFSET : module.BUNDLE_OFFSET + header_size]) struct.pack_into(" None: assert entry_flags == module.REQUIRED +@pytest.fixture(scope="module") +def loader(tmp_path_factory): + library = tmp_path_factory.mktemp("hp-loader") / "loader.so" + subprocess.run(["cc", "-Wall", "-Wextra", "-Werror", "-shared", "-fPIC", + str(ROOT / "app/apps/hp_boot/hp_boot_bundle.c"), "-o", str(library)], check=True) + result = ctypes.CDLL(str(library)) + result.rs_hp_boot_header_valid.argtypes = [ctypes.c_void_p] + result.rs_hp_boot_header_valid.restype = ctypes.c_bool + result.rs_hp_boot_message_status.argtypes = [ctypes.c_uint32] * 6 + result.rs_hp_boot_message_status.restype = ctypes.c_uint32 + return result + + +@pytest.mark.parametrize("workload", ["linux", "smoke", "rtthread"]) +def test_v2_loader_accepts_canonical_bundle_and_rejects_bad_headers(tmp_path, loader, workload): + module = load_packager() + firmware = tmp_path / "lp.bin" + firmware.write_bytes(b"LP") + images = tmp_path / "images" + images.mkdir() + for _, name, _, _ in module.artifacts_for(workload): + (images / name).write_bytes(b"payload") + output = tmp_path / "bundle.bin" + module.package(argparse.Namespace(firmware=firmware, images=images, output=output, + manifest=tmp_path / "bundle.json", workload=workload)) + header = output.read_bytes()[module.BUNDLE_OFFSET:module.BUNDLE_OFFSET + 128] + assert loader.rs_hp_boot_header_valid(ctypes.create_string_buffer(header)) + # Every mutation has a corrected CRC, so structural checks are exercised. + mutations = [(4, 1), (8, 64), (12, 0), (16, 0xFFFFFFFF), (24, 3), (28, 99), + (32, 99), (36, 0x100000), (36, 0xFFFFFFFF), (40, 0x38000004), + (44, 0), (44, 0x80001), (52, 3)] + mutations += [(60, 0x101000)] if workload == "linux" else [(56, 1)] + for offset, value in mutations: + changed = bytearray(header) + struct.pack_into(" None: rtl_pattern = re.compile(r"^`define\s+APB4_HP_MAILBOX__(\w+)\s+12'h([0-9A-Fa-f]+)$") c_pattern = re.compile( diff --git a/tests/test_hp_platform.py b/tests/test_hp_platform.py index 4e071309..38de598d 100644 --- a/tests/test_hp_platform.py +++ b/tests/test_hp_platform.py @@ -10,6 +10,7 @@ from pathlib import Path import pytest from scripts import build_hp_linux +from scripts.run_hp_sim import MARKERS ROOT = Path(__file__).resolve().parents[1] @@ -24,7 +25,7 @@ def test_hp_profile_and_locked_generator_contract() -> None: profile = (ROOT / "configs/ci/ihp130-hp.mk").read_text(encoding="utf-8") assert "HAVE_HP" in profile and "YES" in profile - assert "rv32imafdc_zicbom_max" in profile + assert "rv64imafdc_zicbom_max" in profile assert "APP" in profile and "hp_boot" in profile assert "LINK_TYPE" in profile and "ld2_all_sram" in profile @@ -32,6 +33,8 @@ def test_hp_profile_and_locked_generator_contract() -> None: encoding="utf-8" ) for requirement in ( + "param.xlen = 64", + "param.physicalWidth = 32", 'param.addISA("m", "a", "f", "d", "c", "s", "u", "zicbom", "zicntr", "zihpm")', "param.decoders = 2", "param.lanes = 2", @@ -56,14 +59,14 @@ def test_hp_linux_simulation_uses_explicit_fast_flash_acceptance() -> None: assert "HP_LINUX_SIM_TIME ?= 0" in makefile assert "hp-linux-sim: hp-bundle comp" in makefile - assert "VERILATOR_SIM_ARGS=--fast-flash sim" in makefile + assert "--workload linux --timeout $(HP_LINUX_SIM_TIME)" in makefile for marker in ( "VERILATOR_FAST_FLASH=enabled", "retroSoC HP Linux ready", "HP_LINUX_READY", "SIM_TEST_PASS code=0", ): - assert f"--require '{marker}'" in makefile + assert marker in (*MARKERS["linux"], "SIM_TEST_PASS code=0", "VERILATOR_FAST_FLASH=enabled") assert "VERILATOR_SIM_ARGS ?=" in verilator_makefile assert "$(VERILATOR_SIM_ARGS) -t $(SOC_SIM_TIME)" in verilator_makefile assert '("fast-flash"' in emulator @@ -117,8 +120,7 @@ def test_hp_smoke_simulation_requires_ga2d_result_and_cache_lifecycle_markers() target = makefile.split("hp-smoke-sim: hp-smoke-bundle comp", 1)[1].split("\nifeq", 1)[0] assert "HP_SMOKE_SIM_TIME ?= 300" in makefile - assert "SOC_SIM_TIME=$(HP_SMOKE_SIM_TIME)" in target - assert "VERILATOR_SIM_ARGS=--fast-flash" in target + assert "--workload smoke --timeout $(HP_SMOKE_SIM_TIME)" in target for marker in ( "SIM_TEST_PASS code=0", @@ -126,7 +128,7 @@ def test_hp_smoke_simulation_requires_ga2d_result_and_cache_lifecycle_markers() "HP_GA2D_PASS", "HP_GA2D_CACHE_CLEAN", ): - assert f"--require '{marker}'" in target + assert marker in (*MARKERS["smoke"], "SIM_TEST_PASS code=0") def test_hp_cache_handshake_budget_covers_cbo_and_mailbox_round_trip() -> None: @@ -217,6 +219,14 @@ def test_hp_linux_device_tree_compiles_and_can_patch_initrd_end(tmp_path: Path) ).strip() assert initrd_end == "39123456" assert hart_id == "1" + reservation = subprocess.check_output( + [fdtget, "-t", "x", str(dtb), "/reserved-memory/opensbi@38000000", "reg"], text=True + ).strip() + assert reservation == "38000000 80000" + properties = subprocess.check_output( + [fdtget, "-p", str(dtb), "/reserved-memory/opensbi@38000000"], text=True + ).splitlines() + assert "no-map" in properties dts = (ROOT / "app/ports/linux/linux/retrosoc_hp.dts").read_text(encoding="utf-8") assert '"zicbom"' in dts assert "riscv,cbom-block-size = <64>;" in dts diff --git a/tests/test_hp_tools.py b/tests/test_hp_tools.py new file mode 100644 index 00000000..7784da09 --- /dev/null +++ b/tests/test_hp_tools.py @@ -0,0 +1,78 @@ +"""RV64 artifact admission and workload-specific simulation verdicts.""" + +from __future__ import annotations + +import json +import signal +import struct +import subprocess +import sys +import time +from pathlib import Path + +import pytest + +from scripts.hp_tools import require_rv64_elf +from scripts.run_hp_sim import MARKERS + +ROOT = Path(__file__).resolve().parents[1] + + +@pytest.mark.parametrize("elf_class,machine,accepted", [(2, 243, True), (1, 243, False), (2, 62, False)]) +def test_hp_executable_class(elf_class, machine, accepted, tmp_path): + header = bytearray(64) + header[:6] = b"\x7fELF" + bytes((elf_class, 1)) + struct.pack_into(" None: for text in ( "High-Performance-Core Specification:", "Core: VexiiRiscv(hart 1), dual-issue in-order", - "Base: RV32IMAFDC_Zicbom_Zicntr_Zihpm", - "Mode: M/S/U; Sv32 MMU, 9-bit ASID", + "Base: RV64IMAFDC_Zicbom_Zicntr_Zihpm", + "Mode: M/S/U; Sv39 MMU, 9-bit ASID", "Protection: 16 PMP regions, 4 KiB granularity", "L1: separate 16 KiB, 4-way instruction and data caches", "Maintenance: 64-byte Zicbom CBO; no hardware coherency", - "Bus: native AXI64 I/D + cacheless AXI32 MMIO", + "Bus: native AXI64 I/D/MMIO; MMIO downsized to AXI32", "Embedded RISC-V JTAG, 4 triggers", ): assert text in booter diff --git a/tests/test_publication_api_bundle.py b/tests/test_publication_api_bundle.py index 7dfcda13..467f3beb 100644 --- a/tests/test_publication_api_bundle.py +++ b/tests/test_publication_api_bundle.py @@ -228,12 +228,12 @@ int main(int argc, char **argv) { bool expected=true; switch(scenario) { case 30: break; - case 31: header.reserved=0x1234; header.flags|=0x80; header.entries[0].flags|=0x80; break; - case 32: header.entries[0].flash_offset+=4; break; + case 31: header.workload=0x1234; header.flags|=0x80; header.entries[0].flags|=0x80; expected=false; break; + case 32: header.entries[0].flash_offset+=4; expected=false; break; case 33: header.entries[0].flash_offset+=1; expected=false; break; case 34: header.entries[0].flags=0; expected=false; break; case 35: header.header_crc32^=1; assert(!rs_hp_boot_header_valid(&header)); return 0; - case 36: header.entries[1].flash_offset=header.entries[0].flash_offset; break; + case 36: header.entries[1].flash_offset=header.entries[0].flash_offset; expected=false; break; case 37: header.entries[0].size=0; expected=false; break; case 38: header.header_size+=4; expected=false; break; case 39: header.entry_count=3; expected=false; break; @@ -281,8 +281,8 @@ def c_behavior(tmp_path_factory): } for family, names in selections.items(): extracted += "\n".join(real_definition(sources[family], name) for name in names) - boot = (ROOT / "app/apps/hp_boot/main.c").read_text(encoding="utf-8") - extracted += "\n".join(real_definition(boot, row["function"]) for row in SPEC["boot_bundle"]["loader_bindings"]) + extracted += "\n".join(real_definition((ROOT / row["source"]).read_text(encoding="utf-8"), row["function"]) + for row in SPEC["boot_bundle"]["loader_bindings"]) source = folder / "behavior.c" source.write_text(constants + "\n" + HARNESS.replace("/* REAL_FUNCTIONS */", extracted), encoding="utf-8") exe = folder / "behavior" diff --git a/tests/test_publication_retrieval.py b/tests/test_publication_retrieval.py index 5d55d838..3ced38cb 100644 --- a/tests/test_publication_retrieval.py +++ b/tests/test_publication_retrieval.py @@ -129,7 +129,7 @@ def test_all_code_namespaces_and_boot_codes_are_source_checked(): assert next(g for g in groups if g["id"] == "rib-detail")["expected"]["RIB_RESP_BURSTERR"] == 6 assert next(g for g in groups if g["id"] == "sdio-dma-mask")["kind"] == "mask" assert len(next(g for g in groups if g["id"] == "jpeg-error")["rows"]) == 19 - assert [r["value"] for r in rr.boot_status(ROOT, SPEC["boot"])["rows"]] == list(range(18)) + assert [r["value"] for r in rr.boot_status(ROOT, SPEC["boot"])["rows"]] == list(range(20)) @pytest.mark.parametrize("mutation", ["missing", "value", "meaning", "mask"]) diff --git a/tests/test_publication_software.py b/tests/test_publication_software.py index 9ddb0e97..46711461 100644 --- a/tests/test_publication_software.py +++ b/tests/test_publication_software.py @@ -140,7 +140,7 @@ def test_linux_platform_uses_source_values_and_order(): ("app/ports/linux/linux/retrosoc_hp.dts", "cpu@1", "cpu@0"), ("app/ports/linux/linux/retrosoc_hp.dts", "linux,initrd-end = <0x39000000>", "linux,initrd-end = <0x39100000>"), ("app/ports/linux/opensbi/retrosoc_hp/platform.c", ".mtime_freq = 1000000UL", ".mtime_freq = 2000000UL"), - ("app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp", "0x10019024 32 0x4C4E5801", "0x10019024 32 0x4C4E5802"), + ("app/ports/linux/hp_ready.c", "0x4C4E5801", "0x4C4E5802"), ]) def test_linux_consistency_and_ready_event_drift_fail(source_tree, relative, old, new): replace(source_tree, relative, old, new) @@ -149,20 +149,21 @@ def test_linux_consistency_and_ready_event_drift_fail(source_tree, relative, old def test_ready_request_cannot_move_before_payload(source_tree): - path = source_tree / "app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp" + path = source_tree / "app/ports/linux/hp_ready.c" lines = path.read_text().splitlines() - first = next(i for i, line in enumerate(lines) if "devmem" in line) - lines[first], lines[first + 3] = lines[first + 3], lines[first] + first = next(i for i, line in enumerate(lines) if "mailbox[8]" in line) + last = next(i for i, line in enumerate(lines) if "mailbox[11]" in line) + lines[first], lines[last] = lines[last], lines[first] path.write_text("\n".join(lines)) - with pytest.raises(ValueError, match="sequence"): + with pytest.raises(ValueError, match="publication"): sr.linux_platform(source_tree) def test_ready_text_order_cannot_be_silently_reversed(source_tree): - path = source_tree / "app/ports/linux/rootfs-overlay/etc/init.d/S99retrosoc-hp" + path = source_tree / "app/ports/linux/hp_ready.c" text = path.read_text() - message = ' echo "retroSoC HP Linux ready"' - lines = [line for line in text.splitlines() if 'echo "retroSoC HP Linux ready"' not in line] + message = ' (void)puts("retroSoC HP Linux ready");' + lines = [line for line in text.splitlines() if 'puts("retroSoC HP Linux ready")' not in line] lines.append(message) path.write_text("\n".join(lines)) with pytest.raises(ValueError, match="message/publication"): diff --git a/tests/test_script_tools.py b/tests/test_script_tools.py index 0c3bd2ff..b1f7ce8a 100644 --- a/tests/test_script_tools.py +++ b/tests/test_script_tools.py @@ -1887,7 +1887,7 @@ def test_run_flow_records_interruption(tmp_path: Path) -> None: process.communicate(timeout=10) assert process.returncode == 130 data = json.loads(result.read_text(encoding="utf-8")) - assert data["status"] == "failed" + assert data["status"] == "interrupted" assert data["exit_code"] == 130 assert data["error"] == "interrupted"