Repository navigation
STM32F10xx Serial bus fault on large data #1399
Description
Activity
Hi @chrysophylax
It's failed due to the CDC queue which are full and so set the application buffer to NULL for next dequeue but USB continue to receive without checking the buffer validity while it should wait space to release PMA buffer.
The update remove this part of code as all HAL/LL PCD/USB have been reworked:
07c4ee4I will try to correct this issue but not so easy. One workaround could be to increase the buffer size.
#define CDC_RECEIVE_QUEUE_BUFFER_SIZE ((uint16_t)(CDC_QUEUE_MAX_PACKET_SIZE * 3)) Definitely hoping for a fix here 😔
Is there a workaround for this?
One workaround could be to increase the buffer size
But size depends on your needs, depends on your application, so it is up to you to implement it:
Arduino_Core_STM32/cores/arduino/stm32/usb/cdc/cdc_queue.h#define CDC_RECEIVE_QUEUE_BUFFER_SIZE ((uint16_t)(CDC_QUEUE_MAX_PACKET_SIZE * 3)) What's the recommended maximum value, and do I just replace the value in the library for the board? I can tune it down from there if I can at least try it out without it panicking.
What's the recommended maximum value, and do I just replace the value in the library for the board?
There is not recommended value, as it depends on application. Up to you to test.
Yes, replace it in the library. Arduino_Core_STM32/cores/arduino/stm32/usb/cdc/cdc_queue.hThere is not recommended value, as it depends on application. Up to you to test.
What's the maximum? And what are the side effects? Is it possible to brick the board this way or enter a state where it can't be reflashed?
I changed the multiplier from
* 3on RX to42and it seems fine. Not sure if that's excessive or not, but yeah the board works normally now.There is not recommended value, as it depends on application. Up to you to test.
What's the maximum? And what are the side effects? Is it possible to brick the board this way or enter a state where it can't be reflashed?
CDC_QUEUE_MAX_PACKET_SIZEis 64 so for 42 you allocate 2688 bytes.Reacted by Ben GuildCDC_QUEUE_MAX_PACKET_SIZEis 64 so for 42 you allocate 2688 bytes.How quickly does this get cleared? What's the effective RX rate?
It depends of several stuff. Host PC, user application, .... The main issue here is when there is no more space in the Rx buffer the USB should stop transfer until a free room is available.
this is kind of 'off-topic', under bulk transfer section, under 'OUT'
https://www.usbmadesimple.co.uk/ums_3.htm
if the device can't accept the data, it is supposed to send 'NAK', i'm not sure if this is possible in the usb code.
this would likely also need some rounds of tests if this is done. e.g. could we keep sending 'NAK'
if the device is perpetually busy? An unorthodox 'flow control' sort of.As for the sketch sending data to the host. i'd think it'd be necessary to check
Serial.availableForWrite()if the data should not be lost before doingSerial.write()orSerial.print(). Otherwise, i'd think Serial should simply overwrite old data in the buffer, or just drop the latest addition. The host can't be deemed to be 'always there'.either way, it seemed quite possible for a 'dead lock' situation to happen.
With this bug I can't use a newer core than 1.9.0
Is there another way, than increase CDC_RECEIVE_QUEUE_BUFFER_SIZE in in the library? Arduino_Core_STM32/cores/arduino/stm32/usb/cdc/cdc_queue.h
Currently not as the fix is not so simple to deploy.
Is there a way to increase CDC_RECEIVE_QUEUE_BUFFER_SIZE without change a file in the core?
No
at the moment, i'd suggest this as a partial solution, you could copy the relevant codes for the usb stack etc and perhaps place that in the sketch folder etc. it may take editing the codes. and omit selecting usb-cdc if you are using that particular set of things in the stack. there are some occasions like SPI etc where I need some custom functionalities i'd do that. for SPI i simply omit the library and copy the SPI library codes into the sketch folder and make a custom SPI driver
Still reproducible on current
main(4a8b28a). Adding the fault capture and
a couple of measurements, since this thread has had the diagnosis but not the
register evidence, and opening a PR: #3058.Board: STM32F103C8 "Blue Pill", USB CDC on PA11/PA12, core 2.10.1 (PlatformIO
framework-arduinoststm32 4.21001.250617).Minimal reproducer — no application code, just a drain loop:
#include <Arduino.h> void setup() { SerialUSB.begin(); } void loop() { while (SerialUSB.available() > 0) { (void)SerialUSB.read(); } static uint32_t last = 0; if (millis() - last >= 1000) { last = millis(); SerialUSB.println("alive"); } }
Open the port on the host and do a single
write(b"a" * 512).alivestops,
USART1 stops answering too, and the board stays dead until reset.Fault, captured by halting the core over SWD right after the crash:
IPSR = 3 (HardFault), PC = Default_Handler (the b.n self-loop) CFSR = 0x00000400 -> BFSR bit 2, IMPRECISERR (buffered write) HFSR = 0x40000000 -> FORCED Exception frame: R0 = 0x40006140 (USB PMA) R1 = 0x00000000 (destination) R2 = 0x61 ('a', the test byte) R3 = 0x40 (64, packet size) Stacked PC -> USB_ReadPMA, stacked LR -> HAL_PCD_IRQHandlerR1 being 0 is the null
RxBufferthatUSBD_CDC_ClearBuffer()arms the
endpoint with, and R0/R3 are the PMA source and the 64 byte packet size — so
this is exactly the "set the application buffer to NULL ... but USB continue to
receive without checking the buffer validity" that @fpistm described above,
caught in the act.Two things that may be useful:
-
It is bytes in flight, not length. The same 512 bytes written in 16 byte
chunks 20 ms apart are handled fine, because the sketch drains in between.
The trip point depends only on how fast the application drains — a heavier
application on the same board died at 130 bytes. -
Growing the queue only moves the ceiling. Raising
CDC_RECEIVE_QUEUE_BUFFER_PACKET_NUMBERmoved that application's fault from
130 bytes to about 1536 bytes. It does not remove it, and the crash is still
one fast host write away.
What fixes it. Not arming the endpoint at all when there is no room, as
suggested in the comment about NAK above. An unarmed bulk OUT endpoint NAKs,
the host retries, andUSBSerial::read()/readBytes()already call
CDC_resume_receive()after dequeuing, so it re-arms with a real block as soon
as the sketch drains. Verified on hardware by neutralising the
USBD_CDC_ClearBuffer()call with-Wl,--wrap=USBD_CDC_ClearBuffer, which
leaves the same code path: at the stock queue size the sketch above then
handles a single 16 KB host write correctly, and so does the full application.stm32f1xx_hal_pcd.cis byte identical between 2.10.1 and currentmain, and
the two CDC hunks are unchanged, somainis affected the same way. The patch
in #3058 is inlibraries/USBDevice, so it covers all families, but F1 is the
only one I have measured.Side note for scope: the missing
ep->xfer_buffcheck is not F1-specific. On
mainthe "OUT Single Buffering" branch guards onlycount != 0Uin all 17
PMA basedstm32*xx_hal_pcd.cdrivers, and the OTGRXFLVLpath guards only
BCNT != 0Uin F2/F4/F7/H7. That is vendored ST code, so #3058 does not touch
it — it just stops handing those drivers a null buffer.-
- added a commit that references this issue
on Sep 21, 2026 Thank you for the fix!
Describe the bug
When sending large amounts of data over Serial Monitor (string len > 204 ), the processor enters a hard fault.
Hard fault status register (SCB_HFSR) 0xE000ED2C has bit 30 set to 1 (FORCED: Forced hard fault, 1: Forced hard fault.)
Configurable fault status register (SCB_CFSR) 0xE000ED28 has bit 10 set to 1 (IMPRECISERR: Imprecise data bus error)
To Reproduce
On the latest 2.0.0 core
Expected behavior
The text is written back in the Serial monitor.
Screenshots
If applicable, add screenshots to help explain your problem.
Desktop (please complete the following information):
Board (please complete the following information):
Additional context
I have bisected this git repository and identified the issue as originating in commit 00acff6 by @fpistm which updated the HAL Drivers to v1.1.5 for STM32f1xx. The commit before 242671d using HAL v1.1.4 works perfectly as expected.
I attach three screenshots showing the hard fault, the frozen serial monitor, and the working behaviour.


