Kampis Elektroecke

VGA im FPGA – Ausgabe eines Bildes

Zynq FPGA

Da der VGA-Controller nun einsatzbereit ist, kann mit seiner Hilfe ein Bild ausgegeben werden kann. In diesem Beispiel wird der VGA-Controller genutzt um ein Bild aus einem Display-RAM auf dem VGA-Monitor darzustellen. Da das Zybo nicht genug RAM besitzt um 640×480 Pixel zu speichern, habe ich den Bildschirm in 8×8 große Felder unterteilt, auf denen dann ein Text geschrieben werden soll. Am Ende ist der VGA-Controller somit in der Lage 80×60 Zeichen auf dem Bildschirm darzustellen. 

Einbinden des VGA-Controllers:

Im ersten Schritt soll die vorhandene Schaltung des VGA-Controllers als Modul eingefügt werden. Dazu wird eine neue VHDL-Datei namens VGA_Top erstellt und der Clocking Wizard muss aus dem VGA-Controller in die Top-Datei verschoben werden, da die Erzeugung des Taktes nicht im VGA-Controller stattfinden soll.
Das ganze sieht dann so aus:

Die Eingangssignale des Top-Moduls unterscheiden sich geringfügig von denen des VGA-Controllers.
Im Top-Modul sind die Signale für die x- und y-Koordinaten nicht mehr vorhanden, da diese Signale jetzt direkt in die entsprechende Farbe am Monitor umgewandelt werden. Um diese Signale nach außen zu geben, ist ein Anschluss mit dem Namen Color hinzugekommen, welcher wie folgt aufgebaut ist:

  • Rot – Bit 0 bis Bit 4
  • Blau – Bit 5 bis Bit 9
  • Grün – Bit 10 bis Bit 15

Durch die Zusammenfassung der Farben in ein 16-Bit Signal habe ich mir das Speichern der Farbinformationen erleichtert, indem die Farbe eines jeden Pixels des Bildes nun durch einen 16-Bit Wert beschrieben wird.
Im nächsten Schritt wird dann der VGA-Controller instantiiert:

Da in dieser Schaltung Komponenten mit unterschiedlichen Verzögerungen zusammenarbeiten (VGA-Controller, RAM und ROM) ist es unerlässlich, dass die Signale zueinander synchron gehalten werden müssen. Dies ist im Code durch einen separaten Prozess realisiert, den ich hier nicht genauer erklären möchte.

Display-RAM:

Zusätzlich wird ein Display-RAM benötigt, welches in jeder Speicherzelle einen Buchstaben des Bildschirminhaltes speichern soll. Da jedes Zeichen zudem über eine 16-Bit Farbinformation benötigt, muss das Display-RAM eine Kapazität von 4800×24 Bit besitzen. Dabei sind die untersten 8 Bits die Adresse des ASCII-Zeichens und die übrigen 16 Bits sind die Farbinformationen.

Damit der VGA-Controller was anzeigt, habe ich das RAM bereits mit ein paar Zeichen gefüllt. Der Zeichencode x”FFFF76″ beinhaltet so z. B. ein weißes Zeichen (0xFFFF) mit dem Wert 0x76 (der Buchstabe “v”). Für zukünftige Erweiterungen habe ich das Display-RAM mit Schreibleitungen ausgestattet. Damit kann eine nachgeschaltete Logik auch Inhalte in das RAM schreiben (wird in dieser Beschreibung aber nicht berücksichtigt).

Font-ROM:

In dem Display-RAM sind lediglich einzelne Zeicheninformationen und keine Pixelmuster gespeichert. Um sich das Leben nicht all zu schwer zu machen, empfiehlt es sich, dass die gespeicherten Zeichen ASCII-codiert sind. Dies hat den netten Nebeneffekt, dass man den VGA-Controller später auch an das Processing-System anschließen und mittels Software ohne größere Umwege direkt den Text in das RAM schreiben kann.
Es wird also noch so etwas wie ein Font-ROM benötigt, welches das jeweilige ASCII-Zeichen aus dem Display-RAM in ein 8×8 Pixel großes Symbol umwandelt. Dieses Font-ROM habe ich über ein BRAM-ROM realisiert und dann im Code instantiiert:

Die einzelnen Zeichen habe ich mittels coe-Datei während der Synthese ins ROM geladen, wodurch das ROM direkt beschrieben wird.

Die komplette Schaltung:

Diese einzelnen Komponenten werden nun alle instantiiert.

Aus dem VGA-Controller kommen x- und y-Koordinaten des sichtbaren Bereichs. Mit diesen Koordinaten kann die aktuelle Adresse im Display-RAM berechnet werden:

Da jedes Zeichen 8 Pixel breit ist, kann durch die Betrachtung der oberen 7 Bits des Pixelcounters kann das aktuelle Zeichen bestimmt werden:

Die Adresse im Display-RAM wird dann durch die Addition der Zeichenadresse und eines Offsets, welcher von der akuellen Zeile abhängig ist, bestimmt werden:

Der Offset wird bei jedem Zeilenwechsel um eine Zeile, also 80 Zeichen, erhöht:

Einen Taktzyklus später gibt das Display-RAM dann das Zeichen, welches unter Display_Address gespeichert war aus. Das Zeichen im Display-RAM enthält als Informationen die Farben und den ASCII-Code. Beides wird jetzt voneinander getrennt:

Nun liegen die Farbinformationen und das aktuelle Zeichen vor. Im Font-ROM ist jedes Zeichen an der Stelle seines jeweiligen ASCII-Codes gespeichert. Das “v” mit dem ASCII-Code 0x76 ist also an der Adresse 0x76 gespeichert. Das Zeichen ist an der Speicherstelle als 8×8 Matrix gespeichert. Die untersten 8 Bits der x-Koordinate maskieren das Bit der jeweiligen Zeile des Zeichens:

Dabei ist die aktuelle Zeile (also der Wert von Adresse 0x76 für die erste Zeile, der Wert der Adresse 0x77 für die zweite, usw.) des Zeichens in ROM_Data gespeichert. Die ROM-Adresse für die Zeile des Zeichens setzt sich aus der Startadresse des Zeichens, also der ASCII-Wert – hier 0x76, und einem Zeilenoffset zusammen:

Damit ist die Schaltung komplett und kann synthetisiert und implementiert werden. Das Resultat sieht dann folgendermaßen aus:

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

→ Zurück zu FPGA + VHDL

Schreibe einen Kommentar

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