Einsatz des eigenen IP-Cores unter Linux

Zynq FPGA

Nachdem ich euch in zwei vorherigen Anleitungen gezeigt habe, wie ihr ein Linux-Betriebssystem auf dem Zynq installiert und wie ihr euch euren eigenen IP-Core erstellen könnt, zeige ich euch nun wie ihr beides miteinander kombinieren könnt.

-> Vorbereitungen:

Für dieses Tutorial wird der eigene IP-Core, sowie ein lauffähiges Linux-Betriebssystem für den Zynq benötigt.
Außerdem benötigt ihr einen Computer mit einem Linux-Betriebssystem (ich benutze Ubuntu) und einen ARM Cross-Compiler.
Bei dem aktuellen Betriebssystem für den Zynq besteht noch das Problem, dass der User sshd nicht existiert, weswegen der SSH-Server nicht funktioniert.
Dies macht sich durch die beim booten aufkommende Meldung

bemerkbar.
Um dies zu ändern müsst ihr als erstes die Datei /etc/passwd öffnen:

Am Ende der Datei hängt ihr dann die Zeile

an. Schauen wir uns mal kurz an was die Zeile macht…

Die Datei /etc/passwd ist eine zentrale Benutzerdatenbank, wo verschiedene Einstellungen für die diversen Benutzer eines Linux-Systems getätigt werden können.
Insgesamt decken drei Dateien die Benutzerverwaltung ab:

  1. /etc/passwd
  2. /etc/shadow
  3. /etc/group

Uns interessiert aber erst einmal nur die erste Datei, weswegen wir die anderen beiden ignorieren.
Anschließend wird die Datei gespeichert und geschlossen. Danach muss nur noch der SSH Daemon neu gestartet werden

und schon könnt ihr per SSH auf den Zynq zugreifen.

Für die Erstellung der Software wird weiterhin noch ein passender Cross-Compiler benötigt. Hier empfiehlt es sich den Cross-Compiler von Open Embedded zu verwenden:

Jetzt muss noch die Hardware des Chips erweitert werden…

-> Die Hardware für den Chip:

Jetzt wird es Zeit die Hardware für den Chip zu bauen. Benötigt wird auf jeden Fall ein konfiguriertes PS, wie es z.B. hier verwendet wird. Dieses PS wird jetzt um den eigenen GPIO-Core erweitert:

Linux_IP(1)

Im Anschluss daran wird die Schaltung synthetisiert und dem IP-Core ein IO zugewiesen (z.B. eine LED).
Anschließend wird ein Bitstream erzeugt und das Design ins SDK exportiert, welches ihr danach öffnet.
Wenn ihr noch keinen Device Tree und FSBL erzeugt habt, verfahrt ihr nach dieser Anleitung. Ansonsten sollte euer Projekt nun über drei Files im Device Tree verfügen:

  • pl.dtsi
  • ps.dtsi
  • system.dts

Die Datei pl.dtsi beschreibt die Hardware innerhalb der programmierbaren Logik des FPGAs. Wenn ihr sie aufmacht, seht ihr folgendes:

Falls ihr die Datei pl.dtsi nicht in eurem Projekt haben solltet, löscht den Ordner mit dem Device Tree und legt ihn neu an

Ihr könnt nun folgende Eigenschaften erkennen:

  • Name der Hardware: xlnx,GPIO-1.0
  • Startadresse: 0x43c00000
  • Adressbereich: 65536
  • Anzahl Register: 4
  • Bitbreite: 0x20 – 32 Bit

Den Namen der Hardware werdet ihr für den passenden Treiber benötigen. Der Rest der Informationen ist nicht ganz so wichtig, da der Treiber sich diese Informationen direkt aus dem .dtb-File holen wird.
Der kompilierte Device Tree und der FSBL werden, zusammen mit dem .bit-File und dem Kernelimage, auf eine SD-Karte gespeichert und in das Zybo gesteckt.
Anschließend kann das Board gebootet werden.

-> Das passende C-Programm:

Da die Hardware nun soweit fertig ist, können wir uns um die Software kümmern.
Für den IO-Core wird nun noch ein passender Treiber benötigt.
Der komplette Treiber sieht so aus:

Der Code sieht komplizierter aus als er eigentlich ist, daher keine Panik!
Gehen wir die Funktion des Codes mal von Anfang an durch. Der kompilierte Treiber wird über den Befehl

beim Betriebssystem angemeldet und eingebunden. Das Betriebssystem ruft dann die Funktion

auf, welche das Struct

übergeben bekommt, welches den Treiber beschreibt.
Die wichtigsten Zeilen sind die Zeilen

und

Damit wird quasi festgelegt, wann das Modul geladen wird (z.B. wenn es nicht wie bei uns händisch sondern automatisch beim Booten geladen werden soll).
Über den Ausdruck .name und .compatible wird dem Betriebssystem nämlich gesagt, dass es diesen Treiber nur laden soll, wenn im Device Tree ein Gerät mit dem Namen xlnx,GPIO-1.0 vorhanden ist.
Ist dieses Gerät nicht vorhanden, wird auch der Treiber nicht geladen!

Findet der Kernel ein Gerät mit dem entsprechenden Namen, wird die Funktion, die unter .probe angegeben ist, aufgerufen.
Sobald der Treiber mit dem Befehl

wieder vom Betriebssystem abgemeldet wird, wird die Funktion unter .remove aufgerufen.
Die probe-Funktion sieht so aus:

Dort wird als erstes der Abschnitt des Gerätes im Device Tree geöffnet und unter dem Struct Device gespeichert:

Unter dem Eintrag start und end sind nun die jeweiligen Adressen des Gerätes gespeichert.
Diese können nun genutzt werden um den entsprechenden Speicherbereich zu mappen:

Die Startadresse des gemappten Speicherbereiches des IP-Cores ist nun unter Basisadresse verfügbar.
Damit Applikationen auch mit dem Treiber kommunizieren können, wird noch ein entsprechender Eintrag unter /proc gemacht:

Auch hier wird wieder ein Struct definiert, welches die Funktionen des proc-Eintrags beinhaltet:

Sobald die proc-Datei geöffnet oder geschlossen wird, wird die entsprechende Funktion aufgerufen.
Die Funktionen

und

erhöhen oder verringern den Zähler innerhalb der proc-Datei, sobald die Datei geöffnet oder geschlossen wird. So weiß das Betriebssystem immer wie viele Applikationen auf den Treiber zugreifen und verhindert automatisch das Abmelden des Treibers, wenn der Zählerstand > 0 ist.

Wenn eine Applikation nun in die proc-Datei schreiben will, wird die Write-Funktion aufgerufen:

Diese Funktion macht nichts anderes als einen übergebenen Text in einen Zahlenwert (der Text sollte idealerweise „1“ oder „0“ sein) umzuwandeln und diesen an die Startadresse des GPIO-Cores zu schreiben (sprich in das erste Register).
Diese Funktion unterstützt keine Fehlerbehandlung! Bei falschen Übergabewerten kann es sein, dass der Treiber nicht mehr funktioniert und abstürzt!

Der Treiber kann nun mit dem Makefile kompiliert werden:

Die Variablen CROSS_COMPILE und KDIR müssen vorher aber noch mit den passenden Pfaden für den Cross-Compiler und von den Xilinx Linuxfiles befüllt werden.

Jetzt wird der Treiber gebaut,

auf das Zybo kopiert

und eingebunden:

Unter /proc findet ihr anschließend einen Eintrag namens GPIO:

Linux_IP(2)
Zum Testen des Treibers kann nun ein kleines Programm geschrieben werden, welches die proc-Datei öffnet und einen Wert in die Datei schreibt:

Sobald dieses Programm ausgeführt wird, blinkt die LED im 1s Takt.
Mit dem Befehl lsmod könnt ihr euch nun die aktiven Module anschauen und sehen von wie vielen Applikationen diese genutzt werden:

Sobald ihr das Testprogramm beendet, wird der Zähler um eins verringert. Hat der Zählerstand 0 erreicht, kann das Modul mit

entfernt werden. Andernfalls erscheint diese Meldung:

Damit wäre der Treiber an für sich fertig. Natürlich kann er noch um eine entsprechende Lesefunktion erweitert werden, wodurch es einer Applikation möglich ist den Status des Pins abzufragen, aber diese Mühe schenke ich mir hier erst einmal :)

-> Kurzer Exkurs: Ein angepasstes rootfs bauen:

Bisher habt ihr nur gelernt wie ihr mit Hilfe von Bitbake ein minimales rootfs erstellen könnt:

Dieses rootfs enthält natürlich gar nichts an Software.
Aus diesem Grund schauen wir uns mal an, wie wir mit Hilfe des grafischen Konfigurationsprogramms Hob ein eigenes, angepasstes rootfs bauen können.
Dazu werden folgende Pakete benötigt:

Nach der Installation wechselt ihr in das poky-Verzeichnis:

Und dort können die Umgebungsvariablen angelegt und Hob gestartet werden (meta-Layer etc. sollten natürlich auch schon vorhanden und eingetragen sein!):

Es öffnet sich ein Fenster wo ihr wenig später eine Maschine auswählen könnt:

Zybo_Linux_IP(1)

Danach könnt ihr ein image reccipe, also ein Rezept nach dem das rootfs gebaut wird, wählen.
Ein guter Ansatz wäre z.B. das Rezept core-image-minimal-dev zu wählen. Direkt nach der Auswahl erscheint in der unteren, rechten Ecke ein Button namens Edit image recipe, über den ihr das Rezept modifizieren könnt:

Zybo_Linux_IP(2)
Unter dem Reiter All recipes könnt ihr dann die Pakete, die ihr haben wollt, auswählen.
Wenn ihr fertig seid, klickt ihr auf Build packages. Nun beginnt Bitbake mit dem Bauen der Pakete.
Im Anschluss daran könnt ihr entscheiden welche Zusatzpakete ihr dabei haben wollt:

Zybo_Linux_IP(3)
Sobald ihr fertig seid, klickt ihr auf Build image um den Bauprozess zu starten.
Wenn der Prozess durchgelaufen ist findet ihr unter ../poky/build/tmp/deploy/images/zedboard-zynq7 euer rootfs als tar.gz-Datei. Dies kann nun auf einer SD-Karte o.ä. entpackt werden.
Ihr habt zudem noch die Chance euer Rezept zu speichern, sodass ihr später den Bauprozess wiederholen könnt.

 

Dokumentation:

 

-> Zurück zum FPGA + VHDL Tutorial

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Time limit is exhausted. Please reload CAPTCHA.