Skip to main content
Visitor II
May 18, 2017
Question

STM32F4 & STM32F7 USB Host Core (Interrupt flood)

  • May 18, 2017
  • 13 replies
  • 13598 views
Posted on May 19, 2017 at 01:02

Issue:

USB Core issing interrupt at a rate of 8.65us causing high CPU loading.

Software/Chip:

Using USB Host/CDC from STM32Cube V1.15 on a STM32F429 board. 

  USB interrupt sources:

- SOF Interrupts (It is not needed for CDC type transfers) turned this interrupt off and still getting high interrupt rate.

   This is configured in

HAL_StatusTypeDef USB_HostInit (USB_OTG_GlobalTypeDef *USBx, USB_OTG_CfgTypeDef cfg)

.....

  /* Enable interrupts matching to the Host mode ONLY */

  USBx->GINTMSK |= (USB_OTG_GINTMSK_PRTIM            | USB_OTG_GINTMSK_HCIM |\

                 

   USB_OTG_GINTMSK_SOFM

|USB_OTG_GINTSTS_DISCINT|\

                    USB_OTG_GINTMSK_PXFRM_IISOOXFRM  | USB_OTG_GINTMSK_WUIM);

 

  return HAL_OK;

}

- NAK interrupts from bulk in endpoint.  The issue is when the OTG USB gets enough NAKs from the bulk endpoint and issues an interrupt at a rate of 8.625us. 

Solutions

?

Is there a work around that does not degrade USB performance?  The STM32FH7 also has has the same code to re-enable the bulk in endpoint. 

static void HCD_HC_IN_IRQHandler   (HCD_HandleTypeDef *hhcd, uint8_t chnum)

  }

  else if ((USBx_HC(chnum)->HCINT) &  USB_OTG_HCINT_NAK)

  {   

    if(hhcd->hc[chnum].ep_type == EP_TYPE_INTR)

    {

      __HAL_HCD_UNMASK_HALT_HC_INT(chnum);  

      USB_HC_Halt(hhcd->Instance, chnum);   

    }

    else if  ((hhcd->hc[chnum].ep_type == EP_TYPE_CTRL)||

         (hhcd->hc[chnum].ep_type == EP_TYPE_BULK))

    {

      /* re-activate the channel  */

      tmpreg = USBx_HC(chnum)->HCCHAR;

      tmpreg &= ~USB_OTG_HCCHAR_CHDIS;

      tmpreg |= USB_OTG_HCCHAR_CHENA;

      USBx_HC(chnum)->HCCHAR = tmpreg;

    }

    hhcd->hc[chnum].state = HC_NAK;

    __HAL_HCD_CLEAR_HC_INT(chnum, USB_OTG_HCINT_NAK);

  }

}

There are several postings about this issue (The first posting is the best description of the issue)

https://community.st.com/0D50X00009XkYz2SAF

 

https://community.st.com/0D50X00009XkYSSSA3

 

https://community.st.com/0D50X00009Xked0SAB

 

null
    This topic has been closed for replies.

    13 replies

    Technical Moderator
    May 23, 2017
    Posted on May 23, 2017 at 13:33

    Hi,

    Thank you for this reported issue. This is shared internally for further review and investigation.

    I will keep you informed about the taken actions/explanation if needed.

    Thanks

    Imen

    Visitor II
    May 30, 2017
    Posted on May 30, 2017 at 20:52

    Imen,

    Status update?

    Thanks,

    Tim

    Visitor II
    June 29, 2017
    Posted on June 29, 2017 at 16:50

    Status update?

    Visitor II
    January 2, 2018
    Posted on January 02, 2018 at 16:56

    Any news on this?

    I am experiencing the same problem.

    Visitor II
    February 13, 2018
    Posted on February 13, 2018 at 13:17

    Any update? Having the same issue.

    Technical Moderator
    February 27, 2018
    Posted on February 27, 2018 at 11:11

    Dear All,

    Sorry for this late answer.

    In fact, DMA mode is recommended in case using HS IP in order to optimize the CPU and let the core managing the reactivation of the channel.

    Recently

    ,

    we have reworked the HAL driver for STM32F7 to enhance this part

    and i

    t should be available in the next release of the STM32CubeF7 v1.10.0.

    Kind Regards,

    Imen.

    Visitor II
    February 27, 2018
    Posted on February 27, 2018 at 16:23

    Imen,

    When will the STM32F4 fix be ready?

    Tim

    Technical Moderator
    February 27, 2018
    Posted on February 27, 2018 at 16:47

    Hello

    Michals.Tim

    ,

    It is planned to integrate the enhancementin the next release for STM32F4, but, unfortunately, I can't give you any target date for the moment.

    Best Regards,

    Imen

    Visitor II
    May 8, 2018
    Posted on May 08, 2018 at 05:03

    Waiting for a solution for this issue too.

    Visitor II
    May 8, 2018
    Posted on May 08, 2018 at 09:59

    I walkarounded this issue by handling the NAK interrupt async.

    Visitor II
    May 25, 2018

    Posted on May 25, 2018 at 18:17

     

     

     

    I ran into the same issue with an L4 processor and I found a way to work around it that seems to work.  I've never looked at any of the F series software frameworks, but I suspect something similar to what I did will work.  I made the following 2 changes.

    Inside of the interrupt, where you handle the NAK interrupt, I disable it.  Below is what it looks like on the L4 framework (line I added is in bold):

    else if ((USBx_HC(chnum)->HCINT) & USB_OTG_HCINT_NAK)
    {
     if(hhcd->hc[chnum].ep_type == EP_TYPE_INTR)
     {
     __HAL_HCD_UNMASK_HALT_HC_INT(chnum);
     USB_HC_Halt(hhcd->Instance, chnum);
     }
     /* Clear the NAK flag before re-enabling the channel for new IN request */
     hhcd->hc[chnum].state = HC_NAK;
     __HAL_HCD_CLEAR_HC_INT(chnum, USB_OTG_HCINT_NAK);
     // Hack to disable the NAK interrupt completely (it'll be re-enabled in SOF)
     USBx_HC(chnum)->HCINTMSK &= ~USB_OTG_HCINT_NAK;
     if ((hhcd->hc[chnum].ep_type == EP_TYPE_CTRL)||
     (hhcd->hc[chnum].ep_type == EP_TYPE_BULK))
     {
     /* re-activate the channel */
     USBx_HC(chnum)->HCCHAR &= ~USB_OTG_HCCHAR_CHDIS;
     USBx_HC(chnum)->HCCHAR |= USB_OTG_HCCHAR_CHENA;
     }
    }

    This change by itself will break USB as the NAK interrupt does appear to be important for general functionality.  You have to periodically re-enable the NAK interrupt, and I used the SoF interrupt to do that.

    /* Handle Host SOF Interrupts */
    if(__HAL_HCD_GET_FLAG(hhcd, USB_OTG_GINTSTS_SOF))
    {
     HAL_HCD_SOF_Callback(hhcd);
     __HAL_HCD_CLEAR_FLAG(hhcd, USB_OTG_GINTSTS_SOF);
     // Added hack to re-enable the NAK interrupt because leaving it off is bad
     for (i = 0; i < hhcd->Init.Host_channels; i++)
     {
     USBx_HC(i)->HCINTMSK |= USB_OTG_HCINT_NAK;
     }
    }

    Re-enabling it for every channel is probably unnecessary, but since this is running in interrupt space I opted for speed rather than checking each channel and seeing if it needed to be re-enabled.

    I was getting interrupts every 20 microseconds  This change made it so I'd get the SOF interrupt once a ms, then 20 us later get a NAK and then no spurious interrupts until the next SOF.

    Visitor II
    February 23, 2019

    I have to thank you immensely for this post. It saved my bacon! Once I started looking at the USB Host code I was pretty lost. I hesitated but eventually implemented your 2 changes and BOOM! The rest of the threads in my app woke up and were no longer starved! Fat and happy!

    One post here implied that DMA should be on but, I had to turn off DCache for devices to enumerate. Maybe because MPU & SDRAM use? In the end, the DMA had no effect. So I don't know whether I gain more by having DMA on and Dcache off? My app uses HS Host, FS Device, Timers interrupt, STemWin and DMA LCD.

    While solving this bug I found out that :

    1. DMA only for HS on STM32F7
    2. DCache has to be off when using DMA
    3. DMA doesn't affect this problem.

    It seemed like ST was going to address this issue . I believe I have the latest HAL but , it seems nothing was done?

    Thanks again for posting this. I owe you one!

    Visitor II
    July 10, 2019

    ST team, this issue is pending for a VERY LONG time - not reasonable!!

    Any update regarding this topic for F4?

    Technical Moderator
    July 18, 2019

    Hello @AEila​ ,

    Sorry for this delay.

    In fact, this is a limitation in the OTG peripheral and will be fixed to implement the auto channel re-enable when IN-NAK received for next/new STM32 products.

    I shared workaround provided for STM32H7 that implements the needed modifications (Unfortunately, not implemented on the STM32CubeF7 and CubeF4 packages and no plan to integrate officialy on HAL and USB Middleware).

    1. Updated host lib with fix added during enumeration process, and non-periodic MSC IN bulk and CDC IN Bulk processes

    2. Updated HAL

    3. MSC applications with changes on usbd_conf.c/h files

    User needs to set during init of the host controller the number of NAK’s allowed and the relaxing time in usbh_conf.c file via:

    hhcd.Init.NakCount = 3U;

    hhcd.Init.NakTimeout = USBH_HS_NAK_TIMEOUT_MS; /* for HS mode */

    hhcd.Init.NakTimeout = USBH_FS_NAK_TIMEOUT_MS; /* for FS mode */

    adjust the timeout in usbh_conf.h file via:

    #define USBH_FS_NAK_TIMEOUT_MS 1000U /* Timeout in micro Second */

    #define USBH_HS_NAK_TIMEOUT_MS 125U /* Timeout in micro Second */

    Setting Timeout to Zero or NakCount to zero leads to switch to default mode (channel reactivation when NAK Int is received)

    Kind Regards,

    Imen

    Visitor II
    January 2, 2020

    Hello @Imen DAHMEN​ 

    I know this is an old discussion. I am currently using a STM32H757 and I am running into the same issue. I got the source from STM Cube package for the H7 family. I see that that the USB Host library was updated April 2019, however, I dont see any notes relating to a fix for this issue. Is there a plan to fix the drivers for the H7 family?

    Thanks!

    Visitor II
    July 18, 2019

    Thanks Imen, appreciate your feedback.

    I've Cc'ed Guy Livneh, our software manager to follow up as needed.

    Regards,

    Visitor II
    September 20, 2019

    I had this problem and resolve it.

    CDC device has BULK endpoint type, but BULK channel planning in last order. After polling BULK endpoint from host and if device endpoint buffer is empty then device send NAK packet. After received packet on host host planning next request, except BULK endpoint we have not any endpoints and host request next packet at once. BULK endpoints are not planning periodic. You should be replace in CDC interface descriptor endpoint type, from bulk to interrupt, and set bInterval 1ms or more.

    Also STM host library replace hardcoded EP type config in file "stm32_usb_host_library/class/cdc/src/usbh_cdc.c" by code below:

    static USBH_StatusTypeDef USBH_CDC_InterfaceInit(USBH_HandleTypeDef *phost)
    {
     USBH_StatusTypeDef status = USBH_FAIL;
     uint8_t interface;
     CDC_HandleTypeDef *CDC_Handle;
     
     interface = USBH_FindInterface(phost,
     COMMUNICATION_INTERFACE_CLASS_CODE,
     ABSTRACT_CONTROL_MODEL,
     COMMON_AT_COMMAND);
     
     if (interface == 0xFFU) /* No Valid Interface */
     {
     USBH_DbgLog ("Cannot Find the interface for Communication Interface Class.", phost->pActiveClass->Name);
     }
     else
     {
     USBH_SelectInterface(phost, interface);
     phost->pActiveClass->pData = &hcdc_instance;
     CDC_Handle = (CDC_HandleTypeDef*)phost->pActiveClass->pData;
     
     /*Collect the notification endpoint address and length*/
     if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80U)
     {
     CDC_Handle->CommItf.NotifEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
     CDC_Handle->CommItf.NotifEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
     }
     
     /*Allocate the length for host channel number in*/
     CDC_Handle->CommItf.NotifPipe = USBH_AllocPipe(phost, CDC_Handle->CommItf.NotifEp);
     
     /* Open pipe for Notification endpoint */
     USBH_OpenPipe(phost, CDC_Handle->CommItf.NotifPipe, CDC_Handle->CommItf.NotifEp,
     phost->device.address, phost->device.speed,
     phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bmAttributes & 0x3, // this argument had been replaced
     CDC_Handle->CommItf.NotifEpSize);
     
     USBH_LL_SetToggle(phost, CDC_Handle->CommItf.NotifPipe, 0U);
     
     interface = USBH_FindInterface(phost,
     DATA_INTERFACE_CLASS_CODE,
     RESERVED,
     NO_CLASS_SPECIFIC_PROTOCOL_CODE);
     
     if (interface == 0xFFU) /* No Valid Interface */
     {
     USBH_DbgLog ("Cannot Find the interface for Data Interface Class.", phost->pActiveClass->Name);
     }
     else
     {
     uint8_t ep_out_type = 0; // this added
     uint8_t ep_in_type = 0; // -//-
     
     /*Collect the class specific endpoint address and length*/
     if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80U)
     {
     CDC_Handle->DataItf.InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
     CDC_Handle->DataItf.InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
     ep_in_type = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bmAttributes & 0x3;
     }
     else
     {
     CDC_Handle->DataItf.OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress;
     CDC_Handle->DataItf.OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize;
     ep_out_type = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bmAttributes & 0x3;
     }
     
     if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress & 0x80U)
     {
     CDC_Handle->DataItf.InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
     CDC_Handle->DataItf.InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
     ep_in_type = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bmAttributes & 0x3;
     }
     else
     {
     CDC_Handle->DataItf.OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bEndpointAddress;
     CDC_Handle->DataItf.OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].wMaxPacketSize;
     ep_out_type = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[1].bmAttributes & 0x3;
     }
     
     /*Allocate the length for host channel number out*/
     CDC_Handle->DataItf.OutPipe = USBH_AllocPipe(phost, CDC_Handle->DataItf.OutEp);
     
     /*Allocate the length for host channel number in*/
     CDC_Handle->DataItf.InPipe = USBH_AllocPipe(phost, CDC_Handle->DataItf.InEp);
     
     /* Open channel for OUT endpoint */
     USBH_OpenPipe(phost, CDC_Handle->DataItf.OutPipe, CDC_Handle->DataItf.OutEp,
     phost->device.address, phost->device.speed,
     ep_out_type, // this argument had been replaced
     CDC_Handle->DataItf.OutEpSize);
     /* Open channel for IN endpoint */
     USBH_OpenPipe(phost, CDC_Handle->DataItf.InPipe, CDC_Handle->DataItf.InEp,
     phost->device.address, phost->device.speed,
     ep_in_type, // this argument had been replaced
     CDC_Handle->DataItf.InEpSize);
     
     CDC_Handle->state = CDC_IDLE_STATE;
     
     USBH_LL_SetToggle(phost, CDC_Handle->DataItf.OutPipe, 0U);
     USBH_LL_SetToggle(phost, CDC_Handle->DataItf.InPipe, 0U);
     status = USBH_OK;
     }
     }
     return status;
    }

     And in USB device library, where placed your CDC descriptor replace old code by code below, at the bottom:

     /*Endpoint OUT Descriptor*/
     0x07, /* bLength: Endpoint Descriptor size */
     USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
     CDC_OUT_EP, /* bEndpointAddress */
     0x03, /* bmAttributes: Interrupt */ 
     LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
     HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
     0x01, /* bInterval */
     
     /*Endpoint IN Descriptor*/
     0x07, /* bLength: Endpoint Descriptor size */
     USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
     CDC_IN_EP, /* bEndpointAddress */
     0x03, /* bmAttributes: Interrupt */
     LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
     HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
     0x01 /* bInterval */