Forum: Mikrocontroller und Digitale Elektronik FTDI/JTAG buffer overflow?


von pks (Gast)


Lesenswert?

Servus,

ich konfiguriere ein FPGA über eine USB/JTAG-Verbindung mit einem 
FT2232H. Dazu verwende ich den ftd2xx-Treiber und die entsprechende 
lib/API. Meine Software ist so implementiert, dass ich die 
Schreibzugriffe puffere und als 4K-Blöcke an die FT_WRITE()-Funktion 
weiter gebe. Dabei kam es sporadisch immer wieder mal vor, dass dass der 
Update-Vorgang fehlschlägt, also das FPGA nicht zum Leben erwacht. Diese 
lässt sich verhindern, wenn ich nach jedem FT_WRITE()-Aufruf ein 
Sleep(1) einfüge, was mich zu der Vermutung führt, dass da irgendein 
Buffer überläuft.
Eigentlich würde ich aber erwarten, dass wenn ich schon vom Hersteller 
eine Lib bekomme, diese sich selbst um das Timing kümmert. Und im 
User-Manual von FTDI finde ich auch nirgendwo die Vorgabe irgendwelcher 
Wartezeiten. Auf eine entsprechende Anfrage an den Support kam keine 
Antwort.

Weiß jemand mehr über das Thema?

von pks (Gast)


Lesenswert?

Jetzt geht mir gerade auf, dass das hier vielleicht das falsche Forum 
ist...:-)

von Mirco C. (Firma: s@Td) (mcontroller)


Lesenswert?

Hallo,

passt ja gerade noch so :D
Was passiert wenn du in 1K Blöcken schreibst?
Evt. könnte es auch ein Timingproblem des FPGA's sein, das irgendwo 
nicht mitkommt. Kannst du irgendwo eine Frequenz oder Bitabstand 
angeben?
Vill. gibt es irgendwo versteckte Defines mit der Buffergröße?

von pks (Gast)


Lesenswert?

4K ist eben genau die Größe des Sendepuffers auf dem Chip. Wenn ich die 
JTAG-Frequenz runterdrehe, tritt der Fehler tendenziell häufiger auf, 
deshalb auch meine Vermutung mit dem Overflow.
Das mit den 1K Blöcken müsste ich mal ausprobieren...

von Mirco C. (Firma: s@Td) (mcontroller)


Lesenswert?

Es ist dann vill die Frage ob 4K = 4096 oder 4000 bzw. ob die 4K 
wirklich komplett für deine Daten draufgehen können.

von Günter R. (muntablues)


Lesenswert?

Ich glaube auch, dass du für das FPGA zu schnell bist. Die ftd2xx 
schickt im Normalfall die komplette "Wurscht", also die kompletten 4k 
Daten raus, sobald du das FT_Write abgefeuert hast. Auch kehrt sie erst 
zurück, wenn alles gesendet ist, so hab ich das zumindest in 
Errinnerung.

Somit gibt dein Sleep dem FPGA mehr Zeit und nicht nur der FTDI Lib. 
Aber iM DB des FPGA sollte hier schon was zu finden sein...

von pks (Gast)


Lesenswert?

Das glaube ich eben nicht. Gerade wenn ich den JTAG-Takt kleiner mache, 
die Daten also langsamer ins FPGA schiebe, tritt der Fehler ja häufiger 
auf.
Die FT_Write mag ja erst zurückkehren, wenn die Daten per USB 
abgeschickt sind, die Frage ist vielmehr ob sie auch sicherstellt, dass 
im Sendebuffer des Chips auch Platz dafür ist.

Mein Buffer is 4096 Byte groß und beinhaltet sowohl meine Daten als auch 
die FT-MPSSE-Kommandos.

von Falk B. (falk)


Lesenswert?

Das FPGA ist mal ganz sicher nicht zu langsam, denn die können meist 
33MHz und mehr als JTAG Takt. UNd wenn es bei neidrigerem JTAG Takt 
öfter auftritt, deutet das stark auf einen Pufferüberlauf und 
fehlerhaftes Handshake hin.

von Günter R. (muntablues)


Lesenswert?

Welches Kommando verwendest du denn? Irgendwie kommt mir 4096 gesamt 
komisch vor, wenn da die MPSSE auch mit dabei sind. Ich würde eher auf 
4099 kommen, Kommando, LengthL, LengthH, Daten (4096).

Evtl. hilft es dir auch, wenn du ein 0x87 Kommando ans Ende stellst.

Ich hab mal eine Boundary Scan Lib auf Basis der ftd2xx geschrieben und 
da hab ich tausende Bytes in kürzester Zeit geschrieben und gelesen und 
konnte solche Probleme nicht feststellen. Ich behaupte sogar dass die 
FT_Write erst zurück kehrt, wenn die Daten tatsächlich raus gegangen 
sind.

Alternativ könntest du natürlich auch ein Schreib/Lese Kommando nehmen, 
dann kehrt er 100% erst zurück, sobald die Daten durch geschoben sind...

Gruß MB

von pks (Gast)


Lesenswert?

Günter R. schrieb:
> Welches Kommando verwendest du denn? Irgendwie kommt mir 4096
> gesamt
> komisch vor, wenn da die MPSSE auch mit dabei sind. Ich würde eher auf
> 4099 kommen, Kommando, LengthL, LengthH, Daten (4096).
Versteh ich nicht. Ich kopiere halt die Daten in den Puffer, und sobald 
er voll ist oder ich explizit ein flush() aufrufe, wird der Inhalt 
abgeschickt. Was hat das mit den verwendeten Kommandos zu tun?

> Ich behaupte sogar dass die
> FT_Write erst zurück kehrt, wenn die Daten tatsächlich raus gegangen
> sind.
Du meinst auf dem JTAG raus gegangen? Das kann ich mir nicht vorstellen. 
Damit würde mann u.U. ja Geschwindigkeit verschenken.

> Alternativ könntest du natürlich auch ein Schreib/Lese Kommando nehmen,
> dann kehrt er 100% erst zurück, sobald die Daten durch geschoben sind...
Das dürfte den ganzen Vorgang aber auch deutlich bremsen.

von pks (Gast)


Lesenswert?

Hier mal ein Code-Auszug:
1
#define TR_BUFFER_SIZE 4096
2
3
void FtJtagIO::writeBuffer(int numBytes, uint8_t *data)
4
{
5
    uint8_t *dPtr = data;
6
    while(numBytes)
7
    {
8
        if(trSize < TR_BUFFER_SIZE)
9
        {
10
            trBuffer[trSize++] = dPtr ? *(dPtr++) : 0xff;
11
            numBytes--;
12
        }
13
        else
14
        {
15
            flushBuffer();
16
        }
17
    }
18
}
19
20
void FtJtagIO::writeBuffer(uint8_t data)
21
{
22
    writeBuffer(1, &data);
23
}
24
25
void FtJtagIO::flushBuffer()
26
{
27
    if(trSize == 0)
28
        return;
29
30
    ftStatus = FT_Write(ftHandle, trBuffer, trSize, (DWORD*)&trDoneSize);
31
    Sleep(1);
32
33
    if(ftStatus != FT_OK || trDoneSize != trSize)
34
    {
35
        throw std::runtime_error("FT_Write() failed");
36
    }
37
    trSize = 0;
38
}
39
40
void FtJtagIO::shiftTdiData(uint32_t numBits, uint8_t *tdiData,
41
                            bool readTdo)
42
{
43
    int numBytes   = (numBits-1) >> 3;
44
    int numBitsRes = (numBits-1) & 0x7;
45
    char cmdRdMask = readTdo ? 0x20 : 0;
46
    uint8_t *dPtr = tdiData;
47
48
    ...
49
50
    // Write complete bytes
51
    if(numBytes)
52
    {
53
        writeBuffer(CMD_DATABYTES2TDI | cmdRdMask);
54
        writeBuffer((numBytes-1) & 0xFF);
55
        writeBuffer(((numBytes-1) >> 8 ) & 0xFF);
56
        writeBuffer(numBytes, dPtr);
57
        dPtr += numBytes;
58
    }
59
60
    // Write residual bits
61
    if(numBitsRes)
62
    {
63
        writeBuffer(CMD_DATABITS2TDI | cmdRdMask);
64
        writeBuffer((numBitsRes-1) & 0xFF);
65
        writeBuffer(dPtr);
66
    }
67
}

von Günter R. (muntablues)


Lesenswert?

Mit Kommandos meinte ich z.B. CMD_DATABYTES2TDI. Somit hat dein Buffer 
(der der wirklich im flushBuffer() gesendet wird) immer mehr als 4096 
Bytes. Du schickst das Zeug aber schon sobald Gesamt > 4096 ist und das 
ist der Bock, weil dir immer einige Daten fehlen.

Schau mal in die shiftTdiData() Methode und dann siehst du sofort, dass 
immer 3 Bytes pro Aufruf dazu kommen. Den Wert CMD_DATABYTES2TDI kannst 
du in der MPSSE Doku mal nachschlagen und dann siehst du schon was ich 
meine...

Ich würde die Daten extern auf 4096Byte Päckchen umbauen und dann in 
einem Rutsch absenden, dann ist die Performance bestimmt am besten. 
Alterntaiv könnte auch ein Umbau was bringen, in etwa so (ungetetstet):

EDIT: trBuffer muss natürlich groß genug sein!

1
#define TR_BUFFER_SIZE 4096
2
3
void FtJtagIO::writeBuffer(int numBytes, uint8_t *data)
4
{
5
    uint8_t *dPtr = data;
6
    while(numBytes)
7
    {
8
        trBuffer[trSize++] = dPtr ? *(dPtr++) : 0xff;
9
        numBytes--;
10
    }
11
}
12
13
void FtJtagIO::writeBuffer(uint8_t data)
14
{
15
    writeBuffer(1, &data);
16
}
17
18
void FtJtagIO::flushBuffer()
19
{
20
    if(trSize == 0)
21
        return;
22
23
    ftStatus = FT_Write(ftHandle, trBuffer, trSize, (DWORD*)&trDoneSize);
24
25
    if(ftStatus != FT_OK || trDoneSize != trSize)
26
    {
27
        throw std::runtime_error("FT_Write() failed");
28
    }
29
    trSize = 0;
30
}
31
32
void FtJtagIO::shiftTdiData(uint32_t numBits, uint8_t *tdiData,
33
                            bool readTdo)
34
{
35
    int numBytes   = (numBits-1) >> 3;
36
    int numBitsRes = (numBits-1) & 0x7;
37
    char cmdRdMask = readTdo ? 0x20 : 0;
38
    uint8_t *dPtr = tdiData;
39
40
    ...
41
42
    // Write complete bytes
43
    if(numBytes)
44
    {
45
        writeBuffer(CMD_DATABYTES2TDI | cmdRdMask);
46
        writeBuffer((numBytes-1) & 0xFF);
47
        writeBuffer(((numBytes-1) >> 8 ) & 0xFF);
48
        writeBuffer(numBytes, dPtr);
49
        dPtr += numBytes;
50
    }
51
52
    // Write residual bits
53
    if(numBitsRes)
54
    {
55
        writeBuffer(CMD_DATABITS2TDI | cmdRdMask);
56
        writeBuffer((numBitsRes-1) & 0xFF);
57
        writeBuffer(dPtr);
58
    }
59
    if(trSize > TR_BUFFER_SIZE)
60
    {
61
        flushBuffer();
62
    }
63
}

: Bearbeitet durch User
von pks (Gast)


Lesenswert?

Ich hab das Gefühl wir reden aneinander vorbei :-)

Günter R. schrieb:
> Mit Kommandos meinte ich z.B. CMD_DATABYTES2TDI. Somit hat dein Buffer
> (der der wirklich im flushBuffer() gesendet wird) immer mehr als 4096
> Bytes. Du schickst das Zeug aber schon sobald Gesamt > 4096 ist und das
> ist der Bock, weil dir immer einige Daten fehlen.
> Schau mal in die shiftTdiData() Methode und dann siehst du sofort, dass
> immer 3 Bytes pro Aufruf dazu kommen. Den Wert CMD_DATABYTES2TDI kannst
> du in der MPSSE Doku mal nachschlagen und dann siehst du schon was ich
> meine...

Dass da Kommandos hinzugefügt werden, ist mir schon klar. Aber aus Sicht 
der writeBuffer()-Methode ist das doch völlig egal. Die bekommt einen 
beliebig großen anonymen Datenblock übergeben und kopiert diesen 
byteweise in den 4K-Transmit puffer. Immer wenn dieser voll ist, wird 
ein flush aufgerufen, der den puffer leert. Anschließend wird er wieder 
befüllt usw. Das geht so lange, bis alle Daten kopiert sind.
Was in meinem codeschnipsel nicht zu sehen ist - sollte der 
Gesamttransfer (Daten+Kommandos) nicht 4K aligned sein, werden die am 
Ende verbleibenden Daten (plus die nachfolgenden TMS-shift Kommandos) 
beim nächsten RUNTEST-Kommando abgeschickt.
Wo da jetzt Daten fehlen sollen, sehe ich nicht. Wenn das so wäre, 
dürfte das Ganze ja, unabhängig vom Sleep(), gar nicht funktionieren.

von Günter R. (muntablues)


Lesenswert?

Kann schon sein, dass es aneinander vorbei geht gg

ABER ich sehe das nicht so. Nehmen wir deinen Code und spielen mal 
durch.

Buffer hat 4090 Bytes. Nun schiebst du zB nochmal 8Byte nach.

Der Buffer kriegt 1 Byte Kommand, 2 Byte Länge und 8 Byte Daten, doch 
nach dem 3. Byte wird die FT_Write aufgerufen und die Daten geschickt 
(ich denke auch auf der HW) Jetzt sind noch 5 Byte übrig die wieder in 
den Buffer kommen und ob der Treiber jetzt noch weiß, dass es Daten aus 
dem Kommando von vorhin sind, wage ich zu bezweifeln. Irgendwo her 
müssen ja deine Probleme kommen und das wääre für mich eine Erklärung.

Bei meiner JTAG Lib hab ich das alles immer intern vorbereitet und erst 
danach sauber an die FT Lib übergeben und das hat dann auch geklappt.

Ich behaupte wenn du die Daten selber bufferst bis du 4k beisammen hast 
und in einem Rutsch übergibst, rennt die Geschichte. So viel Aufwand ist 
das sicher nicht...

Also dann,

von pks (Gast)


Lesenswert?

Ok, jetzt habe ich zumindest verstanden was Du mir sagen willst :-)
ABER ich sehe das wiederum anders. Der Treiber weiß doch überhaupt 
nichts von irgendwelchen Kommandos, oder sollte es zumindest nicht. 
Seine Aufgabe sollte sein, transparente Daten über USB zum/vom Chip zu 
transportieren und dafür zu sorgen, dass alles ankommt. Die 
Interpretation der Daten ist Sache der MPSSE. Und ich baue doch eine 
Abstraktionsschicht wie obige Klasse FtJagIO doch gerade deshalb, damit 
ich mich als Anwender eben nicht um irgendwelche Buffergrößen etc. 
kümmern muss.
Ich will gar nicht generell ausschließen, dass Dein Vorschlag Erfolg 
haben mag und werde es auch bei Gelegenheit mal ausprobieren. Aber ich 
bin der Meinung, dass wenn Du Recht hast dieses Verhalten ein schlechtes 
Design, wenn nicht gar einen Bug auf Seiten der FTDI-Entwickler 
darstellen würde. Und vor allem sollte es dann irgendwo dokumentiert 
sein. Man findet übrigens eine solche Sleep-Anweisung auch im 
Beispielcode einer Application-Note von FTDI, ohne dass sie näher 
erläutert wäre.
Letztlich wäre es auch noch möglich, dass es sich einfach um einen 
Hardware-Bug handelt, da das Problem mit einem anderen Design (anderes 
FPGA) nicht auftritt. Leider habe ich momentan keine Möglichkeit ein 
anderes Exemplar der gleichen Hardware zu prüfen.

Gruß

von Günter R. (muntablues)


Lesenswert?

Ist Ansichtssache. Meines Erachtens kann es bei deinem Aufbau sein, dass 
du unvollständige Befehle schickst und ob das richtig gehandhabt wird 
ist fraglich. Ich denke dann mal wohl eher nicht.

Du hast aber mit wenigen "Handgriffen" die Möglichkeit deine FtJagIO 
umzubauen, indem du nur die Daten speicherst bis du wirklich 4096Bytes 
hast und dann machst du einen neuen Buffer inkl. Kommando und Länge und 
schickst das per FT_Write. Beim externen Aufruf von FlushBuffer müsstest 
du es natürlich ein wenig anders handhaben, aber Hexerei ist das sicher 
keine.

Naja, ich glaub ich hab alles gesagt was mir dazu einfällt ;-)

So denn,

PS: Viele App Notes und auch viele Tools von FTDI sind einfach nur 
Schrott und man könnte verzweifeln. FT_PROG zB hab ich noch nie 
fehlerfrei erlebt...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.