fix(usb): MPU non-cacheable region + IWDG ordering fix (bd-3ulu)

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 <noreply@anthropic.com>
This commit is contained in:
sl-firmware 2026-02-28 13:51:02 -05:00
parent 909763494f
commit 81d76e4770
5 changed files with 74 additions and 13 deletions

View File

@ -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:

View File

@ -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);

19
src/crsf.c Normal file
View File

@ -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));
}

View File

@ -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;

View File

@ -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"