cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F429 USB bug on updating DIEPCTL register while EPENA set

l-proger
Associate II
Posted on February 20, 2016 at 14:51

Hello and sorry for  my bad English )

I have a problem with FS USB Device (Embedded PHY) on STM32F429VGT6 MCU. 

I implemented custom USB ''vendor device class'' on top of ST USBD Core library and custom communication protocol between STM32 and PC. That device class consists of 1 interface (custom class) with control endpoint + 2 bulk endpoints (IN = 0x81, OUT = 0x01).  All endpoints max packet size == 64 bytes. On PC side, I am using WinUSB driver.

PC can communicate with MCU without problems, restore protocol connection and so on! So, my problem lies in connection restore routine in protocol layer.

Why I need protocol restore routine: if user application crashed in the middle of IN transaction then STM device left in state where some packets of protocol response are still in FIFO and protocol controller is waiting for TX FIFO is empty.  After restart of user application on PC side i need to clean up protocol controller state =>  read bytes in FIFO left from previous response! Otherwise I would not be able to correctly handle new responses (data from old broken response and new one would read as combined).

This part works as expected! 100% tested! But! I really want to make sure that after restarting user application device would function correctly, so, I’m using WinUSB_ResetPipe function before doing software protocol restore. WinUSB_ResetPipe is cleaning STALL condition and data toggle bit for pipe on host side + sends ''clear feature'' request to STM32 (to clear stall and data tggle bits too).

So, this is the bug! If I call WinUSB_ResetPipe then packets received from STM32 device are messed up! Their order is unpredictable! I am using hardware USB protocol analyzer for debugging and I can't understand logic of what I see there.

Moreover, this is not looks like data toggle bit mismatch, I am not just loosing packet (this is handled correctly by my

connection restore routine), packets order is changed! And _part_ of content is discarded (that is strangest thing)! I am sending only 64 bit packets (Always!) but after clearing stall condition I receive zero length packet and 48 bytes packet! I do not understand why 16 bytes from start of packet are discarded!

After some digging, I observed that even rewriting DIEPCTL register with it's own value causing all problems.

uint32_t val = USBx_INEP(1)->DIEPCTL;

USBx_INEP(1)->DIEPCTL=val;  //Next packets are messed up in IN endpoint!

After a bit more digging I found that this happens only if EPENA bit is set in DIEPCTL (aborted transaction due to user application crash on PC side).

So what's wrong with that bit?

6 REPLIES 6
l-proger
Associate II
Posted on February 20, 2016 at 16:45

Ok, I think I got it o_O I have to clear EPENA bit before writing DIEPCTL register if endpoint is already enabled! Otherwisethere will be ''magic bugs'' with FIFO packets. That is double strange that DOEPCTL register does not produce such errors in same conditions.

My bugfix to my problem:

HAL_StatusTypeDef USB_EPClearStall(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep){
if
(ep->is_in == 1U){
uint32_t tmp = USBx_INEP(ep->num)->DIEPCTL & ~(USB_OTG_DIEPCTL_EPDIS | USB_OTG_DIEPCTL_EPENA);
tmp &= ~USB_OTG_DIEPCTL_STALL;
if
(ep->type == EP_TYPE_INTR || ep->type == EP_TYPE_BULK){
tmp |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; 
/* DATA0 */
}
USBx_INEP(ep->num)->DIEPCTL = tmp;
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
if
(ep->type == EP_TYPE_INTR || ep->type == EP_TYPE_BULK){
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM; 
/* DATA0 */
}
}
return
HAL_OK;
}

And ST's source code looks like:

HAL_StatusTypeDef USB_EPClearStall(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep)
{
if
(ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL &= ~USB_OTG_DIEPCTL_STALL;
if
(ep->type == EP_TYPE_INTR || ep->type == EP_TYPE_BULK)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM; 
/* DATA0 */
} 
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL &= ~USB_OTG_DOEPCTL_STALL;
if
(ep->type == EP_TYPE_INTR || ep->type == EP_TYPE_BULK)
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM; 
/* DATA0 */
} 
}
return
HAL_OK;
}

Stig Hornang
Associate II
Posted on February 29, 2016 at 12:06

I might have a similar problem. I'm using the standard CDC driver.

My problem is that whenever the USB transfer gets interrupted like when surprise unplug the cable, noise problems or maybe also host-application crash, the future packets are not received correctly on the host side. What happens is that the host is always one packet behind each TX-call on the device. So when the device transmits one packet it is the previous packet that gets sent as if there suddenly is a one-packet-queue somewhere. Nothing else seems to change so the packet order is correct, packet lengths have not changed and data is not corrupted, but its one behind all the time.

I'm using the proper CDC calls (Set TX-buffer and Transmit) to send each packet and it is double buffered and I'm not sending another packet until the TX flag is false.

The problem originally happened very seldom, until we had a EMC immunity test were it happened a couple of times. To trigger the problem somehow reliably without special equipment, unplugging the cable while data is beeing sent usually works after 4-5 attempts. I monitor the packets and can easily detect when gets device in that lazy mode. 

USB_EPClearStall

 is never called in my scenario so your bugfix doesn't work for me I think. 

Was the fix a permanent working for you?

It does not help to re-connect the device to the host. The only software method I have found to solve it without restarting the device is to re-init the USB-subsystem by calling:

USBD_DeInit(...)

USBD_Init(...)

USBD_RegisterClass(...)

USBD_CDC_RegisterInterface(...)

USBD_Start(...)

Commands which now has to be triggered by the user. Anyway, it is a hack around the real problem.

l-proger
Associate II
Posted on February 29, 2016 at 14:30

Yes, fix is working good. I fixed unplug/plug case too but I lost that code after re-generating project files with CubeMX. So if I remember correctly after replug of usb cable USB host start device enumeration and issues ''Device reset''.USBD_LL_Reset routine call

if
(pdev->pClassData) 
pdev->pClass->DeInit(pdev, pdev->dev_config);

and after successful enumeration USBD_SetClassConfig function call

pdev->pClass->Init(pdev, cfgidx)

Init function should initialize all interface endpoints! So fix can be placed insideUSB_ActivateEndpoint! Just do not overwrite EPENA bit in DIEPCTL insideUSB_ActivateEndpoint.
Stig Hornang
Associate II
Posted on March 01, 2016 at 16:07

The Init and DeInit functions you are referring to here are CDC class functions which are already implemented by the ST USB device library. They in turn call a user implementable init/deinit function, but restarting the USB driver cannot be done in those functions.

I probably need to start off with a simple CDC example and see if I can trigger the error state there by sending a lot of data and unplug.

Right now, I have no idea how to properly identify the erroneous state on the device other than the symptoms that can be seen on the host RX side.

l-proger
Associate II
Posted on March 01, 2016 at 17:40

but restarting the USB driver cannot be done in those functions.

 - why are you trying to restart USB driver? You should not do that. I'm talking about fixing bugs in ST generated code where endpoints are initialized!  When you unplug and then plug usb cable to device your USB host would reenumerate device! Reenumeration = close interface + open interface. Close interface = user deinit code + _deactivate endpoints_. Openinterface = user init code + _activate endpoints_.  My fix should be applied to _activate endpoints_  routine. And again you should not restart USB device!

Just find 

USB_ActivateEndpoint generated function, set breakpoint there, start your debugging session and look at call stack when breakpoint hit. You'll see where and how this function is called.

Stig Hornang
Associate II
Posted on March 02, 2016 at 09:13

Where is your fix (code) or are you asking me to track down a bug in ST code? Latter which I will pursue at a later time if you don't have a ready fix.

I thought you said you did not have a fix because it got lost in the ST code generation. 

USB_ActivateEndpoint

is not a generated function, but it is indeed copied by the STM32CubeMX into the target project from the STM32F4xx_HAL_Driver. So, if there is a bug in the ST code I would believe its either in

STM32F4xx_HAL_Driver or in STM32_USB_Device_Library.

I'm restarting the USB driver because it is only solution that works for me at the moment. It is a terrible work around. I'm not restarting the driver from my USB thread.