Skip to content

STM32F10xx Serial bus fault on large data #1399

Description

@chrysophylax

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

  1. Upload the following minimal Arduino sketch to the board as usual
void setup() {
  Serial.begin(9600);
}

void loop (void) {
  if (Serial.available()) Serial.print((char)Serial.read());
}
  1. 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.
hard-fault
hard-faulted-on-long-serial
working-echo-on-long-serial

Activity

  1. self-assigned this
    on May 24, 2021
  2. added this to the 2.x.x milestone on Jun 4, 2021
  3. fpistm commented on Jul 23, 2021

    @fpistm
    Member

    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.

    #define CDC_RECEIVE_QUEUE_BUFFER_SIZE ((uint16_t)(CDC_QUEUE_MAX_PACKET_SIZE * 3))

  4. benguild commented on Jul 26, 2021

    @benguild

    Definitely hoping for a fix here 😔

  5. benguild commented on Jul 26, 2021

    @benguild

    Is there a workaround for this?

  6. ABOSTM commented on Jul 26, 2021

    @ABOSTM
    Contributor

    @benguild, as said @fpistm,

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

  7. benguild commented on Jul 27, 2021

    @benguild

    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.

  8. ABOSTM commented on Jul 27, 2021

    @ABOSTM
    Contributor

    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

  9. benguild commented on Aug 14, 2021

    @benguild

    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?

  10. benguild commented on Aug 14, 2021

    @benguild

    I changed the multiplier from * 3 on RX to 42 and it seems fine. Not sure if that's excessive or not, but yeah the board works normally now.

  11. fpistm commented on Aug 16, 2021

    @fpistm
    Member

    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_SIZE is 64 so for 42 you allocate 2688 bytes.

  12. benguild commented on Aug 16, 2021

    @benguild

    CDC_QUEUE_MAX_PACKET_SIZE is 64 so for 42 you allocate 2688 bytes.

    How quickly does this get cleared? What's the effective RX rate?

  13. fpistm commented on Aug 16, 2021

    @fpistm
    Member

    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.

  14. removed this from the 2.1.0 milestone on Sep 28, 2021
  15. ag88 commented on Dec 11, 2021

    @ag88
    Contributor

    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.

  16. Ralf9 commented on Jan 29, 2022

    @Ralf9

    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

  17. fpistm commented on Jan 29, 2022

    @fpistm
    Member

    Currently not as the fix is not so simple to deploy.

  18. Ralf9 commented on Jan 29, 2022

    @Ralf9

    Is there a way to increase CDC_RECEIVE_QUEUE_BUFFER_SIZE without change a file in the core?

  19. fpistm commented on Jan 29, 2022

    @fpistm
    Member

    No

  20. ag88 commented on Jan 30, 2022

    @ag88
    Contributor

    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

  21. added theissue type on Feb 5, 2025
  22. KentLee86 commented on Aug 25, 2026

    @KentLee86

    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). 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.

  23. added a commit that references this issue on Sep 21, 2026
    cd7b8c2
  24. chrysophylax commented on Sep 21, 2026

    @chrysophylax
    Author

    Thank you for the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bug 🐛Something isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions