Context
The config zone commands (READ_CONFIG 0x31, WRITE_CONFIG 0x30, CLEAR_CONFIG 0x32) using the STM32F103 internal flash work correctly after a clean build, but the initial investigation revealed a race condition in the I2C interrupt handler.
Root cause identified
Two combined factors:
-
Flash timing incompatible with 30ms hook — W25Q64JV sector erase takes 400ms typical (up to 2s max per datasheet p.76). During this time, the flash rejects all SPI commands except Read Status.
-
I2C IRQ race condition — The I2C callback (HAL_I2C_SlaveRxCpltCallback) runs in interrupt context with priority 0 (highest) and directly modifies shared state (current_task, task_rx, task_rx_len, tx_i2c_data) without critical sections or memory barriers.
Corrections needed
Firmware side
- Add
__disable_irq() / __enable_irq() around current_task modifications in on_I2C_receive_command() (IRQ context) and process_task() (thread context)
- Make config operations non-blocking (state machine like WRITE_DATA):
TASK_CLEAR_CONFIG → send erase → TASK_WAIT_FLASH_BUSY → poll w25q64_is_busy()
- Use
TASK_WAIT_FLASH_BUSY after WRITE_CONFIG and CLEAR_CONFIG
MicroPython side
- Increase polling delay to 50ms instead of 5ms to reduce I2C pressure
- Add sleep after write/erase operations before starting status polling
Current state
The internal flash config zone (PR #4, merged) works correctly. This issue tracks the stabilization of the external W25Q64 flash operations for potential future use.
Related
Context
The config zone commands (READ_CONFIG 0x31, WRITE_CONFIG 0x30, CLEAR_CONFIG 0x32) using the STM32F103 internal flash work correctly after a clean build, but the initial investigation revealed a race condition in the I2C interrupt handler.
Root cause identified
Two combined factors:
Flash timing incompatible with 30ms hook — W25Q64JV sector erase takes 400ms typical (up to 2s max per datasheet p.76). During this time, the flash rejects all SPI commands except Read Status.
I2C IRQ race condition — The I2C callback (
HAL_I2C_SlaveRxCpltCallback) runs in interrupt context with priority 0 (highest) and directly modifies shared state (current_task,task_rx,task_rx_len,tx_i2c_data) without critical sections or memory barriers.Corrections needed
Firmware side
__disable_irq()/__enable_irq()aroundcurrent_taskmodifications inon_I2C_receive_command()(IRQ context) andprocess_task()(thread context)TASK_CLEAR_CONFIG→ send erase →TASK_WAIT_FLASH_BUSY→ pollw25q64_is_busy()TASK_WAIT_FLASH_BUSYafter WRITE_CONFIG and CLEAR_CONFIGMicroPython side
Current state
The internal flash config zone (PR #4, merged) works correctly. This issue tracks the stabilization of the external W25Q64 flash operations for potential future use.
Related