FX3 : UART to USB minimal DMA size workaround

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

cross mob
ChRO_4339966
Level 3
Level 3

Hello,

On my FX3 project I created an EP discussing with a FPGA through UART link.

UART is configured on both sides to work with Byte mode DMA (dmaConfig.dmaMode = CY_U3P_DMA_MODE_BYTE)

All this works like a charm, except that I live with a limitation that I would like to fix : at reception (FPGA->FX3), DMA requires a minimal of 16 bytes to operate, which seems to be a limitation of FX3 (UART?) DMA engine.

This means if my FPGA sends less than 16 bytes of data they will not be forwarded to my USB endpoint.

I would like to keep it as simple as possible to avoid having FX3 firmware sniffing at UART events and processing packets. Is there a simple solution for that ?

Hereunder is my UART configuration :

CyU3PReturnStatus_t ConfigureUartDma(void) {

    CyU3PReturnStatus_t Status = CY_U3P_SUCCESS;

    CyU3PDmaChannelConfig_t dmaConfig;

    // Create a AUTO channel for the USB to UART transfer

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

    dmaConfig.size           = 128; // Maximal number of bytes allowed per DMA buffer.

                                    // A larger message would stuck the EP

    dmaConfig.count          = 32;  // Store up to 32 words in DMA buffers

    dmaConfig.prodSckId      = FPGA_CONFIG_ENDPOINT_SOCKET;

    dmaConfig.consSckId      = UART_CONSUMER_SOCKET;

    dmaConfig.dmaMode        = CY_U3P_DMA_MODE_BYTE;

    Status = CyU3PDmaChannelCreate(&Usb2Uart_Handle, CY_U3P_DMA_TYPE_AUTO, &dmaConfig);

    // Start the DMA Channel with transfer size to Infinite

    Status = CyU3PDmaChannelSetXfer(&Usb2Uart_Handle, 0);

    // Create a AUTO channel for the UART to USB transfer

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

    dmaConfig.size          = 16;   // Minimal value allowed by DMA.

                                    // Any UART message longer than this will be split in 16 bytes "packets"

    dmaConfig.count         = 32;   // Up to 32 words can be stored in DMA buffers

    dmaConfig.prodSckId     = UART_PRODUCER_SOCKET;

    dmaConfig.consSckId     = FPGA_STATUS_ENDPOINT_SOCKET;

    dmaConfig.dmaMode       = CY_U3P_DMA_MODE_BYTE;

    Status = CyU3PDmaChannelCreate(&Uart2Usb_Handle, CY_U3P_DMA_TYPE_AUTO_SIGNAL, &dmaConfig);

    // Start the DMA Channel with transfer size to Infinite

    Status = CyU3PDmaChannelSetXfer(&Uart2Usb_Handle, 0);

    return Status;

}

Thanks in advance

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.

Hello Christophe,

I didn't get the question. Can you restate it?

From the previous post what i understood was that you want to commit the partial buffer.

As per your previous response, DMA channel from UART to USB is AUTO_SIGNAL. Still you can use CyU3PDmaChannelSetWrapUp API in the for{}. But you need to register a call back and handle the PROD_EVENT. In the PROD_EVENT the partial buffer can be committed. (Refer to the modified UsbUart example with DMA_TYPE_ AUTO_SIGNAL channel between uart and usb). You can check the working of the  attached firmware and make changes to your firmware accordingly.

If you wan to use DMA Manual channel you need to commit the data manually to the consumer socket. This is done when the buffer is full or PROD_EVENT occurs and in DMA channel you commit that buffer.

But when the buffer is partially filled there will not be any PROD_EVENTS. So when you call  CyU3PDmaChannelSetWrapUp there will be PROD_EVENT even when the buffer is partially filled. In the callback, this buffer will then be committed to the consumer socket.

We use this API in the for{} (as per usbuart example) to continuously check for partial buffer and commit it.

Regards,

Rashi

Regards,
Rashi

View solution in original post

0 Likes
17 Replies
Rashi_Vatsa
Moderator
Moderator
Moderator
5 likes given 500 solutions authored 1000 replies posted

Hello Christophe,

Please confirm if you referred to the USBUART example in the SDK.

In the USBUARTAppThread_Entry function CyU3PDmaChannelSetWrapUp (&glChHandleUarttoUsb); is called in the for{} loop to  use the channel wrap-up feature to send any partial buffer to  the USB host.

So you can use this API in you firmware as per your  application.

Regards,

Rashi

Regards,
Rashi
0 Likes

Hello Rashi,

Thanks for feedback.

No I am not using USBUART example from SDK. I did built my own application where UART is configured to use (AUTO ?) DMAs.

This is done in 2 steps :

- configure UART (see ConfigureUart() below)

- configure DMA (see code in initial thread message)

Doing so I don't have to have a thread running an infinite loop, but with this drawback I try to fix.

My remaining question is : "Is there a fix for that I do I have to go through Manual DMA + Callback + CyU3PDmaChannelSetWrapUp + infinite loop ? "

CyU3PReturnStatus_t ConfigureUart(void)

{

    CyU3PReturnStatus_t Status = CY_U3P_SUCCESS;

    CyU3PUartConfig_t uartConfig;

    // Start the UART driver

    Status = CyU3PUartInit();

    // Configure my UART link, as fast as possible for interfacing with FPGA

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

    uartConfig.baudRate = CY_U3P_UART_BAUDRATE_4M608K;

    uartConfig.stopBit  = CY_U3P_UART_ONE_STOP_BIT;

    uartConfig.txEnable = CyTrue;

    uartConfig.rxEnable = CyTrue;

    uartConfig.isDma    = CyTrue;

    /* Set the UART configuration; no callback  */

    Status = CyU3PUartSetConfig(&uartConfig, NULL);

    Status = CyU3PUartTxSetBlockXfer(0xFFFFFFFF);                  // Send as much data as I need to

    Status = CyU3PUartRxSetBlockXfer(0xFFFFFFFF);

    return Status;

}

I wrote this code a while back so excuse me if DMA modes of operations are not 100% clear in my mind.

Regards

Christophe

0 Likes
lock attach
Attachments are accessible only for community members.

Hello Christophe,

I didn't get the question. Can you restate it?

From the previous post what i understood was that you want to commit the partial buffer.

As per your previous response, DMA channel from UART to USB is AUTO_SIGNAL. Still you can use CyU3PDmaChannelSetWrapUp API in the for{}. But you need to register a call back and handle the PROD_EVENT. In the PROD_EVENT the partial buffer can be committed. (Refer to the modified UsbUart example with DMA_TYPE_ AUTO_SIGNAL channel between uart and usb). You can check the working of the  attached firmware and make changes to your firmware accordingly.

If you wan to use DMA Manual channel you need to commit the data manually to the consumer socket. This is done when the buffer is full or PROD_EVENT occurs and in DMA channel you commit that buffer.

But when the buffer is partially filled there will not be any PROD_EVENTS. So when you call  CyU3PDmaChannelSetWrapUp there will be PROD_EVENT even when the buffer is partially filled. In the callback, this buffer will then be committed to the consumer socket.

We use this API in the for{} (as per usbuart example) to continuously check for partial buffer and commit it.

Regards,

Rashi

Regards,
Rashi
0 Likes

Hi Rashi,

You are right, I want to commit partially filled buffer.

Then from your example, it looks like this is correct approach.

I'll check if this works in my application and let you know.

Thanks a lot !

Christophe

0 Likes
lock attach
Attachments are accessible only for community members.

Hello Rashi,

I applied proposed modifications to my code.

My UART to USB pipe is now failing.

I instrumented this section of code and I get an error within callback function after initialization :

UartRxDMACallback call 
CyU3PDmaChannelCommitBuffer() failed, 71 = CY_U3P_ERROR_INVALID_SEQUENCE ?

Documentation mentions this happens when DMA is not configured. I checked how device is configured and I believe DMA should be configured.

C file handling UART function is attached for more details.

StartUart() function contained there is called by main program, which also have an infinite loop in a thread calling FlushUARTRxDMA() function every 100 ms.

Thanks for support

Christophe

0 Likes

Hello Christophe,

I went through the source code of the CyU3PDmaChannelCommitBuffer() API.

The error 71 (0x47) is when the socket is not in suspend mode.

Please confirm that this FlushUARTRxDMA function is not called before the channel is created i.e. before calling CyU3PDmaChannelCreate and CyU3PDmaChannelSetXfer

Regards,

Rashi

Regards,
Rashi
0 Likes

Rashi,

Here is code portion calling the function (within a specific thread) :

    // Now run forever

    while (1)

    {

        CyU3PThreadSleep(100);

        // Check for UART data availabity (partially full DMA buffer)

        if (glIsUartActive) {

          Status = FlushUARTRxDMA();

          CheckStatus("FlushUARTRxDMA()", Status);

        }

    }

glIsUartActive is set to true within StartUart() function after UART and associated DMAs are configured:

CyU3PReturnStatus_t StartUart(void)

{

    CyU3PReturnStatus_t Status = CY_U3P_SUCCESS;

    // Configure UART for interfacing with FPGA

    Status = ConfigureUart();

    CheckStatus("ConfigureUart()", Status);

    // Configure USB endpoints linked to UART

    Status = ConfigureUartEP();

    CheckStatus("ConfigureUartEP()", Status);

    // Create DMAs between EP and UART

    Status = ConfigureUartDma();

    CheckStatus("ConfigureUartDma()", Status);

    // Mark USB <-> UART communication as active

    glIsUartActive = CyTrue;

    return Status;

}

So function should be called AFTER dma is configure within ConfigureUartDma() function.

Christophe

0 Likes

Hello Christophe,

Can you share your complete firmware as I am not able to reproduce the problem at my end.

The source code of CyU3PDmaChannelCommitBuffer() API checks  the state of socket (suspend or not)

Also, share the debug logs.

Regards,

Rashi

Regards,
Rashi
0 Likes

Hello Christophe,

From the firmware i found that you are using one Uart consumer socket for debugging and also you are creating a channel from USB to UART.

Uart block has only two sockets i.e. one consumer socket ans one producer socket. So you can use the consumer socket for one purpose only either it can be for USB > UART channel or for debugging purpose.

You can see in the UsbtoUart example doesn't have provision for debug prints due to this reason.

So can you please comment out the API's used for debugging

You can check whether the API is failing or not by calling the CYU3PDeviceRest(CyFalse) API in the condition checking for error

Regards,

Rashi

Regards,
Rashi
0 Likes

Hello Rashi,

I think there is a lack of understanding here.

FX3 firmware operates as a bridge between UART module and EP2 (IN/OUT).

My Debug console is targeted towards EP4.

So UART and associated EP are not used to carry debug messages.

Also, note all this was working OK (except 16 bytes limitation we are discussing) before I tried using CyU3PDmaChannelSetWrapUp.

Regards

Christophe

0 Likes

Hello Christophe,

Please refer to this KBA Invalid Sequence Error in Multi-Channel Commit Buffer - KBA218830 . This is for multichannel commit buffer failure but similar conditions are checked in the source code of CyU3PDmaChannelCommitBuffer API. This KBA is for UVC applications but the cause of this error in your application is due to slow host. ( IN tokens)

As per this KBA CyU3PDmaChannelCommitBuffer() and CyU3PDmaChannelSetWrapUp() Returns INVALID_SEQUENCE Errors When Us...  you have set the clock to 403.2 MHz

When do you get this failure after some  transfers or as soon as you program the FX3 with the firmware. Can you share the debug prints when this error occurs. It would also help me to understand the flow of the code.

Regards,

Rashi

Regards,
Rashi
0 Likes

Hello Rashi,

Sorry for late reply, but I had really little time to work on this till yesterday.

I looked at KBAs you mentionned. 1st one is the kind of issue I was suspecting.

Actually, at initialisation every "important" step is marked for debug (DebugPrint) and then pushed to a specific EP.

What happens is that it looks like this buffer tends to become full at initialisation and blocks execution of the code.

I then need to poll my EP to continue execution.

This is really close to KBA description.

For your information, here is the kind of log I get when all messages are printed :

Initialize USB Debug Console Successful

USBThread completed init. Monitoring USB events...

CyU3PUartInit Successful

CyU3PUartSetConfig Successful

CyU3PUartTxSetBlockXfer Successful

CyU3PUartRxSetBlockXfer Successful

ConfigureUart() Successful

Device ready, DebugThread waking up

Setup FPGA_CONFIG_ENDPOINT Successful

Flush FPGA_CONFIG_ENDPOINT Successful

Setup FPGA_STATUS_ENDPOINT Successful

Flush FPGA_STATUS_ENDPOINT Successful

ConfigureUartEP() Successful

Usb2Uart DmaChannelCreate Successful

Usb2Uart DmaChannelStart Successful

Uart2Usb DmaChannelCreate Successful

Uart2Usb DmaChannelStart Successful

ConfigureUartDma() Successful

StartUart() Successful

UartRxDMACallback call

CyU3PDmaChannelCommitBuffer() failed, 71 = CY_U3P_ERROR_INVALID_SEQUENCE

?

CyU3PDmaChannelSetWrapUp() Successful

FlushUARTRxDMA() Successful

I then tried a 1st fix that consists in suspending all DebugPrint until UART DMA callback is called.

I received same error log on Debug EP at CommitBuffer call:

USBThread completed init. Monitoring USB events...

UartRxDMACallback call

CyU3PDmaChannelCommitBuffer() failed, 71 = CY_U3P_ERROR_INVALID_SEQUENCE

?

CyU3PDmaChannelSetWrapUp() Successful

FlushUARTRxDMA() Successful

Device ready, DebugThread waking up?

KBA218830 solution proposal seems to bring other problem, since I cannot afford stopping GPIF state machine (will be used at the same time to transport data from FPGA to my host application). Or maybe could I apply this to UART and then acting only on UART config ?

It is still possible in my code modification to add this CyU3PDmaChannelCommitBuffer/CyU3PDmaChannelSetWrapUp there is something that went wrong...

Maybe UartRxDMACallback definition is incorrect ? Are arguments passed from OS to callback function ? What is chHandle parameters that is unused there ?

void UartRxDMACallback(

        CyU3PDmaChannel   *chHandle, /* Handle to the DMA channel. */

        CyU3PDmaCbType_t   type,     /* Callback type.             */

        CyU3PDmaCBInput_t *input)    /* Callback status.           */

{

    CyU3PReturnStatus_t Status = CY_U3P_SUCCESS;

    if (type == CY_U3P_DMA_CB_PROD_EVENT)

    {

        glIsDebugActive = CyTrue;

        DebugPrint(4, "\r\nUartRxDMACallback call");

        Status = CyU3PDmaChannelCommitBuffer (&Uart2Usb_Handle, input->buffer_p.count, 0);

        CheckStatus("CyU3PDmaChannelCommitBuffer()", Status);

        glPktsPending++;

    }

}

Regards

Christophe

0 Likes

Hello Christophe,

Can you comment out CyU3PDmaChannelCommitBuffer API in the PROD event.

Don't call any API inside the PROD event. For AUTO and AUTO_SIGNAL channel you don't need to call CyU3PDmaChannelCommitBuffer

The buffer will be committed without calling that API

Regards,

Rashi

Regards,
Rashi
0 Likes

Rashi,

This did not solve the issue. Actually, removing call to this function in DMA callback makes callback useless.

Nevertheless, I noticed some difference :

- without call to CyU3PDmaChannelCommitBuffer in DMA callback, there is no USB frame sent on my EP

- with call to CyU3PDmaChannelCommitBuffer in DMA callback, I get a USB frame, but it is empty.

Christophe

0 Likes

Hello Christophe,

Yes, i understand that there is no use of DMA callback , it is because the DMA channel is AUTO_SIGNAL / AUTO. The producer sockets directly signals the consumer socket that buffer is ready.CyU3PDmaChannelCommitBuffer is needed when it is Manual channel.

I tried commenting out the API in the DMA callback in the firmware i shared to you before. I an able to see the data i the teraterm which is sent from UART.

Try sending the data from UART and probe the USB lines. Please share the traces. You can use Wireshark for that.

Regards,

Rashi

Regards,
Rashi
0 Likes
lock attach
Attachments are accessible only for community members.

Rashi,

I commented out call to CommitBuffer in callback function.

I kept the message indicating the callback function is called in my code

Here are logs I can see at initialisation :

Initialize USB Debug Console Successful

USBThread completed init. Monitoring USB events...

CyU3PUartInit Successful

CyU3PUartSetConfig Successful

CyU3PUartTxSetBlockXfer Successful

ConfigureUart() Successful

Setup FPGA_CONFIG_ENDPOINT Successful

Device ready, DebugThread waking up

Flush FPGA_CONFIG_ENDPOINT Successful

Setup FPGA_STATUS_ENDPOINT Successful

Flush FPGA_STATUS_ENDPOINT Successful

ConfigureUartEP() Successful

Usb2Uart DmaChannelCreate Successful

Usb2Uart DmaChannelStart Successful

Uart2Usb DmaChannelCreate Successful

Uart2Usb DmaChannelStart Successful

ConfigureUartDma() Successful

StartUart() Successful

UartRxDMACallback call

UartRxDMACallback call

UartRxDMACallback call

UartRxDMACallback call

UartRxDMACallback call

UartRxDMACallback call

UartRxDMACallback call

UartRxDMACallback call

0,

Timeout

You can see callback is called 8 times without any data being received on UART.

You can see in PCAP at 6s taht I end 6 bytes of data on UART (and I expect 8 bytes coming back from UART. I could saw bytes flowing on UART using oscilloscope).

I then try to read EP2 at t = 17s and I can see a bulk read with no data

Then I can see when polling debug frames that a new call to Callback is done (I actually noticed call to callback is done at each bulk in request)

Other bulk messages in PCAP are debug information sent by FX3 FW.

Regards

Christophe

0 Likes

Hello Christophe,

As the CyU3PDmaChannelSetWrapUp API is called in the for loop, it would start committing  the buffer even when there is no data in the buffer(buffer.count = 0). When at 17s, there is BULK IN token from the host, the first buffer with no data will be consumed by the consumer socket. The data you sent would be ther in the buffer but in a queue after multiple IN tokens you may get the data you sent from UART

You need to synchronize this as per your application. If you want, that the data from Uart to USB  should be should be transferred only  after the data transfer from USB (OUT) to Uart is completed. Then you can call CyU3PDmaChannelSetWrapUp  only when USB (OUT) to Uart  transfer is completed.

In the default UsbUart example, CyU3PDmaChannelSetWrapUp  API is called every 50 ms by setting CyU3PThreadSleep (50)

Regards,

Rashi

Regards,
Rashi
0 Likes