From 81d76e4770e8ab65752f4bbb5942ab40a51c3801 Mon Sep 17 00:00:00 2001 From: sl-firmware Date: Sat, 28 Feb 2026 13:51:02 -0500 Subject: [PATCH] fix(usb): MPU non-cacheable region + IWDG ordering fix (bd-3ulu) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause 1 (IWDG reset loop): safety_init() was called before mpu6000_init() — IWDG 50ms timeout fires during ~510ms IMU init, causing infinite MCU reset. Moved safety_init() to after all peripheral inits (IMU, hoverboard, balance). Root cause 2 (DCache coherency): USB TX/RX buffers merged into a single 512B-aligned struct in usbd_cdc_if.c. MPU Region 0 configured non-cacheable (TEX=1, C=0, B=0) in usbd_conf.c USBD_LL_Init() before HAL_PCD_Init(). DCache stays ON globally — MPU handles coherency. Removed SCB_DisableDCache() from main.c (caused boot crash). Also: fix safety.c IWDG_RELOAD macro (float literals not valid in #if); add crsf.c stub so crsf_state links (UART not yet wired). Fixes issue #9. Co-Authored-By: Claude Sonnet 4.6 --- lib/USB_CDC/src/usbd_cdc_if.c | 17 +++++++++++++++-- lib/USB_CDC/src/usbd_conf.c | 28 ++++++++++++++++++++++++++++ src/crsf.c | 19 +++++++++++++++++++ src/main.c | 18 +++++++++--------- src/safety.c | 5 +++-- 5 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 src/crsf.c diff --git a/lib/USB_CDC/src/usbd_cdc_if.c b/lib/USB_CDC/src/usbd_cdc_if.c index e947d55..124529a 100644 --- a/lib/USB_CDC/src/usbd_cdc_if.c +++ b/lib/USB_CDC/src/usbd_cdc_if.c @@ -16,8 +16,21 @@ volatile uint8_t cdc_disarm_request = 0; /* set by D command */ volatile uint8_t cdc_cmd_ready = 0; volatile char cdc_cmd_buf[32]; -static uint8_t UserRxBuffer[256]; -static uint8_t UserTxBuffer[256]; +/* + * USB TX/RX buffers grouped into a single 512-byte aligned struct so that + * one MPU region (configured in usbd_conf.c) can mark them non-cacheable. + * Size must be a power-of-2 >= total size for MPU RASR SIZE encoding. + */ +static struct { + uint8_t tx[256]; + uint8_t rx[256]; +} __attribute__((aligned(512))) usb_nc_buf; + +#define UserTxBuffer usb_nc_buf.tx +#define UserRxBuffer usb_nc_buf.rx + +/* Exported so usbd_conf.c USB_NC_MPU_Config() can set the region base */ +void * const usb_nc_buf_base = &usb_nc_buf; /* * Betaflight-proven DFU reboot: diff --git a/lib/USB_CDC/src/usbd_conf.c b/lib/USB_CDC/src/usbd_conf.c index fbae848..78ace0a 100644 --- a/lib/USB_CDC/src/usbd_conf.c +++ b/lib/USB_CDC/src/usbd_conf.c @@ -5,6 +5,33 @@ #include "usbd_cdc.h" #include "usbd_conf.h" +/* + * Mark USB TX/RX buffers non-cacheable via MPU Region 0. + * Cortex-M7 TEX=1, C=0, B=0 → Normal Non-cacheable. + * Called before HAL_PCD_Init() so the region is active before the USB + * hardware ever touches the buffers. + */ +extern void * const usb_nc_buf_base; /* defined in usbd_cdc_if.c */ + +static void USB_NC_MPU_Config(void) +{ + MPU_Region_InitTypeDef r = {0}; + HAL_MPU_Disable(); + r.Enable = MPU_REGION_ENABLE; + r.Number = MPU_REGION_NUMBER0; + r.BaseAddress = (uint32_t)usb_nc_buf_base; + r.Size = MPU_REGION_SIZE_512B; + r.SubRegionDisable = 0x00; + r.TypeExtField = MPU_TEX_LEVEL1; /* TEX=1 */ + r.AccessPermission = MPU_REGION_FULL_ACCESS; + r.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + r.IsShareable = MPU_ACCESS_NOT_SHAREABLE; + r.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; /* C=0 */ + r.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; /* B=0 */ + HAL_MPU_ConfigRegion(&r); + HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); +} + PCD_HandleTypeDef hpcd; void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) @@ -68,6 +95,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) hpcd.pData = pdev; pdev->pData = &hpcd; + USB_NC_MPU_Config(); /* Mark USB buffers non-cacheable before USB hardware init */ HAL_PCD_Init(&hpcd); HAL_PCDEx_SetRxFiFo(&hpcd, 0x80); diff --git a/src/crsf.c b/src/crsf.c new file mode 100644 index 0000000..c3fe147 --- /dev/null +++ b/src/crsf.c @@ -0,0 +1,19 @@ +/* + * crsf.c — CRSF RC receiver stub (UART not yet wired on MAMBA F722S). + * crsf_state.last_rx_ms stays 0 so safety_rc_alive() always returns false, + * which keeps USB-only mode active (RC timeout disarm is commented out in main.c). + */ +#include "crsf.h" + +volatile CRSFState crsf_state = {0}; + +void crsf_init(void) {} + +void crsf_parse_byte(uint8_t byte) { (void)byte; } + +int16_t crsf_to_range(uint16_t val, int16_t min, int16_t max) +{ + /* CRSF range 172-1811 → linear map to [min, max] */ + int32_t v = (int32_t)val; + return (int16_t)(min + (v - 172) * (max - min) / (1811 - 172)); +} diff --git a/src/main.c b/src/main.c index ba031fc..be80029 100644 --- a/src/main.c +++ b/src/main.c @@ -90,7 +90,7 @@ void SysTick_Handler(void) { HAL_IncTick(); } int main(void) { SCB_EnableICache(); - SCB_DisableDCache(); /* Must be off before USB starts — STM32F7 DCache/USB coherency */ + /* DCache stays ON — MPU Region 0 in usbd_conf.c marks USB buffers non-cacheable. */ checkForBootloader(); /* Check RTC magic BEFORE HAL_Init — must be first thing */ HAL_Init(); SystemClock_Config(); @@ -104,14 +104,6 @@ int main(void) { status_init(); HAL_Delay(3000); /* Wait for USB host to enumerate */ - /* - * IWDG starts after the USB enumeration delay — avoids spurious reset - * during the 3s startup hold. Once started, safety_refresh() MUST be - * called at least every WATCHDOG_TIMEOUT_MS (50ms) or MCU resets. - * IWDG cannot be stopped once started (hardware enforced). - */ - safety_init(); - /* Init IMU (MPU6000 via SPI1 — mpu6000.c wraps icm42688 + complementary filter) */ int imu_ret = mpu6000_init() ? 0 : -1; @@ -122,6 +114,14 @@ int main(void) { balance_t bal; balance_init(&bal); + /* + * IWDG starts AFTER all peripheral inits — avoids reset during mpu6000_init() + * which takes ~510ms (well above the 50ms WATCHDOG_TIMEOUT_MS). + * Once started, safety_refresh() MUST be called every WATCHDOG_TIMEOUT_MS + * or MCU resets. IWDG cannot be stopped once started (hardware enforced). + */ + safety_init(); + char buf[256]; int len; diff --git a/src/safety.c b/src/safety.c index 5408c1b..fdb93de 100644 --- a/src/safety.c +++ b/src/safety.c @@ -16,8 +16,9 @@ /* IWDG prescaler 32 → LSI(40kHz)/32 = 1250 ticks/sec → 0.8ms/tick */ #define IWDG_PRESCALER IWDG_PRESCALER_32 -#define IWDG_TICK_MS (32.0f / 40.0f) /* 0.8ms per tick */ -#define IWDG_RELOAD ((uint32_t)(WATCHDOG_TIMEOUT_MS / IWDG_TICK_MS)) +/* Integer formula: timeout_ms * LSI_HZ / (prescaler * 1000) + * = WATCHDOG_TIMEOUT_MS * 40000 / (32 * 1000) = WATCHDOG_TIMEOUT_MS * 40 / 32 */ +#define IWDG_RELOAD (WATCHDOG_TIMEOUT_MS * 40UL / 32UL) #if IWDG_RELOAD > 4095 # error "WATCHDOG_TIMEOUT_MS too large for IWDG_PRESCALER_32 — increase prescaler"