Kampis Elektroecke

Processing System und GPIO

Zynq FPGA

Der Zynq kombiniert ein FPGA und einen Dual Core ARM Prozessor. Beide Komponenten können unabhängig voneinander programmieren werden, um sie so beliebig anpassen und einsetzen zu können. In diesem Artikel zeige ich, wie das Processing System (PS) des Zynq genutzt werden kann und wie man mit dem PS auf das FPGA zugreift.

Erstellen des Blockschaltbildes:

Zu allererst wird in Vivado ein neues Projekt erstellt. In dem geöffneten Projekt wird über das Menü Create Block Design ein neues Blockschaltbild mit dem Namen System erstellt:

Jetzt können mit einem Klick auf Add IP folgende IP-Blöcke eingefügt werden:

Anzahl Blockname
2x AXI GPIO
1x ZYNQ7 Processing System

Durch einen Klick auf einen der Blöcke erscheint links unten, neben dem Blockschaltbild, ein Fenster, in dem die Blöcke umbenannt werden können. Auf diese Weise werden die GPIO-Blöcke in LED und Schalter umbenannt.

Im nächsten Schritt wird mit einem Klick auf Run Connection Automation am oberen Rand des Blockschaltbild, ein Assistent gestartet, welcher die einzelnen Blöcke miteinander verbindet. Als letztes werden die Verbindungen der GPIO Blöcke nach außen geführt, indem mit der rechten Maustaste auf das Verbindungsstück des Anschlusses GPIO geklickt und anschließend der Menüpunkt Make External ausgewählt wird:

Durch einem Doppelklick auf den GPIO-Block wird ein Konfigurationsmenü geöffnet. Über dieses Menü wird der Wert GPIO Width auf Wert 4 gesetzt, wodurch der GPIO-Block einen 4-Bit breiten Ein- bzw. Ausgabeport führt.

Diese Schritte werden anschließend für den zweiten GPIO Block wiederholt. Zuletzt benenne ich die Ausgangssignale noch um. Die fertige Schaltung sieht jetzt folgendermaßen aus:

Als nächstes wird das Processing System konfiguriert, indem das Konfigurationsmenü mit einen Doppelklick auf den ZYNQ7 Processing System-Block geöffnet wird.

Über Import XPS Settings wird mit Hilfe der Konfigurationsdatei System.xml die Grobkonfiguration durchgeführt:

Nachdem die Einstellungen geladen worden sind, können einige unnötige Komponenten wieder entfernt werden.

Menü Komponente
MIO Configuration Memory Interfaces Quad SPI Flash
MIO Configuration I/O Peripherals ENET 0, USB 0, SD 0, I2C 0, USART 0

Mit einem Klick auf OK bestätigt wird die Änderung bestätigt. Das Processing System nimmt im Designer nun die folgende Form an:

Das Design wird anschließend gespeichert und nach einem Klick auf Project Manager wird das Menü Sources des Projektes geöffnet. Im Projektmanager wird dann die Datei System.bd ausgewählt und mit einem Rechtsklick das Menü geöffnet:

Es werden nun die Punkte Generate Output Products… und Create HDL Wrapper… ausgeführt um einen HDL-Wrapper für das Blockschaltbild zu erzeugen. Dieser Wrapper ist für die Erstellung des Bitstreams notwendig, da er das oberste Design festlegt (ähnlich wie die Funktion main in C) und zudem die Verbindung zwischen Peripherie und Prozessor beschreibt.

Sobald der HDL Wrapper erstellt wurde, kann über Run Synthesis die Hardwaresynthese gestartet werden. Dieser Vorgang dauert etwa 3-4 Minuten und in dem sich danach öffnenden Fenster wird dann auf Open Synthesized Design geklickt um das IO Planing zu starten:

Über das Menü WindowI/O Ports wird das Planungsmenü geöffnet, über das die Signale, welche in dem Blockschaltbild nach außen geführt wurden, den I/Os des FPGAs zugeordnet werden können.

Die Zuweisung wird gespeichert und anschließend mit einem Klick auf Generate Bitstream der Bitstream erzeugt. Sobald die Generierung des Bitstreams abgeschlossen ist, wird die sich öffnende Meldung mit OK bestätigt. Damit wäre das PS hardwaretechnisch fertig. Nun geht es an die Software für den ARM Prozessor!

Die passende Software:

Im ersten Schritt wird die Hardware, also der Bitstream, für das SDK exportiert, indem der Punkt Export aus dem Menü File ausgeführt wird. Dabei nicht vergessen den Haken bei Include Bitstream zu setzen.

Über den Button Launch SDK wird dann das Xilinx SDK gestartet. In dem geöffneten SDK wird im Project Explorer ein Ordner namens System_wrapper_hw_plattform_0 angezeigt. Dies ist der automatisch erzeugte Code für das Processing System im System Wrapper.

Über einen Klick auf File → New → Board Support Package werden die notwendigen Treiber und Informationen für die programmierbare Logik angelegt:

Dieses Fenster einfach mit einem Klick auf Finish und OK schließen. Anschließend wird über File → New→ New Application Projekt ein neues Projekt angelegt:

Über einen Klick auf Next kann ein Beispielprojekte ausgewählt werden. Da wir kein fertiges Beispielprojekt benötigen, wählen wir Empty Application aus und klicken auf Finish.

Der neu angelegte Ordner System wird nun geöffnet und das Verzeichnis src ausgewählt. Über File → New → Source File wird eine neue C-Datei mit dem Namen main.c erstellt:

In diese Datei müssen zuerst die notwendigen Headerdateien eingefügt werden:

Die erste Headerdatei beinhaltet die IDs und Adressen jedes IP-Cores die in dem Design integriert sind. Mit der zweiten Headerdatei werden spezielle Funktionen für den AXI-GPIO-Block eingefügt.

Bevor ein IP-Block verwendet werden kann, muss dieser initialisiert werden. In dem Reiter system.mss finden sich alle aktiven IP-Blöcke und dort kann zu jedem IP-Block eine Dokumentation geöffnet werden, in der die einzelnen Funktionen eines IP-Blocks nachgeschlagen werden können.

Der AXI-GPIO-Block wird über die nachfolgende Funktion initialisiert.

Bei der Variable Instanz handelt es sich um einen Zeiger vom Typ XGpio, der auf den fertig initialisierten GPIO-Block zeigt und bei jedem weiteren Funktionsaufruf für einen GPIO-Block mit angegeben werden. Die Variable ID ist die ID des jeweiligen Blockes. Sie dient der Identifikation und ist in der Datei xparameters.h zu finden.

Die Codezeilen zum initialisieren der beiden GPIO-Blöcke sieht damit folgendermaßen aus:

Im nächsten Schritt müssen die Blöcke als Ein- bzw. Ausgang gesetzt werden.

Dabei ist die Variable Instanz der vorhin initialisierte GPIO Block und die Variable Kanal der verwendete Kanal des GPIO-Blocks. Jeder GPIO-Block verfügt über zwei Kanäle, wobei nur der Erste verwendet wird.

Über Direction wird ein Pin als Eingang oder als Ausgang geschaltet. Der Wert 1 setzt einen Pin als Ausgang und der Wert 0 setzt den Pin dann als Eingang:

Die GPIO Blöcke sind nun fertig konfiguriert. Mit der nachfolgenden Funktion kann der Status der Eingänge abgefragt werden. was wir in einer while()-Schleife auch direkt machen:

Auf ähnliche Weise können die Ausgänge des GPIO-Blocks gesetzt werden:

Zur Übersicht noch einmal das komplette Programm.

Jetzt kann alles auf das Zybo übertragen werden. Dazu wird das Projekt im Project Explorer ausgewählt und anschließend das Menü RunRun Configurations ausgewählt. Danach wird durch einen Doppelclick auf Xilinx C/C++ application (System Debugger) ein neuer Eintrag angelegt.

Über den Button Run werden die Einstellungden anschließend ausgeführt. Es wird ein Reset des kompletten Systems durchgeführt und anschließend werden der Prozessor und das FPGA programmiert, sowie die Anwendung ausgeführt.

Das komplette Projekt steht in meinem GitLab-Repository zum Download bereit.

Last Updated on

Schreibe einen Kommentar

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