You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Upload the following minimal Arduino sketch to the board as usual
Connect using the serial monitor and send the following
One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections. The bedding was hardly able to cover it and seemed ready to slide off any moment. His many legs, pitifully thin compared with the size of the rest of him, waved about helplessly as he looked. "What's happened to me?" he thought.
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):
OS: Linux 5.12.5-arch1-1
Arduino IDE version: 1.8.15
STM32 core version: 2.0.0
Tools menu settings if not the default: USB Support: CDC Supersede Serial
Upload method: STM32CubeProgrammer(SWD)
Board (please complete the following information):
Name: BluePill STM32F103C8T6 Medium-density with 128K Flash
Hardware Revision: Rev X/ N/A
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.
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: 07c4ee4
I will try to correct this issue but not so easy. One workaround could be to increase the buffer size.
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
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.h
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 doing Serial.write() or Serial.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.
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>voidsetup() {
SerialUSB.begin();
}
voidloop() {
while (SerialUSB.available() > 0) {
(void)SerialUSB.read();
}
staticuint32_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). alive stops,
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_IRQHandler
R1 being 0 is the null RxBuffer that USBD_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_NUMBER moved 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, and USBSerial::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.c is byte identical between 2.10.1 and current main, and
the two CDC hunks are unchanged, so main is affected the same way. The patch
in #3058 is in libraries/USBDevice, so it covers all families, but F1 is the
only one I have measured.
Side note for scope: the missing ep->xfer_buff check is not F1-specific. On main the "OUT Single Buffering" branch guards only count != 0U in all 17
PMA based stm32*xx_hal_pcd.c drivers, and the OTG RXFLVL path guards only BCNT != 0U in F2/F4/F7/H7. That is vendored ST code, so #3058 does not touch
it — it just stops handing those drivers a null buffer.
Activity
fpistm commentedon Jul 23, 2021
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:
07c4ee4
I will try to correct this issue but not so easy. One workaround could be to increase the buffer size.
Arduino_Core_STM32/cores/arduino/stm32/usb/cdc/cdc_queue.h
Line 57 in 0686e2d
benguild commentedon Jul 26, 2021
Definitely hoping for a fix here 😔
benguild commentedon Jul 26, 2021
Is there a workaround for this?
ABOSTM commentedon Jul 26, 2021
@benguild, as said @fpistm,
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
Arduino_Core_STM32/cores/arduino/stm32/usb/cdc/cdc_queue.h
Line 57 in 0686e2d
benguild commentedon Jul 27, 2021
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.
ABOSTM commentedon Jul 27, 2021
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.h
benguild commentedon Aug 14, 2021
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?
benguild commentedon Aug 14, 2021
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.fpistm commentedon Aug 16, 2021
CDC_QUEUE_MAX_PACKET_SIZEis 64 so for 42 you allocate 2688 bytes.benguild commentedon Aug 16, 2021
How quickly does this get cleared? What's the effective RX rate?
fpistm commentedon Aug 16, 2021
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.
ag88 commentedon Dec 11, 2021
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.
Ralf9 commentedon Jan 29, 2022
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
fpistm commentedon Jan 29, 2022
Currently not as the fix is not so simple to deploy.
Ralf9 commentedon Jan 29, 2022
Is there a way to increase CDC_RECEIVE_QUEUE_BUFFER_SIZE without change a file in the core?
fpistm commentedon Jan 29, 2022
No
ag88 commentedon Jan 30, 2022
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
KentLee86 commentedon Aug 25, 2026
Still reproducible on current
main(4a8b28a). Adding the fault capture anda 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:
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:
R1 being 0 is the null
RxBufferthatUSBD_CDC_ClearBuffer()arms theendpoint 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 from130 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, and
USBSerial::read()/readBytes()already callCDC_resume_receive()after dequeuing, so it re-arms with a real block as soonas the sketch drains. Verified on hardware by neutralising the
USBD_CDC_ClearBuffer()call with-Wl,--wrap=USBD_CDC_ClearBuffer, whichleaves 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, andthe two CDC hunks are unchanged, so
mainis affected the same way. The patchin #3058 is in
libraries/USBDevice, so it covers all families, but F1 is theonly one I have measured.
Side note for scope: the missing
ep->xfer_buffcheck is not F1-specific. Onmainthe "OUT Single Buffering" branch guards onlycount != 0Uin all 17PMA based
stm32*xx_hal_pcd.cdrivers, and the OTGRXFLVLpath guards onlyBCNT != 0Uin F2/F4/F7/H7. That is vendored ST code, so #3058 does not touchit — it just stops handing those drivers a null buffer.
fix(cdc): stop arming OUT endpoint with a NULL buffer
chrysophylax commentedon Sep 21, 2026
Thank you for the fix!