Also eher "Unverständliche USB-Effekte, in hoher Geschwindigkeit".
Ein Puffer gilt als „gefüllt“, wenn:
Was die SIE, das serielle USB-Interface, in diesem Falle macht, ist – raten Sie mal! – undokumentiert! Vermutlich wird NAK zurückgeschickt [svw. „Lieber Host, ich habe keinen Platz, versuche es im nächsten Mikroframe noch einmal.“], oder auch keine Antwort [svw. „Lieber Host, ich kann dich nicht hören, versuche es im nächsten Mikroframe noch einmal, aber höchstens dreimal, dann melde Deinem Anwenderprogramm, dass es schief ging.“]. Oder aber, die SIE platziert die Daten irgendwie über Puffer und 8051-XRAM hinweg, und schießt nebenbei die Firmware ab…Also muss der Puffer stets größer/gleich der maximalen USB-Länge gewählt werden.
Puffer werden stets abgeschlossen und gelten dann als gefĂĽllt.
Wichtig (Faustregel): Leere Puffer sind normalerweise niemals in 8051-ZVE-Besitz, sondern rutschen automatisch zur entsprechenden „Tankstelle“:
Entladestation: Die ZVE kann in das (Betanken und) Leeren der Puffer eingreifen:
Instanz | Zugriff |
---|---|
ZVE | wahlfrei (adressierbar) |
USB | en bloc (USB ist zwar seriell, aber ohne Belang! U.a. wegen CRC) |
GPIF | sequenziell (Byte fĂĽr Byte oder Wort fĂĽr Wort) |
Die Sichtweise von GPIF auf Ausgabe-Puffer:
Null-Byte-Pakete: Sie sind lästig und sollten vermieden werden. Ein Windows-Host weigert sich, Null-Byte-Bulk-Pakete zu senden; bei Isochron geht es womöglich doch.
Ungerade-Bytezahl-Pakete: Was ein 16-bit-GPIF in dieser
Situation macht ist noch völlig ungeklärt und nirgends dokumentiert!
Vermutlich wird in diesem Fall ein ungĂĽltiges High-Byte ausgegeben.
Also: vermeiden!!
Bei IN-Paketen kann dies nicht passieren; auĂźer wenn man
den automatischen Puffer-Abschluss auf einen ungeraden FĂĽllstand gelegt
hat. (Was das GPIF dann macht, ist undokumentiert!!)
Was Cypress vorfĂĽhrt, ist alles andere als lehrreich. Bedenken Sie folgenden Satz, der fĂĽr die Mikrocontrollerprogrammierung fundamental ist:
Wenn ein Problem ohne Interrupts lösbar ist, dann ist das bereits ein Grund dagegen.Sie können Interruptanforderungen (IRQs) sehr einfach in einer Abfrageschleife abtesten und darauf reagieren: Ihr Programm wird nicht nur schneller, es entfallen alle Race Conditions, und es ist einfach debugbar.
In der Hauptschleife der 8051-Firmware werten Sie einfach den Inhalt von USBIRQ (für Endpoint EP0) sowie die OUTxCS / INxCS für die übrigen Endpoints aus und verzweigen entsprechend. Nicht anders machen es die Cypress-Beispiele, nur umständlicher.
Sieht so aus als hätte irgendein Chef bei Cypress befohlen, dass ihre Beispiele unbedingt den ach-so-tollen vektorisierten USB-Interrupt verwenden müssen.
AutoOut sorgt dafür, dass vom USB gefüllte Puffer automatisch zum GPIF-Besitz übergehen. (Sie werden zu Slave FIFOs.) FIFO-Statusleitungen (im Slave-FIFO-Betrieb also externe Leitungen) verändern entsprechend dem Füllstand ihren Zustand.
Das GPIF bleibt jedoch weiterhin „ausgeschaltet“! Ich habe noch keine Funktion hinbekommen, s.u.!
AutoIn sorgt dafür, dass vom GPIF (oder Slave FIFO) gefüllte und terminierte(!) Puffer zum USB-Besitz übergehen. Dabei darf der Füllstand eines Puffers nicht die maximale USB-Paketgröße übersteigen; also für Bulk-Pipes im Full-Speed-Modus maximal 64 Bytes!
Was die SIE in diesem Falle tut, ist – es wird langsam langweilig – undokumentiert! Wahrscheinlich ist, sie sendet auf Gedeih und Verderb alles, und erzeugt USB-Babble, lässt auf PC-Seite Speicher überlaufen (je nach Host-Controller), lässt Windows abstürzen, wer weiß? Die SIE weiß ja nichts von Maximallängen!Wenn mehrere FIFO-Datenströme über das GPIF laufen sollen (bspw. ein OUT- und ein IN-Strom), muss der Programmierer das GPIF bei entsprechendem Bedarf „anstoßen“ (am einfachsten durch Schreiben auf GpifTrig @BB) - es gibt hierfür keinen Automatismus!! Und die GPIF-Wellenform muss so gestaltet werden, dass sie auch umgehend endet:
Das folgende Bild zeigt außerdem die Möglichkeit, auch die 8051-Firmware per USB laden zu lassen. Packt man alles in eine Treiberdatei (.SYS), ist ein komplettes Software-Update sehr einfach. Ein derartiges Gerät startet in mehreren Phasen:
Ein RAM-Pufferspeicher wird fast immer benötigt, wenn bei hoher USB-Datenrate Unterbrechungen im 100-ms-Bereich überbrückt werden sollen; der FPGA-interne RAM ist dafür viel zu klein oder zu teuer. Für den Mikrocontroller genügt fast immer der kleine 56-polige Gehäusetyp.
Zur Konfiguration eines Virtex-FPGA mit der Methode Slave SelectMAP benötigt man folgende Verbindungen, die man auch im nachhinein (also wenn FPGA im regulären Betrieb) als schnelle Übertragungsstrecke nutzen kann:
Cypress CY7C68013A | Xilinx Virtex II |
---|---|
IFCLK | CCLK Dedizierter Konfigurationsanschluss |
PB0..7 | D7..0 Bits stĂĽrzen! Wirklich! Bei Virtex ist D0 das MSB! Wird oft ĂĽberlesen. |
CTL0 | CS_B |
CTL1 | RDWR_B |
RDY0 | BUSY Wird nur bei komprimierten oder verschlĂĽsselten Konfigurationsdaten gebraucht. Beim Virtex4 ausschlieĂźlich zum RĂĽcklesen |
PA2 oder irgendein Ausgabepin | PROG_B Dedizierter Konfigurationsanschluss |
PA3 oder irgendein Ein/Ausgabepin | DONE Dedizierter Konfigurationsanschluss |
PA4 oder irgendein Eingabepin | INIT_B Nicht unbedingt nötig, siehe Text |
INIT_B wird nur benötigt, um:
// GPIF Waveform 1: FIFOWr // // Interval 0 1 2 ... Idle (7) // _________ _________ _________ _________ _________ // // AddrMode Same Val Same Val Same Val // DataMode Activate Activate NO Data // NextData SameData NextData NextData // Int Trig No Int No Int No Int // IF/Wait IF IF IF // Term A EF+1 EF+1 (egal) // LFunc AND AND AND // Term B EF+1 EF+1 (egal) // Branch1 Then 2 Then 2 ThenIdle // Branch0 Else 1 Else 1 ElseIdle // Re-Exec No Yes No // Sngl/CRC Default Default Default // CS_B 0 0 1 // RDWR_B 0 0 1Die Einstellung „NextData“ führt zu Beginn des Zustandes zum Lese-Takt und damit zum Datenwechsel. Mit geringer GPIF-Taktverzögerung, sofern der FX2 die Taktquelle ist.
Man braucht kein SyncDelay bei 48 MHz, weil nur 3 Takte Wartezeit erforderlich sind und der C-Compiler genĂĽgend (unsinnige) Befehle einstreut.EPxGpifFlgSel= 1 // Anhalten bei Empty-Flag, nicht bei TerminalCountEPxFifoCfg= 0x20 // Handarbeit, 8 bit, OEP1 (Empty-Flag 1 Byte frĂĽher)
Wichtig (Die verflixte Null): Das GPIF darf nicht aktiviert werden, wenn Null Bytes in den Ausgabepuffern stehen! Die Zustandsmaschine würde ein ungültiges Byte zum FPGA schaffen.Im Experiment war es egal, ob IFCLK (Interface-Takt) negiert oder nicht-negiert betrieben wurde (Virtex-II, 48 MHz).Bei nur einem Byte läuft die Zustandsmaschine direkt von Zustand 0 (= Fall-Through-Byte zum FPGA schicken) zum Zustand 2 (= FIFO leer lesen, aber FPGA laden beenden [CS_B=1]). Notfalls kann man sich darauf verlassen, dass der (Windows-)Host kein Null-Byte-Paket schickt.
Ob Bytes im Puffer vorhanden sind, kann nicht mittels EPxCS, EPxFifoFlags oder EPxFifoFlgs abgefragt werden, sobald das EF+1-Feature aktiviert ist. Vermutlich ist EPxFifoBc zu benutzen.
Eine in dieser Hinsicht „eigensichere“ Zustandsmaschine zu konstruieren ist nicht möglich, wenn man das EF+1-Feature benutzt.
Wie man sich vorstellen kann, sind auch größere FPGAs mit einem Fingerschnipp konfiguriert. Man kann von einer Datenrate von 30 MByte/s (halbe High-Speed-USB-Bandbreite) ausgehen. Serielle Schneckentempo-Konfiguration war gestern.
Der Verzicht auf den FX2 und Implementation des USB auf dem FPGA selbst bedeutet FPGA-Ressourcenverbrauch und kommt in der Praxis teurer als der zusätzliche Chip. Vom Entwicklungsaufwand ganz zu schweigen. Und den Konfigurations-Flash braucht man in diesem Falle ja auch noch.
Wäre das Xilinx-USB-JTAG-Protokoll (iMPACT) dokumentiert, könnte der FX2 gleich noch die Rolle des „USB Cable“ zum Debuggen übernehmen! Wäre schön, aber das hat noch niemand geknackt…
Genug geträumt! Zurück zur Konfiguration!
// GPIF Waveform 1: FIFOWr // // Interval 0 1 2 3 4 Idle (7) // _________ _________ _________ _________ _________ _________ // // AddrMode Same Val Same Val Same Val Same Val Same Val // DataMode Activate Activate Activate Activate NO Data // NextData SameData SameData NextData SameData NextData // Int Trig No Int No Int No Int No Int No Int // IF/Wait IF IF IF IF IF // Term A EF+1 BUSY EF+1 BUSY (egal) // LFunc AND AND AND AND AND // Term B EF+1 BUSY EF+1 BUSY (egal) // Branch1 Then 3 Then 0 Then 3 Then 0 ThenIdle // Branch0 Else 1 Else 2 Else 1 Else 4 ElseIdle // Re-Exec No No No No No // Sngl/CRC Default Default Default Default Default // CS_B 0 1 0 1 1 // RDWR_B 0 0 0 0 0
_____ _____ _____ _____ |0 |---Empty-->|3 | ____ |4 | |i | | A | | |---Busy-->|> |--------->| | |_____|<--Busy----|_____| |_____| |_____| ^ | ^ | _|___ | Zeichenerklärung: Busy Empty Empty 0 Zustands-Nummer (i=Idle) | | | A Aktivität (von CS_B) |___v ____ __|__ > NextData (akt. FIFO-Daten verwerfen) |1 |---Busy--->|2 | | | _____ |> A | Empty = letztes Byte in FIFO |_____|<--Empty---|_____| Busy = FPGA lehnte Byte ab
Auch mit Drosselung sind größere FPGAs mit einem Fingerschnipp konfiguriert. Die theoretische Datenrate von 15 MByte/s (halbe IFCLK-Taktfrequenz) wird wegen diverser Windows-Aussetzer nicht erreicht. Trotzdem: affenschnell.
EPxGpifFlgSel= 1 ; // Anhalten bei Empty-Flag, nicht bei TerminalCountEPxFifoCfg|= 0x20 ; // Handarbeit, 8 bit, OEP1 (Empty-Flag 1 Byte frĂĽher)GpifReadyCfg|= 0x40 ; // SAS=1, synchron, ein RDYx-Flipflop