mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat: refactor and enhance documentation for Linux runtime, boot acceptance, and NPU integration
- Updated fault reference documentation to clarify Linux termination behavior and NPU-P6 requirements. - Enhanced Linux runtime section with emphasis on platform setup and kernel handoff processes. - Revised overview section to include detailed descriptions of NPU capabilities and memory configurations. - Improved product use documentation to specify Linux rootfs behavior and acceptance checkpoints. - Clarified reset summary to differentiate between Linux and smoke/NPU-P6 acceptance processes. - Updated software section to reflect changes in Linux image loading and readiness protocols. - Modified build scripts to support RV64 architecture and ensure proper ELF requirements. - Enhanced NPU compiler to support cycle counting for RV64 and RV32 architectures. - Added tests to validate new configurations and ensure compliance with updated publication contracts.
This commit is contained in:
30 files changed
+539
-135
No files matched your search
@@ -1020,7 +1020,7 @@ hp-smoke-sim: hp-smoke-bundle comp
|
|||||||
python3 $(ROOT_PATH)/scripts/run_hp_sim.py --emulator $(VERILATOR_EMU) \
|
python3 $(ROOT_PATH)/scripts/run_hp_sim.py --emulator $(VERILATOR_EMU) \
|
||||||
--image $(HP_SMOKE_BUNDLE_BIN) --output $(SIM_BUILD_ROOT)/hp-smoke \
|
--image $(HP_SMOKE_BUNDLE_BIN) --output $(SIM_BUILD_ROOT)/hp-smoke \
|
||||||
--workload smoke --timeout $(HP_SMOKE_SIM_TIME)
|
--workload smoke --timeout $(HP_SMOKE_SIM_TIME)
|
||||||
$(HP_APU_STAMP): $(ROOT_PATH)/scripts/build_hp_apu.py \
|
$(HP_APU_STAMP): $(ROOT_PATH)/scripts/build_hp_apu.py $(ROOT_PATH)/scripts/hp_tools.py \
|
||||||
$(ROOT_PATH)/app/ports/hp-apu/start.S \
|
$(ROOT_PATH)/app/ports/hp-apu/start.S \
|
||||||
$(ROOT_PATH)/app/ports/hp-apu/main.c \
|
$(ROOT_PATH)/app/ports/hp-apu/main.c \
|
||||||
$(ROOT_PATH)/app/ports/hp-apu/linker.ld \
|
$(ROOT_PATH)/app/ports/hp-apu/linker.ld \
|
||||||
@@ -1029,7 +1029,7 @@ $(HP_APU_STAMP): $(ROOT_PATH)/scripts/build_hp_apu.py \
|
|||||||
$(MEMORY_MAP_STAMP) $(USER_EXTENSIONS_STAMP)
|
$(MEMORY_MAP_STAMP) $(USER_EXTENSIONS_STAMP)
|
||||||
python3 $(ROOT_PATH)/scripts/build_hp_apu.py \
|
python3 $(ROOT_PATH)/scripts/build_hp_apu.py \
|
||||||
--source-dir $(ROOT_PATH)/app/ports/hp-apu \
|
--source-dir $(ROOT_PATH)/app/ports/hp-apu \
|
||||||
--output $(HP_APU_BUILD_DIR) --cross $(CROSS) \
|
--output $(HP_APU_BUILD_DIR) --cross $(HP_CROSS) \
|
||||||
--include $(MEMORY_MAP_C_DIR) \
|
--include $(MEMORY_MAP_C_DIR) \
|
||||||
--include $(USER_EXTENSIONS_DIR)/include \
|
--include $(USER_EXTENSIONS_DIR)/include \
|
||||||
--include $(ROOT_PATH)/crt/include \
|
--include $(ROOT_PATH)/crt/include \
|
||||||
@@ -1199,4 +1199,4 @@ regress-nightly:
|
|||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY)
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY)
|
||||||
|
|
||||||
sim-asm: asm
|
sim-asm: asm
|
||||||
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
|
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
|
||||||
@@ -33,6 +33,7 @@ The build selects an application with `APP=<name>`. The supported profiles are:
|
|||||||
| `coremark` | SRAM-resident Hazard3 CoreMark measurement; use the committed quick or standard profile. |
|
| `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. |
|
| `debug` | Minimal SRAM image used only by the Hazard3 OpenOCD/GDB acceptance flow. |
|
||||||
| `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. |
|
| `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. |
|
||||||
|
| `apu_release` | PSRAM-resident LP/HP APU evidence flow: LP-only microcode/KWS model load, ownership handoff to RV64 HP, HP-submitted WAV/KWS jobs with Zicbom maintenance, and LP-only register fault probe. |
|
||||||
| `shell` | Interactive application that adds shell services, board drivers, media, FatFs, CoreMark, and UserIP integration. |
|
| `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. |
|
| `xpi_flash_loader` | SRAM-resident, GDB-called service image for sector-preserving JTAG programming of the qualified NSS0 NOR. |
|
||||||
|
|
||||||
@@ -40,6 +41,25 @@ The application manifest is loaded from `app/apps/<name>/app.mk`. It appends
|
|||||||
`APP_SRCS` and, when necessary, `APP_INC_DIRS` to the SDK sources selected by
|
`APP_SRCS` and, when necessary, `APP_INC_DIRS` to the SDK sources selected by
|
||||||
the central build.
|
the central build.
|
||||||
|
|
||||||
|
NPU-P5 acceptance extends existing compositions rather than adding an APP.
|
||||||
|
`NPU_P5_ACCEPTANCE=YES` is valid only with `ci_smoke` or `hp_boot` and is part
|
||||||
|
of the reproducible variant key. The LP path links the generated KWS plan into
|
||||||
|
`ci_smoke`, uses the real LP completion interrupt, and writes the normal
|
||||||
|
SYSCTRL verdict. The RV64 HP smoke bundle assigns resource 9 to hart 1, runs
|
||||||
|
the same plan with polling and 64-byte Zicbom maintenance, then returns
|
||||||
|
ownership before LP publishes the final verdict. The option defaults to `NO`,
|
||||||
|
preserving normal PR firmware size and behavior.
|
||||||
|
|
||||||
|
NPU-P6 likewise reuses `APP=hp_boot`; it does not add an application profile
|
||||||
|
or public ABI. `NPU_P6_ACCEPTANCE=YES` selects a private qualification payload,
|
||||||
|
and `NPU_P6_WORKLOAD=kws|vww` selects one frozen generated plan. The payload
|
||||||
|
reads a CRC-protected 100-case shard from the existing HP `Image` slot, runs
|
||||||
|
the portable C and production NPU paths through CPU Softmax, measures both
|
||||||
|
with HP `rdcycle`, and emits fail-closed per-case records. The first ten global
|
||||||
|
cases also overlap an identical GA2D DMA copy with both measured paths.
|
||||||
|
Normal and P5 builds remain unchanged because P5 and P6 acceptance flags are
|
||||||
|
mutually exclusive and both default to `NO`.
|
||||||
|
|
||||||
Build and simulate the supported profiles from the repository root:
|
Build and simulate the supported profiles from the repository root:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -328,7 +328,9 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
printf("HP_LINUX_READY\n");
|
printf("HP_LINUX_READY\n");
|
||||||
if (header.workload == RS_HP_BOOT_WORKLOAD_LINUX) {
|
if (header.workload == RS_HP_BOOT_WORKLOAD_LINUX) {
|
||||||
|
#if !defined(RS_NPU_P6_ACCEPTANCE)
|
||||||
rs_test_finish(RS_TEST_PASSED, UINT8_C(0));
|
rs_test_finish(RS_TEST_PASSED, UINT8_C(0));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
message.code = RS_HP_BOOT_GA2D_START_COMMAND;
|
message.code = RS_HP_BOOT_GA2D_START_COMMAND;
|
||||||
message.argument = RS_HP_BOOT_GA2D_START_ARG;
|
message.argument = RS_HP_BOOT_GA2D_START_ARG;
|
||||||
|
|||||||
@@ -96,6 +96,12 @@ static int8_t s_npu_output[RS_NPU_P6_OUTPUT_BYTES];
|
|||||||
static bool s_uart_ok;
|
static bool s_uart_ok;
|
||||||
|
|
||||||
static uint64_t rs_npu_p6_cycles(void) {
|
static uint64_t rs_npu_p6_cycles(void) {
|
||||||
|
#if defined(__riscv_xlen) && (__riscv_xlen == 64)
|
||||||
|
uint64_t cycle;
|
||||||
|
|
||||||
|
__asm__ volatile("rdcycle %0" : "=r"(cycle));
|
||||||
|
return cycle;
|
||||||
|
#else
|
||||||
uint32_t high_before;
|
uint32_t high_before;
|
||||||
uint32_t low;
|
uint32_t low;
|
||||||
uint32_t high_after;
|
uint32_t high_after;
|
||||||
@@ -106,6 +112,7 @@ static uint64_t rs_npu_p6_cycles(void) {
|
|||||||
__asm__ volatile("rdcycleh %0" : "=r"(high_after));
|
__asm__ volatile("rdcycleh %0" : "=r"(high_after));
|
||||||
} while (high_before != high_after);
|
} while (high_before != high_after);
|
||||||
return ((uint64_t)high_before << 32U) | low;
|
return ((uint64_t)high_before << 32U) | low;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rs_npu_p6_uart_character(char character) {
|
static void rs_npu_p6_uart_character(char character) {
|
||||||
@@ -182,11 +189,14 @@ static bool rs_npu_p6_equal(const uint8_t *left, const uint8_t *right, uint32_t
|
|||||||
return equal;
|
return equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rs_npu_p6_invalidate_range(const void *pointer, uint32_t bytes);
|
||||||
|
|
||||||
static bool rs_npu_p6_parse_shard(rs_npu_p6_shard_t *shard) {
|
static bool rs_npu_p6_parse_shard(rs_npu_p6_shard_t *shard) {
|
||||||
const uint8_t *image = (const uint8_t *)(uintptr_t)RS_NPU_P6_SHARD_BASE;
|
const uint8_t *image = (const uint8_t *)(uintptr_t)RS_NPU_P6_SHARD_BASE;
|
||||||
const uint32_t *words = (const uint32_t *)(const void *)image;
|
const uint32_t *words = (const uint32_t *)(const void *)image;
|
||||||
uint32_t payload_bytes;
|
uint32_t payload_bytes;
|
||||||
|
|
||||||
|
rs_npu_p6_invalidate_range(image, RS_NPU_P6_HEADER_BYTES);
|
||||||
if ((shard == NULL) || (words[0] != RS_NPU_P6_SHARD_MAGIC) ||
|
if ((shard == NULL) || (words[0] != RS_NPU_P6_SHARD_MAGIC) ||
|
||||||
(words[1] != RS_NPU_P6_SHARD_SCHEMA) || (words[2] != RS_NPU_P6_WORKLOAD_ID) ||
|
(words[1] != RS_NPU_P6_SHARD_SCHEMA) || (words[2] != RS_NPU_P6_WORKLOAD_ID) ||
|
||||||
(words[4] != UINT32_C(10)) || (words[6] != RS_NPU_P6_CASES_PER_SHARD) ||
|
(words[4] != UINT32_C(10)) || (words[6] != RS_NPU_P6_CASES_PER_SHARD) ||
|
||||||
@@ -201,6 +211,7 @@ static bool rs_npu_p6_parse_shard(rs_npu_p6_shard_t *shard) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
payload_bytes = words[6] * words[9];
|
payload_bytes = words[6] * words[9];
|
||||||
|
rs_npu_p6_invalidate_range(&image[RS_NPU_P6_HEADER_BYTES], payload_bytes);
|
||||||
if (rs_npu_p6_crc32(&image[RS_NPU_P6_HEADER_BYTES], payload_bytes) != words[10]) {
|
if (rs_npu_p6_crc32(&image[RS_NPU_P6_HEADER_BYTES], payload_bytes) != words[10]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -262,6 +273,15 @@ static void rs_npu_p6_field_u64(const char *name, uint64_t value) {
|
|||||||
rs_npu_p6_uart_u64(value);
|
rs_npu_p6_uart_u64(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rs_npu_p6_stage(uint32_t case_index, const char *stage, uint64_t cycles) {
|
||||||
|
rs_npu_p6_uart_text("NPU_P6_STAGE workload=" RS_NPU_P6_WORKLOAD_NAME);
|
||||||
|
rs_npu_p6_field_u32("index", case_index);
|
||||||
|
rs_npu_p6_uart_text(" stage=");
|
||||||
|
rs_npu_p6_uart_text(stage);
|
||||||
|
rs_npu_p6_field_u64("cycles", cycles);
|
||||||
|
rs_npu_p6_uart_character('\n');
|
||||||
|
}
|
||||||
|
|
||||||
static bool rs_npu_p6_case_fail(uint32_t case_index, const char *stage) {
|
static bool rs_npu_p6_case_fail(uint32_t case_index, const char *stage) {
|
||||||
rs_npu_p6_uart_text("NPU_P6_CASE_FAIL workload=" RS_NPU_P6_WORKLOAD_NAME);
|
rs_npu_p6_uart_text("NPU_P6_CASE_FAIL workload=" RS_NPU_P6_WORKLOAD_NAME);
|
||||||
rs_npu_p6_field_u32("index", case_index);
|
rs_npu_p6_field_u32("index", case_index);
|
||||||
@@ -301,7 +321,7 @@ static void rs_npu_p6_case_log(uint32_t shard_index, uint32_t case_index, uint32
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool rs_npu_p6_run_case(const uint8_t *record, uint32_t expected_index, uint32_t shard_index,
|
static bool rs_npu_p6_run_case(const uint8_t *record, uint32_t expected_index, uint32_t shard_index,
|
||||||
bool cold_cache) {
|
bool cold_cache, bool trace_case) {
|
||||||
const uint32_t *header = (const uint32_t *)(const void *)record;
|
const uint32_t *header = (const uint32_t *)(const void *)record;
|
||||||
const int8_t *input = (const int8_t *)(const void *)&record[RS_NPU_P6_CASE_HEADER_BYTES];
|
const int8_t *input = (const int8_t *)(const void *)&record[RS_NPU_P6_CASE_HEADER_BYTES];
|
||||||
const uint8_t *expected_terminal = &record[RS_NPU_P6_CASE_HEADER_BYTES + RS_NPU_P6_INPUT_BYTES];
|
const uint8_t *expected_terminal = &record[RS_NPU_P6_CASE_HEADER_BYTES + RS_NPU_P6_INPUT_BYTES];
|
||||||
@@ -313,8 +333,13 @@ static bool rs_npu_p6_run_case(const uint8_t *record, uint32_t expected_index, u
|
|||||||
uint64_t started;
|
uint64_t started;
|
||||||
uint64_t reference_cycles;
|
uint64_t reference_cycles;
|
||||||
uint64_t npu_cycles;
|
uint64_t npu_cycles;
|
||||||
|
rs_status_t status;
|
||||||
bool contention = expected_index < UINT32_C(10);
|
bool contention = expected_index < UINT32_C(10);
|
||||||
|
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "case-start", UINT64_C(0));
|
||||||
|
}
|
||||||
|
|
||||||
if ((header[0] != expected_index) ||
|
if ((header[0] != expected_index) ||
|
||||||
(rs_npu_p6_default_regions(&s_workspace, ®ions) != RS_OK) ||
|
(rs_npu_p6_default_regions(&s_workspace, ®ions) != RS_OK) ||
|
||||||
(rs_npu_p6_prepare(&s_workspace, ®ions, input, RS_NPU_P6_INPUT_BYTES) != RS_OK)) {
|
(rs_npu_p6_prepare(&s_workspace, ®ions, input, RS_NPU_P6_INPUT_BYTES) != RS_OK)) {
|
||||||
@@ -341,17 +366,39 @@ static bool rs_npu_p6_run_case(const uint8_t *record, uint32_t expected_index, u
|
|||||||
if (contention && !rs_npu_p6_start_contention()) {
|
if (contention && !rs_npu_p6_start_contention()) {
|
||||||
return rs_npu_p6_case_fail(expected_index, "contention-reference-start");
|
return rs_npu_p6_case_fail(expected_index, "contention-reference-start");
|
||||||
}
|
}
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "reference-start", UINT64_C(0));
|
||||||
|
}
|
||||||
started = rs_npu_p6_cycles();
|
started = rs_npu_p6_cycles();
|
||||||
if ((rs_npu_p6_reference_execute(&s_workspace.descriptors[0][0], RS_NPU_P6_DESCRIPTOR_COUNT,
|
status = rs_npu_p6_reference_execute(&s_workspace.descriptors[0][0], RS_NPU_P6_DESCRIPTOR_COUNT,
|
||||||
&memory) != RS_OK) ||
|
&memory);
|
||||||
(rs_npu_p6_softmax((const int8_t *)&s_workspace.arena[terminal_offset],
|
if (status != RS_OK) {
|
||||||
s_reference_output) != RS_OK)) {
|
|
||||||
return rs_npu_p6_case_fail(expected_index, "reference-execute");
|
return rs_npu_p6_case_fail(expected_index, "reference-execute");
|
||||||
}
|
}
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "reference-compute-done", rs_npu_p6_cycles() - started);
|
||||||
|
}
|
||||||
|
if (rs_npu_p6_softmax((const int8_t *)&s_workspace.arena[terminal_offset],
|
||||||
|
s_reference_output) != RS_OK) {
|
||||||
|
return rs_npu_p6_case_fail(expected_index, "reference-softmax");
|
||||||
|
}
|
||||||
reference_cycles = rs_npu_p6_cycles() - started;
|
reference_cycles = rs_npu_p6_cycles() - started;
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "softmax-done", reference_cycles);
|
||||||
|
}
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "ga2d-reference-start", UINT64_C(0));
|
||||||
|
}
|
||||||
|
started = rs_npu_p6_cycles();
|
||||||
if (contention && (rs_ga2d_wait(RS_TIMEOUT_DEFAULT * UINT32_C(64)) != RS_OK)) {
|
if (contention && (rs_ga2d_wait(RS_TIMEOUT_DEFAULT * UINT32_C(64)) != RS_OK)) {
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "ga2d-reference-timeout", rs_npu_p6_cycles() - started);
|
||||||
|
}
|
||||||
return rs_npu_p6_case_fail(expected_index, "contention-reference-wait");
|
return rs_npu_p6_case_fail(expected_index, "contention-reference-wait");
|
||||||
}
|
}
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "ga2d-reference-done", rs_npu_p6_cycles() - started);
|
||||||
|
}
|
||||||
if (!rs_npu_p6_equal(&s_workspace.arena[terminal_offset], expected_terminal,
|
if (!rs_npu_p6_equal(&s_workspace.arena[terminal_offset], expected_terminal,
|
||||||
RS_NPU_P6_OUTPUT_BYTES) ||
|
RS_NPU_P6_OUTPUT_BYTES) ||
|
||||||
!rs_npu_p6_equal((const uint8_t *)(const void *)s_reference_output, expected_softmax,
|
!rs_npu_p6_equal((const uint8_t *)(const void *)s_reference_output, expected_softmax,
|
||||||
@@ -364,15 +411,31 @@ static bool rs_npu_p6_run_case(const uint8_t *record, uint32_t expected_index, u
|
|||||||
if (contention && !rs_npu_p6_start_contention()) {
|
if (contention && !rs_npu_p6_start_contention()) {
|
||||||
return rs_npu_p6_case_fail(expected_index, "contention-npu-start");
|
return rs_npu_p6_case_fail(expected_index, "contention-npu-start");
|
||||||
}
|
}
|
||||||
if (rs_npu_p6_execute(&s_workspace, (RS_NPU_P6_WORKLOAD_ID << 24U) | expected_index,
|
if (trace_case) {
|
||||||
RS_TIMEOUT_DEFAULT * UINT32_C(64), s_npu_output, RS_NPU_P6_OUTPUT_BYTES,
|
rs_npu_p6_stage(expected_index, "npu-start", UINT64_C(0));
|
||||||
&s_profile) != RS_OK) {
|
}
|
||||||
|
status = rs_npu_p6_execute(&s_workspace, (RS_NPU_P6_WORKLOAD_ID << 24U) | expected_index,
|
||||||
|
RS_TIMEOUT_DEFAULT * UINT32_C(64), s_npu_output,
|
||||||
|
RS_NPU_P6_OUTPUT_BYTES, &s_profile);
|
||||||
|
if (status != RS_OK) {
|
||||||
return rs_npu_p6_case_fail(expected_index, "npu-execute");
|
return rs_npu_p6_case_fail(expected_index, "npu-execute");
|
||||||
}
|
}
|
||||||
npu_cycles = s_profile.npu_wait_cycles + s_profile.softmax_cycles;
|
npu_cycles = s_profile.npu_wait_cycles + s_profile.softmax_cycles;
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "npu-done", npu_cycles);
|
||||||
|
rs_npu_p6_stage(expected_index, "ga2d-npu-start", UINT64_C(0));
|
||||||
|
}
|
||||||
|
started = rs_npu_p6_cycles();
|
||||||
if (contention && (rs_ga2d_wait(RS_TIMEOUT_DEFAULT * UINT32_C(64)) != RS_OK)) {
|
if (contention && (rs_ga2d_wait(RS_TIMEOUT_DEFAULT * UINT32_C(64)) != RS_OK)) {
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "ga2d-npu-timeout", rs_npu_p6_cycles() - started);
|
||||||
|
}
|
||||||
return rs_npu_p6_case_fail(expected_index, "contention-npu-wait");
|
return rs_npu_p6_case_fail(expected_index, "contention-npu-wait");
|
||||||
}
|
}
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "ga2d-npu-done", rs_npu_p6_cycles() - started);
|
||||||
|
rs_npu_p6_stage(expected_index, "golden-start", UINT64_C(0));
|
||||||
|
}
|
||||||
if ((reference_cycles == 0U) || (npu_cycles == 0U) ||
|
if ((reference_cycles == 0U) || (npu_cycles == 0U) ||
|
||||||
(s_profile.counters.retired_descriptors != RS_NPU_P6_DESCRIPTOR_COUNT) ||
|
(s_profile.counters.retired_descriptors != RS_NPU_P6_DESCRIPTOR_COUNT) ||
|
||||||
!rs_npu_p6_equal(&s_workspace.arena[terminal_offset], expected_terminal,
|
!rs_npu_p6_equal(&s_workspace.arena[terminal_offset], expected_terminal,
|
||||||
@@ -381,8 +444,14 @@ static bool rs_npu_p6_run_case(const uint8_t *record, uint32_t expected_index, u
|
|||||||
RS_NPU_P6_OUTPUT_BYTES) ||
|
RS_NPU_P6_OUTPUT_BYTES) ||
|
||||||
!rs_npu_p6_equal((const uint8_t *)(const void *)s_reference_output,
|
!rs_npu_p6_equal((const uint8_t *)(const void *)s_reference_output,
|
||||||
(const uint8_t *)(const void *)s_npu_output, RS_NPU_P6_OUTPUT_BYTES)) {
|
(const uint8_t *)(const void *)s_npu_output, RS_NPU_P6_OUTPUT_BYTES)) {
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "golden-fail", UINT64_C(0));
|
||||||
|
}
|
||||||
return rs_npu_p6_case_fail(expected_index, "npu-golden");
|
return rs_npu_p6_case_fail(expected_index, "npu-golden");
|
||||||
}
|
}
|
||||||
|
if (trace_case) {
|
||||||
|
rs_npu_p6_stage(expected_index, "golden-done", UINT64_C(0));
|
||||||
|
}
|
||||||
rs_npu_p6_case_log(shard_index, expected_index, header[1], cold_cache, contention,
|
rs_npu_p6_case_log(shard_index, expected_index, header[1], cold_cache, contention,
|
||||||
reference_cycles, npu_cycles);
|
reference_cycles, npu_cycles);
|
||||||
return s_uart_ok;
|
return s_uart_ok;
|
||||||
@@ -390,10 +459,21 @@ static bool rs_npu_p6_run_case(const uint8_t *record, uint32_t expected_index, u
|
|||||||
|
|
||||||
int rs_hp_npu_p6_acceptance(void) {
|
int rs_hp_npu_p6_acceptance(void) {
|
||||||
rs_npu_p6_shard_t shard;
|
rs_npu_p6_shard_t shard;
|
||||||
|
rs_status_t status;
|
||||||
|
|
||||||
s_uart_ok = true;
|
s_uart_ok = true;
|
||||||
if ((rs_npu_irq_ack(RS_NPU_IRQ_ALL) != RS_OK) || (rs_npu_irq_enable(0U) != RS_OK) ||
|
status = rs_npu_irq_ack(RS_NPU_IRQ_ALL);
|
||||||
!rs_npu_p6_parse_shard(&shard)) {
|
if (status != RS_OK) {
|
||||||
|
rs_npu_p6_uart_text("NPU_P6_FAIL stage=irq-ack\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
status = rs_npu_irq_enable(0U);
|
||||||
|
if (status != RS_OK) {
|
||||||
|
rs_npu_p6_uart_text("NPU_P6_FAIL stage=irq-enable\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!rs_npu_p6_parse_shard(&shard)) {
|
||||||
|
rs_npu_p6_uart_text("NPU_P6_FAIL stage=shard-parse\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
rs_npu_p6_uart_text("NPU_P6_BEGIN workload=" RS_NPU_P6_WORKLOAD_NAME);
|
rs_npu_p6_uart_text("NPU_P6_BEGIN workload=" RS_NPU_P6_WORKLOAD_NAME);
|
||||||
@@ -406,7 +486,8 @@ int rs_hp_npu_p6_acceptance(void) {
|
|||||||
for (uint32_t index = 0U; index < shard.case_count; ++index) {
|
for (uint32_t index = 0U; index < shard.case_count; ++index) {
|
||||||
const uint8_t *record = &shard.payload[index * shard.record_bytes];
|
const uint8_t *record = &shard.payload[index * shard.record_bytes];
|
||||||
|
|
||||||
if (!rs_npu_p6_run_case(record, shard.first_case + index, shard.shard_index, index == 0U)) {
|
if (!rs_npu_p6_run_case(record, shard.first_case + index, shard.shard_index, index == 0U,
|
||||||
|
index == 0U)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,6 +508,17 @@ static void rs_npu_p6_clean_range(const void *pointer, uint32_t bytes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rs_npu_p6_invalidate_range(const void *pointer, uint32_t bytes) {
|
||||||
|
uintptr_t cursor = (uintptr_t)pointer & ~(uintptr_t)UINT32_C(63);
|
||||||
|
uintptr_t end = ((uintptr_t)pointer + bytes + UINT32_C(63)) & ~(uintptr_t)UINT32_C(63);
|
||||||
|
|
||||||
|
while (cursor < end) {
|
||||||
|
__asm__ volatile("cbo.inval 0(%0)" : : "r"(cursor) : "memory");
|
||||||
|
cursor += UINT32_C(64);
|
||||||
|
}
|
||||||
|
__asm__ volatile("fence r, r" : : : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
void rs_hp_npu_p6_cache_clean(void) {
|
void rs_hp_npu_p6_cache_clean(void) {
|
||||||
rs_npu_p6_clean_range(&s_workspace, sizeof(s_workspace));
|
rs_npu_p6_clean_range(&s_workspace, sizeof(s_workspace));
|
||||||
rs_npu_p6_clean_range(&s_profile, sizeof(s_profile));
|
rs_npu_p6_clean_range(&s_profile, sizeof(s_profile));
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ profiles. The `ihp130-hazard3-coremark.mk` profile is the automated SRAM
|
|||||||
CoreMark quick measurement; its `-standard` counterpart is reserved for a
|
CoreMark quick measurement; its `-standard` counterpart is reserved for a
|
||||||
10-second hardware run. The nightly workflow reuses the IHP130 CI profile and
|
10-second hardware run. The nightly workflow reuses the IHP130 CI profile and
|
||||||
runs the quick CoreMark profile.
|
runs the quick CoreMark profile.
|
||||||
|
`ci/ihp130.mk` is the Mini PRODUCT IHP130 profile used by the NPU-P6 flow. At
|
||||||
|
root revision `d084002bfe99`, P0 numerical qualification and KWS/VWW corpus
|
||||||
|
generation passed, and the RV64 KWS/VWW bundles passed payload/header
|
||||||
|
validation. Stage-instrumented KWS shard 0 and shard 1 both stop in the pinned
|
||||||
|
portable-C reference at `reference-start`; no `NPU_P6_CASE` or P6
|
||||||
|
qualification report is claimed. See the [NPU verification contract](../docs/ip/npu-verification.md)
|
||||||
|
and retained logs under
|
||||||
|
`build/ihp130-2026-10-01-10-30-b2e490436ac7/npu/p6/`.
|
||||||
`ci/ihp130-hp.mk` is the asymmetric Linux application profile. It starts HP
|
`ci/ihp130-hp.mk` is the asymmetric Linux application profile. It starts HP
|
||||||
from the external 72 MHz safe clock and LP from REF24, runs `hp_boot` entirely
|
from the external 72 MHz safe clock and LP from REF24, runs `hp_boot` entirely
|
||||||
from 32 KiB on-chip SRAM, enables VexiiRiscv `Zicbom` with 64-byte blocks, and
|
from 32 KiB on-chip SRAM, enables VexiiRiscv `Zicbom` with 64-byte blocks, and
|
||||||
@@ -21,6 +29,11 @@ timing evidence are qualified.
|
|||||||
`ci/ihp130-rtthread.mk` selects the same RV64 HP hardware and LP loader for
|
`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
|
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`.
|
`make CONFIG=configs/ci/ihp130-rtthread.mk SIMU=VERILATOR hp-rtthread-sim`.
|
||||||
|
`ci/ihp130-apu.mk` is the RV64 APU LP/HP evidence profile. It runs
|
||||||
|
`apu_release` from PSRAM, stages the embedded APUMC/APUM/WAV/KWS assets through
|
||||||
|
SDRAM, performs the LP-only image loads, hands APU ownership to HP, and checks
|
||||||
|
HP-submitted WAV/KWS jobs plus the LP-only register fault probe in the full-SoC
|
||||||
|
Verilator simulation via `hp-apu-sim`.
|
||||||
All PRODUCT profiles use `rv64imafdc_zicbom_max`; LP compiler/ISA remain RV32.
|
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
|
`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.
|
by GDB/OpenOCD; it is a programming utility, not a normal boot application.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ PDK := IHP130
|
|||||||
HAVE_PLL := NO
|
HAVE_PLL := NO
|
||||||
HAVE_HP := YES
|
HAVE_HP := YES
|
||||||
APU_ENABLE_P7 := YES
|
APU_ENABLE_P7 := YES
|
||||||
HP_CONFIG := rv32imafdc_zicbom_max
|
HP_CONFIG := rv64imafdc_zicbom_max
|
||||||
HAVE_SRAM_IF := YES
|
HAVE_SRAM_IF := YES
|
||||||
HAVE_SRAM_MACRO := YES
|
HAVE_SRAM_MACRO := YES
|
||||||
SRAM_SIZE_KIB := 32
|
SRAM_SIZE_KIB := 32
|
||||||
@@ -20,4 +20,4 @@ SIM_SUCCESS_MARKER := SIM_TEST_PASS
|
|||||||
RTL_SIM_TIMEOUT := 20000000
|
RTL_SIM_TIMEOUT := 20000000
|
||||||
SOC_SIM_TIME := 3600
|
SOC_SIM_TIME := 3600
|
||||||
FIRMWARE_NAME := retrosoc_apu_release
|
FIRMWARE_NAME := retrosoc_apu_release
|
||||||
SIM_FIRMWARE_NAME := retrosoc_apu_release
|
SIM_FIRMWARE_NAME := retrosoc_apu_release
|
||||||
@@ -24,8 +24,19 @@ static musl/BusyBox and a small uncompressed CPIO with a dedicated `/init`.
|
|||||||
V2 flash bundles discriminate Linux (1, four entries), smoke (2, one executable)
|
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
|
and RT-Thread (3, one executable). Rebuild V1 bundles. The loader checks strict
|
||||||
flags, workload/count/types, fixed addresses, bounds, non-overlap, unused slots
|
flags, workload/count/types, fixed addresses, bounds, non-overlap, unused slots
|
||||||
and CRC. Only smoke enters the GA2D/cache lifecycle protocol. LP alone owns
|
and CRC. In ordinary builds only smoke enters the GA2D/cache lifecycle
|
||||||
the final SYSCTRL TEST_STATUS write for all workloads.
|
protocol. LP alone owns the final SYSCTRL TEST_STATUS write for all workloads.
|
||||||
|
|
||||||
|
The private NPU-P6 acceptance build reuses workload 1's four-entry transport so
|
||||||
|
the frozen corpus shard occupies the existing `Image` slot. Its compile-time
|
||||||
|
`RS_NPU_P6_ACCEPTANCE` path deliberately continues beyond the Linux-ready
|
||||||
|
checkpoint into the same GA2D/cache lifecycle protocol; an ordinary Linux
|
||||||
|
build still terminates at Linux-ready. This does not add a workload number or
|
||||||
|
change the V2 header ABI.
|
||||||
|
|
||||||
|
The `ihp130-apu` evidence profile likewise uses workload 1 only as the existing
|
||||||
|
four-entry transport. Its application-specific `apu_release` LP loader owns the
|
||||||
|
APU acceptance semantics; that bundle label does not claim a Linux boot result.
|
||||||
|
|
||||||
## Measured runs
|
## Measured runs
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,31 @@ model substitution or faster reference/NPU memory placement is allowed to
|
|||||||
obscure the comparison. Record first-run and steady-state behavior using the
|
obscure the comparison. Record first-run and steady-state behavior using the
|
||||||
same declared cache initialization policy for both paths.
|
same declared cache initialization policy for both paths.
|
||||||
|
|
||||||
|
### Reference Baseline Freeze Decision (2026-10-01)
|
||||||
|
|
||||||
|
The P6 reference baseline remains the pinned, independently implemented
|
||||||
|
portable-C INT8 path defined above. A host-framework implementation, a
|
||||||
|
precomputed result source, a different memory placement, or an otherwise
|
||||||
|
faster reference path MUST NOT replace it in a qualification run. Such a
|
||||||
|
replacement would change the measured denominator and requires an explicit
|
||||||
|
verification-contract refreeze before implementation.
|
||||||
|
|
||||||
|
The stage-instrumented Mini IHP130 diagnostic run at root revision
|
||||||
|
`d084002bfe99`, using `configs/ci/ihp130.mk` and the locked VexiiRiscv revision,
|
||||||
|
entered `reference-start` for both a contention KWS shard and a non-contention
|
||||||
|
KWS shard, but did not reach `reference-compute-done` within the bounded host
|
||||||
|
run. This establishes a portable-C reference runtime/evidence gap, not an NPU
|
||||||
|
completion failure: neither run reached GA2D wait or NPU submit/wait. P6 stays
|
||||||
|
incomplete until the pinned reference completes the required corpora and the
|
||||||
|
architectural cycle intervals are recorded. Host elapsed time remains
|
||||||
|
diagnostic only and cannot be substituted for the required HP cycle evidence.
|
||||||
|
|
||||||
|
The stage markers are diagnostic observability only and do not alter the NPU
|
||||||
|
ABI, case record schema, workload corpus, cache policy, or performance formula.
|
||||||
|
Any future reference optimization must preserve the independent arithmetic,
|
||||||
|
layer outputs, memory placement and cycle-accounting contract, and must be
|
||||||
|
reviewed as a separate implementation change before P6 execution resumes.
|
||||||
|
|
||||||
Performance uses architecturally visible HP cycle intervals and frozen NPU
|
Performance uses architecturally visible HP cycle intervals and frozen NPU
|
||||||
counters, not Verilator host elapsed time, simulator throughput or a
|
counters, not Verilator host elapsed time, simulator throughput or a
|
||||||
peak-MAC-derived estimate. The reference and NPU intervals MUST execute in the
|
peak-MAC-derived estimate. The reference and NPU intervals MUST execute in the
|
||||||
|
|||||||
+6
-4
@@ -36,10 +36,12 @@ Authoritative repository inputs are:
|
|||||||
- the [verification contract](npu-verification.md), which is normative for
|
- the [verification contract](npu-verification.md), which is normative for
|
||||||
qualification but cannot change the architecture or ABI below.
|
qualification but cannot change the architecture or ABI below.
|
||||||
|
|
||||||
CONFIRMED FROM REPOSITORY: the current product has nine AXI64 masters,
|
CONFIRMED FROM REPOSITORY AT THE 2026-09-16 FREEZE: the product baseline had
|
||||||
seven-bit global IDs, 32 KiB system SRAM, a 72 MHz initial HP fabric clock,
|
nine AXI64 masters, seven-bit global IDs, 32 KiB system SRAM, a 72 MHz initial
|
||||||
and no hardware cache coherence. SDRAM already has a burst-capable frontend.
|
HP fabric clock, and no hardware cache coherence. The implemented NPU phases
|
||||||
The private NPU store is additional to system SRAM. The earlier
|
now append the frozen master/resource index 9, producing the ten-master
|
||||||
|
topology specified below without renumbering an earlier identity. SDRAM already
|
||||||
|
has a burst-capable frontend. The private NPU store is additional to system SRAM. The earlier
|
||||||
[Mini NPU survey](mini-npu.md) remains historical research; its AXI32 and
|
[Mini NPU survey](mini-npu.md) remains historical research; its AXI32 and
|
||||||
128 KiB baseline and stream-first recommendation do not govern this feature.
|
128 KiB baseline and stream-first recommendation do not govern this feature.
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ PCLK-to-HP AXI64/ID3 bridge as a direct single-job private-AXI64 2D engine
|
|||||||
controlled at `APB4_GA2D`, with resource-owned IRQ. Existing hart,
|
controlled at `APB4_GA2D`, with resource-owned IRQ. Existing hart,
|
||||||
memory, and source identities remain fixed.
|
memory, and source identities remain fixed.
|
||||||
|
|
||||||
|
The frozen [NPU specification](ip/npu.md) subsequently appends AXI64
|
||||||
|
master/prefix 9 and Resource Controller entry 9, expanding the implemented
|
||||||
|
PRODUCT data plane to ten masters without renumbering the preceding entries.
|
||||||
|
Its APB4 shell is at `APB4_NPU`; the resource-owned interrupt routes to LP
|
||||||
|
vector 33 or HP PLIC source 12.
|
||||||
|
|
||||||
## Product contract
|
## Product contract
|
||||||
|
|
||||||
Every committed `MINI_MODE=PRODUCT` profile instantiates two fixed harts:
|
Every committed `MINI_MODE=PRODUCT` profile instantiates two fixed harts:
|
||||||
@@ -79,7 +85,7 @@ same memory admission, inactive-pad, ACL, and fault path as HP and DMA.
|
|||||||
|
|
||||||
## Native AXI64 data plane
|
## Native AXI64 data plane
|
||||||
|
|
||||||
`soc_data_plane` contains a 9-master, 6-target AXI64 crossbar. Read and write
|
`soc_data_plane` contains a 10-master, 6-target AXI64 crossbar. Read and write
|
||||||
channels progress independently, and different source IDs may be active against
|
channels progress independently, and different source IDs may be active against
|
||||||
the same or different targets. The same source ID is blocked until completion.
|
the same or different targets. The same source ID is blocked until completion.
|
||||||
SRAM and SDRAM accept four reads and two writes; serial memories and the error
|
SRAM and SDRAM accept four reads and two writes; serial memories and the error
|
||||||
@@ -97,6 +103,7 @@ and a Common FIFO preserves write-data order where AXI4 W has no ID.
|
|||||||
| JPEG | PCLK-to-HP AXI64 async bridge, ID prefix 6; one normal read and one normal write credit, class 8 |
|
| JPEG | PCLK-to-HP AXI64 async bridge, ID prefix 6; one normal read and one normal write credit, class 8 |
|
||||||
| EXT-H | PCLK-to-HP AXI64 async bridge, ID prefix 7 |
|
| EXT-H | PCLK-to-HP AXI64 async bridge, ID prefix 7 |
|
||||||
| GA2D | dedicated PCLK-to-HP AXI64/ID3 async bridge; direct single-job FILL/COPY/CONVERT/BLEND engine |
|
| GA2D | dedicated PCLK-to-HP AXI64/ID3 async bridge; direct single-job FILL/COPY/CONVERT/BLEND engine |
|
||||||
|
| NPU | HP-native AXI64 master/prefix 9 with one read and one write outstanding; production descriptor and tensor DMA |
|
||||||
|
|
||||||
Targets are SRAM, SDRAM, QPI PSRAM, OPI/HyperBus PSRAM, XPI/flash, and a
|
Targets are SRAM, SDRAM, QPI PSRAM, OPI/HyperBus PSRAM, XPI/flash, and a
|
||||||
finite-latency error slave. SRAM is a native AXI64, seven-bit-ID target in HP and
|
finite-latency error slave. SRAM is a native AXI64, seven-bit-ID target in HP and
|
||||||
@@ -155,7 +162,7 @@ Its IRQ is delivered to LP IRQ 28 or HP PLIC source 3 according to owner,
|
|||||||
never both. Software discovers it through `<retrosoc/hal/extension.h>`.
|
never both. Software discovers it through `<retrosoc/hal/extension.h>`.
|
||||||
|
|
||||||
The Resource Controller at `0x2000_A000` is the central owner and IRQ authority
|
The Resource Controller at `0x2000_A000` is the central owner and IRQ authority
|
||||||
for DMA, USB2, SDIO0/1, SPI-SD, EXT-H, JPEG, APU, and the P5 GA2D engine.
|
for DMA, USB2, SDIO0/1, SPI-SD, EXT-H, JPEG, APU, the P5 GA2D engine, and NPU.
|
||||||
Resource 8 routes its raw IRQ exclusively to LP vector 32/external ordinal 30
|
Resource 8 routes its raw IRQ exclusively to LP vector 32/external ordinal 30
|
||||||
or HP PLIC source 11 according to owner, while its associated AXI bridge carries
|
or HP PLIC source 11 according to owner, while its associated AXI bridge carries
|
||||||
only that engine's direct FILL/COPY/CONVERT/BLEND traffic. The existing HP
|
only that engine's direct FILL/COPY/CONVERT/BLEND traffic. The existing HP
|
||||||
@@ -163,7 +170,9 @@ smoke workload remains a FILL/COPY subset; it is not P5 composition or cache
|
|||||||
coherency evidence.
|
coherency evidence.
|
||||||
Handoff requires idle, owner lock is sticky, and rejected handoffs raise LP IRQ
|
Handoff requires idle, owner lock is sticky, and rejected handoffs raise LP IRQ
|
||||||
29. APU index 7 routes exclusively to LP IRQ31 or HP PLIC source10. The
|
29. APU index 7 routes exclusively to LP IRQ31 or HP PLIC source10. The
|
||||||
controller also carries the AON cache request/clean acknowledgement used before
|
NPU at index 9 routes exclusively to LP vector 33/external ordinal 31 or HP
|
||||||
|
PLIC source 12; its AXI master carries only production descriptor/tensor traffic.
|
||||||
|
The controller also carries the AON cache request/clean acknowledgement used before
|
||||||
HP drain. See
|
HP drain. See
|
||||||
[`ip/resource-controller.md`](ip/resource-controller.md).
|
[`ip/resource-controller.md`](ip/resource-controller.md).
|
||||||
Resource 8 qualifies its HP block acknowledgement with a fresh synchronized
|
Resource 8 qualifies its HP block acknowledgement with a fresh synchronized
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ def split_npu_conditions(body: str) -> tuple[str, list[tuple[str, str]]]:
|
|||||||
condition = None
|
condition = None
|
||||||
allowed = {f"defined({name})" for name in NPU_DEFINES}
|
allowed = {f"defined({name})" for name in NPU_DEFINES}
|
||||||
allowed.add("||".join(f"defined({name})" for name in NPU_DEFINES))
|
allowed.add("||".join(f"defined({name})" for name in NPU_DEFINES))
|
||||||
|
allowed.add(f"!defined({NPU_DEFINES[1]})")
|
||||||
for line in body.splitlines():
|
for line in body.splitlines():
|
||||||
directive = line.strip()
|
directive = line.strip()
|
||||||
if directive.startswith("#if "):
|
if directive.startswith("#if "):
|
||||||
@@ -335,7 +336,11 @@ def collect_boot_acceptance(root: Path, function_body: Callable[[str, str], str]
|
|||||||
)
|
)
|
||||||
if any(main.count(branch) != 1 for branch in workload_branches):
|
if any(main.count(branch) != 1 for branch in workload_branches):
|
||||||
raise ValueError("HP boot workload selection changed")
|
raise ValueError("HP boot workload selection changed")
|
||||||
if (main.count("rs_test_finish(RS_TEST_PASSED,") != 3 or not main.endswith(phases[-1])
|
linux_exit_condition = f"!defined({NPU_DEFINES[1]})"
|
||||||
|
linux_exit_blocks = [compact(body) for condition, body in gated if condition == linux_exit_condition]
|
||||||
|
if linux_exit_blocks != ["rs_test_finish(RS_TEST_PASSED,UINT8_C(0));"]:
|
||||||
|
raise ValueError("HP boot Linux-ready terminal guard changed")
|
||||||
|
if (main.count("rs_test_finish(RS_TEST_PASSED,") != 2 or not main.endswith(phases[-1])
|
||||||
or re.findall(r"cache_clean_completed=(true|false);", main) != ["false", "true"]
|
or re.findall(r"cache_clean_completed=(true|false);", main) != ["false", "true"]
|
||||||
or re.findall(r"s_hp_boot_ga2d_owned_by_hp=(true|false);", main) != ["true", "false"]):
|
or re.findall(r"s_hp_boot_ga2d_owned_by_hp=(true|false);", main) != ["true", "false"]):
|
||||||
raise ValueError("HP boot terminal pass or cache completion changed")
|
raise ValueError("HP boot terminal pass or cache completion changed")
|
||||||
@@ -375,5 +380,7 @@ def collect_boot_acceptance(root: Path, function_body: Callable[[str, str], str]
|
|||||||
return {"default_poll_iterations": int(default[0]), "event_poll_multiplier": int(multiplier[0]),
|
return {"default_poll_iterations": int(default[0]), "event_poll_multiplier": int(multiplier[0]),
|
||||||
"event_poll_iterations": iterations, "npu_defines": list(NPU_DEFINES),
|
"event_poll_iterations": iterations, "npu_defines": list(NPU_DEFINES),
|
||||||
"messages": messages, "ready_writes": ready_writes,
|
"messages": messages, "ready_writes": ready_writes,
|
||||||
"rootfs_ready_only": True, "terminal_requires_cache_handoff": True,
|
"rootfs_ready_only": True, "linux_ready_is_terminal": True,
|
||||||
|
"smoke_terminal_requires_cache_handoff": True,
|
||||||
|
"npu_p6_reuses_linux_transport": True,
|
||||||
"failure_reset_requires_no_hp_ga2d_owner": True}
|
"failure_reset_requires_no_hp_ga2d_owner": True}
|
||||||
@@ -1,22 +1,23 @@
|
|||||||
#import "../style.typ": *
|
#import "../style.typ": *
|
||||||
|
#change-start("v05-emphasis-architecture","Selected body emphasis: architecture")
|
||||||
#import "../figures.typ": *
|
#import "../figures.typ": *
|
||||||
#import "../waveforms.typ": timing
|
#import "../waveforms.typ": timing
|
||||||
#import "../diagram-packages.typ": memory-window-diagram
|
#import "../diagram-packages.typ": memory-window-diagram
|
||||||
#import "../soc-architecture.typ": soc-functional-diagram
|
#import "../soc-architecture.typ": soc-functional-diagram
|
||||||
|
|
||||||
= System Architecture
|
= System Architecture
|
||||||
#change-start("dev-architecture","Nine-master fabric, address map, LP IRQ and lifecycle reference")
|
#change-start("dev-architecture","Ten-master fabric, address map, LP IRQ and lifecycle reference")
|
||||||
== Introduction
|
== Introduction
|
||||||
=== SoC Architecture
|
=== SoC Architecture
|
||||||
Mini PRODUCT contains two fixed harts, an LP-owned control plane, a native HP data fabric and
|
Mini PRODUCT contains *two fixed harts*, an LP-owned control plane, a native HP data fabric and
|
||||||
two APB4 register islands. There is no hardware cache coherency. Software transfers shared
|
two APB4 register islands. There is *no hardware cache coherency*. Software transfers shared
|
||||||
buffer ownership explicitly and performs the required fences and cache maintenance.
|
buffer ownership explicitly and performs the required fences and cache maintenance.
|
||||||
|
|
||||||
#figure(soc-functional-diagram(), caption:[Mini PRODUCT functional architecture and clock domains.])<soc-functional-overview>
|
#figure(soc-functional-diagram(), caption:[Mini PRODUCT functional architecture and clock domains.])<soc-functional-overview>
|
||||||
|
|
||||||
=== Management Processor
|
=== Management Processor
|
||||||
==== Hazard3
|
==== Hazard3
|
||||||
Hazard3 is hart 0 and the root-management processor. The reference profile selects RV32IM
|
Hazard3 is hart 0 and the *root-management processor*. The reference profile selects RV32IM
|
||||||
firmware. Its AHB-Lite interface is adapted to AXI32 for control access; memory transactions
|
firmware. Its AHB-Lite interface is adapted to AXI32 for control access; memory transactions
|
||||||
are directed through the LP gateway to the shared data plane. The management JTAG path provides
|
are directed through the LP gateway to the shared data plane. The management JTAG path provides
|
||||||
halt, resume, register and system-bus access. LP retains control while HP is held in reset.
|
halt, resume, register and system-bus access. LP retains control while HP is held in reset.
|
||||||
@@ -25,7 +26,7 @@ halt, resume, register and system-bus access. LP retains control while HP is hel
|
|||||||
=== Application Processor
|
=== Application Processor
|
||||||
==== VexiiRiscv
|
==== VexiiRiscv
|
||||||
The generated HP hart is a dual-issue RV64IMAFDC + Zicbom configuration, with supervisor/user
|
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
|
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
|
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.
|
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")
|
#source-note("docs/lp-hp-architecture.md", title:"LP/HP architecture and boot contract")
|
||||||
@@ -60,7 +61,8 @@ byte strobes, burst limits, access permissions and IP-specific side effects rema
|
|||||||
=== Interconnect Matrix
|
=== Interconnect Matrix
|
||||||
The #data.policies.len() initiator identities access #data.targets.len() memory targets. The crossbar arbitrates reads and
|
The #data.policies.len() initiator identities access #data.targets.len() memory targets. The crossbar arbitrates reads and
|
||||||
writes separately for each target. I/O gateway A combines USB2, SDIO0 and the APU private
|
writes separately for each target. I/O gateway A combines USB2, SDIO0 and the APU private
|
||||||
master; gateway B combines SDIO1 and SPI-SD. JPEG occupies slot 6 and GA2D slot 8; EXT-H uses slot 7.
|
master; gateway B combines SDIO1 and SPI-SD. JPEG occupies slot 6, EXT-H slot 7,
|
||||||
|
GA2D slot 8 and NPU slot 9.
|
||||||
|
|
||||||
#note[The matrix on the next page is generated from the RTL access policy. R/W permission is
|
#note[The matrix on the next page is generated from the RTL access policy. R/W permission is
|
||||||
subject to active memory-pad mode, resource ownership, target readiness and EXT-H address bounds.
|
subject to active memory-pad mode, resource ownership, target readiness and EXT-H address bounds.
|
||||||
@@ -69,7 +71,7 @@ An allowed entry does not guarantee throughput.]
|
|||||||
#pagebreak()
|
#pagebreak()
|
||||||
#figure(matrix-diagram(), caption:[AXI64 memory access matrix: R = read, W = write, - = denied.])<bus-matrix>
|
#figure(matrix-diagram(), caption:[AXI64 memory access matrix: R = read, W = write, - = denied.])<bus-matrix>
|
||||||
The I-cache is the only instruction-permitted initiator. HP cache attributes are preserved;
|
The I-cache is the only instruction-permitted initiator. HP cache attributes are preserved;
|
||||||
DMA, I/O gateways, LP gateway, JPEG, EXT-H and GA2D require non-cacheable transactions. XPI is read-only
|
DMA, I/O gateways, LP gateway, JPEG, EXT-H, GA2D and NPU require non-cacheable transactions. XPI is read-only
|
||||||
on this data plane; indirect writes use its APB-controlled command engine.
|
on this data plane; indirect writes use its APB-controlled command engine.
|
||||||
|
|
||||||
Denied accesses return a finite error response with source attribution. EXT-H has additional
|
Denied accesses return a finite error response with source attribution. EXT-H has additional
|
||||||
@@ -157,9 +159,11 @@ within an aggregate source. Resource-controlled interrupts are routed according
|
|||||||
)
|
)
|
||||||
|
|
||||||
HP has local software/timer interrupts and a 32-source, two-context PLIC. Source 0 is
|
HP has local software/timer interrupts and a 32-source, two-context PLIC. Source 0 is
|
||||||
reserved; sources 1-11 are UART1, mailbox, EXT-H, DMA, USB2, SDIO0, SDIO1, SPI-SD, JPEG, APU
|
reserved; sources 1-12 are UART1, mailbox, EXT-H, DMA, USB2, SDIO0, SDIO1, SPI-SD, JPEG, APU,
|
||||||
and GA2D. Sources 12-31 are reserved. GA2D uses LP vector bit 32 (external ordinal 30),
|
GA2D and NPU. Sources 13-31 are reserved. GA2D uses LP vector bit 32 (external ordinal 30),
|
||||||
which is distinct from HP PLIC source 11. The contexts drive machine and supervisor external interrupts.
|
which is distinct from HP PLIC source 11; NPU uses LP vector bit 33 (external ordinal 31)
|
||||||
|
or HP PLIC source 12 according to Resource Controller ownership. The contexts drive machine
|
||||||
|
and supervisor external interrupts.
|
||||||
Claim/complete and priority rules are defined in the HP platform contract.
|
Claim/complete and priority rules are defined in the HP platform contract.
|
||||||
#block(above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[
|
#block(above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[
|
||||||
#set text(size:9pt)
|
#set text(size:9pt)
|
||||||
@@ -170,3 +174,5 @@ Claim/complete and priority rules are defined in the HP platform contract.
|
|||||||
#include "register-programming.typ"
|
#include "register-programming.typ"
|
||||||
#include "interface-subsets.typ"
|
#include "interface-subsets.typ"
|
||||||
#change-end("dev-architecture")
|
#change-end("dev-architecture")
|
||||||
|
|
||||||
|
#change-end("v05-emphasis-architecture")
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
#import "../style.typ": *
|
#import "../style.typ": *
|
||||||
|
#change-start("v05-emphasis-boot-bundle-format","Selected body emphasis: boot bundle format")
|
||||||
#import "../diagram-packages.typ": binary-figures, storage-figures
|
#import "../diagram-packages.typ": binary-figures, storage-figures
|
||||||
#let bundle = data.system_reference.software.bundle_reference
|
#let bundle = data.system_reference.software.bundle_reference
|
||||||
#let hex(value) = code("0x"+upper(str(value,base:16)))
|
#let hex(value) = code("0x"+upper(str(value,base:16)))
|
||||||
|
|
||||||
=== HP Boot Bundle Binary Format <boot-bundle-format>
|
=== HP Boot Bundle Binary Format <boot-bundle-format>
|
||||||
|
|
||||||
The supplied V2 format is a typed workload 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
|
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
|
0x38000000 with the remaining three descriptor slots zero. Workload values are 1, 2 and 3
|
||||||
respectively. V1 bundles must be rebuilt.
|
respectively. V1 bundles must be rebuilt.
|
||||||
The format version is #bundle.version; it is independent of the datasheet's v0.4 revision.
|
The format version is #bundle.version; it is independent of the datasheet's v0.5 revision.
|
||||||
The bundle starts at Flash offset #hex(bundle.bundle_offset), after the LP firmware area.
|
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
|
Its fixed header is #bundle.fixed_header_bytes bytes, each descriptor is #bundle.entry_bytes
|
||||||
bytes and the complete header including #bundle.entry_count descriptors is #bundle.header_bytes bytes.
|
bytes and the complete header including #bundle.entry_count descriptors is #bundle.header_bytes bytes.
|
||||||
@@ -17,8 +18,8 @@ bytes and the complete header including #bundle.entry_count descriptors is #bund
|
|||||||
The following offsets are relative to the beginning of the named structure. The descriptor
|
The following offsets are relative to the beginning of the named structure. The descriptor
|
||||||
array starts immediately after the fixed header, so descriptor n begins at
|
array starts immediately after the fixed header, so descriptor n begins at
|
||||||
#code(str(bundle.fixed_header_bytes)+" + n * "+str(bundle.entry_bytes)), for n = 0…#(bundle.entry_count - 1).
|
#code(str(bundle.fixed_header_bytes)+" + n * "+str(bundle.entry_bytes)), for n = 0…#(bundle.entry_count - 1).
|
||||||
All listed fields occupy four bytes. Use the explicit serialization, not an arbitrary host
|
All listed fields occupy four bytes. Use the *explicit serialization, not an arbitrary host
|
||||||
structure layout. The existing load addresses and maximum allocations remain in the preceding
|
structure layout*. The existing load addresses and maximum allocations remain in the preceding
|
||||||
image-layout table.
|
image-layout table.
|
||||||
|
|
||||||
#binary-figures("software-boot","software")
|
#binary-figures("software-boot","software")
|
||||||
@@ -91,3 +92,5 @@ The example values are recalculated from the actual packager rather than maintai
|
|||||||
#source-note("tests/test_hp_boot_bundle.py",title:"Existing package layout/CRC test boundary")
|
#source-note("tests/test_hp_boot_bundle.py",title:"Existing package layout/CRC test boundary")
|
||||||
|
|
||||||
#storage-figures("software-boot",section:"software")
|
#storage-figures("software-boot",section:"software")
|
||||||
|
|
||||||
|
#change-end("v05-emphasis-boot-bundle-format")
|
||||||
@@ -64,8 +64,9 @@ one application. Preserve application identity, execution order and preceding lo
|
|||||||
These result codes belong to the supplied HP boot application. TEST_STATUS's result byte is
|
These result codes belong to the supplied HP boot application. TEST_STATUS's result byte is
|
||||||
application-defined; other firmware can assign different meanings. Code 1 can be written
|
application-defined; other firmware can assign different meanings. Code 1 can be written
|
||||||
before a reliable console exists. The ready, result and cache-clean waits have bounded polling
|
before a reliable console exists. The ready, result and cache-clean waits have bounded polling
|
||||||
budgets and distinct failure stages. The initial ready marker precedes the remaining GA2D
|
budgets and distinct failure stages. Ordinary Linux terminates after the checked ready message;
|
||||||
acceptance/cache handoff; it does not itself write a successful TEST_STATUS value.
|
for smoke and the dedicated NPU-P6 build, that marker precedes the remaining GA2D
|
||||||
|
acceptance/cache handoff and does not itself write a successful TEST_STATUS value.
|
||||||
|
|
||||||
#ds-table("boot-result-codes",[HP boot application terminal result codes],
|
#ds-table("boot-result-codes",[HP boot application terminal result codes],
|
||||||
([Code],[Meaning / producing stage]),
|
([Code],[Meaning / producing stage]),
|
||||||
@@ -74,8 +75,9 @@ acceptance/cache handoff; it does not itself write a successful TEST_STATUS valu
|
|||||||
The #code("HP_BOOT_FAILED:<code>") diagnostic is emitted by the failure helper before its
|
The #code("HP_BOOT_FAILED:<code>") diagnostic is emitted by the failure helper before its
|
||||||
terminal status write; UART initialization failure uses the direct terminal path. Entry-copy
|
terminal status write; UART initialization failure uses the direct terminal path. Entry-copy
|
||||||
failure is reported only after both the DMA attempt and software copy/CRC fallback fail.
|
failure is reported only after both the DMA attempt and software copy/CRC fallback fail.
|
||||||
After copying, HP release, initial readiness, job result and cache-clean handoff are distinct
|
After copying, HP release and workload-specific terminal checkpoints remain distinct. Smoke and
|
||||||
checkpoints. NPU resource handoff is conditional on its acceptance build options. The failure
|
NPU-P6 additionally require job result and cache-clean handoff. NPU resource handoff is
|
||||||
|
conditional on its acceptance build options. The failure
|
||||||
helper preserves HP state after GA2D ownership moves to HP; a failed verdict does not guarantee
|
helper preserves HP state after GA2D ownership moves to HP; a failed verdict does not guarantee
|
||||||
a completed reset or DMA drain. See @boot-configuration for prerequisites/recovery and
|
a completed reset or DMA drain. See @boot-configuration for prerequisites/recovery and
|
||||||
@image-maintenance for separate programming-tool result semantics.
|
@image-maintenance for separate programming-tool result semantics.
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
#import "../style.typ": *
|
#import "../style.typ": *
|
||||||
|
#change-start("v05-emphasis-linux-runtime","Selected body emphasis: linux runtime")
|
||||||
#import "../system-figures.typ": sequence-diagram
|
#import "../system-figures.typ": sequence-diagram
|
||||||
#let platform = data.system_reference.software.platform
|
#let platform = data.system_reference.software.platform
|
||||||
|
|
||||||
=== OpenSBI platform and console path <linux-runtime>
|
=== OpenSBI platform and console path <linux-runtime>
|
||||||
The supplied OpenSBI platform exposes one application hart, with hart ID #platform.hart_id.
|
The supplied OpenSBI platform exposes *one application hart*, with hart ID #platform.hart_id.
|
||||||
LP remains the separate management firmware processor. The platform's ACLINT descriptor covers
|
LP remains the separate management firmware processor. The platform's ACLINT descriptor covers
|
||||||
two hart-indexed register positions beginning at zero so it can address hart 1; that storage
|
two hart-indexed register positions beginning at zero so it can address hart 1; that storage
|
||||||
range is not a declaration that Linux runs on both harts. The kernel configuration disables SMP.
|
range is not a declaration that Linux runs on both harts. The *kernel configuration disables SMP*.
|
||||||
|
|
||||||
The early platform setup programs UART1, registers its polled console and initializes machine
|
The early platform setup programs UART1, registers its polled console and initializes machine
|
||||||
software-interrupt support. Timer initialization uses the ACLINT machine timer. Console writes
|
software-interrupt support. Timer initialization uses the ACLINT machine timer. Console writes
|
||||||
wait while TX is full; this loop has no local timeout. These implemented callbacks do not
|
wait while TX is full; this loop has *no local timeout*. These implemented callbacks do not
|
||||||
establish that all optional SBI services or all peripherals have native Linux drivers.
|
establish that all optional SBI services or all peripherals have native Linux drivers.
|
||||||
|
|
||||||
#figure(sequence-diagram((
|
#figure(sequence-diagram((
|
||||||
@@ -57,6 +58,7 @@ apply the generic OpenSBI FDT fixups.
|
|||||||
#source-note("scripts/build_hp_linux.py",title:"Actual initrd-end patch and FW_JUMP build arguments")
|
#source-note("scripts/build_hp_linux.py",title:"Actual initrd-end patch and FW_JUMP build arguments")
|
||||||
|
|
||||||
=== Kernel and rootfs ready handoff
|
=== Kernel and rootfs ready handoff
|
||||||
|
#change-start("v05-refresh-linux-mailbox","HAL-derived mailbox purposes and RV64 ready-only userspace boundary")
|
||||||
The dedicated /init mounts procfs, sysfs, devtmpfs and tmpfs, checks file I/O and child execution,
|
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
|
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,
|
mailbox writes with an I/O fence before the doorbell. This is a userspace checkpoint, not proof of all drivers, networking,
|
||||||
@@ -66,7 +68,7 @@ alone is not the LP's readiness condition.
|
|||||||
#ds-table("linux-ready-writes",[Userspace helper mailbox publication order],
|
#ds-table("linux-ready-writes",[Userspace helper mailbox publication order],
|
||||||
([Order],[Address],[Written value],[Purpose]),
|
([Order],[Address],[Written value],[Purpose]),
|
||||||
platform.ready_writes.enumerate().map(((i,row))=>(str(i+1),code(row.address),code(row.value),
|
platform.ready_writes.enumerate().map(((i,row))=>(str(i+1),code(row.address),code(row.value),
|
||||||
([Linux-ready event],[Argument / ready state],[Sequence],[Interrupt request]).at(i))),
|
[#code(row.register) \ #row.purpose])),
|
||||||
widths:(0.4fr,1.1fr,1.1fr,1.85fr))
|
widths:(0.4fr,1.1fr,1.1fr,1.85fr))
|
||||||
|
|
||||||
The helper checks open/mmap errors for /dev/mem and reports init failures through event 3
|
The helper checks open/mmap errors for /dev/mem and reports init failures through event 3
|
||||||
@@ -79,4 +81,8 @@ Resource ownership and non-coherent buffer rules still apply after Linux starts.
|
|||||||
transactions. This description adds no new successful boot or board-validation claim.
|
transactions. This description adds no new successful boot or board-validation claim.
|
||||||
#source-note("app/ports/linux/hp_ready.c",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/ports/linux/init",title:"Minimal userspace acceptance checks")
|
||||||
|
#source-note("crt/src/hal/hp_mailbox.c",title:"Event, argument and sequence register purposes")
|
||||||
#source-note("app/apps/hp_boot/main.c",title:"LP sequence/event checks and final terminal result")
|
#source-note("app/apps/hp_boot/main.c",title:"LP sequence/event checks and final terminal result")
|
||||||
|
#change-end("v05-refresh-linux-mailbox")
|
||||||
|
|
||||||
|
#change-end("v05-emphasis-linux-runtime")
|
||||||
@@ -3,13 +3,16 @@
|
|||||||
|
|
||||||
// Publication status is explicit; successful build/tests do not change these marks.
|
// Publication status is explicit; successful build/tests do not change these marks.
|
||||||
#let artifact-status = (
|
#let artifact-status = (
|
||||||
(name:"Available",selected:true),
|
(name:"Available",fill:"full"),
|
||||||
(name:"Functional",selected:false),
|
(name:"Functional",fill:"left-half"),
|
||||||
(name:"Reproduced",selected:false),
|
(name:"Reproduced",fill:"empty"),
|
||||||
(name:"Tapedout",selected:false),
|
(name:"Tapedout",fill:"empty"),
|
||||||
)
|
)
|
||||||
|
|
||||||
#cover(evaluation:artifact-status)[
|
#cover(evaluation:artifact-status)[
|
||||||
|
#change-start("v05-rill-cover","Rill bilingual prototype brand in the existing cover metadata row")
|
||||||
|
#change-start("v05-emphasis-cover","Cover contact, partial Functional mark and selected feature emphasis")
|
||||||
|
#change-start("v05-product-brief","v0.5 document identity and implemented accelerator features")
|
||||||
#text(9pt, weight: "semibold", fill: gold)[PRODUCT DATASHEET / #doc.status]
|
#text(9pt, weight: "semibold", fill: gold)[PRODUCT DATASHEET / #doc.status]
|
||||||
#v(rhythm.cover-gap)
|
#v(rhythm.cover-gap)
|
||||||
#text(30pt, weight: "semibold")[retroSoC Mini]
|
#text(30pt, weight: "semibold")[retroSoC Mini]
|
||||||
@@ -18,9 +21,10 @@
|
|||||||
#v(rhythm.cover-gap)
|
#v(rhythm.cover-gap)
|
||||||
#text(13pt)[An Open-Source, Linux-Capable Asymmetric Dual-Core SoC]
|
#text(13pt)[An Open-Source, Linux-Capable Asymmetric Dual-Core SoC]
|
||||||
#v(rhythm.cover-meta-gap)
|
#v(rhythm.cover-meta-gap)
|
||||||
|
// Inter's 1490/2048 em cap edge keeps CJK fallback from increasing this row.
|
||||||
#grid(columns: (1fr, 1fr),
|
#grid(columns: (1fr, 1fr),
|
||||||
[*#doc.document_id* · v#doc.version \ #doc.date],
|
[#text(font:("Inter","Noto Sans CJK SC"),weight:"bold",top-edge:0.7275390625em)[#doc.brand_name(#doc.brand_name_zh)] · v#doc.version \ #doc.date],
|
||||||
align(right)[#doc.author \ #doc.maintainer],
|
align(right)[#doc.author#link("mailto:"+doc.author_email)[(#doc.author_email)] \ #doc.maintainer],
|
||||||
)
|
)
|
||||||
#v(rhythm.cover-rule-gap)
|
#v(rhythm.cover-rule-gap)
|
||||||
#line(length:100%, stroke:1.1pt + gold)
|
#line(length:100%, stroke:1.1pt + gold)
|
||||||
@@ -33,14 +37,15 @@
|
|||||||
*Compute and control*
|
*Compute and control*
|
||||||
- Hazard3 LP: boot, clocks, resource ownership and fault recovery.
|
- Hazard3 LP: boot, clocks, resource ownership and fault recovery.
|
||||||
- Dual-issue VexiiRiscv HP: RV64IMAFDC + Zicbom, Sv39.
|
- Dual-issue VexiiRiscv HP: RV64IMAFDC + Zicbom, Sv39.
|
||||||
|
- NPU: *64 dense MACs*, eight INT8 operator classes and 64 KiB private SRAM.
|
||||||
- JTAG debug; fixed EXT-L control and EXT-H AXI64 slots.
|
- JTAG debug; fixed EXT-L control and EXT-H AXI64 slots.
|
||||||
|
|
||||||
*Memory and interconnect*
|
*Memory and interconnect*
|
||||||
- 32 KiB banked SRAM in this profile; 4/16/32/64/128 KiB build options.
|
- *32 KiB banked SRAM* in this profile; 4/16/32/64/128 KiB build options.
|
||||||
- AXI32 control, native AXI64 data paths and APB4 registers.
|
- AXI32 control, native AXI64 data paths and APB4 registers.
|
||||||
- SDRAM, QPI PSRAM and OPI/HyperBus-style external-memory controllers.
|
- SDRAM, QPI PSRAM and OPI/HyperBus-style external-memory controllers.
|
||||||
- XPI: four chip selects, 16 LUT sequences, 1/2/4-bit SDR transfers.
|
- XPI: four chip selects, 16 LUT sequences, 1/2/4-bit SDR transfers.
|
||||||
- Eight DMA channels; linked-list TCDs, 16-beat bursts and stream endpoints.
|
- *Eight DMA channels*; linked-list TCDs, 16-beat bursts and stream endpoints.
|
||||||
|
|
||||||
*Platform services*
|
*Platform services*
|
||||||
- Two 32-bit timers with 16-bit prescalers; four-channel PWM.
|
- Two 32-bit timers with 16-bit prescalers; four-channel PWM.
|
||||||
@@ -51,8 +56,8 @@
|
|||||||
|
|
||||||
#colbreak()
|
#colbreak()
|
||||||
*Connectivity*
|
*Connectivity*
|
||||||
- 32 GPIOs: atomic outputs, pin interrupts and two alternate-function selections.
|
- *32 GPIOs*: atomic outputs, pin interrupts and two alternate-function selections.
|
||||||
- Two UARTs with 64-byte TX/RX FIFOs; UART0 DMA and RTS/CTS.
|
- Two UARTs with *64-byte TX/RX FIFOs*; UART0 DMA and RTS/CTS.
|
||||||
- Two I2C controllers: 7/10-bit addressing and 16-entry command/RX FIFOs.
|
- Two I2C controllers: 7/10-bit addressing and 16-entry command/RX FIFOs.
|
||||||
- SPI-SD, two 1/4-bit SDR SDIO hosts and bidirectional PS/2.
|
- SPI-SD, two 1/4-bit SDR SDIO hosts and bidirectional PS/2.
|
||||||
- USB2: 8 endpoints, 16 host channels, 16 KiB packet RAM; external ULPI PHY.
|
- USB2: 8 endpoints, 16 host channels, 16 KiB packet RAM; external ULPI PHY.
|
||||||
@@ -62,7 +67,7 @@
|
|||||||
- Stereo 16/24-bit I2S master; 8-bit RGB565/YUV422 DVP with crop/snapshot capture.
|
- Stereo 16/24-bit I2S master; 8-bit RGB565/YUV422 DVP with crop/snapshot capture.
|
||||||
- Baseline JPEG encode/decode up to 2048 × 2048, with private DMA.
|
- Baseline JPEG encode/decode up to 2048 × 2048, with private DMA.
|
||||||
- GA2D fill/copy/convert/opaque blend and A8 masks.
|
- GA2D fill/copy/convert/opaque blend and A8 masks.
|
||||||
- APU WAV/FLAC decode; MP3/KWS not advertised.
|
- APU WAV/FLAC decode; fixed KWS in the P7 configuration; MP3 unsupported.
|
||||||
- AES-128/192/256, SHA-224/256 and raw RSA-2048; programmable CRC.
|
- AES-128/192/256, SHA-224/256 and raw RSA-2048; programmable CRC.
|
||||||
- RNG source remains unqualified for production entropy.
|
- RNG source remains unqualified for production entropy.
|
||||||
|
|
||||||
@@ -74,10 +79,15 @@
|
|||||||
#note(below: 0pt)[This draft describes a reviewed RTL/configuration snapshot. It does not establish silicon
|
#note(below: 0pt)[This draft describes a reviewed RTL/configuration snapshot. It does not establish silicon
|
||||||
speed grades, electrical limits, production availability or certification. Gen2/Gen2+ is the
|
speed grades, electrical limits, production availability or certification. Gen2/Gen2+ is the
|
||||||
retained document title; no separate derivative specifications are inferred.]
|
retained document title; no separate derivative specifications are inferred.]
|
||||||
|
#change-end("v05-product-brief")
|
||||||
|
#change-end("v05-emphasis-cover")
|
||||||
|
#change-end("v05-rill-cover")
|
||||||
]
|
]
|
||||||
|
|
||||||
#pagebreak()
|
#pagebreak()
|
||||||
== Overview
|
== Overview
|
||||||
|
#change-start("v05-emphasis-overview","Cover-adjacent overview and evidence context")
|
||||||
|
#change-start("v05-overview","NPU-inclusive functional inventory and configuration reference")
|
||||||
retroSoC Mini combines a small management processor with an application processor and shared
|
retroSoC Mini combines a small management processor with an application processor and shared
|
||||||
memory and I/O. Hazard3 retains authority over startup, clock transitions, resource ownership
|
memory and I/O. Hazard3 retains authority over startup, clock transitions, resource ownership
|
||||||
and fault recovery. VexiiRiscv supplies the application-side compute and Linux integration.
|
and fault recovery. VexiiRiscv supplies the application-side compute and Linux integration.
|
||||||
@@ -86,6 +96,7 @@ experimentation, education and ASIC prototyping.
|
|||||||
|
|
||||||
#figure(product-diagram(), caption:[Integrated Mini PRODUCT IP inventory, organized by function.])<product-diagram>
|
#figure(product-diagram(), caption:[Integrated Mini PRODUCT IP inventory, organized by function.])<product-diagram>
|
||||||
|
|
||||||
|
#change-start("v05-refresh-identity","Reviewed source identity for this same-version revision")
|
||||||
#ds-table("profile", [Document reference configuration],
|
#ds-table("profile", [Document reference configuration],
|
||||||
([Property], [Reference]),
|
([Property], [Reference]),
|
||||||
(
|
(
|
||||||
@@ -98,3 +109,7 @@ experimentation, education and ASIC prototyping.
|
|||||||
([MPW compatibility], code(doc.mpw_profile)),
|
([MPW compatibility], code(doc.mpw_profile)),
|
||||||
), widths:(0.8fr,1.9fr),
|
), widths:(0.8fr,1.9fr),
|
||||||
)
|
)
|
||||||
|
#change-end("v05-overview")
|
||||||
|
#change-end("v05-refresh-identity")
|
||||||
|
|
||||||
|
#change-end("v05-emphasis-overview")
|
||||||
@@ -98,11 +98,12 @@ SBI-based, even though OpenSBI accesses UART1 underneath. Native custom peripher
|
|||||||
and an application resource-handoff service are separate work.
|
and an application resource-handoff service are separate work.
|
||||||
|
|
||||||
Before boot, validate the external memory configuration and the exact bundle generated for
|
Before boot, validate the external memory configuration and the exact bundle generated for
|
||||||
the selected snapshot. The Linux rootfs publishes an initial ready event. The current HP
|
the selected snapshot. The Linux rootfs publishes a ready event that terminates the ordinary
|
||||||
acceptance loader also requires GA2D result, cache-clean and resource-return checkpoints;
|
Linux workload. Smoke and the dedicated NPU-P6 build additionally require GA2D result,
|
||||||
the rootfs ready service does not implement those later exchanges. Follow @boot-configuration
|
cache-clean and resource-return checkpoints; the Linux ready service does not implement those
|
||||||
for the bounded polling budgets, matching acceptance payload and failure/ownership policy.
|
later exchanges. Follow @boot-configuration for the bounded polling budgets, matching
|
||||||
Neither an initial event nor a complete acceptance pass establishes all Linux peripheral drivers.
|
acceptance payload and failure/ownership policy. Neither the Linux-ready verdict nor a complete
|
||||||
|
freestanding acceptance pass establishes all Linux peripheral drivers.
|
||||||
|
|
||||||
=== Camera and audio data paths
|
=== Camera and audio data paths
|
||||||
#change-start("system-media", "Camera and audio circuit composition")
|
#change-start("system-media", "Camera and audio circuit composition")
|
||||||
|
|||||||
@@ -42,8 +42,9 @@ separate universal IRQ-enable policy. Check each producer's local reset, cause a
|
|||||||
An initialization dependency is not a fixed delay specification. Prefer documented ready,
|
An initialization dependency is not a fixed delay specification. Prefer documented ready,
|
||||||
idle, lock and error observations with bounded software waits. The HP loader bounds its
|
idle, lock and error observations with bounded software waits. The HP loader bounds its
|
||||||
ready/result/cache-clean polls by iteration counts, not fixed elapsed time; stalled MMIO can
|
ready/result/cache-clean polls by iteration counts, not fixed elapsed time; stalled MMIO can
|
||||||
still prevent progress. Its initial ready event is separate from complete acceptance and
|
still prevent progress. Linux-ready is terminal for the ordinary Linux workload, while smoke
|
||||||
ownership return. The generic LP CRT's earlier PSRAM-ready loop remains unbounded; see
|
and NPU-P6 continue through acceptance and ownership return. The generic LP CRT's earlier
|
||||||
|
PSRAM-ready loop remains unbounded; see
|
||||||
@lp-runtime and @boot-configuration for the distinct startup stages.
|
@lp-runtime and @boot-configuration for the distinct startup stages.
|
||||||
#change-end("v05-refresh-reset-waits")
|
#change-end("v05-refresh-reset-waits")
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,16 @@ The separate APU P9 acceptance profile performs LP APUMC/APUC/APUM loading and b
|
|||||||
audio/KWS work; it is not the Linux boot profile. NPU compiler output and HP smoke payloads
|
audio/KWS work; it is not the Linux boot profile. NPU compiler output and HP smoke payloads
|
||||||
likewise do not supply a native Linux driver. See @apu and @npu for their software boundaries.
|
likewise do not supply a native Linux driver. See @apu and @npu for their software boundaries.
|
||||||
|
|
||||||
#figure(boot-diagram(), caption:[Linux image loading and initial ready checkpoint. The loader's complete acceptance protocol continues below.])<boot-flow>
|
#figure(boot-diagram(), caption:[Linux image loading and ready checkpoint. Smoke and NPU-P6 acceptance continue through the later protocol.])<boot-flow>
|
||||||
|
|
||||||
The HP bundle contains locked OpenSBI, Linux, device-tree and initramfs inputs. LP validates
|
The HP bundle contains locked OpenSBI, Linux, device-tree and initramfs inputs. LP validates
|
||||||
the header and payload bounds, attempts transfer with the boot DMA context and CRC, and
|
the header and payload bounds, attempts transfer with the boot DMA context and CRC, and
|
||||||
falls back to a software copy/CRC if that attempt fails. It fences memory, clears the LP
|
falls back to a software copy/CRC if that attempt fails. It fences memory, clears the LP
|
||||||
mailbox interrupt and requests HP release. It then waits for the expected readiness event;
|
mailbox interrupt and requests HP release. It then waits for the expected readiness event;
|
||||||
it does not publish entry/DTB addresses through a boot mailbox before release. The same loader
|
it does not publish entry/DTB addresses through a boot mailbox before release. For an ordinary
|
||||||
also serves freestanding HP acceptance payloads. Its later GA2D/cache-clean protocol is required
|
Linux workload the checked userspace ready event is terminal. The same loader also serves
|
||||||
before a successful TEST_STATUS write; the *initial ready message alone does not complete it*.
|
freestanding HP acceptance payloads: smoke and the dedicated NPU-P6 build continue through the
|
||||||
|
later GA2D/cache-clean protocol, where the *initial ready message alone does not complete it*.
|
||||||
#source-note("docs/lp-hp-architecture.md", title:"Boot-bundle ABI, lifecycle and failure handling")
|
#source-note("docs/lp-hp-architecture.md", title:"Boot-bundle ABI, lifecycle and failure handling")
|
||||||
|
|
||||||
=== Prerequisites and image layout
|
=== Prerequisites and image layout
|
||||||
@@ -62,7 +63,8 @@ port guide for the generated output layout and boot packaging commands.
|
|||||||
+ Apply the memory fence, clear the LP mailbox interrupt, request release, and check the
|
+ Apply the memory fence, clear the LP mailbox interrupt, request release, and check the
|
||||||
immediate HP status result. Observe #code("HP_BOOT_RELEASED") only as a release checkpoint.
|
immediate HP status result. Observe #code("HP_BOOT_RELEASED") only as a release checkpoint.
|
||||||
+ Wait for the exact ready event, argument and sequence. #code("HP_LINUX_READY") marks this
|
+ Wait for the exact ready event, argument and sequence. #code("HP_LINUX_READY") marks this
|
||||||
checkpoint; a freestanding acceptance payload uses the same marker without booting Linux.
|
checkpoint. Ordinary Linux terminates successfully here; smoke continues, and the dedicated
|
||||||
|
NPU-P6 build reuses the four-entry Linux transport but also continues.
|
||||||
+ Send the GA2D start command and wait for the result message with its distinct sequence.
|
+ Send the GA2D start command and wait for the result message with its distinct sequence.
|
||||||
Confirm GA2D safe idle before requesting HP hold.
|
Confirm GA2D safe idle before requesting HP hold.
|
||||||
+ Observe the cache-clean request, receive the cache message, acknowledge clean state and
|
+ Observe the cache-clean request, receive the cache message, acknowledge clean state and
|
||||||
@@ -79,11 +81,10 @@ The budget is *not a duration in milliseconds*, and a stalled MMIO access can pr
|
|||||||
progress. A different sequence remains pending until the budget expires; matching the sequence
|
progress. A different sequence remains pending until the budget expires; matching the sequence
|
||||||
with a wrong event or argument fails immediately. A mailbox API error also fails the wait.
|
with a wrong event or argument fails immediately. A mailbox API error also fails the wait.
|
||||||
|
|
||||||
The supplied Linux rootfs service sends *only the initial ready message*. It contains no GA2D
|
The supplied Linux rootfs service sends *only the ready message*. That is sufficient for the
|
||||||
command responder or cache-clean service. A Linux init checkpoint therefore does not satisfy
|
ordinary Linux workload's terminal checkpoint. It contains no GA2D command responder or
|
||||||
the complete current acceptance loader. A matching HP acceptance payload supplies those later
|
cache-clean service and therefore cannot substitute for the smoke or NPU-P6 acceptance payloads,
|
||||||
responses; deploying a Linux service that does so is additional integration work, not a native
|
which supply those later responses. See @linux-runtime.
|
||||||
graphics-driver capability established by this publication. See @linux-runtime.
|
|
||||||
|
|
||||||
The software fallback computes CRC from bytes read from the source while copying. It performs
|
The software fallback computes CRC from bytes read from the source while copying. It performs
|
||||||
full destination readback only for entries up to the loader's small-entry threshold; it must
|
full destination readback only for entries up to the loader's small-entry threshold; it must
|
||||||
@@ -101,8 +102,9 @@ or buffer reuse. No automatic image retry or signed recovery image selection is
|
|||||||
A board supervisor/watchdog remains a separate protection against stalled accesses or software.
|
A board supervisor/watchdog remains a separate protection against stalled accesses or software.
|
||||||
|
|
||||||
For target acceptance, capture profile/image hashes, enabled acceptance options, LP load/release
|
For target acceptance, capture profile/image hashes, enabled acceptance options, LP load/release
|
||||||
checkpoints, ready/result/cache messages, final ownership and the simulator verdict. Do not treat
|
checkpoints, ready/result/cache messages where applicable, final ownership and the simulator
|
||||||
UART startup or the ready marker alone as success. Linux peripheral qualification requires
|
verdict. Do not treat UART startup as success or apply the Linux-ready terminal rule to smoke or
|
||||||
|
NPU-P6. Linux peripheral qualification requires
|
||||||
the separate support matrix and matching platform evidence.
|
the separate support matrix and matching platform evidence.
|
||||||
|
|
||||||
#block(above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[
|
#block(above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[
|
||||||
|
|||||||
@@ -8,10 +8,17 @@ from pathlib import Path
|
|||||||
|
|
||||||
from publications.implementation_reference import without_comments
|
from publications.implementation_reference import without_comments
|
||||||
from publications.api_bundle_reference import bundle_layout, validate_api, dependencies as api_bundle_dependencies
|
from publications.api_bundle_reference import bundle_layout, validate_api, dependencies as api_bundle_dependencies
|
||||||
|
from publications.boot_acceptance_reference import (
|
||||||
|
SOURCES as BOOT_SOURCES,
|
||||||
|
collect_boot_acceptance,
|
||||||
|
ready_mailbox,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def dependencies(spec: dict) -> set[str]:
|
def dependencies(spec: dict) -> set[str]:
|
||||||
paths = set(spec.get("sources", []))
|
paths = set(spec.get("sources", []))
|
||||||
|
if spec:
|
||||||
|
paths.update(BOOT_SOURCES)
|
||||||
paths.update(api_bundle_dependencies(spec))
|
paths.update(api_bundle_dependencies(spec))
|
||||||
paths.update(spec.get("runtime_profiles", []))
|
paths.update(spec.get("runtime_profiles", []))
|
||||||
for row in spec.get("bindings", []):
|
for row in spec.get("bindings", []):
|
||||||
@@ -174,29 +181,11 @@ def linux_platform(root: Path) -> dict:
|
|||||||
raise ValueError("CPU and UART device-tree clock declarations disagree")
|
raise ValueError("CPU and UART device-tree clock declarations disagree")
|
||||||
if dt_value("linux,initrd-start") != dt_value("linux,initrd-end"):
|
if dt_value("linux,initrd-start") != dt_value("linux,initrd-end"):
|
||||||
raise ValueError("reviewed initrd template placeholder changed")
|
raise ValueError("reviewed initrd template placeholder changed")
|
||||||
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")
|
|
||||||
registers = ("HP_EVENT", "HP_ARG0", "HP_SEQUENCE", "HP_DOORBELL")
|
|
||||||
purposes = ("Linux-ready event", "Ready-state argument", "Publication sequence",
|
|
||||||
"LP interrupt request")
|
|
||||||
return {"hart_id": 1, "timebase_hz": timebase, "clock_hz": int(clocks[0]),
|
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],
|
"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"),
|
"memory_bytes": int(memory[0][1], 0), "initrd_start": dt_value("linux,initrd-start"),
|
||||||
"initrd_template_end": dt_value("linux,initrd-end"), "bootargs": bootargs[0],
|
"initrd_template_end": dt_value("linux,initrd-end"), "bootargs": bootargs[0],
|
||||||
"ready_writes": [
|
"ready_writes": ready_mailbox(root)}
|
||||||
{"address": f"0x{address:08X}", "value": f"0x{value:08X}",
|
|
||||||
"register": register, "purpose": purpose}
|
|
||||||
for (address, value), register, purpose in zip(
|
|
||||||
writes, registers, purposes, strict=True
|
|
||||||
)
|
|
||||||
]}
|
|
||||||
|
|
||||||
|
|
||||||
def collect_software(root: Path, spec: dict) -> dict:
|
def collect_software(root: Path, spec: dict) -> dict:
|
||||||
@@ -206,6 +195,7 @@ def collect_software(root: Path, spec: dict) -> dict:
|
|||||||
raise ValueError("software diagnostic application coverage changed")
|
raise ValueError("software diagnostic application coverage changed")
|
||||||
return {**copy.deepcopy(spec), "sources": sorted(dependencies(spec)), "profiles": runtime_profiles(root, spec),
|
return {**copy.deepcopy(spec), "sources": sorted(dependencies(spec)), "profiles": runtime_profiles(root, spec),
|
||||||
"irq": irq_support(root), "platform": linux_platform(root),
|
"irq": irq_support(root), "platform": linux_platform(root),
|
||||||
|
"boot_acceptance": collect_boot_acceptance(root, function_body),
|
||||||
"api_reference": validate_api(root, spec["api_semantics"], function_body),
|
"api_reference": validate_api(root, spec["api_semantics"], function_body),
|
||||||
"bundle_reference": bundle_layout(root, spec["boot_bundle"], function_body),
|
"bundle_reference": bundle_layout(root, spec["boot_bundle"], function_body),
|
||||||
"applications": [application_diagnostics(root, app) for app in spec["applications"]]}
|
"applications": [application_diagnostics(root, app) for app in spec["applications"]]}
|
||||||
+10
-4
@@ -7,6 +7,11 @@ import argparse
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
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:
|
def build(args: argparse.Namespace) -> None:
|
||||||
output = args.output.resolve()
|
output = args.output.resolve()
|
||||||
@@ -20,8 +25,8 @@ def build(args: argparse.Namespace) -> None:
|
|||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
compiler,
|
compiler,
|
||||||
"-march=rv32imafdc_zicbom_zicsr_zifencei",
|
"-march=rv64imafdc_zicbom_zicsr_zifencei",
|
||||||
"-mabi=ilp32d",
|
"-mabi=lp64d",
|
||||||
"-nostdlib",
|
"-nostdlib",
|
||||||
"-nostartfiles",
|
"-nostartfiles",
|
||||||
"-ffreestanding",
|
"-ffreestanding",
|
||||||
@@ -38,20 +43,21 @@ def build(args: argparse.Namespace) -> None:
|
|||||||
],
|
],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
|
require_rv64_elf(elf)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[objcopy, "-O", "binary", str(elf), str(images / "fw_jump.bin")],
|
[objcopy, "-O", "binary", str(elf), str(images / "fw_jump.bin")],
|
||||||
check=True,
|
check=True,
|
||||||
)
|
)
|
||||||
(images / "retrosoc_hp.dtb").write_bytes(b"SMOK")
|
(images / "retrosoc_hp.dtb").write_bytes(b"SMOK")
|
||||||
(images / "Image").write_bytes(b"SMOK")
|
(images / "Image").write_bytes(b"SMOK")
|
||||||
(images / "rootfs.cpio.gz").write_bytes(b"SMOK")
|
(images / "rootfs.cpio").write_bytes(b"SMOK")
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument("--source-dir", type=Path, required=True)
|
parser.add_argument("--source-dir", type=Path, required=True)
|
||||||
parser.add_argument("--output", 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-")
|
||||||
parser.add_argument("--include", type=Path, action="append", default=[])
|
parser.add_argument("--include", type=Path, action="append", default=[])
|
||||||
build(parser.parse_args())
|
build(parser.parse_args())
|
||||||
|
|
||||||
|
|||||||
+24
-21
@@ -7,6 +7,11 @@ import argparse
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
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:
|
def build(args: argparse.Namespace) -> None:
|
||||||
output = args.output.resolve()
|
output = args.output.resolve()
|
||||||
@@ -16,35 +21,33 @@ def build(args: argparse.Namespace) -> None:
|
|||||||
compiler = f"{args.cross}gcc"
|
compiler = f"{args.cross}gcc"
|
||||||
objcopy = f"{args.cross}objcopy"
|
objcopy = f"{args.cross}objcopy"
|
||||||
command = [
|
command = [
|
||||||
compiler,
|
compiler,
|
||||||
"-march=rv32imafdc_zicbom_zicsr_zifencei",
|
"-march=rv64imafdc_zicbom_zicsr_zifencei",
|
||||||
"-mabi=ilp32d",
|
"-mabi=lp64d",
|
||||||
"-nostdlib",
|
"-nostdlib",
|
||||||
"-nostartfiles",
|
"-nostartfiles",
|
||||||
"-ffreestanding",
|
"-ffreestanding",
|
||||||
"-O3",
|
"-O3",
|
||||||
"-Wall",
|
"-Wall",
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-Werror=implicit-function-declaration",
|
"-Werror=implicit-function-declaration",
|
||||||
"-Werror=return-type",
|
"-Werror=return-type",
|
||||||
"-Wl,--build-id=none",
|
"-Wl,--build-id=none",
|
||||||
f"-Wl,-T,{args.linker.resolve()}",
|
f"-Wl,-T,{args.linker.resolve()}",
|
||||||
"-o",
|
"-o",
|
||||||
str(elf),
|
str(elf),
|
||||||
str(args.source.resolve()),
|
str(args.source.resolve()),
|
||||||
]
|
]
|
||||||
for include in args.include:
|
for include in args.include:
|
||||||
command.append(f"-I{include.resolve()}")
|
command.append(f"-I{include.resolve()}")
|
||||||
command.extend(str(source.resolve()) for source in args.extra_source)
|
command.extend(str(source.resolve()) for source in args.extra_source)
|
||||||
command.extend(args.define)
|
command.extend(args.define)
|
||||||
subprocess.run(command, check=True)
|
subprocess.run(command, check=True)
|
||||||
|
require_rv64_elf(elf)
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[objcopy, "-O", "binary", str(elf), str(images / "fw_jump.bin")],
|
[objcopy, "-O", "binary", str(elf), str(images / "hp_smoke.bin")],
|
||||||
check=True,
|
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:
|
def main() -> None:
|
||||||
@@ -52,7 +55,7 @@ def main() -> None:
|
|||||||
parser.add_argument("--source", type=Path, required=True)
|
parser.add_argument("--source", type=Path, required=True)
|
||||||
parser.add_argument("--linker", type=Path, required=True)
|
parser.add_argument("--linker", type=Path, required=True)
|
||||||
parser.add_argument("--output", 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-")
|
||||||
parser.add_argument("--extra-source", type=Path, action="append", default=[])
|
parser.add_argument("--extra-source", type=Path, action="append", default=[])
|
||||||
parser.add_argument("--include", type=Path, action="append", default=[])
|
parser.add_argument("--include", type=Path, action="append", default=[])
|
||||||
parser.add_argument("--define", action="append", default=[])
|
parser.add_argument("--define", action="append", default=[])
|
||||||
|
|||||||
@@ -261,7 +261,11 @@ static const rs_{prefix}_reloc_t rs_{prefix}_npu_relocations[{len(job.relocation
|
|||||||
{_c_bytes(f'rs_{prefix}_npu_params', job.params)}
|
{_c_bytes(f'rs_{prefix}_npu_params', job.params)}
|
||||||
|
|
||||||
static uint64_t rs_{prefix}_cycles(void) {{
|
static uint64_t rs_{prefix}_cycles(void) {{
|
||||||
#if defined(__riscv)
|
#if defined(__riscv_xlen) && (__riscv_xlen == 64)
|
||||||
|
uint64_t cycle;
|
||||||
|
__asm__ volatile("rdcycle %0" : "=r"(cycle));
|
||||||
|
return cycle;
|
||||||
|
#elif defined(__riscv)
|
||||||
uint32_t high0; uint32_t low; uint32_t high1;
|
uint32_t high0; uint32_t low; uint32_t high1;
|
||||||
do {{ __asm__ volatile("rdcycleh %0" : "=r"(high0));
|
do {{ __asm__ volatile("rdcycleh %0" : "=r"(high0));
|
||||||
__asm__ volatile("rdcycle %0" : "=r"(low));
|
__asm__ volatile("rdcycle %0" : "=r"(low));
|
||||||
|
|||||||
@@ -110,6 +110,32 @@ def build_variants(timestamp: str, log_dir: Path) -> dict[str, Path]:
|
|||||||
return roots
|
return roots
|
||||||
|
|
||||||
|
|
||||||
|
def qualification_configuration(variants: dict[str, Path]) -> dict[str, object]:
|
||||||
|
selected = ("MINI_MODE", "APP", "SIMU", "HP_CONFIG", "EXT_CLK_HZ")
|
||||||
|
configurations: dict[str, dict[str, str]] = {}
|
||||||
|
for workload, variant in variants.items():
|
||||||
|
manifest = json.loads((variant / "meta/manifest.json").read_text(encoding="utf-8"))
|
||||||
|
configuration = manifest.get("configuration")
|
||||||
|
if not isinstance(configuration, dict) or any(
|
||||||
|
not isinstance(configuration.get(name), str) for name in selected
|
||||||
|
):
|
||||||
|
raise ValueError(f"P6 {workload} manifest lacks its PRODUCT configuration")
|
||||||
|
configurations[workload] = {name: configuration[name] for name in selected}
|
||||||
|
if set(configurations) != {"kws", "vww"} or configurations["kws"] != configurations["vww"]:
|
||||||
|
raise ValueError("P6 workload manifests use different PRODUCT configurations")
|
||||||
|
configuration = configurations["kws"]
|
||||||
|
expected = {
|
||||||
|
"MINI_MODE": "PRODUCT",
|
||||||
|
"APP": "hp_boot",
|
||||||
|
"SIMU": "VERILATOR",
|
||||||
|
"HP_CONFIG": "rv64imafdc_zicbom_max",
|
||||||
|
"EXT_CLK_HZ": "72000000",
|
||||||
|
}
|
||||||
|
if configuration != expected:
|
||||||
|
raise ValueError(f"P6 manifest configuration differs from the frozen target: {configuration}")
|
||||||
|
return {**configuration, "EXT_CLK_HZ": int(configuration["EXT_CLK_HZ"])}
|
||||||
|
|
||||||
|
|
||||||
def parse_case_line(line: str) -> dict[str, Any]:
|
def parse_case_line(line: str) -> dict[str, Any]:
|
||||||
if not line.startswith(CASE_PREFIX):
|
if not line.startswith(CASE_PREFIX):
|
||||||
raise ValueError("not an NPU-P6 case record")
|
raise ValueError("not an NPU-P6 case record")
|
||||||
@@ -168,10 +194,10 @@ def package_shard(
|
|||||||
shard_dir = output / workload / f"shard-{shard_index:03d}"
|
shard_dir = output / workload / f"shard-{shard_index:03d}"
|
||||||
images = shard_dir / "images"
|
images = shard_dir / "images"
|
||||||
images.mkdir(parents=True, exist_ok=True)
|
images.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copy2(variant / "hp-smoke/images/fw_jump.bin", images / "fw_jump.bin")
|
shutil.copy2(variant / "hp-smoke/images/hp_smoke.bin", images / "fw_jump.bin")
|
||||||
shutil.copy2(Path(shard["file"]["path"]), images / "Image")
|
shutil.copy2(Path(shard["file"]["path"]), images / "Image")
|
||||||
(images / "retrosoc_hp.dtb").write_bytes(b"SMOK")
|
(images / "retrosoc_hp.dtb").write_bytes(b"SMOK")
|
||||||
(images / "rootfs.cpio.gz").write_bytes(b"SMOK")
|
(images / "rootfs.cpio").write_bytes(b"SMOK")
|
||||||
bundle = shard_dir / "retrosoc_npu_p6.bin"
|
bundle = shard_dir / "retrosoc_npu_p6.bin"
|
||||||
manifest = shard_dir / "bundle.json"
|
manifest = shard_dir / "bundle.json"
|
||||||
completed = run(
|
completed = run(
|
||||||
@@ -396,6 +422,7 @@ def main() -> int:
|
|||||||
output = args.output_dir.resolve()
|
output = args.output_dir.resolve()
|
||||||
output.mkdir(parents=True, exist_ok=True)
|
output.mkdir(parents=True, exist_ok=True)
|
||||||
variants = build_variants(args.build_timestamp, output / "build-logs")
|
variants = build_variants(args.build_timestamp, output / "build-logs")
|
||||||
|
configuration = qualification_configuration(variants)
|
||||||
emulator = variants["kws"] / "sim/verilator/emu"
|
emulator = variants["kws"] / "sim/verilator/emu"
|
||||||
if not emulator.is_file():
|
if not emulator.is_file():
|
||||||
raise ValueError("P6 PRODUCT Verilator emulator is missing")
|
raise ValueError("P6 PRODUCT Verilator emulator is missing")
|
||||||
@@ -441,13 +468,7 @@ def main() -> int:
|
|||||||
["git", "-C", str(ROOT), "rev-parse", "HEAD"], text=True
|
["git", "-C", str(ROOT), "rev-parse", "HEAD"], text=True
|
||||||
).strip(),
|
).strip(),
|
||||||
"profile": "configs/ci/ihp130.mk",
|
"profile": "configs/ci/ihp130.mk",
|
||||||
"configuration": {
|
"configuration": configuration,
|
||||||
"MINI_MODE": "PRODUCT",
|
|
||||||
"APP": "hp_boot",
|
|
||||||
"SIMU": "VERILATOR",
|
|
||||||
"HP_CONFIG": "rv32imafdc_zicbom_max",
|
|
||||||
"EXT_CLK_HZ": 72_000_000,
|
|
||||||
},
|
|
||||||
"measurement": {
|
"measurement": {
|
||||||
"counter": "HP rdcycle",
|
"counter": "HP rdcycle",
|
||||||
"reference": "portable INT8 C through CPU Softmax",
|
"reference": "portable INT8 C through CPU Softmax",
|
||||||
|
|||||||
@@ -128,6 +128,40 @@ def test_hp_smoke_p5_npu_payload_has_stack_cache_and_polling_plan() -> None:
|
|||||||
assert "rs_npu_irq_enable(0U)" in acceptance
|
assert "rs_npu_irq_enable(0U)" in acceptance
|
||||||
assert "rs_npu_irq_ack(RS_NPU_IRQ_ALL)" in acceptance
|
assert "rs_npu_irq_ack(RS_NPU_IRQ_ALL)" in acceptance
|
||||||
assert '"--extra-source"' in builder and '"--include"' in builder
|
assert '"--extra-source"' in builder and '"--include"' in builder
|
||||||
|
assert '"-march=rv64imafdc_zicbom_zicsr_zifencei"' in builder
|
||||||
|
assert '"-mabi=lp64d"' in builder
|
||||||
|
assert "require_rv64_elf(elf)" in builder
|
||||||
|
assert 'images / "hp_smoke.bin"' in builder
|
||||||
|
assert 'images / "fw_jump.bin"' not in builder
|
||||||
|
|
||||||
|
|
||||||
|
def test_hp_apu_profile_and_payload_use_the_rv64_product_contract() -> None:
|
||||||
|
profile = (ROOT / "configs/ci/ihp130-apu.mk").read_text(encoding="utf-8")
|
||||||
|
builder = (ROOT / "scripts/build_hp_apu.py").read_text(encoding="utf-8")
|
||||||
|
makefile = (ROOT / "Makefile").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "HP_CONFIG := rv64imafdc_zicbom_max" in profile
|
||||||
|
assert '"-march=rv64imafdc_zicbom_zicsr_zifencei"' in builder
|
||||||
|
assert '"-mabi=lp64d"' in builder
|
||||||
|
assert "require_rv64_elf(elf)" in builder
|
||||||
|
assert 'images / "rootfs.cpio"' in builder
|
||||||
|
assert "--output $(HP_APU_BUILD_DIR) --cross $(HP_CROSS)" in makefile
|
||||||
|
|
||||||
|
completed = subprocess.run(
|
||||||
|
[
|
||||||
|
"make",
|
||||||
|
"-s",
|
||||||
|
"CONFIG=configs/ci/ihp130-apu.mk",
|
||||||
|
"BUILD_TIMESTAMP=2026-09-30-18-40",
|
||||||
|
"config",
|
||||||
|
],
|
||||||
|
cwd=ROOT,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
assert completed.returncode == 0, completed.stdout + completed.stderr
|
||||||
|
assert "HP_CONFIG rv64imafdc_zicbom_max" in completed.stdout
|
||||||
|
|
||||||
|
|
||||||
def test_hp_smoke_simulation_requires_ga2d_result_and_cache_lifecycle_markers() -> None:
|
def test_hp_smoke_simulation_requires_ga2d_result_and_cache_lifecycle_markers() -> None:
|
||||||
@@ -146,6 +180,16 @@ def test_hp_smoke_simulation_requires_ga2d_result_and_cache_lifecycle_markers()
|
|||||||
assert marker in (*MARKERS["smoke"], "SIM_TEST_PASS code=0")
|
assert marker in (*MARKERS["smoke"], "SIM_TEST_PASS code=0")
|
||||||
|
|
||||||
|
|
||||||
|
def test_npu_p6_linux_transport_does_not_take_the_normal_ready_exit() -> None:
|
||||||
|
source = (ROOT / "app/apps/hp_boot/main.c").read_text(encoding="utf-8")
|
||||||
|
linux_branch = source.split(
|
||||||
|
"if (header.workload == RS_HP_BOOT_WORKLOAD_LINUX) {", 1
|
||||||
|
)[1].split("}", 1)[0]
|
||||||
|
|
||||||
|
assert "#if !defined(RS_NPU_P6_ACCEPTANCE)" in linux_branch
|
||||||
|
assert "rs_test_finish(RS_TEST_PASSED" in linux_branch
|
||||||
|
|
||||||
|
|
||||||
def test_hp_cache_handshake_budget_covers_cbo_and_mailbox_round_trip() -> None:
|
def test_hp_cache_handshake_budget_covers_cbo_and_mailbox_round_trip() -> None:
|
||||||
source = (ROOT / "rtl/mini/top/retrosoc.sv").read_text(encoding="utf-8")
|
source = (ROOT / "rtl/mini/top/retrosoc.sv").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|||||||
@@ -257,6 +257,10 @@ def test_reproducible_package_and_generated_c(tmp_path, name, model, digest, log
|
|||||||
for entry in a["report"]["operators"])
|
for entry in a["report"]["operators"])
|
||||||
assert a["report"]["totals"]["descriptor_read_bytes"] == descriptor_bytes
|
assert a["report"]["totals"]["descriptor_read_bytes"] == descriptor_bytes
|
||||||
assert a["report"]["totals"]["dma_read_bytes"] == descriptor_bytes + operator_reads
|
assert a["report"]["totals"]["dma_read_bytes"] == descriptor_bytes + operator_reads
|
||||||
|
generated_source = (first / f"{name}_npu.c").read_text(encoding="utf-8")
|
||||||
|
assert "#if defined(__riscv_xlen) && (__riscv_xlen == 64)" in generated_source
|
||||||
|
assert '__asm__ volatile("rdcycle %0" : "=r"(cycle));' in generated_source
|
||||||
|
assert '#elif defined(__riscv)\n uint32_t high0;' in generated_source
|
||||||
compiler = shutil.which("cc")
|
compiler = shutil.which("cc")
|
||||||
if compiler is None:
|
if compiler is None:
|
||||||
pytest.fail("host C compiler is required")
|
pytest.fail("host C compiler is required")
|
||||||
|
|||||||
@@ -21,6 +21,36 @@ import run_npu_p6_netlist as netlist # noqa: E402
|
|||||||
import run_npu_p6_verilator as verilator # noqa: E402
|
import run_npu_p6_verilator as verilator # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def test_p6_cycle_counter_supports_rv64_and_rv32() -> None:
|
||||||
|
runner = (ROOT / "app/benchmark/npu/npu_p6_runner.c").read_text(encoding="utf-8")
|
||||||
|
assert "#if defined(__riscv_xlen) && (__riscv_xlen == 64)" in runner
|
||||||
|
assert '__asm__ volatile("rdcycle %0" : "=r"(cycle));' in runner
|
||||||
|
assert '__asm__ volatile("rdcycleh %0" : "=r"(high_before));' in runner
|
||||||
|
|
||||||
|
|
||||||
|
def test_p6_shard_parser_invalidates_image_before_header_and_payload_reads() -> None:
|
||||||
|
runner = (ROOT / "app/benchmark/npu/npu_p6_runner.c").read_text(encoding="utf-8")
|
||||||
|
assert "rs_npu_p6_invalidate_range(image, RS_NPU_P6_HEADER_BYTES);" in runner
|
||||||
|
assert "rs_npu_p6_invalidate_range(&image[RS_NPU_P6_HEADER_BYTES], payload_bytes);" in runner
|
||||||
|
assert '"cbo.inval 0(%0)"' in runner
|
||||||
|
|
||||||
|
|
||||||
|
def test_p6_first_case_has_bounded_stage_markers_without_changing_case_schema() -> None:
|
||||||
|
runner = (ROOT / "app/benchmark/npu/npu_p6_runner.c").read_text(encoding="utf-8")
|
||||||
|
assert '"NPU_P6_STAGE workload="' in runner
|
||||||
|
for stage in (
|
||||||
|
'"reference-start"',
|
||||||
|
'"softmax-done"',
|
||||||
|
'"ga2d-reference-start"',
|
||||||
|
'"npu-start"',
|
||||||
|
'"npu-done"',
|
||||||
|
'"golden-done"',
|
||||||
|
):
|
||||||
|
assert stage in runner
|
||||||
|
assert "index == 0U))" in runner
|
||||||
|
assert '"NPU_P6_CASE workload="' in runner
|
||||||
|
|
||||||
|
|
||||||
def _case(index: int, workload: str = "kws") -> corpus.CorpusCase:
|
def _case(index: int, workload: str = "kws") -> corpus.CorpusCase:
|
||||||
input_data = bytes([index & 0xFF]) * corpus.INPUT_BYTES[workload]
|
input_data = bytes([index & 0xFF]) * corpus.INPUT_BYTES[workload]
|
||||||
terminal_data = bytes([(index + 1) & 0xFF]) * corpus.OUTPUT_BYTES[workload]
|
terminal_data = bytes([(index + 1) & 0xFF]) * corpus.OUTPUT_BYTES[workload]
|
||||||
@@ -120,6 +150,70 @@ def test_verilator_case_parser_is_exact_and_cycle_accounted() -> None:
|
|||||||
verilator.parse_case_line(broken)
|
verilator.parse_case_line(broken)
|
||||||
|
|
||||||
|
|
||||||
|
def test_verilator_report_uses_matching_variant_manifests(tmp_path: Path) -> None:
|
||||||
|
variants = {name: tmp_path / name for name in ("kws", "vww")}
|
||||||
|
configuration = {
|
||||||
|
"MINI_MODE": "PRODUCT",
|
||||||
|
"APP": "hp_boot",
|
||||||
|
"SIMU": "VERILATOR",
|
||||||
|
"HP_CONFIG": "rv64imafdc_zicbom_max",
|
||||||
|
"EXT_CLK_HZ": "72000000",
|
||||||
|
}
|
||||||
|
for variant in variants.values():
|
||||||
|
(variant / "meta").mkdir(parents=True)
|
||||||
|
(variant / "meta/manifest.json").write_text(
|
||||||
|
json.dumps({"configuration": configuration}), encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert verilator.qualification_configuration(variants) == {
|
||||||
|
**configuration,
|
||||||
|
"EXT_CLK_HZ": 72_000_000,
|
||||||
|
}
|
||||||
|
|
||||||
|
mismatched = {**configuration, "HP_CONFIG": "rv32imafdc_zicbom_max"}
|
||||||
|
(variants["vww"] / "meta/manifest.json").write_text(
|
||||||
|
json.dumps({"configuration": mismatched}), encoding="utf-8"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="different PRODUCT configurations"):
|
||||||
|
verilator.qualification_configuration(variants)
|
||||||
|
|
||||||
|
(variants["kws"] / "meta/manifest.json").write_text(
|
||||||
|
json.dumps({"configuration": mismatched}), encoding="utf-8"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="frozen target"):
|
||||||
|
verilator.qualification_configuration(variants)
|
||||||
|
|
||||||
|
|
||||||
|
def test_verilator_shard_reuses_v2_linux_transport(tmp_path: Path) -> None:
|
||||||
|
variant = tmp_path / "variant"
|
||||||
|
(variant / "hp-smoke/images").mkdir(parents=True)
|
||||||
|
(variant / "sw").mkdir()
|
||||||
|
hp_payload = b"rv64-hp-payload"
|
||||||
|
shard_payload = b"npu-p6-shard"
|
||||||
|
(variant / "hp-smoke/images/hp_smoke.bin").write_bytes(hp_payload)
|
||||||
|
(variant / "sw/retrosoc_fw.bin").write_bytes(b"lp")
|
||||||
|
shard_file = tmp_path / "shard.bin"
|
||||||
|
shard_file.write_bytes(shard_payload)
|
||||||
|
|
||||||
|
bundle, manifest = verilator.package_shard(
|
||||||
|
"kws",
|
||||||
|
{"index": 0, "file": {"path": str(shard_file)}},
|
||||||
|
variant,
|
||||||
|
tmp_path / "packages",
|
||||||
|
)
|
||||||
|
record = json.loads(manifest.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
assert bundle.is_file()
|
||||||
|
assert record["workload"] == "linux"
|
||||||
|
assert record["xlen"] == 64
|
||||||
|
assert record["artifacts"]["fw_jump.bin"]["sha256"] == hashlib.sha256(
|
||||||
|
hp_payload
|
||||||
|
).hexdigest()
|
||||||
|
assert record["artifacts"]["Image"]["sha256"] == hashlib.sha256(
|
||||||
|
shard_payload
|
||||||
|
).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def test_compiler_comparison_explains_material_cycle_difference() -> None:
|
def test_compiler_comparison_explains_material_cycle_difference() -> None:
|
||||||
estimate = {
|
estimate = {
|
||||||
"artifact": {"path": "npu.json", "bytes": 1, "sha256": "0" * 64},
|
"artifact": {"path": "npu.json", "bytes": 1, "sha256": "0" * 64},
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ def test_actual_boot_requires_bounded_complete_acceptance():
|
|||||||
assert [(row["event"], row["argument"], row["sequence"]) for row in result["messages"]] == [
|
assert [(row["event"], row["argument"], row["sequence"]) for row in result["messages"]] == [
|
||||||
(1, 0x4C4E5801, 1), (2, 0x47413244, 2), (4, 0x43424F4B, 3)]
|
(1, 0x4C4E5801, 1), (2, 0x47413244, 2), (4, 0x43424F4B, 3)]
|
||||||
assert result["rootfs_ready_only"]
|
assert result["rootfs_ready_only"]
|
||||||
assert result["terminal_requires_cache_handoff"]
|
assert result["linux_ready_is_terminal"]
|
||||||
|
assert result["smoke_terminal_requires_cache_handoff"]
|
||||||
|
assert result["npu_p6_reuses_linux_transport"]
|
||||||
assert result["failure_reset_requires_no_hp_ga2d_owner"]
|
assert result["failure_reset_requires_no_hp_ga2d_owner"]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ def test_real_software_records_separate_startup_and_irq_selections():
|
|||||||
assert rows["xpi_flash_loader"]["startup"] == "app/apps/xpi_flash_loader/startup.S"
|
assert rows["xpi_flash_loader"]["startup"] == "app/apps/xpi_flash_loader/startup.S"
|
||||||
assert result["irq"]["enabled_core_causes"] == ["IRQ_M_SOFT", "IRQ_M_TIMER"]
|
assert result["irq"]["enabled_core_causes"] == ["IRQ_M_SOFT", "IRQ_M_TIMER"]
|
||||||
assert result["irq"]["counts"]["RS_EXTERNAL_IRQ_COUNT"] == 62
|
assert result["irq"]["counts"]["RS_EXTERNAL_IRQ_COUNT"] == 62
|
||||||
|
assert result["boot_acceptance"]["event_poll_iterations"] == 64000000
|
||||||
|
assert result["boot_acceptance"]["linux_ready_is_terminal"]
|
||||||
|
assert result["boot_acceptance"]["smoke_terminal_requires_cache_handoff"]
|
||||||
assert sr.dependencies(result) == sr.dependencies(SPEC)
|
assert sr.dependencies(result) == sr.dependencies(SPEC)
|
||||||
|
|
||||||
|
|
||||||
@@ -161,7 +164,7 @@ def test_ready_request_cannot_move_before_payload(source_tree):
|
|||||||
last = next(i for i, line in enumerate(lines) if "mailbox[11]" in line)
|
last = next(i for i, line in enumerate(lines) if "mailbox[11]" in line)
|
||||||
lines[first], lines[last] = lines[last], lines[first]
|
lines[first], lines[last] = lines[last], lines[first]
|
||||||
path.write_text("\n".join(lines))
|
path.write_text("\n".join(lines))
|
||||||
with pytest.raises(ValueError, match="publication"):
|
with pytest.raises(ValueError, match="mailbox sequence"):
|
||||||
sr.linux_platform(source_tree)
|
sr.linux_platform(source_tree)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,22 @@ def test_apu_capability_and_digest_remain_profile_specific(data):
|
|||||||
assert all(r["capability"] & 2 == 0 for r in rows) # MP3 remains clear.
|
assert all(r["capability"] & 2 == 0 for r in rows) # MP3 remains clear.
|
||||||
|
|
||||||
|
|
||||||
|
def test_merge_recovery_preserves_publication_contracts(data):
|
||||||
|
root = ROOT / "publications/datasheets"
|
||||||
|
overview = (root / "sections/overview.typ").read_text(encoding="utf-8")
|
||||||
|
architecture = (root / "sections/architecture.typ").read_text(encoding="utf-8")
|
||||||
|
software = data["system_reference"]["software"]
|
||||||
|
|
||||||
|
assert '(name:"Functional",fill:"left-half")' in overview
|
||||||
|
assert "v05-rill-cover" in overview
|
||||||
|
assert "64 dense MACs" in overview
|
||||||
|
assert "fixed KWS in the P7 configuration" in overview
|
||||||
|
assert "Ten-master fabric" in architecture
|
||||||
|
assert "GA2D slot 8 and NPU slot 9" in architecture
|
||||||
|
assert software["boot_acceptance"]["linux_ready_is_terminal"]
|
||||||
|
assert software["boot_acceptance"]["smoke_terminal_requires_cache_handoff"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("kind", ["npu-shell", "apu-blanket", "npu-counter", "npu-qualified", "npu-profile"])
|
@pytest.mark.parametrize("kind", ["npu-shell", "apu-blanket", "npu-counter", "npu-qualified", "npu-profile"])
|
||||||
def test_stale_live_accelerator_prose_is_not_accepted(data, kind):
|
def test_stale_live_accelerator_prose_is_not_accepted(data, kind):
|
||||||
catalog, features, content, annotations, profiles = [
|
catalog, features, content, annotations, profiles = [
|
||||||
|
|||||||
Reference in new issue
Block a user