General Category > Discussion - Drivers

FT4222: Very high CPU load and gaps between bytes

(1/2) > >>

a4711:
We are using a FT4222 with libFT4222 1.4.4.44 to communicate to an I2C peripheral at 400 KHz. Our communication involves a mix of writes and reads, but mainly we are reading out chunks of 128-256 bytes. We are facing two problems:

* The CPU load caused by libft4222 is extremely high. On a Raspberry Pi Zero this is becoming a bottleneck.
* There a gaps between bytes on all platforms.
Here you can see the gaps:

As you can see, there is no gap between address byte and the first data byte, but every subsequent byte. On a modern x86 processor and Windows the gaps are at least 8µs long, on a Raspberry PI3 and Linux they are 17µs large. One byte takes about 23µs which means we are wasting 40% of our bandwidth. On a FT232H we don't see these kind of gaps and the CPU load is much better.

We are using the FT4222_I2CMaster_Read() function as shown in the example, nothing else.


* How can we avoid having such gaps in the transfer?
* How can we achieve a lower CPU load?

FTDI Community:
Hello,

We have a technical note about USB data transfer efficiency that can help you: https://ftdichip.com/wp-content/uploads/2020/08/TN_103_FTDI_USB_Data_Transfer_EfficiencyFT_000097.pdf.

The main points are:

• Send as much data to the IC from the host application as possible in a single write. This will
maximize the size of the data packets being sent to the device and hence minimize the number of
packets required and time to transfer an amount of data.

• Set the latency timer to a value appropriate for the application. Note that a low latency timer
value may result in many short incoming USB packets rather than a single large packet, thus
diminishing performance.

you should also refer to section 5.3 of the FT4222 datasheet: https://ftdichip.com/wp-content/uploads/2020/07/DS_FT4222H.pdf

This section outlines the I2C bus interface.

also which example are you referring to? the one shown in the LibFT4222 user guide or the one included in the library download?

Best Regards

FTDI Community

a4711:
Thank You for your reply.


--- Quote ---Set the latency timer to a value appropriate for the application. Note that a low latency timer
value may result in many short incoming USB packets rather than a single large packet, thus
diminishing performance.

--- End quote ---

The latency timer is set to 16 ms (the default). Changing the value has only a very small effect on the gaps and and absolutely no effect on the CPU load on Linux.


--- Quote ---you should also refer to section 5.3 of the FT4222 datasheet: https://ftdichip.com/wp-content/uploads/2020/07/DS_FT4222H.pdf
This section outlines the I2C bus interface.

--- End quote ---

I do not understand how this relates to the problem I am reporting here.


--- Quote ---also which example are you referring to? the one shown in the LibFT4222 user guide or the one included in the library download?

--- End quote ---

From the library download. To illustrate my problem I modified the contained I2C example like this:


--- Code: ---static int exercise4222(DWORD locationId)
{
    int                  success = 0;
    FT_STATUS            ftStatus;
    FT_HANDLE            ftHandle = (FT_HANDLE)NULL;
    FT4222_STATUS        ft4222Status;
    FT4222_Version       ft4222Version;
    const uint16         slaveAddr = 0x58;
    uint16               bytesToRead ;
    uint16               bytesRead = 0;
    uint16               bytesToWrite;
    uint16               bytesWritten = 0;
    char                *writeBuffer;
    uint8_t              pageBuffer[BYTES_PER_PAGE + 1];
    int                  page;


    ftStatus = FT_OpenEx((PVOID)(uintptr_t)locationId,
                         FT_OPEN_BY_LOCATION,
                         &ftHandle);
    if (ftStatus != FT_OK)
    {
        printf("FT_OpenEx failed (error %d)\n",
               (int)ftStatus);
        goto exit;
    }

    ft4222Status = FT4222_GetVersion(ftHandle,
                                     &ft4222Version);
    if (FT4222_OK != ft4222Status)
    {
        printf("FT4222_GetVersion failed (error %d)\n",
               (int)ft4222Status);
        goto exit;
    }

    printf("Chip version: %08X, LibFT4222 version: %08X\n",
           (unsigned int)ft4222Version.chipVersion,
           (unsigned int)ft4222Version.dllVersion);

    // Configure the FT4222 as an I2C Master
    ft4222Status = FT4222_I2CMaster_Init(ftHandle, 400);
    if (FT4222_OK != ft4222Status)
    {
        printf("FT4222_I2CMaster_Init failed (error %d)!\n",
               ft4222Status);
        goto exit;
    }

    // Reset the I2CM registers to a known state.
    ft4222Status = FT4222_I2CMaster_Reset(ftHandle);
    if (FT4222_OK != ft4222Status)
    {
        printf("FT4222_I2CMaster_Reset failed (error %d)!\n",
               ft4222Status);
        goto exit;
    }

    for (;;) {

        // Sequential read from slave EEPROM's current word address.
        uint8_t content[512];
        bytesToRead = sizeof(content);
        ft4222Status = FT4222_I2CMaster_Read(ftHandle,
                                             slaveAddr,
                                             content,
                                             bytesToRead,
                                             &bytesRead);
        if (FT4222_OK != ft4222Status)
        {
            printf("FT4222_I2CMaster_Read failed (error %d)\n",
                   (int)ft4222Status);
            goto exit;
        }

        if (bytesRead != bytesToRead)
        {
            printf("FT4222_I2CMaster_Read read %u of %u bytes.\n",
                   bytesRead,
                   bytesToRead);
            goto exit;
        }

        usleep(100000);
    }

    success = 1;

exit:
    if (ftHandle != (FT_HANDLE)NULL)
    {
        (void)FT4222_UnInitialize(ftHandle);
        (void)FT_Close(ftHandle);
    }

    return success;
}

--- End code ---

It reads out 512 byte chunks in a loop with 100ms sleep in between transfers.


* There are still gaps between 8µs and 17µs between each byte
* The CPU load on a RPi3b is >50% which is unexpectedly high. On a RPi0 it is even higher due to the slower CPU.
I found the following note in the errata (3.1.2):

--- Quote ---The FT4222H doesn’t support the latency timer feature and causes the USB scheduler to be busy and
uses too much CPU resource.

--- End quote ---

This looks exactly like what I am seeing, but I have chip revision D.

Can you reproduce this issue? How to solve it?

FTDI Community:
Hello,

As we are working from home at the moment, I do not have the required hardware to see if I can recreate your issue. However, I have sent your code to my colleague who does have access to the required hardware, so they can test it. I'll let you know what they find.

In the mean time, have you tried running the example as is? Just to see if the hardware is functioning properly?

Best Regards

FTDI Community

a4711:

--- Quote ---In the mean time, have you tried running the example as is? Just to see if the hardware is functioning properly?

--- End quote ---

My original code is much more complex. Therefore I thought I would strip it down and fit it into the "official" example.
 
Just copy-paste this function into the example file i2cm.c and run it on Linux and include <unistd.h>. On Windows you will have to replace usleep by Sleep. It works on my end. If you could run it on a Raspberry Pi you could see how high the CPU load is. But the gaps are visible on any platform I have checked.

Navigation

[0] Message Index

[#] Next page

Go to full version