FPGA Konfiguration mit AVR Butterfly
Einleitung
Dieses Projekte stellt gewissermaßen eine Ergänzung zu Low_Cost_FPGA_Konfiguration dar. Es wurde ein AVR Butterfly genutzt, um einen FPGA Xilinx Spartan-3-1000 via JTAG zu konfigurieren.
Hardware
Da die Pins CCLK, Din und Done nicht zugänglich sind, mußte der Weg über die JTAG-Schnittstelle gegangen werden. Es wurde ein kleines Trägerboard für den Butterfly entwickelt, um alle benötigten Schnittstellen bequem unterzubringen.
Der Dataflash auf dem Butterfly-Board hat eine Größe von 512 kByte. Da passen die ca. 402 kByte Konfigurationsdaten [1] für den der Spartan-3-1000 gut rein. Wer einen größeren FPGA konfigurieren möchte, braucht also auch den passenden Speicher dafür.
Schaltplan
Der Butterfly wird mit einer externen Betriebsspannung von 3,3 V versorgt. Damit ergeben sich 3,3 V Pegel für die JTAG-Schnittstelle (Achtung, bei neueren FPGAs liegt die JTAG-Spannung bei 2,5 V oder 1,8 V). Die Referenzspannung der Zielhardware kann per Software geprüft werden.
Das eigentliche Programmiergerät kann an JTAG_IN angeschlossen werden. Auf den in XAPP058 angegebenen Multiplexer zum Umschalten der JTAG-Quelle kann verzichtet werden, da der Mikrocontroller seine Ausgänge (ebenso wie das Programmiergerät) bei Nichtgebrauch hochohmig schaltet. Man darf natürlich nicht gleichzeitig mit dem Programmer und dem AVR per JTAG auf das FPGA zuzugreifen.
Software
Als Grundgerüst für die Butterfly-Software wurde die gcc-Portiertung der Butterfly-Demo [2] verwendet. Um die Daten sicher in den Dataflash zu bekommen, wird das XMODEM-Protokoll verwendet. Dazu gibt es von Atmel die Application Note AVR350 [3]. Und um letztendlich den FPGA zu konfigurieren, wurde von Xilinx die Application Note XAPP058 [4] benutzt.
Damit das alles in den 16 kByte Flash des ATmega169 passt, wurde der Democode entschlackt. Dann erfolgt die Anpassung des Xilinx-Codes: MAX_LEN (lenval.h) muß rigeros verringert werden, damit die Datenstrukturen von Xilinx in den Speicher von 1 kByte passen. Ich habe 128 gewählt. Das ist auch die maximale Größe, die Impact für die Konfigurationshäppchen verwendet. Außerdem muß noch die Kompression deaktiviert werden: XSVF_SUPPORT_COMPRESSION (micro.c). Damit schrumpft die Variable xsvfInfo auf 546 Byte.
Alle Funktionen können bequem über das Bedienpanel des Butterfly abgerufen werden:
Menüstruktur
Workflow
Im Allgemeinen wird sich der folgende Workflow ergeben:
- XSVF-Daten generieren
- Download
- Konfigurieren des FPGA
XSVF-Daten generieren
Die XSVF-Dateien können inzwischen direkt durch Xilinx-Impact erstellt werden. Ich nutze dafür dieses kleine Skript (bit2xsvf.cmd):
setmode -bs
setCable -port xsvf -file "design.xsvf"
addDevice -p 1 -file "design.bit"
program -p 1
closeCable
quit
Durch
$ impact -batch bit2xsvf.cmd
wird die XSVF-Datei auf der Kommandozeile erstellt.
Download
Für den Download der XSVF-Datei in den Dataflash wird die serielle Schnittstelle auf 115200 Baud eingestellt und die Datei per XMODEM transferiert. Mit der Linux-Kommandozeile geht das z.B. so:
$ stty -F /dev/ttyS0 115200
$ sz --xmodem design.xsvf > /dev/ttyS0 < /dev/ttyS0
Zur Kontrolle, kann der Inhalt des Dataflash auf der seriellen Schnittstelle ausgegeben werden (dump):
Page: 0
0x0000: 0x07 0x00 0x13 0x00 0x14 0x00 0x12 0x00 0x12 0x01 0x04 0x00 0x00 0x00 0x00 0x02 ................
0x0010: 0x06 0x09 0x08 0x00 0x00 0x00 0x20 0x01 0x0F 0xFF 0xFF 0xFF 0x09 0x00 0x00 0x00 ...... .........
0x0020: 0x00 0xF1 0x42 0x80 0x93 0x02 0x06 0x09 0x09 0x00 0x00 0x00 0x00 0xF1 0x42 0x80 ..B...........B.
0x0030: 0x93 0x02 0x06 0x3F 0x02 0x06 0x0B 0x04 0x00 0x00 0x36 0xB0 0x02 0x06 0x05 0x04 ...?......6.....
Konfigurieren des FPGA
Die eigentliche Konfiguration des FPGA erfolgt mit dem Menüpunkt "Program FPGA". Wenn alles geklappt hat steht nach kurzer Zeit "done" im Display, sonst erhält man eine entsprechende Fehlermeldung.
Optimierungen
In der ersten funktionierenden Implementation benötigte das Programm ca. 400 Sekunden um das FPGA zu konfigurieren. Das entspricht einer Datentransferrate von 1,1 kByte/s. Völlig unakzeptabel.
Also musste optimiert werden. Der RC-Oszillator des AVR wird beim Startup auf 1 MHz Taktfrequenz kalibriert. Wenn man den Takteiler von 8 auf 1 stellt, läuft der AVR schon mit 8 MHz. Durch Programmieren des OSCCAL-Registers mit 0x7F ließ sich mein ATmega auf 15 MHz beschleunigen. Damit ließ sich die Konfigurationszeit auf 25 Sekunden (17 kByte/s) verringern. Schon besser.
Damit sich die Zeit für den XMODEM-Transfer ebenfalls verkürzt, wird die Baudrate der seriellen Schnittstelle auf 115200 eingestellt. Der ATmega läuft nur für die Konfiguration mit der vollen Geschwindigkeit. Für eine fehlerfreie serielle Datenübertragung wird der RC-Oszillator auf eine Taktfrequenz von 11,0592 MHz kalibriert. Bei wem das nicht klappen sollte, der muß eine kleinere Taktfrequenz im Makefile (F_CPU) einstellen. Laut ATmega169-Datenblatt müßten 8 MHz +/-10 Prozent immer funktionieren.
Eine weitere Geschwindigkeitssteigerung konnte durch die Nutzung des USI-Moduls erreicht werden. Normalerweise werden TDI, TDO, TCK und TMS durch Bitbanging erzeugt. Xilinx hat diese Funktionen in ports.c schön gekapselt. Sehr portabel, aber nicht schnell. Durch direkten Aufruf der USI-Register in der Funktion xsvfShiftOnly konnte eine weitere wesentliche Beschleunigung erreicht werden.
Weiterhin ist die Reihenfolge der Bits in den JTAG-Daten (LSB...MSB) nicht passend für das USI-Datenregister. Es müssen also alle Bytes gespiegelt werden. Am schnellsten geht dies mit einer Tabelle, die leider etwas Speicher kostet. Außerdem wurde die Zählerbreite lNumBits von 32 auf 16 Bit reduziert.
Damit ergibt sich letztendlich eine akzeptable Konfigurationszeit von 5,6 Sekunden und damit eine Transferrate von 75 kByte/s. Der Flash des ATmega169 ist mit 12 kByte gut gefüllt, läßt aber noch etwas Luft für Erweiterungen.
Ausblick
Um die folgenden Features könnte das Projekt noch erweitert werden:
- Lesen der Konfiguration von SD-Karte
- direkte Interpretation von .bit-Dateien
- Unterstützung von komprimierten Daten