How to debug PRIMEing problem with HSUSB - K61

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to debug PRIMEing problem with HSUSB - K61

769 Views
mrharmonsr
Contributor III

I'm trying to debug an issue with priming a high speed usb endpoint on a K61 processor.

I've been using a modified version of the Freescale USB stack (4.1) for over a year without issues.

Now I'm trying to add a new protocol (MSD) and have found that occasionally an OUT endpoint fails to prime.

The FW writes a 4 to the EPPRIME register and waits for the bit to clear, it never does.

The FW only uses 1 transfer descriptor per endpoint, so the Queue head is always empty when trying to add and prime another transfer descriptor.

We can detect when the problem occurs by counting NAK interrupts on the endpoint.

When we detect the problem the EPSR register does not show a receive buffer ready.

I've looked at the TX descriptor and Queue Head descriptors in memory, but can't see a problem.

Can anyone suggest how to debug this issue?

Labels (2)
Tags (1)
0 Kudos
3 Replies

599 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi,

If customer could download and check with USB Stack V5.0 bare metal USB stack for K70(K61) product from here?

Thank you for the attention.


Have a great day,
Mike

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

599 Views
mrharmonsr
Contributor III

Not an answer to my question!

I've compared the priming functions in both libraries and still can't find the problem.

0 Kudos

599 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi,

Sorry for the later reply.

I compare the MQX V4.2 USB Stack for K70 HS USB vs. USB Stack V4.1.1 for K70 HS USB, the USB drive code with some difference.

Please refer below  MQX V4.2 driver function _usb_dci_usbhs_add_dTD()

/*FUNCTION*-------------------------------------------------------------
*
*  Function Name  : _usb_dci_usbhs_add_dTD
*  Returned Value : USB_OK or error code
*  Comments       :
*        Adds a device transfer desriptor(s) to the queue.
*
*END*-----------------------------------------------------------------*/
uint8_t _usb_dci_usbhs_add_dTD
(
    /* [IN] the USB_dev_initialize state structure */
    _usb_device_handle         handle,

    /* [IN] The transfer descriptor address */
    XD_STRUCT_PTR              xd_ptr
)
{   /* Body */
    volatile USB_EHCI_DEV_STATE_STRUCT_PTR  usb_dev_ptr;
    volatile USBHS_REG_STRUCT_PTR     dev_ptr;
    volatile USBHS_EP_TR_STRUCT_PTR   dTD_ptr, temp_dTD_ptr, first_dTD_ptr = NULL;
    volatile USBHS_EP_QUEUE_HEAD_STRUCT_PTR ep_queue_head_ptr;
    uint32_t curr_pkt_len, remaining_len, curr_offset, bit_pos, addr;
    uint32_t ep_status;
    uint8_t temp;

    /*********************************************************************
     For a optimal implementation, we need to detect the fact that
     we are adding DTD to an empty list. If list is empty, we can
     actually skip several programming steps esp. those for ensuring
     that there is no race condition.The following bool will be useful
     in skipping some code here.
     *********************************************************************/
     bool   list_empty = FALSE;
     bool   read_safe;

    usb_dev_ptr = (USB_EHCI_DEV_STATE_STRUCT_PTR)handle;
    dev_ptr = (USBHS_REG_STRUCT_PTR)usb_dev_ptr->G.DEV_PTR;

    remaining_len = xd_ptr->WTOTALLENGTH;

    curr_offset = 0;
    temp = (uint8_t)(2 * xd_ptr->EP_NUM + xd_ptr->BDIRECTION);
    bit_pos = (uint32_t)(1 << (16 * xd_ptr->BDIRECTION + xd_ptr->EP_NUM));

    ep_queue_head_ptr = (USBHS_EP_QUEUE_HEAD_STRUCT_PTR)
        EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_LIST_ADDR) +
        temp;

    #if PSP_HAS_DATA_CACHE
        if (ep_queue_head_ptr < (USBHS_EP_QUEUE_HEAD_STRUCT_PTR)__UNCACHED_DATA_START || ((ep_queue_head_ptr + sizeof(USBHS_EP_QUEUE_HEAD_STRUCT)) > (USBHS_EP_QUEUE_HEAD_STRUCT_PTR)__UNCACHED_DATA_END))
            USB_dcache_flush_mlines((void *)ep_queue_head_ptr, sizeof(USBHS_EP_QUEUE_HEAD_STRUCT));
    #endif

   /*********************************************************************
    This loops iterates through the length of the transfer and divides
    the data in to DTDs each handling the a max of 0x4000 bytes of data.
    The first DTD in the list is stored in a void *called first_dTD_ptr.
    This void *is later linked in to QH for processing by the hardware.
    *********************************************************************/

    do
    {
        /* Check if we need to split the transfer into multiple dTDs */
        if(remaining_len > VUSB_EP_MAX_LENGTH_TRANSFER)
        {
            curr_pkt_len = VUSB_EP_MAX_LENGTH_TRANSFER;
        }
        else
        {
            curr_pkt_len = remaining_len;
        }

        remaining_len -= curr_pkt_len;

        /* Get a dTD from the queue */
        EHCI_DTD_QGET(usb_dev_ptr->DTD_HEAD, usb_dev_ptr->DTD_TAIL, dTD_ptr);

        if (!dTD_ptr)
        {
            return USBERR_TR_FAILED;
        }

        usb_dev_ptr->DTD_ENTRIES--;

        if (curr_offset == 0)
        {
            first_dTD_ptr = dTD_ptr;
        }

        #if PSP_HAS_DATA_CACHE
            /**************************************************************
             USB Memzero does not bypass the cache and hence we must use
             DTD void *to update the memory and bypass the cache. If
             your DTD are allocated from an uncached region, you can
             eliminitate this approach and switch back to USB_mem_zero().
             **************************************************************/
            EHCI_MEM_WRITE(dTD_ptr->NEXT_TR_ELEM_PTR, 0);
            EHCI_MEM_WRITE(dTD_ptr->SIZE_IOC_STS, 0);
            EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR0, 0);
            EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR1,0);
            EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR2,0);
            EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR3,0);
            EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR4,0);
        #else
            /* Zero the dTD. Leave the last 4 bytes as that is the
               scratch pointer*/
            USB_mem_zero((void *) dTD_ptr,(sizeof(USBHS_EP_TR_STRUCT) - 4));
        #endif

        /* Initialize the dTD */
        dTD_ptr->SCRATCH_PTR->PRIVATE = handle;

        /* Set the Terminate bit */
        EHCI_MEM_WRITE(dTD_ptr->NEXT_TR_ELEM_PTR, VUSB_EP_QUEUE_HEAD_NEXT_TERMINATE);

        /*************************************************************
         FIX ME: For hig-speed and high-bandwidth ISO IN endpoints,
         we must initialize the multiplied field so that Host can issues
         multiple IN transactions on the endpoint. See the DTD data
         structure for MultiIO field.

         S Garg 11/06/2003
         *************************************************************/

        /* Set the reserved field to 0 */
        EHCI_MEM_CLEAR_BITS(dTD_ptr->SIZE_IOC_STS,USBHS_TD_RESERVED_FIELDS);

        /* 4K apart buffer page pointers */

        addr = (uint32_t)(xd_ptr->WSTARTADDRESS + curr_offset);
        EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR0, addr);
        EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR1, (addr + 4096));
        EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR2, (addr + (4096*2)));
        EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR3, (addr + (4096*3)));
        EHCI_MEM_WRITE(dTD_ptr->BUFF_PTR4, (addr + (4096*4)));

        curr_offset += curr_pkt_len;

        /* Fill in the transfer size */
        if (!remaining_len)
        {
            EHCI_MEM_WRITE(dTD_ptr->SIZE_IOC_STS,((curr_pkt_len << USBHS_TD_LENGTH_BIT_POS) | USBHS_TD_IOC | USBHS_TD_STATUS_ACTIVE));
        }
        else
        {
            EHCI_MEM_WRITE(dTD_ptr->SIZE_IOC_STS,((curr_pkt_len << USBHS_TD_LENGTH_BIT_POS) | USBHS_TD_STATUS_ACTIVE));
        }
        #if PSP_HAS_DATA_CACHE
            /* This is efficient only if dTD_ptr is allocated in the cached area */
            if (dTD_ptr < (USBHS_EP_TR_STRUCT_PTR)__UNCACHED_DATA_START ||
                    ((dTD_ptr + sizeof(USBHS_EP_TR_STRUCT)) > (USBHS_EP_TR_STRUCT_PTR)__UNCACHED_DATA_END))
                USB_dcache_flush_mlines((void *)dTD_ptr,sizeof(USBHS_EP_TR_STRUCT));
        #endif
        /* Maintain the first and last device transfer descriptor per
           endpoint and direction */
        if (!usb_dev_ptr->EP_DTD_HEADS[temp])
        {
            usb_dev_ptr->EP_DTD_HEADS[temp] = dTD_ptr;
            /***********************************************
             If list does not have a head, it means that list
             is empty. An empty condition is detected.
             ***********************************************/
            list_empty = TRUE;
        }

        /* Check if the transfer is to be queued at the end or beginning */
        temp_dTD_ptr = usb_dev_ptr->EP_DTD_TAILS[temp];

        /* Remember which XD to use for this dTD */
        dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD = (void *)xd_ptr;

        /* New tail */
        usb_dev_ptr->EP_DTD_TAILS[temp] = dTD_ptr;

        if (temp_dTD_ptr)
        {
            /* Should not do |=. The Terminate bit should be zero */
            EHCI_MEM_WRITE(temp_dTD_ptr->NEXT_TR_ELEM_PTR, (uint32_t)dTD_ptr);
        }

    } while (remaining_len); /* EndWhile */


    /**************************************************************
     In the loop above DTD has already been added to the list
     However endpoint has not been primed yet. If list is not empty
     we need safer ways to add DTD to the
     existing list. Else we just skip to adding DTD to QH safely.
     **************************************************************/
    if(list_empty) /* If List is Empty : case 1*/
    {
        /* No other transfers on the queue */
        /* Step 1 of Executing a Transfer Descriptor documentation */
        EHCI_MEM_WRITE(ep_queue_head_ptr->NEXT_DTD_PTR,(uint32_t)first_dTD_ptr);
        /* Step 2 of Executing a Transfer Descriptor documentation */
        EHCI_MEM_WRITE(ep_queue_head_ptr->SIZE_IOC_INT_STS,0);

        /* Prime the Endpoint */
        /* Step 3 of Executing a Transfer Descriptor documentation */
        EHCI_REG_WRITE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME,bit_pos);
    }
    else /* If list is not empty : case 2*/
    {
        #ifdef TRIP_WIRE
            /*********************************************************
             Hardware v3.2+ require the use of semaphore to ensure that
             QH is safely updated.
             *********************************************************/

            /*********************************************************
             Check the prime bit. If set return USB_OK
             *********************************************************/
            if (EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) &
                bit_pos)
            {
                return USB_OK;
            }

            read_safe = FALSE;
            while(!read_safe)
            {
                /*********************************************************
                 start with setting the semaphores
                 *********************************************************/
                EHCI_REG_SET_BITS(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD,
                    EHCI_CMD_ATDTW_TRIPWIRE_SET);

                /*********************************************************
                 read endpoint status, store for later usage
                 *********************************************************/
                ep_status = EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS);

                /*********************************************************
                 Check the tripwire status
                 *********************************************************/
                if(EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD) &
                    EHCI_CMD_ATDTW_TRIPWIRE_SET)
                {
                    read_safe = TRUE;
                }
            } /*end while loop */

            /*********************************************************
             Clear the semaphore
             *********************************************************/
            EHCI_MEM_CLEAR_BITS(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD,
                EHCI_CMD_SETUP_TRIPWIRE_SET);

            /*********************************************************
             Check endpoint status
             *********************************************************/
            if (!(ep_status & bit_pos)) {
                /* No other transfers on the queue */
                /* Step 1 of Executing a Transfer Descriptor documentation */
                EHCI_MEM_WRITE(ep_queue_head_ptr->NEXT_DTD_PTR,(uint32_t)first_dTD_ptr);
                /* Step 2 of Executing a Transfer Descriptor documentation */
                EHCI_MEM_WRITE(ep_queue_head_ptr->SIZE_IOC_INT_STS,0);

                #if PSP_HAS_DATA_CACHE
                    /* This is efficient only if dTD_ptr is allocated in the cached area */
                    if (ep_queue_head_ptr < (USBHS_EP_QUEUE_HEAD_STRUCT_PTR)__UNCACHED_DATA_START || ((ep_queue_head_ptr + sizeof(USBHS_EP_QUEUE_HEAD_STRUCT)) > (USBHS_EP_QUEUE_HEAD_STRUCT_PTR)__UNCACHED_DATA_END))
                        USB_dcache_flush_mlines((void *)ep_queue_head_ptr, sizeof(USBHS_EP_QUEUE_HEAD_STRUCT));
                #endif

                /* Prime the Endpoint */
                /* Step 3 of Executing a Transfer Descriptor documentation */
                EHCI_REG_WRITE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME,bit_pos);
            }

        #else   /*workaround old method */
            /* Start CR 1015 */
            /* Prime the Endpoint */
            EHCI_REG_WRITE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME, bit_pos);

            if(!(EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos))
            {
               /* old workaround will be compiled */
               while(EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & bit_pos)
               {
                    /* Wait for the ENDPTPRIME to go to zero */
               }

               if(EHCI_REG_READ(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos)
               {
                    /* The endpoint was not not primed so no other transfers on the queue */
                    return USB_OK;
               }
            }
            else
            {
                return USB_OK;
            }

            /* No other transfers on the queue */
            EHCI_MEM_WRITE(ep_queue_head_ptr->NEXT_DTD_PTR,(uint32_t)first_dTD_ptr);
            EHCI_MEM_WRITE(ep_queue_head_ptr->SIZE_IOC_INT_STS,0);

            #if PSP_HAS_DATA_CACHE
                /* This is efficient only if dTD_ptr is allocated in the cached area */
                if (ep_queue_head_ptr < (uint8_t *)__UNCACHED_DATA_START || ((ep_queue_head_ptr + sizeof(USBHS_EP_QUEUE_HEAD_STRUCT)) > (uint8_t *)__UNCACHED_DATA_END))
                    USB_dcache_flush_mlines((void *)ep_queue_head_ptr, sizeof(USBHS_EP_QUEUE_HEAD_STRUCT));
            #endif

            /* Prime the Endpoint */
            EHCI_REG_WRITE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME,bit_pos);
        #endif
    }
    return USB_OK;
   /* End CR 1015 */
} /* EndBody */

I attached the MQX V4.2 ehci driver for your reference.

Wish it helps.


Have a great day,
Mike

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos