USB – Troubleshooting FT232H FIFO Slow Data Transfer

data transferfifoft232ftdiusb

So, I have an FT232H chip, specifically this breakout.

I'm trying to read data as fast as possible from it, so I've set it to synchronous 245 FIFO mode, which the datasheet says should yield up to 40 Mbytes/s. I've wired AC3 = WR# to ground, on the assumption that constantly signaling the intent to write won't slow it down, assuming it works at all. Which it does – I have a raspberry pico counting down on 8 pins at a rate of 60Mhz, confirmed by an oscilloscope, and the data I get from the FTDI chip (which has a data clock of 60MHz) DOES match up (not perfectly, since the clocks aren't actually synchronized) – I get a stream of bytes counting down one by one, and changing frequencies or count step sizes yields the expected behavior, skipping or duplicating entries, etc. The end of one read lines up with the start of the next, too. BUT, measuring the total data read vs time it took to read, I'm only actually reading at a rate of about 9 Mbyte/s. (In this setup, MHz = Mbyte/s.) I infer that it periodically skips large segments of data, transmitting contiguous chunks of about 1/7th the total data. I'd be fine with SOME dropout, sure, since 60 Mbyte/s is higher than what the spec claims, but 9 Mbyte/s is a lot smaller, and less than my application calls for.

Here's (part of) my code, using the D2XX api, adapted from the LargeRead example included in the linux x64 driver download. I also have a Rust project, which is my main code, but the C code seems better for baseline sanity checks, and for sharing. I'm using Ubuntu 20.04.

#include <stdio.h>
#include <stdlib.h>
#include "../ftd2xx.h"

#define BUF_SIZE 0x10000*3 // If I do *4 or higher, the read hangs

int main(int argc, char *argv[]) {
    char * pcBufRead;
    DWORD dwBytesRead;
    FT_HANDLE ftHandle;
    FT_STATUS ftStatus;
    int iport;
    
    if(argc > 1) {
        sscanf(argv[1], "%d", &iport);
    }
    else {
        iport = 0;
    }
            
    ftStatus = FT_Open(iport, &ftHandle);
    if(ftStatus != FT_OK) {
        printf("FT_Open(%d) failed\n", iport);
        return 1;
    }

    FT_ResetDevice(ftHandle);
    FT_Purge(ftHandle, FT_PURGE_RX || FT_PURGE_TX);
    FT_SetChars(ftHandle, 0, 0, 0, 0);
    FT_SetFlowControl(ftHandle, FT_FLOW_RTS_CTS, 0, 0); //CHECK ???
    FT_SetTimeouts(ftHandle, 0, 0);             // infinite timeouts    
    FT_SetBitMode(ftHandle, 0xFF, 0x040);
    printf("start\n");

    struct timespec tstart={0,0}, tend={0,0};
    clock_gettime(CLOCK_MONOTONIC, &tstart);
    long totalBs = 0;
    int n = BUF_SIZE;
    pcBufRead = (char *)malloc(n);
    for (int i = 1; i <= 0x100; i++) {
        FT_Read(ftHandle, pcBufRead, n, &dwBytesRead);
        totalBs += dwBytesRead;
    }
    clock_gettime(CLOCK_MONOTONIC, &tend);
    double us = (((double)tend.tv_sec + 1.0e-9*tend.tv_nsec)*1000000) - (((double)tstart.tv_sec + 1.0e-9*tstart.tv_nsec)*1000000);
    printf("%ld @ %.0f = %ld B/s\n", totalBs, us, (long)(totalBs / (us/1000000)));
 
    free(pcBufRead);
    FT_Close(ftHandle);
    
    return 0;
}

And I get:

$ ./largeread 
start
50331648 @ 5483920 = 9178041 B/s

I tested reading a large file from a USB stick in case the socket was slow – got 18 Mbyte/s, so seems like 9 isn't the cap.

Anybody know why this FTDI chip is reading data so much slower than the datasheet says it should?

Edit: I've now tried it on Windows, too – the same code, the same circuit configuration plugged into Linux gives like 8.5MB/s, but on Windows gives like 45MB/s. So I guess now the question becomes, are their Linux drivers just slow, or is there some way of fixing it, perhaps some setting I've missed?

Best Answer

A little internet search yielded that this is a well known issue. Just search for "However, one of our Linux customers has used a third party FTDI library called LibFTDI to get 40 MByte/sec performance with Sync FIFO with the latest Linux kernels" for details.

Bottom line is, that FTDI seems to know about this issue but did not fix it for several years. It is recommended to switch to libftdi. There is a stream_test.c which should allow you quickly to check for the speed.