2024-11-04 01:17 AM
Hello,
I found an interesting problem with the USB controller. Our device uses MCU STM32F756, with USB in FS Device mode.
We have a total of three USB IN endpoints, controlled via Azure RTOS / USBX middleware. With a heavy load on USB endpoints, sometimes an endpoint gets stuck. By observing the registers of the USB controller, I found that the given endpoint is ready to send data, but the FIFO empty interrupt is not enabled. These interrupts are controlled using the OTG_DIEPEMPMSK register.
The problem is that this register is shared by all endpoints and setting or clearing the relevant bit is not atomic. If the executive thread is preempted by the scheduler or the USB interrupt handler in the middle of a read-modify-write operation, the OTG_DIEPEMPMSK register may not be set correctly.
I solved the problem by using ATOMIC_SET_BIT / ATOMIC_CLEAR_BIT macros around the OTG_DIEPEMPMSK register (in HAL_PCD_IRQHandler, PCD_WriteEmptyTxFifo and especially the USB_EPStartXfer functions). It looks like the USB device is working exactly as it should.
Solved! Go to Solution.
2024-11-05 03:34 AM
Hi @FBL
The problem is caused by a concurrency (race-condition) when accessing the OTG_DIEPEMPMSK register, between the interrupt handler and the USB threads.
HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma)
{
...
/* Enable the Tx FIFO Empty Interrupt for this EP */
if (ep->xfer_len > 0U)
{
USBx_DEVICE->DIEPEMPMSK |= 1UL << (ep->num & EP_ADDR_MSK); // <- this not ATOMIC!
}
...
void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
{
...
fifoemptymsk = (uint32_t)(0x1UL << (epnum & EP_ADDR_MSK));
USBx_DEVICE->DIEPEMPMSK &= ~fifoemptymsk;
...
I observed this problem with BULK IN transfers. It's definitely not caused by misconfiguration or improper handling of USB transfers. It is caused by a bad implementation of the USB controller handling in the above functions.
I used this solution:
USB_EPStartXfer function:
ATOMIC_SET_BIT (USBx_DEVICE->DIEPEMPMSK, 1UL << (ep->num & EP_ADDR_MSK));
HAL_PCD_IRQHandler function:
ATOMIC_CLEAR_BIT (USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
2024-11-05 03:07 AM
Hi @PeBo
I am trying to reproduce the issue could you point me directly to the issue to report? The issue observed is only seen in periodic or non-periodic transfers or both? We may need to diagnose further to resolve the issue. It could be linked to misconfiguration or improper handling of USB transfers.
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2024-11-05 03:31 AM
The problem was and is present in all STM32 USB procedures from the very beginning.
2024-11-05 03:34 AM
Hi @FBL
The problem is caused by a concurrency (race-condition) when accessing the OTG_DIEPEMPMSK register, between the interrupt handler and the USB threads.
HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma)
{
...
/* Enable the Tx FIFO Empty Interrupt for this EP */
if (ep->xfer_len > 0U)
{
USBx_DEVICE->DIEPEMPMSK |= 1UL << (ep->num & EP_ADDR_MSK); // <- this not ATOMIC!
}
...
void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
{
...
fifoemptymsk = (uint32_t)(0x1UL << (epnum & EP_ADDR_MSK));
USBx_DEVICE->DIEPEMPMSK &= ~fifoemptymsk;
...
I observed this problem with BULK IN transfers. It's definitely not caused by misconfiguration or improper handling of USB transfers. It is caused by a bad implementation of the USB controller handling in the above functions.
I used this solution:
USB_EPStartXfer function:
ATOMIC_SET_BIT (USBx_DEVICE->DIEPEMPMSK, 1UL << (ep->num & EP_ADDR_MSK));
HAL_PCD_IRQHandler function:
ATOMIC_CLEAR_BIT (USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
2024-11-05 05:01 AM
Thank you for highlighting the issue! We have an internal ticket for atomic access not yet implemented. I will come back with updates soon!
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.