FX3: GPIF DMA Callback GetBuffer() returning 0 count buffers

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
ThCo_3071396
Level 1
Level 1

Hi,

I'm running a CYUSB3014 in a USB camera application, using code based on the app note AN75779.

The FX3 is configured with a manual many-one DMA which takes 16368-byte buffers from the GPIF block, appends a 16-byte header to them (to make a full 16kb) and then commits them to the USB buffers. To do this it uses the GPIF DMA Callback.

The callback code is as follows:

/*******************************************************************************

* Name:   void Streaming_GpifToUsbDmaCallback (    CyU3PDmaChannel   *chHandle,

*                                                   CyU3PDmaCbType_t   type,

*                                                   CyU3PDmaCBInput_t *input )

* Inputs: CyU3PDmaChannel   *chHandle - pointer to the DMA channel

*            CyU3PDmaCbType_t   type - reason for the callback

*            CyU3PDmaCBInput_t *input - pointer to DMA data

* Return: None.

* Notes:  Callback for the GPIF to USB DMA.

******************************************************************************/

void Streaming_GpifToUsbDmaCallback (    CyU3PDmaMultiChannel   *chHandle,

                                         CyU3PDmaCbType_t   type,

                                         CyU3PDmaCBInput_t *input )

{

    CyU3PDmaBuffer_t    dmaBuffer;

    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;

    /* The DMA has encountered an error */

    if (type == CY_U3P_DMA_CB_ERROR)

    {

        CyU3PDebugPrint (4, "\nHardware error in DMA\n");

    }

    /* Producer event notification - packet is ready to send */

    if (type == CY_U3P_DMA_CB_PROD_EVENT && stream_state == STREAMING_ON)

    {

        status = CyU3PDmaMultiChannelGetBuffer (chHandle, &dmaBuffer, CYU3P_NO_WAIT);

        if(status == CY_U3P_SUCCESS)

        {

            /* Don't know why this occurs... but it does! */

            if(dmaBuffer.count == 0)

            {

                CyU3PDmaMultiChannelDiscardBuffer(chHandle);

                return;

            }

            // GENERATE HEADER (excluded for clarity)

            AddHeader(dmaBuffer.buffer - sizeof(header_t), header);

            // Commit the full 16kb buffer to maximise USB 3.0 burst usage

            status = CyU3PDmaMultiChannelCommitBuffer (chHandle, dmaBuffer.size, dmaBuffer.status);

            if(status != CY_U3P_SUCCESS)

            {

                //Turn off the sensor and stop streaming

            }

            // Only occurs on an end of frame block

            if(dmaBuffer.count != DMA_BUF_FULL)

            {

                //Collect some stats

            }

        }

        else

        {

            CyU3PDebugPrint (4, "\nError in getting buffer\n");

        }

    }

}

The key lines are those between 25 and 43, everything after that is just tallying up some stats about frame numbers and some error checking.

If I remove lines 31 to 36 the streaming halts, but with lines 31 to 36 included I start to see missing data at the PC end, as if some FULL buffers have been discarded. To test my theory I set some breakpoints in this function and examined what was going on:

Here you can see a successful transaction, with the breakpoint triggered on line 40:

success.png

The CyU3PDmaCBInput_t input points to a buffer of length 16368 (as expected) and after the CyU3PDmaMultiChannelGetBuffer function call the dmaBuffer structure also points to a buffer at a different memory location of 16368-bytes. From this I assume the input buffer has been copied to a new location, which is pointed to by the dmaBuffer struct, which will then allow it to be successfully commited to the USB buffers for transfer to the PC? I'm not 100% sure on this theory of operations however, as you can see that the buffer pointer points to 2 different characters in each case, which makes me wonder which of these 2 buffers is the "actual" data. Maybe one of these buffers is the previous buffer to be committed in the streaming process? If anyone could shed light on this that'd be wonderful.

The following is an unsuccessful transaction, triggered by a breakpoint at line 34:

fail.png

Here you can see the CyU3PDmaCBInput_t input still points to a buffer of length 16368 (as expected), but after the CyU3PDmaMultiChannelGetBuffer function call the dmaBuffer structure thinks it is of length 0, whilst still pointing to a buffer at a different memory location. This could be the buffer from the previous transaction (as hypothesised above), or it could be legitimate data and the "count" is just wrong? But either way I'm confused as to how the CyU3PDmaMultiChannelGetBuffer
function returns CY_U3P_SUCCESS from a get buffer call which returned no buffer.

For anyone still playing along, here is my DMA setup:

    /* Create a DMA channel for the GPIF to USB transfer. */

    CyU3PMemSet ((uint8_t *)&dmaCfg, 0, sizeof (dmaCfg));

    dmaCfg.size  = CY_FX_DMA_BUF_SIZE;

    dmaCfg.count = CY_FX_DMA_BUF_COUNT;

    dmaCfg.validSckCount = 2;

    dmaCfg.prodSckId [0]  = (CyU3PDmaSocketId_t)CY_U3P_PIB_SOCKET_0;

    dmaCfg.prodSckId [1]  = (CyU3PDmaSocketId_t)CY_U3P_PIB_SOCKET_1;

    dmaCfg.consSckId [0]  = (CyU3PDmaSocketId_t)(CY_FX_EP_CONSUMER_SOCKET | CY_FX_EP_BULK_CONSUMER_SOCKET);

    dmaCfg.dmaMode = CY_U3P_DMA_MODE_BYTE;

    dmaCfg.prodHeader = 16;

    dmaCfg.prodFooter = 0;

    dmaCfg.consHeader = 0;

    /* Register a callback for a producer event, and a consumer event. */

    dmaCfg.notification = CY_U3P_DMA_CB_PROD_EVENT | CY_U3P_DMA_CB_CONS_EVENT | CY_U3P_DMA_CB_ERROR;

    /* Attach the callback function */

    dmaCfg.cb = (CyU3PDmaMultiCallback_t)Streaming_GpifToUsbDmaCallback;

    apiRetStatus = CyU3PDmaMultiChannelCreate (&DMA_consumer_channel_handle, CY_U3P_DMA_TYPE_MANUAL_MANY_TO_ONE, &dmaCfg);

    if (apiRetStatus != CY_U3P_SUCCESS)

    {

        CyU3PDebugPrint (4, "CyU3PDmaMultiChannelCreate failed, Error code = %d\n", apiRetStatus);

        App_ErrorHandler(apiRetStatus);

    }

Any thoughts would be greatly appreciated!

0 Likes
3 Replies
JayakrishnaT_76
Moderator
Moderator
Moderator
First question asked 1000 replies posted 750 replies posted

Hello,

First of all, your hypothesis on the difference seen in the buffer pointers could be true. Please refer to the standard AN75779 project. Here, inside the PROD_EVENT, the API CyU3PDmaMultiChannelGetBuffer() is called at the beginning. But, there is a possibility that CyU3PDmaMultiChannelGetBuffer() will return CY_U3P_ERROR_INVALID_SEQUENCE here. In such a case, do nothing. We make up the missed produce event by making repeated commit actions in the subsequent produce event callbacks. This is not implemented in your code. Please refer to the following snapshot having the implementation in standard AN75779 and please change your code accordingly:

pastedImage_0.png

In addition to this, it is not recommended to add Debug prints in the callback functions. So, please remove lines 55 to 58 from your DMA callback function's code shared in your query.

Also, the CyU3PDmaMultiChannelGetBuffer function can return CY_U3P_SUCCESS if a ZLP is received. From the snapshot of the unsuccessful transaction (breakpoint at line 34), the buffer status is 10 when the count is 0. A buffer status of 10 is used to indicate that the buffer is occupied (can be 0 length also) and that it is the last buffer of a transfer.

Please change the code for DMA callback function as suggested above. You can discard the buffers having count equal to 0. Please let me know the results after these modifications.

Best Regards,

Jayakrishna

Best Regards,
Jayakrishna
0 Likes

Hi Jayakrishna,

Thanks for your info.

I modified the callback as you suggested:

/*******************************************************************************

* Name:   void Streaming_GpifToUsbDmaCallback (    CyU3PDmaChannel   *chHandle,

*                                                   CyU3PDmaCbType_t   type,

*                                                   CyU3PDmaCBInput_t *input )

* Inputs: CyU3PDmaChannel   *chHandle - pointer to the DMA channel

*            CyU3PDmaCbType_t   type - reason for the callback

*            CyU3PDmaCBInput_t *input - pointer to DMA data

* Return: None.

* Notes:  Callback for the GPIF to USB DMA.

******************************************************************************/

void Streaming_GpifToUsbDmaCallback (    CyU3PDmaMultiChannel   *chHandle,

                                         CyU3PDmaCbType_t   type,

                                         CyU3PDmaCBInput_t *input )

{

    CyU3PDmaBuffer_t    dmaBuffer;

    CyU3PReturnStatus_t status = CY_U3P_SUCCESS;

    /* The DMA has encountered an error */

    if (type == CY_U3P_DMA_CB_ERROR)

    {

        CyU3PDebugPrint (4, "\nHardware error in DMA\n");

    }

    /* Producer event notification - packet is ready to send */

    if (type == CY_U3P_DMA_CB_PROD_EVENT && stream_state == STREAMING_ON)

    {

        status = CyU3PDmaMultiChannelGetBuffer (chHandle, &dmaBuffer, CYU3P_NO_WAIT);

        while(status == CY_U3P_SUCCESS)

        {

            /* Don't know why this occurs... but it does! */

            if(dmaBuffer.count == 0)

            {

                CyU3PDmaMultiChannelDiscardBuffer(chHandle);

                return;

            }

            //GENERATE HEADER (excluded for clarity)

            AddHeader(dmaBuffer.buffer - sizeof(header_t), header);

            // Commit the full 16kb buffer to maximise USB 3.0 burst usage

            status = CyU3PDmaMultiChannelCommitBuffer (chHandle, dmaBuffer.size, dmaBuffer.status);

            if(status != CY_U3P_SUCCESS)

            {

                //Turn off the sensor and stop streaming

            }

            // Only occurs on an end of frame block

            if(dmaBuffer.count != DMA_BUF_FULL)

            {

                //Collect some stats

            }

            status = CyU3PDmaMultiChannelGetBuffer (chHandle, &dmaBuffer, CYU3P_NO_WAIT);

            if(status == CY_U3P_SUCCESS)

                extra_commit_count++;

        }

    }

}

Unfortunately this seems to stop the USB from ennumerating at all. It seems that once the callback fires it gets stuck in a loop where every CyU3PDmaMultiChannelGetBuffer call returns CY_U3P_SUCCESS.

It almost seems like the callback is too slow to keep up with the data rate? But that seems improbable given the relatively trivial tasks inside the callback.

0 Likes

Hello,

Please find my comments below:

1. Inside the main () function, ensure that the 2nd and 3rd parameter of the API CyU3PDeviceCacheControl() are set to CyFalse as shown below:

apiRetStatus = CyU3PDeviceCacheControl (CyTrue, CyFalse, CyFalse);

2. Do not use any breakpoints while testing.

3. Please confirm that the data transfer takes place only after enumeration of the device. This is because in your previous comment, you mentioned that the enumeration does not happen with this modification on the code.

4. Please let me know when is the variable stream_state set to STREAMING_ON.

5. After the API CyU3PDmaMultiChannelDiscardBuffer() is called, please do not use return. Instead, add the remaining code inside the else for the following if statement:

if(dmaBuffer.count == 0)

6. Please let me know if you can add some information along with the header which can be used to understand the number of times the while loop was executed after a PROD_EVENT was triggered. We can observe this by taking a USB trace using Wireshark.

7. Is your state machine same as AN75779 state machine or is it a custom state machine? Can you please share the state machine if it is a custom state machine?

8. Please confirm that you are using FX3 SDK 1.3.4. Also, ensure that the FX3SDKVERSION in Project Properties-> C/C++ Build -> Build Variables is set to 1_3_4.

Best Regards,

Jayakrishna

Best Regards,
Jayakrishna
0 Likes