Files
retroSoC/app/apps/hp_boot/main.c
T
maksyuki 422f48d0cd 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.
2026-10-01 12:41:05 +08:00

394 lines
15 KiB
C

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/dma.h>
#include <retrosoc/hal/ga2d.h>
#include <retrosoc/hal/hp_mailbox.h>
#if defined(RS_NPU_P5_ACCEPTANCE) || defined(RS_NPU_P6_ACCEPTANCE)
#include <retrosoc/hal/npu.h>
#endif
#include <retrosoc/hal/resource.h>
#include <retrosoc/hal/sdram.h>
#include <retrosoc/hal/sysctrl.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/lib/printf.h>
#include <retrosoc/service/test.h>
#include "hp_boot_bundle.h"
#define RS_HP_BOOT_HEADER_SIZE UINT32_C(128)
#define RS_HP_BOOT_READY_EVENT UINT32_C(1)
#define RS_HP_BOOT_READY_ARG UINT32_C(0x4C4E5801)
#define RS_HP_BOOT_GA2D_START_COMMAND UINT32_C(0x47413250)
#define RS_HP_BOOT_GA2D_START_ARG UINT32_C(0x47413244)
#define RS_HP_BOOT_GA2D_PASS_EVENT UINT32_C(2)
#define RS_HP_BOOT_GA2D_FAIL_EVENT UINT32_C(3)
#define RS_HP_BOOT_GA2D_CACHE_EVENT UINT32_C(4)
#define RS_HP_BOOT_GA2D_PASS_ARG UINT32_C(0x47413244)
#define RS_HP_BOOT_GA2D_CACHE_ARG UINT32_C(0x43424F4B)
#define RS_HP_BOOT_MAILBOX_READY_SEQUENCE UINT32_C(1)
#define RS_HP_BOOT_MAILBOX_RESULT_SEQUENCE UINT32_C(2)
#define RS_HP_BOOT_MAILBOX_CACHE_SEQUENCE UINT32_C(3)
#define RS_HP_BOOT_READBACK_STRIDE_BYTES UINT32_C(1024)
#define RS_HP_BOOT_DMA_TIMEOUT (RS_TIMEOUT_DEFAULT * UINT32_C(64))
#define RS_HP_BOOT_EVENT_TIMEOUT (RS_TIMEOUT_DEFAULT * UINT32_C(64))
static rs_dma_tcd_t s_hp_boot_tcd __attribute__((aligned(64)));
static bool s_hp_boot_ga2d_owned_by_hp;
_Static_assert(sizeof(rs_hp_boot_header_t) == RS_HP_BOOT_HEADER_SIZE,
"HP boot bundle header ABI mismatch");
static void rs_hp_boot_read_header(rs_hp_boot_header_t *header) {
volatile const uint8_t *source =
(volatile const uint8_t *)(uintptr_t)(RS_SOC_FLASH_BASE + RS_HP_BOOT_BUNDLE_OFFSET);
uint8_t *destination = (uint8_t *)header;
for (uint32_t index = 0U; index < sizeof(*header); ++index) {
destination[index] = source[index];
}
}
static bool rs_hp_boot_copy_entry(const rs_hp_boot_entry_t *entry) {
volatile const uint32_t *source_words =
(volatile const uint32_t *)(uintptr_t)(RS_SOC_FLASH_BASE + entry->flash_offset);
volatile uint32_t *destination_words = (volatile uint32_t *)(uintptr_t)entry->load_address;
uint32_t word_count = entry->size / (uint32_t)sizeof(uint32_t);
uint32_t crc = UINT32_C(0xFFFFFFFF);
bool verify_full = entry->size <= RS_HP_BOOT_READBACK_STRIDE_BYTES;
for (uint32_t index = 0U; index < word_count; ++index) {
uint32_t value = source_words[index];
destination_words[index] = value;
if (verify_full && (destination_words[index] != value)) {
return false;
}
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)value);
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)(value >> 8U));
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)(value >> 16U));
crc = rs_hp_boot_crc32_byte(crc, (uint8_t)(value >> 24U));
}
for (uint32_t index = word_count * (uint32_t)sizeof(uint32_t); index < entry->size; ++index) {
volatile const uint8_t *source_bytes =
(volatile const uint8_t *)(uintptr_t)(RS_SOC_FLASH_BASE + entry->flash_offset);
volatile uint8_t *destination_bytes = (volatile uint8_t *)(uintptr_t)entry->load_address;
uint8_t value = source_bytes[index];
destination_bytes[index] = value;
if (verify_full && (destination_bytes[index] != value)) {
return false;
}
crc = rs_hp_boot_crc32_byte(crc, value);
}
return (~crc) == entry->crc32;
}
static bool rs_hp_boot_dma_copy_entry(const rs_hp_boot_entry_t *entry) {
rs_status_t status;
s_hp_boot_tcd.next_ptr = UINT32_C(0);
s_hp_boot_tcd.source = RS_SOC_FLASH_BASE + entry->flash_offset;
s_hp_boot_tcd.destination = entry->load_address;
s_hp_boot_tcd.byte_count = entry->size;
s_hp_boot_tcd.source_stride = 0;
s_hp_boot_tcd.destination_stride = 0;
s_hp_boot_tcd.y_count = UINT16_C(1);
s_hp_boot_tcd.reserved = UINT16_C(0);
s_hp_boot_tcd.control = RS_DMA_TCD_VALID | RS_DMA_TCD_SRC_INC | RS_DMA_TCD_DST_INC |
RS_DMA_TCD_CRC_ENABLE | RS_DMA_TCD_CRC_FINAL |
(UINT32_C(3) << RS_DMA_TCD_PRIORITY_SHIFT) |
(UINT32_C(16) << RS_DMA_TCD_BURST_SHIFT);
s_hp_boot_tcd.control |= ((uint32_t)RS_DMA_KIND_MM_TO_MM << RS_DMA_TCD_KIND_SHIFT) |
((uint32_t)RS_DMA_REQUEST_SOFTWARE << RS_DMA_TCD_REQUEST_SHIFT);
s_hp_boot_tcd.crc_expected = entry->crc32;
s_hp_boot_tcd.crc_seed = UINT32_C(0xFFFFFFFF);
s_hp_boot_tcd.crc_result = UINT32_C(0);
s_hp_boot_tcd.status = UINT32_C(0);
s_hp_boot_tcd.bytes_done = UINT32_C(0);
s_hp_boot_tcd.error_status = UINT32_C(0);
s_hp_boot_tcd.reserved_tail = UINT32_C(0);
s_hp_boot_tcd.reserved_tail2 = UINT32_C(0);
__asm__ volatile("fence rw, rw" ::: "memory");
status = rs_dma_submit_tcd(RS_DMA_CHANNEL_HP, &s_hp_boot_tcd, RS_HP_BOOT_DMA_TIMEOUT);
__asm__ volatile("fence rw, rw" ::: "memory");
return (status == RS_OK) && (s_hp_boot_tcd.bytes_done == entry->size) &&
(s_hp_boot_tcd.crc_result == entry->crc32);
}
static bool rs_hp_boot_wait_sdram(void) {
rs_sdram_status_t status;
for (uint32_t timeout = 0U; timeout < RS_TIMEOUT_DEFAULT; ++timeout) {
if ((rs_sdram_get_status(&status) == RS_OK) && status.ready && !status.init_busy &&
!status.error) {
return true;
}
}
return false;
}
static bool rs_hp_boot_ga2d_idle(rs_resource_owner_t expected_owner) {
rs_ga2d_capability_t capability;
rs_ga2d_status_t ga2d_status;
rs_resource_status_t resource_status;
return (rs_ga2d_get_capability(&capability) == RS_OK) &&
(capability.features == RS_GA2D_CAPABILITY_P5) &&
(capability.limits == RS_GA2D_LIMITS_P5) &&
(capability.formats == RS_GA2D_FORMAT_CAPABILITY_P5) &&
(rs_ga2d_get_status(&ga2d_status) == RS_OK) && !ga2d_status.busy &&
!ga2d_status.draining && !ga2d_status.error && !ga2d_status.aborted &&
!ga2d_status.recovery_required && ga2d_status.data_ready &&
(rs_resource_get_status(RS_RESOURCE_GA2D, &resource_status) == RS_OK) &&
(resource_status.owner == expected_owner) && resource_status.idle &&
!resource_status.fault && !resource_status.blocked && !resource_status.quiesced &&
!resource_status.in_reset;
}
static bool rs_hp_boot_wait_ga2d_idle(rs_resource_owner_t expected_owner) {
for (uint32_t timeout = 0U; timeout < RS_HP_BOOT_EVENT_TIMEOUT; ++timeout) {
if (rs_hp_boot_ga2d_idle(expected_owner)) {
return true;
}
}
return false;
}
#if defined(RS_NPU_P5_ACCEPTANCE) || defined(RS_NPU_P6_ACCEPTANCE)
static bool rs_hp_boot_npu_idle(rs_resource_owner_t expected_owner) {
rs_npu_status_t npu_status;
rs_resource_status_t resource_status;
return (rs_npu_get_status(&npu_status) == RS_OK) &&
((npu_status.flags &
(RS_NPU_STATUS_BUSY | RS_NPU_STATUS_DRAINING | RS_NPU_STATUS_RECOVERING)) == 0U) &&
(rs_resource_get_status(RS_RESOURCE_NPU, &resource_status) == RS_OK) &&
(resource_status.owner == expected_owner) && resource_status.idle &&
!resource_status.fault && !resource_status.blocked && !resource_status.quiesced &&
!resource_status.in_reset;
}
static bool rs_hp_boot_wait_npu_idle(rs_resource_owner_t expected_owner) {
uint32_t timeout;
for (timeout = 0U; timeout < RS_HP_BOOT_EVENT_TIMEOUT; ++timeout) {
if (rs_hp_boot_npu_idle(expected_owner)) {
return true;
}
}
return false;
}
#endif
static bool rs_hp_boot_wait_message(uint32_t code, uint32_t argument, uint32_t sequence) {
rs_hp_mailbox_message_t message;
for (uint32_t timeout = 0U; timeout < RS_HP_BOOT_EVENT_TIMEOUT; ++timeout) {
uint32_t status;
if (rs_hp_mailbox_receive_from_hp(&message) != RS_OK) {
return false;
}
status = rs_hp_boot_message_status(message.code, message.argument, message.sequence, code,
argument, sequence);
if (status == 2U) {
printf("HP_PAYLOAD_FAILED:%u\n", (unsigned int)message.argument);
return false;
}
if (status == 1U) {
return true;
}
}
return false;
}
static bool rs_hp_boot_wait_cache_request(void) {
rs_resource_cache_status_t cache_status;
for (uint32_t timeout = 0U; timeout < RS_HP_BOOT_EVENT_TIMEOUT; ++timeout) {
if (rs_resource_get_cache_status(&cache_status) != RS_OK) {
return false;
}
if (cache_status.request && !cache_status.clean) {
return true;
}
}
return false;
}
static bool rs_hp_boot_wait_hp_held(void) {
rs_sysctrl_hp_status_t hp_status;
for (uint32_t timeout = 0U; timeout < RS_HP_BOOT_EVENT_TIMEOUT; ++timeout) {
if ((rs_sysctrl_get_hp_status(&hp_status) == RS_OK) && hp_status.reset_asserted &&
!hp_status.released && !hp_status.draining && !hp_status.forced_fault &&
rs_hp_boot_ga2d_idle(RS_RESOURCE_OWNER_HP)
#if defined(RS_NPU_P5_ACCEPTANCE) || defined(RS_NPU_P6_ACCEPTANCE)
&& rs_hp_boot_npu_idle(RS_RESOURCE_OWNER_HP)
#endif
) {
return true;
}
}
return false;
}
static void rs_hp_boot_fail(uint8_t code) {
if (!s_hp_boot_ga2d_owned_by_hp) {
(void)rs_sysctrl_set_hp_release(false);
}
printf("HP_BOOT_FAILED:%u\n", (unsigned int)code);
rs_test_finish(RS_TEST_FAILED, code);
}
int main(void) {
bool cache_clean_completed = false;
rs_hp_boot_header_t header;
rs_sysctrl_hp_status_t hp_status;
rs_hp_mailbox_message_t message;
rs_status_t release_status;
if (rs_uart_init(CPU_FREQ * UINT32_C(1000000), UART_BPS) != RS_OK) {
rs_test_finish(RS_TEST_FAILED, UINT8_C(1));
}
if ((rs_sysctrl_set_hp_release(false) != RS_OK) ||
(rs_sysctrl_select_hp_debug(false) != RS_OK) ||
(rs_sysctrl_get_hp_status(&hp_status) != RS_OK) || !hp_status.present ||
!hp_status.reset_asserted) {
rs_hp_boot_fail(UINT8_C(2));
}
if (!rs_hp_boot_wait_sdram()) {
rs_hp_boot_fail(UINT8_C(3));
}
rs_hp_boot_read_header(&header);
if (!rs_hp_boot_header_valid(&header)) {
rs_hp_boot_fail(UINT8_C(4));
}
for (uint32_t index = 0U; index < header.entry_count; ++index) {
printf("HP_BOOT_LOAD:%u:%u\n", (unsigned int)header.entries[index].type,
(unsigned int)header.entries[index].size);
if (!rs_hp_boot_dma_copy_entry(&header.entries[index]) &&
!rs_hp_boot_copy_entry(&header.entries[index])) {
rs_hp_boot_fail((uint8_t)(UINT8_C(5) + (uint8_t)index));
}
}
if (rs_hp_mailbox_probe() != RS_OK) {
rs_hp_boot_fail(UINT8_C(9));
}
if (header.workload == RS_HP_BOOT_WORKLOAD_SMOKE) {
if (!rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_LP) ||
(rs_resource_set_owner(RS_RESOURCE_GA2D, RS_RESOURCE_OWNER_HP, false) != RS_OK)) {
rs_hp_boot_fail(UINT8_C(9));
}
s_hp_boot_ga2d_owned_by_hp = true;
if (!rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_HP)) {
rs_hp_boot_fail(UINT8_C(9));
}
}
#if defined(RS_NPU_P5_ACCEPTANCE) || defined(RS_NPU_P6_ACCEPTANCE)
if (!rs_hp_boot_wait_npu_idle(RS_RESOURCE_OWNER_LP) ||
(rs_resource_set_owner(RS_RESOURCE_NPU, RS_RESOURCE_OWNER_HP, false) != RS_OK)) {
rs_hp_boot_fail(UINT8_C(18));
}
#endif
#if defined(RS_NPU_P5_ACCEPTANCE) || defined(RS_NPU_P6_ACCEPTANCE)
if (!rs_hp_boot_wait_npu_idle(RS_RESOURCE_OWNER_HP)) {
rs_hp_boot_fail(UINT8_C(18));
}
#endif
__asm__ volatile("fence rw, rw" ::: "memory");
if ((rs_hp_mailbox_clear_lp_interrupt() != RS_OK) ||
(rs_sysctrl_set_hp_release(true) != RS_OK) ||
(rs_sysctrl_get_hp_status(&hp_status) != RS_OK) || !hp_status.released) {
rs_hp_boot_fail(UINT8_C(10));
}
printf("HP_BOOT_RELEASED\n");
if (header.workload == RS_HP_BOOT_WORKLOAD_RTTHREAD) {
if (!rs_hp_boot_wait_message(RS_HP_BOOT_READY_EVENT, RS_HP_RTTHREAD_READY_ARG, 1U)) {
rs_hp_boot_fail(UINT8_C(18));
}
printf("HP_RTTHREAD_READY\n");
message.code = RS_HP_RTTHREAD_IRQ_COMMAND;
message.argument = RS_HP_RTTHREAD_IRQ_ARG;
message.sequence = 1U;
if ((rs_hp_mailbox_send_to_hp(&message) != RS_OK) ||
!rs_hp_boot_wait_message(2U, RS_HP_RTTHREAD_READY_ARG, 2U)) {
rs_hp_boot_fail(UINT8_C(19));
}
printf("HP_RTTHREAD_PASS\n");
rs_test_finish(RS_TEST_PASSED, UINT8_C(0));
}
if (!rs_hp_boot_wait_message(RS_HP_BOOT_READY_EVENT, RS_HP_BOOT_READY_ARG,
RS_HP_BOOT_MAILBOX_READY_SEQUENCE)) {
rs_hp_boot_fail(UINT8_C(11));
}
printf("HP_LINUX_READY\n");
if (header.workload == RS_HP_BOOT_WORKLOAD_LINUX) {
#if !defined(RS_NPU_P6_ACCEPTANCE)
rs_test_finish(RS_TEST_PASSED, UINT8_C(0));
#endif
}
message.code = RS_HP_BOOT_GA2D_START_COMMAND;
message.argument = RS_HP_BOOT_GA2D_START_ARG;
message.sequence = RS_HP_BOOT_MAILBOX_READY_SEQUENCE;
if (rs_hp_mailbox_send_to_hp(&message) != RS_OK) {
rs_hp_boot_fail(UINT8_C(12));
}
if (!rs_hp_boot_wait_message(RS_HP_BOOT_GA2D_PASS_EVENT, RS_HP_BOOT_GA2D_PASS_ARG,
RS_HP_BOOT_MAILBOX_RESULT_SEQUENCE)) {
rs_hp_boot_fail(UINT8_C(13));
}
printf("HP_GA2D_PASS\n");
#if defined(RS_NPU_P5_ACCEPTANCE)
printf("HP_NPU_PASS\n");
#endif
#if defined(RS_NPU_P6_ACCEPTANCE)
printf("HP_NPU_P6_PASS\n");
#endif
if (!rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_HP)) {
rs_hp_boot_fail(UINT8_C(14));
}
printf("HP_GA2D_IDLE\n");
release_status = rs_sysctrl_set_hp_release(false);
if ((release_status != RS_OK) && (release_status != RS_EIO)) {
rs_hp_boot_fail(UINT8_C(15));
}
printf("HP_GA2D_CACHE_REQUESTED\n");
if (rs_hp_boot_wait_cache_request()) {
printf("HP_GA2D_CACHE_REQUEST\n");
if (rs_hp_boot_wait_message(RS_HP_BOOT_GA2D_CACHE_EVENT, RS_HP_BOOT_GA2D_CACHE_ARG,
RS_HP_BOOT_MAILBOX_CACHE_SEQUENCE)) {
printf("HP_GA2D_CACHE_MESSAGE\n");
if (rs_resource_acknowledge_cache_clean() == RS_OK) {
printf("HP_GA2D_CACHE_ACK\n");
if (rs_hp_boot_wait_hp_held()) {
printf("HP_GA2D_HELD\n");
cache_clean_completed = true;
}
}
}
}
if (!cache_clean_completed) {
rs_hp_boot_fail(UINT8_C(16));
}
if (rs_resource_set_owner(RS_RESOURCE_GA2D, RS_RESOURCE_OWNER_LP, false) != RS_OK) {
rs_hp_boot_fail(UINT8_C(17));
}
s_hp_boot_ga2d_owned_by_hp = false;
if (!rs_hp_boot_wait_ga2d_idle(RS_RESOURCE_OWNER_LP)) {
rs_hp_boot_fail(UINT8_C(17));
}
#if defined(RS_NPU_P5_ACCEPTANCE) || defined(RS_NPU_P6_ACCEPTANCE)
if ((rs_resource_set_owner(RS_RESOURCE_NPU, RS_RESOURCE_OWNER_LP, false) != RS_OK) ||
!rs_hp_boot_wait_npu_idle(RS_RESOURCE_OWNER_LP)) {
rs_hp_boot_fail(UINT8_C(19));
}
#endif
printf("HP_GA2D_CACHE_CLEAN\n");
rs_test_finish(RS_TEST_PASSED, UINT8_C(0));
}