From 550ba31747853088dbe74f9cd32a44958f3fb3b4 Mon Sep 17 00:00:00 2001 From: ZGZ713912 Date: Thu, 22 Oct 2026 15:07:06 +0000 Subject: [PATCH 1/3] feat(usb): add USB recovery and fault injection support --- firmware/c_board/app/src/app.cpp | 5 ++ firmware/c_board/app/src/usb/dcd_recovery.hpp | 4 ++ .../c_board/app/src/watchdog/watchdog.hpp | 6 +- firmware/c_board/bootloader/CMakeLists.txt | 1 + .../c_board/bootloader/src/flash/metadata.hpp | 3 + .../c_board/bootloader/src/flash/writer.hpp | 3 + firmware/c_board/bootloader/src/main.cpp | 9 +++ .../c_board/bootloader/src/utility/assert.cpp | 23 +++++++ firmware/c_board/bsp/tinyusb | 2 +- firmware/rmcs_board/CMakeLists.txt | 2 + firmware/rmcs_board/app/CMakeLists.txt | 10 +++ firmware/rmcs_board/app/src/app.cpp | 12 ++++ .../rmcs_board/app/src/usb/dcd_recovery.hpp | 4 ++ .../rmcs_board/app/src/usb/fault_inject.c | 65 +++++++++++++++++++ .../rmcs_board/app/src/usb/fault_inject.hpp | 5 ++ .../rmcs_board/app/src/watchdog/watchdog.hpp | 7 +- firmware/rmcs_board/boards/lite/board.c | 8 ++- firmware/rmcs_board/boards/lite/board.h | 3 + firmware/rmcs_board/boards/pro/board.c | 8 ++- firmware/rmcs_board/boards/pro/board.h | 3 + firmware/rmcs_board/bootloader/CMakeLists.txt | 1 + .../bootloader/src/flash/xpi_nor.hpp | 3 + firmware/rmcs_board/bootloader/src/main.cpp | 10 +++ .../bootloader/src/utility/assert.cpp | 24 +++++++ firmware/rmcs_board/bsp/hpm_sdk | 2 +- 25 files changed, 213 insertions(+), 10 deletions(-) create mode 100644 firmware/c_board/app/src/usb/dcd_recovery.hpp create mode 100644 firmware/c_board/bootloader/src/utility/assert.cpp create mode 100644 firmware/rmcs_board/app/src/usb/dcd_recovery.hpp create mode 100644 firmware/rmcs_board/app/src/usb/fault_inject.c create mode 100644 firmware/rmcs_board/app/src/usb/fault_inject.hpp create mode 100644 firmware/rmcs_board/bootloader/src/utility/assert.cpp diff --git a/firmware/c_board/app/src/app.cpp b/firmware/c_board/app/src/app.cpp index db70da1..54c9d7e 100644 --- a/firmware/c_board/app/src/app.cpp +++ b/firmware/c_board/app/src/app.cpp @@ -20,6 +20,7 @@ #include "firmware/c_board/app/src/spi/spi.hpp" #include "firmware/c_board/app/src/timer/timer.hpp" #include "firmware/c_board/app/src/uart/uart.hpp" +#include "firmware/c_board/app/src/usb/dcd_recovery.hpp" #include "firmware/c_board/app/src/usb/vendor.hpp" #include "firmware/c_board/app/src/utility/boot_mailbox.hpp" #include "firmware/c_board/app/src/utility/interrupt_lock.hpp" @@ -75,6 +76,10 @@ App::App() { // NOLINTNEXTLINE(readability-convert-member-functions-to-static) [[noreturn]] void App::run() { while (true) { + if (dwc2_recovery_required()) { + while (true) {} + } + tud_task(); gpio::gpio->poll_periodic_input_samples(); diff --git a/firmware/c_board/app/src/usb/dcd_recovery.hpp b/firmware/c_board/app/src/usb/dcd_recovery.hpp new file mode 100644 index 0000000..03b1c30 --- /dev/null +++ b/firmware/c_board/app/src/usb/dcd_recovery.hpp @@ -0,0 +1,4 @@ +#pragma once + +// Defined in bsp/tinyusb/src/portable/synopsys/dwc2/dwc2_common.c. +extern "C" bool dwc2_recovery_required(); diff --git a/firmware/c_board/app/src/watchdog/watchdog.hpp b/firmware/c_board/app/src/watchdog/watchdog.hpp index d56e8c8..05f224a 100644 --- a/firmware/c_board/app/src/watchdog/watchdog.hpp +++ b/firmware/c_board/app/src/watchdog/watchdog.hpp @@ -7,6 +7,10 @@ #include "firmware/c_board/app/src/utility/lazy.hpp" +#ifndef LIBRMCS_WATCHDOG_RELOAD +# define LIBRMCS_WATCHDOG_RELOAD 250U +#endif + namespace librmcs::firmware::watchdog { class Watchdog { @@ -27,7 +31,7 @@ class Watchdog { static void feed() { LL_IWDG_ReloadCounter(IWDG); } private: - static constexpr uint32_t kReloadValue = 250U; + static constexpr uint32_t kReloadValue = LIBRMCS_WATCHDOG_RELOAD; }; inline constinit Watchdog::Lazy watchdog; diff --git a/firmware/c_board/bootloader/CMakeLists.txt b/firmware/c_board/bootloader/CMakeLists.txt index 3862d81..317ee61 100644 --- a/firmware/c_board/bootloader/CMakeLists.txt +++ b/firmware/c_board/bootloader/CMakeLists.txt @@ -42,6 +42,7 @@ target_include_directories(c_board_bootloader PRIVATE ) c_board_apply_target_options(c_board_bootloader "STM32F407XX_BOOTLOADER.ld") +target_compile_definitions(c_board_bootloader PRIVATE LIBRMCS_WATCHDOG_RELOAD=2000) target_link_libraries(c_board_bootloader PRIVATE tinyusb_bootloader ) diff --git a/firmware/c_board/bootloader/src/flash/metadata.hpp b/firmware/c_board/bootloader/src/flash/metadata.hpp index 3185aae..01234b9 100644 --- a/firmware/c_board/bootloader/src/flash/metadata.hpp +++ b/firmware/c_board/bootloader/src/flash/metadata.hpp @@ -4,6 +4,7 @@ #include +#include "firmware/c_board/app/src/watchdog/watchdog.hpp" #include "firmware/c_board/bootloader/src/flash/layout.hpp" #include "firmware/c_board/bootloader/src/flash/unlock_guard.hpp" #include "firmware/c_board/bootloader/src/utility/assert.hpp" @@ -187,6 +188,8 @@ class Metadata { erase.Sector = FLASH_SECTOR_3; erase.NbSectors = 1; + watchdog::Watchdog::feed(); + uint32_t sector_error; { const auto guard = UnlockGuard(); diff --git a/firmware/c_board/bootloader/src/flash/writer.hpp b/firmware/c_board/bootloader/src/flash/writer.hpp index d1567a7..d4fb0cf 100644 --- a/firmware/c_board/bootloader/src/flash/writer.hpp +++ b/firmware/c_board/bootloader/src/flash/writer.hpp @@ -9,6 +9,7 @@ #include +#include "firmware/c_board/app/src/watchdog/watchdog.hpp" #include "firmware/c_board/bootloader/include/tusb_config.h" #include "firmware/c_board/bootloader/src/flash/layout.hpp" #include "firmware/c_board/bootloader/src/flash/unlock_guard.hpp" @@ -144,6 +145,8 @@ class Writer { erase.Sector = kAppSectors[index].sector; erase.NbSectors = 1; + watchdog::Watchdog::feed(); + uint32_t sector_error = 0U; utility::assert_always(HAL_FLASHEx_Erase(&erase, §or_error) == HAL_OK); } diff --git a/firmware/c_board/bootloader/src/main.cpp b/firmware/c_board/bootloader/src/main.cpp index c2beeaf..f87e8fd 100644 --- a/firmware/c_board/bootloader/src/main.cpp +++ b/firmware/c_board/bootloader/src/main.cpp @@ -7,6 +7,8 @@ #include #include +#include "firmware/c_board/app/src/usb/dcd_recovery.hpp" +#include "firmware/c_board/app/src/watchdog/watchdog.hpp" #include "firmware/c_board/bootloader/src/flash/layout.hpp" #include "firmware/c_board/bootloader/src/flash/validation.hpp" #include "firmware/c_board/bootloader/src/usb/dfu.hpp" @@ -54,10 +56,17 @@ int main() { utility::jump_to_app(flash::kAppStartAddress); } + // watchdog::watchdog.init(); + utility::assert_always(tusb_rhport_init(0, nullptr)); while (true) { + if (dwc2_recovery_required()) { + while (true) {} + } + tud_task(); usb::Dfu::instance().poll(); + watchdog::watchdog->feed(); } } diff --git a/firmware/c_board/bootloader/src/utility/assert.cpp b/firmware/c_board/bootloader/src/utility/assert.cpp new file mode 100644 index 0000000..0e6f0de --- /dev/null +++ b/firmware/c_board/bootloader/src/utility/assert.cpp @@ -0,0 +1,23 @@ +#include "core/src/utility/assert.hpp" + +#include + +#include // IWYU pragma: keep (disable irq) + +namespace librmcs::core::utility { + +const char* volatile assert_file = nullptr; +volatile unsigned int assert_line = 0; +const char* volatile assert_function = nullptr; + +[[noreturn]] void assert_func(const std::source_location& location) { + __disable_irq(); + + assert_file = location.file_name(); + assert_line = location.line(); + assert_function = location.function_name(); + + __builtin_trap(); +} + +} // namespace librmcs::core::utility diff --git a/firmware/c_board/bsp/tinyusb b/firmware/c_board/bsp/tinyusb index bb24b0c..1291ead 160000 --- a/firmware/c_board/bsp/tinyusb +++ b/firmware/c_board/bsp/tinyusb @@ -1 +1 @@ -Subproject commit bb24b0cada3606af54a0545ac6c48bf673669381 +Subproject commit 1291ead105d43fb74eef6b055c309bb8d6df9124 diff --git a/firmware/rmcs_board/CMakeLists.txt b/firmware/rmcs_board/CMakeLists.txt index 38f2469..6869a85 100644 --- a/firmware/rmcs_board/CMakeLists.txt +++ b/firmware/rmcs_board/CMakeLists.txt @@ -12,6 +12,7 @@ set(RV_ARCH "rv32imafdcb" CACHE STRING "RISC-V arch") set(RV_ABI "ilp32d" CACHE STRING "RISC-V ABI") set(BOARD_BOOTLOADER_MODE "auto" CACHE STRING "Bootloader mode (auto / manual)") set_property(CACHE BOARD_BOOTLOADER_MODE PROPERTY STRINGS auto manual) +option(LIBRMCS_USB_FAULT_INJECT "Force a DCD bus-reset timeout to exercise the USB recovery path" OFF) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "debug") @@ -40,6 +41,7 @@ function(add_rmcs_board_subbuild target_name source_subdir) -DSOURCE_PATH_MAP=${LIBRMCS_SOURCE_PATH_MAP} -DLIBRMCS_PROJECT_VERSION=${LIBRMCS_PROJECT_VERSION} -DLIBRMCS_DEBUG_ASSERTS=${LIBRMCS_DEBUG_ASSERTS} + -DLIBRMCS_USB_FAULT_INJECT=${LIBRMCS_USB_FAULT_INJECT} ) if(CMAKE_MAKE_PROGRAM) list(APPEND configure_args -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}) diff --git a/firmware/rmcs_board/app/CMakeLists.txt b/firmware/rmcs_board/app/CMakeLists.txt index 593f871..1ad3795 100644 --- a/firmware/rmcs_board/app/CMakeLists.txt +++ b/firmware/rmcs_board/app/CMakeLists.txt @@ -71,9 +71,19 @@ file(GLOB_RECURSE PROJECT_SOURCE CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c" ) +list(REMOVE_ITEM PROJECT_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/src/usb/fault_inject.c") +if(LIBRMCS_USB_FAULT_INJECT) + list(APPEND PROJECT_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/src/usb/fault_inject.c") +endif() + sdk_app_src(${PROJECT_SOURCE}) sdk_app_src("$ENV{HPM_SDK_BASE}/middleware/tinyusb/src/class/dfu/dfu_rt_device.c") +if(LIBRMCS_USB_FAULT_INJECT) + target_compile_definitions(app PRIVATE LIBRMCS_USB_FAULT_INJECT=1) + target_link_options(${APP_ELF_NAME} PRIVATE "-Wl,--wrap=usb_dcd_bus_reset") +endif() + sdk_linker_global_symbols("_uf2_bl_length=0x20000") set_property(TARGET app PROPERTY CXX_STANDARD 23) diff --git a/firmware/rmcs_board/app/src/app.cpp b/firmware/rmcs_board/app/src/app.cpp index ccd5e97..b93823d 100644 --- a/firmware/rmcs_board/app/src/app.cpp +++ b/firmware/rmcs_board/app/src/app.cpp @@ -12,6 +12,10 @@ #include "firmware/rmcs_board/app/src/spi/bmi088/temperature.hpp" #include "firmware/rmcs_board/app/src/timer/timer.hpp" #include "firmware/rmcs_board/app/src/uart/uart.hpp" +#include "firmware/rmcs_board/app/src/usb/dcd_recovery.hpp" +#ifdef LIBRMCS_USB_FAULT_INJECT +# include "firmware/rmcs_board/app/src/usb/fault_inject.hpp" +#endif #include "firmware/rmcs_board/app/src/usb/vendor.hpp" #include "firmware/rmcs_board/app/src/utility/boot_mailbox.hpp" #include "firmware/rmcs_board/app/src/utility/interrupt_lock.hpp" @@ -54,6 +58,14 @@ App::App() { // NOLINTNEXTLINE(readability-convert-member-functions-to-static) [[noreturn]] void App::run() { while (true) { + if (usb_device_recovery_required()) { + while (true) {} + } + +#ifdef LIBRMCS_USB_FAULT_INJECT + usb_fault_inject_poll(); +#endif + tud_task(); usb::vendor->try_transmit(); diff --git a/firmware/rmcs_board/app/src/usb/dcd_recovery.hpp b/firmware/rmcs_board/app/src/usb/dcd_recovery.hpp new file mode 100644 index 0000000..3b8d676 --- /dev/null +++ b/firmware/rmcs_board/app/src/usb/dcd_recovery.hpp @@ -0,0 +1,4 @@ +#pragma once + +// Defined in bsp/hpm_sdk/components/usb/device/hpm_usb_device.c. +extern "C" bool usb_device_recovery_required(); diff --git a/firmware/rmcs_board/app/src/usb/fault_inject.c b/firmware/rmcs_board/app/src/usb/fault_inject.c new file mode 100644 index 0000000..f6ce415 --- /dev/null +++ b/firmware/rmcs_board/app/src/usb/fault_inject.c @@ -0,0 +1,65 @@ +#include +#include + +#ifdef LIBRMCS_USB_FAULT_INJECT + +# include +# include +# include +# include +# include + +# define KEY_SAMPLE_TICKS (40000U) /* 10 ms at 4 MHz */ +# define RECONNECT_TICKS (400000U) /* 100 ms at 4 MHz */ + +extern hpm_stat_t __real_usb_dcd_bus_reset(USB_Type* ptr, uint16_t ep0_max_packet_size); + +static bool key_initialized; +static bool key_was_pressed; +static bool armed; +static bool reconnect_pending; +static uint32_t last_sample_ticks; +static uint32_t disconnect_ticks; + +static uint32_t mchtmr_ticks(void) { + const volatile uint32_t* const mtime = (const volatile uint32_t*)HPM_MCHTMR_BASE; + return mtime[0]; +} + +void usb_fault_inject_poll(void) { + const uint32_t now = mchtmr_ticks(); + + if (!key_initialized) { + board_init_user_key(); + key_initialized = true; + last_sample_ticks = now; + } + + if (reconnect_pending && (uint32_t)(now - disconnect_ticks) >= RECONNECT_TICKS) { + reconnect_pending = false; + (void)tud_connect(); + } + + if ((uint32_t)(now - last_sample_ticks) < KEY_SAMPLE_TICKS) + return; + last_sample_ticks = now; + + const bool pressed = board_user_key_pressed(); + if (pressed && !key_was_pressed) { + armed = true; + reconnect_pending = tud_disconnect(); + disconnect_ticks = now; + } + key_was_pressed = pressed; +} + +hpm_stat_t __wrap_usb_dcd_bus_reset(USB_Type* ptr, uint16_t ep0_max_packet_size) { + if (armed) { + armed = false; + return status_timeout; + } + + return __real_usb_dcd_bus_reset(ptr, ep0_max_packet_size); +} + +#endif diff --git a/firmware/rmcs_board/app/src/usb/fault_inject.hpp b/firmware/rmcs_board/app/src/usb/fault_inject.hpp new file mode 100644 index 0000000..6979165 --- /dev/null +++ b/firmware/rmcs_board/app/src/usb/fault_inject.hpp @@ -0,0 +1,5 @@ +#pragma once + +#ifdef LIBRMCS_USB_FAULT_INJECT +extern "C" void usb_fault_inject_poll(void); +#endif diff --git a/firmware/rmcs_board/app/src/watchdog/watchdog.hpp b/firmware/rmcs_board/app/src/watchdog/watchdog.hpp index 08ebaae..c504709 100644 --- a/firmware/rmcs_board/app/src/watchdog/watchdog.hpp +++ b/firmware/rmcs_board/app/src/watchdog/watchdog.hpp @@ -3,12 +3,17 @@ #include #include +#include #include #include #include "core/src/utility/assert.hpp" #include "firmware/rmcs_board/app/src/utility/lazy.hpp" +#ifndef LIBRMCS_WATCHDOG_TIMEOUT_US +# define LIBRMCS_WATCHDOG_TIMEOUT_US (500'000U) +#endif + namespace librmcs::firmware::watchdog { class Watchdog { @@ -35,7 +40,7 @@ class Watchdog { static void feed() { core::utility::assert_always(ewdg_refresh(HPM_EWDG0) == status_success); } private: - static constexpr uint32_t kTimeoutUs = 500'000U; + static constexpr uint32_t kTimeoutUs = LIBRMCS_WATCHDOG_TIMEOUT_US; static constexpr uint32_t kClockFrequencyHz = 32'768U; }; diff --git a/firmware/rmcs_board/boards/lite/board.c b/firmware/rmcs_board/boards/lite/board.c index a16f2cb..e9b543e 100644 --- a/firmware/rmcs_board/boards/lite/board.c +++ b/firmware/rmcs_board/boards/lite/board.c @@ -34,7 +34,7 @@ static inline void init_py_pins_as_soc_gpio(void); static inline void board_init_clock(void); static inline void board_init_usb_dp_dm_pins(void); -static void init_bootloader_force_stay_button_pin(void) { +void board_init_user_key(void) { const uint32_t pad_ctl = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1) | IOC_PAD_PAD_CTL_HYS_SET(1); @@ -47,17 +47,19 @@ static void init_bootloader_force_stay_button_pin(void) { gpio_set_pin_input(HPM_GPIO0, GPIO_OE_GPIOA, 7); } +bool board_user_key_pressed(void) { return gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOA, 7) == 0U; } + static void restore_bootloader_force_stay_button_pin(void) { HPM_IOC->PAD[IOC_PAD_PA07].FUNC_CTL = IOC_PA07_FUNC_CTL_JTAG_TMS; } bool board_check_bootloader_force_stay_requested(void) { - init_bootloader_force_stay_button_pin(); + board_init_user_key(); bool pressed = true; for (uint32_t sample_index = 0; sample_index < 4; ++sample_index) { board_delay_us(250); - if (gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOA, 7) != 0U) { + if (!board_user_key_pressed()) { pressed = false; break; } diff --git a/firmware/rmcs_board/boards/lite/board.h b/firmware/rmcs_board/boards/lite/board.h index c235ae9..93b90e8 100644 --- a/firmware/rmcs_board/boards/lite/board.h +++ b/firmware/rmcs_board/boards/lite/board.h @@ -26,6 +26,9 @@ void board_init_usb(void); /* Bootloader-specific helper for force-stay button handling. */ bool board_check_bootloader_force_stay_requested(void); +void board_init_user_key(void); +bool board_user_key_pressed(void); + void board_delay_us(uint32_t us); void board_delay_ms(uint32_t ms); diff --git a/firmware/rmcs_board/boards/pro/board.c b/firmware/rmcs_board/boards/pro/board.c index 1b63170..a0cbf0a 100644 --- a/firmware/rmcs_board/boards/pro/board.c +++ b/firmware/rmcs_board/boards/pro/board.c @@ -34,7 +34,7 @@ static inline void init_py_pins_as_soc_gpio(void); static inline void board_init_clock(void); static inline void board_init_usb_dp_dm_pins(void); -static void init_bootloader_force_stay_button_pin(void) { +void board_init_user_key(void) { const uint32_t pad_ctl = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1) | IOC_PAD_PAD_CTL_HYS_SET(1); @@ -48,12 +48,14 @@ static void init_bootloader_force_stay_button_pin(void) { gpio_set_pin_input(HPM_GPIO0, GPIO_OE_GPIOY, 3); } +bool board_user_key_pressed(void) { return gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOY, 3) == 0U; } + bool board_check_bootloader_force_stay_requested(void) { - init_bootloader_force_stay_button_pin(); + board_init_user_key(); for (uint32_t sample_index = 0; sample_index < 4; ++sample_index) { board_delay_us(250); - if (gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOY, 3) != 0U) + if (!board_user_key_pressed()) return false; } diff --git a/firmware/rmcs_board/boards/pro/board.h b/firmware/rmcs_board/boards/pro/board.h index dc9c1bd..5a5ac67 100644 --- a/firmware/rmcs_board/boards/pro/board.h +++ b/firmware/rmcs_board/boards/pro/board.h @@ -38,6 +38,9 @@ void board_init_usb(void); /* Bootloader-specific helper for force-stay button handling. */ bool board_check_bootloader_force_stay_requested(void); +void board_init_user_key(void); +bool board_user_key_pressed(void); + void board_delay_us(uint32_t us); void board_delay_ms(uint32_t ms); diff --git a/firmware/rmcs_board/bootloader/CMakeLists.txt b/firmware/rmcs_board/bootloader/CMakeLists.txt index 74d25e1..d3ccf6f 100644 --- a/firmware/rmcs_board/bootloader/CMakeLists.txt +++ b/firmware/rmcs_board/bootloader/CMakeLists.txt @@ -63,6 +63,7 @@ endif() sdk_compile_definitions(-DLIBRMCS_PROJECT_VERSION_STRING="${LIBRMCS_PROJECT_VERSION}") sdk_compile_definitions(-DLIBRMCS_DEBUG_ASSERTS=${LIBRMCS_DEBUG_ASSERTS}) +sdk_compile_definitions(-DLIBRMCS_WATCHDOG_TIMEOUT_US=1000000) sdk_compile_definitions(-DCFG_TUSB_MCU=OPT_MCU_HPM) sdk_compile_definitions(-DUSB_HOST_MCU_CORE=HPM_CORE0) sdk_compile_definitions(-DLIBRMCS_BOOTLOADER_MODE_AUTO=${bootloader_mode_auto}) diff --git a/firmware/rmcs_board/bootloader/src/flash/xpi_nor.hpp b/firmware/rmcs_board/bootloader/src/flash/xpi_nor.hpp index 32c537d..12a05b6 100644 --- a/firmware/rmcs_board/bootloader/src/flash/xpi_nor.hpp +++ b/firmware/rmcs_board/bootloader/src/flash/xpi_nor.hpp @@ -14,6 +14,7 @@ #include #include +#include "firmware/rmcs_board/app/src/watchdog/watchdog.hpp" #include "firmware/rmcs_board/bootloader/src/flash/layout.hpp" #include "firmware/rmcs_board/bootloader/src/utility/assert.hpp" @@ -31,6 +32,8 @@ class XpiNor { void erase_sector(uintptr_t address) { utility::assert_debug((address % sector_size_) == 0U); + watchdog::Watchdog::feed(); + const uint32_t irq_flags = disable_global_irq(CSR_MSTATUS_MIE_MASK); const hpm_stat_t status = rom_xpi_nor_erase_sector( BOARD_APP_XPI_NOR_XPI_BASE, xpi_xfer_channel_auto, &nor_config_, diff --git a/firmware/rmcs_board/bootloader/src/main.cpp b/firmware/rmcs_board/bootloader/src/main.cpp index 5f7515b..27b3d33 100644 --- a/firmware/rmcs_board/bootloader/src/main.cpp +++ b/firmware/rmcs_board/bootloader/src/main.cpp @@ -3,6 +3,8 @@ #include #include +#include "firmware/rmcs_board/app/src/usb/dcd_recovery.hpp" +#include "firmware/rmcs_board/app/src/watchdog/watchdog.hpp" #include "firmware/rmcs_board/bootloader/src/flash/validation.hpp" #include "firmware/rmcs_board/bootloader/src/usb/dfu.hpp" #include "firmware/rmcs_board/bootloader/src/usb/usb_descriptors.hpp" @@ -27,6 +29,9 @@ int main() { // Reset-time clocks already run CPU0 at 360 MHz, so board_init() would only bump it to // 480 MHz while adding avoidable startup latency on the direct-to-app path. board_init(); + + watchdog::watchdog.init(); + board_init_usb(); (void)usb::get_usb_descriptors(); @@ -37,7 +42,12 @@ int main() { utility::assert_always(tusb_rhport_init(0, &init_config)); while (true) { + if (usb_device_recovery_required()) { + while (true) {} + } + tud_task(); usb::Dfu::instance().poll(); + watchdog::watchdog->feed(); } } diff --git a/firmware/rmcs_board/bootloader/src/utility/assert.cpp b/firmware/rmcs_board/bootloader/src/utility/assert.cpp new file mode 100644 index 0000000..eb39007 --- /dev/null +++ b/firmware/rmcs_board/bootloader/src/utility/assert.cpp @@ -0,0 +1,24 @@ +#include "core/src/utility/assert.hpp" + +#include + +#include +#include + +namespace librmcs::core::utility { + +const char* volatile assert_file = nullptr; +volatile unsigned int assert_line = 0; +const char* volatile assert_function = nullptr; + +[[noreturn]] void assert_func(const std::source_location& location) { + disable_global_irq(CSR_MSTATUS_MIE_MASK); + + assert_file = location.file_name(); + assert_line = location.line(); + assert_function = location.function_name(); + + __builtin_trap(); +} + +} // namespace librmcs::core::utility diff --git a/firmware/rmcs_board/bsp/hpm_sdk b/firmware/rmcs_board/bsp/hpm_sdk index a82a535..97168c2 160000 --- a/firmware/rmcs_board/bsp/hpm_sdk +++ b/firmware/rmcs_board/bsp/hpm_sdk @@ -1 +1 @@ -Subproject commit a82a53544d5679a43b70f390c8bbbb8e91f6b8c1 +Subproject commit 97168c2d52ce9997fa9af7a5cd37f7f8cd90befb From c09777df6a73bcdb636daff9cb81b4a94ce57c11 Mon Sep 17 00:00:00 2001 From: ZGZ713912 Date: Fri, 23 Oct 2026 13:07:06 +0000 Subject: [PATCH 2/3] fix: update tinyusb submodule URL to correct repository --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 711750a..cd26963 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/Alliance-Algorithm/hpm_sdk.git [submodule "firmware/c_board/bsp/tinyusb"] path = firmware/c_board/bsp/tinyusb - url = https://github.com/qzhhhi/tinyusb.git + url = https://github.com/Alliance-Algorithm/tinyusb.git [submodule "firmware/c_board/bsp/cmsis-device-f4"] path = firmware/c_board/bsp/cmsis-device-f4 url = https://github.com/STMicroelectronics/cmsis-device-f4.git From 013c8a286da2a799bdd43e5fea2fb1391a951396 Mon Sep 17 00:00:00 2001 From: ZGZ713912 Date: Tue, 27 Oct 2026 08:43:33 +0000 Subject: [PATCH 3/3] refactor(board): remove USB fault injection code --- firmware/c_board/bootloader/src/main.cpp | 2 +- firmware/rmcs_board/CMakeLists.txt | 2 - firmware/rmcs_board/app/CMakeLists.txt | 10 --- firmware/rmcs_board/app/src/app.cpp | 7 -- .../rmcs_board/app/src/usb/fault_inject.c | 65 ------------------- .../rmcs_board/app/src/usb/fault_inject.hpp | 5 -- firmware/rmcs_board/boards/lite/board.c | 8 +-- firmware/rmcs_board/boards/lite/board.h | 3 - firmware/rmcs_board/boards/pro/board.c | 8 +-- firmware/rmcs_board/boards/pro/board.h | 3 - 10 files changed, 7 insertions(+), 106 deletions(-) delete mode 100644 firmware/rmcs_board/app/src/usb/fault_inject.c delete mode 100644 firmware/rmcs_board/app/src/usb/fault_inject.hpp diff --git a/firmware/c_board/bootloader/src/main.cpp b/firmware/c_board/bootloader/src/main.cpp index f87e8fd..0401282 100644 --- a/firmware/c_board/bootloader/src/main.cpp +++ b/firmware/c_board/bootloader/src/main.cpp @@ -56,7 +56,7 @@ int main() { utility::jump_to_app(flash::kAppStartAddress); } - // watchdog::watchdog.init(); + watchdog::watchdog.init(); utility::assert_always(tusb_rhport_init(0, nullptr)); diff --git a/firmware/rmcs_board/CMakeLists.txt b/firmware/rmcs_board/CMakeLists.txt index 6869a85..38f2469 100644 --- a/firmware/rmcs_board/CMakeLists.txt +++ b/firmware/rmcs_board/CMakeLists.txt @@ -12,7 +12,6 @@ set(RV_ARCH "rv32imafdcb" CACHE STRING "RISC-V arch") set(RV_ABI "ilp32d" CACHE STRING "RISC-V ABI") set(BOARD_BOOTLOADER_MODE "auto" CACHE STRING "Bootloader mode (auto / manual)") set_property(CACHE BOARD_BOOTLOADER_MODE PROPERTY STRINGS auto manual) -option(LIBRMCS_USB_FAULT_INJECT "Force a DCD bus-reset timeout to exercise the USB recovery path" OFF) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "debug") @@ -41,7 +40,6 @@ function(add_rmcs_board_subbuild target_name source_subdir) -DSOURCE_PATH_MAP=${LIBRMCS_SOURCE_PATH_MAP} -DLIBRMCS_PROJECT_VERSION=${LIBRMCS_PROJECT_VERSION} -DLIBRMCS_DEBUG_ASSERTS=${LIBRMCS_DEBUG_ASSERTS} - -DLIBRMCS_USB_FAULT_INJECT=${LIBRMCS_USB_FAULT_INJECT} ) if(CMAKE_MAKE_PROGRAM) list(APPEND configure_args -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}) diff --git a/firmware/rmcs_board/app/CMakeLists.txt b/firmware/rmcs_board/app/CMakeLists.txt index 1ad3795..593f871 100644 --- a/firmware/rmcs_board/app/CMakeLists.txt +++ b/firmware/rmcs_board/app/CMakeLists.txt @@ -71,19 +71,9 @@ file(GLOB_RECURSE PROJECT_SOURCE CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c" ) -list(REMOVE_ITEM PROJECT_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/src/usb/fault_inject.c") -if(LIBRMCS_USB_FAULT_INJECT) - list(APPEND PROJECT_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/src/usb/fault_inject.c") -endif() - sdk_app_src(${PROJECT_SOURCE}) sdk_app_src("$ENV{HPM_SDK_BASE}/middleware/tinyusb/src/class/dfu/dfu_rt_device.c") -if(LIBRMCS_USB_FAULT_INJECT) - target_compile_definitions(app PRIVATE LIBRMCS_USB_FAULT_INJECT=1) - target_link_options(${APP_ELF_NAME} PRIVATE "-Wl,--wrap=usb_dcd_bus_reset") -endif() - sdk_linker_global_symbols("_uf2_bl_length=0x20000") set_property(TARGET app PROPERTY CXX_STANDARD 23) diff --git a/firmware/rmcs_board/app/src/app.cpp b/firmware/rmcs_board/app/src/app.cpp index b93823d..19aa6cf 100644 --- a/firmware/rmcs_board/app/src/app.cpp +++ b/firmware/rmcs_board/app/src/app.cpp @@ -13,9 +13,6 @@ #include "firmware/rmcs_board/app/src/timer/timer.hpp" #include "firmware/rmcs_board/app/src/uart/uart.hpp" #include "firmware/rmcs_board/app/src/usb/dcd_recovery.hpp" -#ifdef LIBRMCS_USB_FAULT_INJECT -# include "firmware/rmcs_board/app/src/usb/fault_inject.hpp" -#endif #include "firmware/rmcs_board/app/src/usb/vendor.hpp" #include "firmware/rmcs_board/app/src/utility/boot_mailbox.hpp" #include "firmware/rmcs_board/app/src/utility/interrupt_lock.hpp" @@ -62,10 +59,6 @@ App::App() { while (true) {} } -#ifdef LIBRMCS_USB_FAULT_INJECT - usb_fault_inject_poll(); -#endif - tud_task(); usb::vendor->try_transmit(); diff --git a/firmware/rmcs_board/app/src/usb/fault_inject.c b/firmware/rmcs_board/app/src/usb/fault_inject.c deleted file mode 100644 index f6ce415..0000000 --- a/firmware/rmcs_board/app/src/usb/fault_inject.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -#ifdef LIBRMCS_USB_FAULT_INJECT - -# include -# include -# include -# include -# include - -# define KEY_SAMPLE_TICKS (40000U) /* 10 ms at 4 MHz */ -# define RECONNECT_TICKS (400000U) /* 100 ms at 4 MHz */ - -extern hpm_stat_t __real_usb_dcd_bus_reset(USB_Type* ptr, uint16_t ep0_max_packet_size); - -static bool key_initialized; -static bool key_was_pressed; -static bool armed; -static bool reconnect_pending; -static uint32_t last_sample_ticks; -static uint32_t disconnect_ticks; - -static uint32_t mchtmr_ticks(void) { - const volatile uint32_t* const mtime = (const volatile uint32_t*)HPM_MCHTMR_BASE; - return mtime[0]; -} - -void usb_fault_inject_poll(void) { - const uint32_t now = mchtmr_ticks(); - - if (!key_initialized) { - board_init_user_key(); - key_initialized = true; - last_sample_ticks = now; - } - - if (reconnect_pending && (uint32_t)(now - disconnect_ticks) >= RECONNECT_TICKS) { - reconnect_pending = false; - (void)tud_connect(); - } - - if ((uint32_t)(now - last_sample_ticks) < KEY_SAMPLE_TICKS) - return; - last_sample_ticks = now; - - const bool pressed = board_user_key_pressed(); - if (pressed && !key_was_pressed) { - armed = true; - reconnect_pending = tud_disconnect(); - disconnect_ticks = now; - } - key_was_pressed = pressed; -} - -hpm_stat_t __wrap_usb_dcd_bus_reset(USB_Type* ptr, uint16_t ep0_max_packet_size) { - if (armed) { - armed = false; - return status_timeout; - } - - return __real_usb_dcd_bus_reset(ptr, ep0_max_packet_size); -} - -#endif diff --git a/firmware/rmcs_board/app/src/usb/fault_inject.hpp b/firmware/rmcs_board/app/src/usb/fault_inject.hpp deleted file mode 100644 index 6979165..0000000 --- a/firmware/rmcs_board/app/src/usb/fault_inject.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#ifdef LIBRMCS_USB_FAULT_INJECT -extern "C" void usb_fault_inject_poll(void); -#endif diff --git a/firmware/rmcs_board/boards/lite/board.c b/firmware/rmcs_board/boards/lite/board.c index e9b543e..a16f2cb 100644 --- a/firmware/rmcs_board/boards/lite/board.c +++ b/firmware/rmcs_board/boards/lite/board.c @@ -34,7 +34,7 @@ static inline void init_py_pins_as_soc_gpio(void); static inline void board_init_clock(void); static inline void board_init_usb_dp_dm_pins(void); -void board_init_user_key(void) { +static void init_bootloader_force_stay_button_pin(void) { const uint32_t pad_ctl = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1) | IOC_PAD_PAD_CTL_HYS_SET(1); @@ -47,19 +47,17 @@ void board_init_user_key(void) { gpio_set_pin_input(HPM_GPIO0, GPIO_OE_GPIOA, 7); } -bool board_user_key_pressed(void) { return gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOA, 7) == 0U; } - static void restore_bootloader_force_stay_button_pin(void) { HPM_IOC->PAD[IOC_PAD_PA07].FUNC_CTL = IOC_PA07_FUNC_CTL_JTAG_TMS; } bool board_check_bootloader_force_stay_requested(void) { - board_init_user_key(); + init_bootloader_force_stay_button_pin(); bool pressed = true; for (uint32_t sample_index = 0; sample_index < 4; ++sample_index) { board_delay_us(250); - if (!board_user_key_pressed()) { + if (gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOA, 7) != 0U) { pressed = false; break; } diff --git a/firmware/rmcs_board/boards/lite/board.h b/firmware/rmcs_board/boards/lite/board.h index 93b90e8..c235ae9 100644 --- a/firmware/rmcs_board/boards/lite/board.h +++ b/firmware/rmcs_board/boards/lite/board.h @@ -26,9 +26,6 @@ void board_init_usb(void); /* Bootloader-specific helper for force-stay button handling. */ bool board_check_bootloader_force_stay_requested(void); -void board_init_user_key(void); -bool board_user_key_pressed(void); - void board_delay_us(uint32_t us); void board_delay_ms(uint32_t ms); diff --git a/firmware/rmcs_board/boards/pro/board.c b/firmware/rmcs_board/boards/pro/board.c index a0cbf0a..1b63170 100644 --- a/firmware/rmcs_board/boards/pro/board.c +++ b/firmware/rmcs_board/boards/pro/board.c @@ -34,7 +34,7 @@ static inline void init_py_pins_as_soc_gpio(void); static inline void board_init_clock(void); static inline void board_init_usb_dp_dm_pins(void); -void board_init_user_key(void) { +static void init_bootloader_force_stay_button_pin(void) { const uint32_t pad_ctl = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1) | IOC_PAD_PAD_CTL_HYS_SET(1); @@ -48,14 +48,12 @@ void board_init_user_key(void) { gpio_set_pin_input(HPM_GPIO0, GPIO_OE_GPIOY, 3); } -bool board_user_key_pressed(void) { return gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOY, 3) == 0U; } - bool board_check_bootloader_force_stay_requested(void) { - board_init_user_key(); + init_bootloader_force_stay_button_pin(); for (uint32_t sample_index = 0; sample_index < 4; ++sample_index) { board_delay_us(250); - if (!board_user_key_pressed()) + if (gpio_read_pin(HPM_GPIO0, GPIO_DI_GPIOY, 3) != 0U) return false; } diff --git a/firmware/rmcs_board/boards/pro/board.h b/firmware/rmcs_board/boards/pro/board.h index 5a5ac67..dc9c1bd 100644 --- a/firmware/rmcs_board/boards/pro/board.h +++ b/firmware/rmcs_board/boards/pro/board.h @@ -38,9 +38,6 @@ void board_init_usb(void); /* Bootloader-specific helper for force-stay button handling. */ bool board_check_bootloader_force_stay_requested(void); -void board_init_user_key(void); -bool board_user_key_pressed(void); - void board_delay_us(uint32_t us); void board_delay_ms(uint32_t ms);