FPGA-VGA-Puffer. Wie liest und schreibt man?

Ich habe ein Altera DE2-Board und versuche, Sprites zu zeichnen. Ich habe einige Probleme bei der Implementierung eines Bildschirmpuffers.

Ich habe eine Anzeigeeinheit, die mit einer Rate von 25 MHz Pixel für die VGA-Anzeige ausgibt.

Ich hatte gehofft, einen Puffer in SDRAM zu implementieren. Die ursprüngliche Idee war, Pixel das nächste Pixel mit einer Rate von 25 MHz aus dem SDRAM zu laden. Dies funktioniert, aber ich kann weder Pixel mit dieser Geschwindigkeit in das SDRAM schreiben noch den Bildschirm schnell genug für jeden neuen Frame löschen. Ich brauche 2 Takte, um Daten zu schreiben, und mein Board arbeitet mit 50 MHz, sodass ich gerade genug Zeit habe, um einen vollständigen Lesevorgang durchzuführen.

Ich würde annehmen, dass ich etwas furchtbar, furchtbar falsch mache. Wie wird eine solche Zeichenfläche normalerweise in VHDL implementiert?

Was ich am nächsten finden konnte, war die Verwendung eines 2-3-3-Farbschemas (RGB), um jedes Pixel abzurufen und während der VGA-Zeit "Veranda" (Ausblenden) auf den Arbeitsspeicher der Leinwand zu schreiben. Dies bedeutet, dass ich bei jedem der 25-MHz-Takte nur 15% des Bildschirms aktualisieren kann und ich irgendwie muss, dass meine Schaltung weiß, welche 15% sie aktualisiert?

Ich kann nicht herausfinden, wie man die doppelte Pufferung verwendet, weil ich nicht herausfinden kann, wie man beim Lesen Daten in den Speicher schreibt. Gibt es eine Möglichkeit, das Bit-Banging des Protokolls zu vermeiden? Wie macht dieser Typ das?

Geben Sie hier die Bildbeschreibung ein

@davidcary, mit einigen Details zur Herangehensweise an die doppelte Pufferung haben Sie die Frage beantwortet. Mir ist klar, dass das Zeit braucht, und ich kann nicht mit Steinen werfen, wenn jemand oft einen kurzen Witz als Kommentar abgibt, um dem Benutzer bei seinem Problem zu helfen, bis jemand eine qualitativ hochwertige Antwort schreiben kann.
Wenn Sie sagen "Gibt es eine Möglichkeit, das Bit-Banging des Protokolls zu vermeiden?", Vermutlich bedeutet dies, dass Sie den SDRAM-Controller nicht selbst geschrieben haben. Ich empfehle dies zu tun, es ist eine gute Übung, und Sie werden mehr darüber verstehen, wie SDRAM funktioniert und wie Sie seine Timings ausnutzen können.

Antworten (2)

Einige Ansätze, die für einige Anzeigestile nützlich sein können, bestehen darin, das Anzeigefeld in Kacheln zu unterteilen

  1. Beschränken Sie jede Kachel auf die Verwendung eines kleinen Satzes von Farben, wodurch die Verwendung von weniger als 8 Bits pro Pixel ermöglicht wird, oder
  2. Verwenden Sie ein oder zwei Bytes von jeder Kachel, um einen Ort auszuwählen, von dem Bitmap-Daten gelesen werden sollen.
Der erste Ansatz könnte die Rate reduzieren, mit der Daten aus dem Anzeigespeicher gelesen werden mussten. Wenn man beispielsweise Kacheln verwendet, die 16 x 16 groß sind und jeweils vier Farben haben könnten, die aus einem Satz von 256 ausgewählt werden könnten, dann könnte man ohne Verwendung von zusätzlichem RAM im FPGA die Anzahl der Speicherauslesungen pro 16 Pixel auf acht reduzieren (vier Farbwerte, plus vier Bytes für die Bitmap). Wenn man dem FPGA 160 Byte Pufferung/RAM(*) hinzufügt, könnte man die Anzahl der Speicherlesevorgänge pro 16 Pixel auf vier reduzieren, indem man alle 16 Abtastzeilen zusätzliche 160 Lesevorgänge verwendet, um den nächsten Satz Kachelfarben zu lesen. Wenn man 16 Farben pro Kachel haben wollte, würde der zweite Ansatz zusätzliche 640 Bytes RAM erfordern, es sei denn, man würde der Anzahl unterschiedlicher Paletten, die auf einer Zeile existieren könnten, einige Beschränkungen auferlegen.

Der zweite Ansatz würde wahrscheinlich eher die zur Erzeugung einer Anzeige erforderliche Gesamtspeicherbandbreite erhöhen als verringern, würde aber die Speichermenge verringern, die aktualisiert werden müsste, um die Anzeige zu ändern – man könnte ein oder zwei Bytes ändern, um ein 8x8 oder zu aktualisieren 16x16 Bereich des Bildschirms. Je nachdem, was Sie anzuzeigen versuchen, kann es hilfreich sein, bei diesem Ansatz ein Speichergerät zum Speichern der Kachelformen und ein anderes zum Speichern der Kachelauswahl zu verwenden. Man könnte zum Beispiel einen schnellen 32Kx8-RAM verwenden, um ein paar 80x60-Kachelkarten mit zwei Bytes pro Kachel zu halten. Wenn das FPGA keine Pufferung hätte, müsste es alle vier Pixel ein Byte lesen; Selbst mit einem statischen 40-ns-RAM bleibt der CPU genügend Zeit, um die Anzeige zu aktualisieren (ein vollständiger Bildschirm wäre nur 9600 Bytes groß).

Übrigens, wenn man kein 32Kx8-RAM hinzufügen möchte, sondern 320 Bytes Pufferung/RAM(**) zum FPGA hinzufügen könnte, könnte man einen Tile-Map-Ansatz verwenden, aber die CPU oder DMA 160 Bytes zuführen lassen Anzeige alle 8 Abtastzeilen. Das würde den Controller auch dann etwas belasten, wenn sich nichts am Display ändert, könnte aber die Schaltung vereinfachen.

(*) Der Puffer könnte als RAM oder als Folge von 32 40-Bit-langen Schieberegistern plus ein wenig Steuerlogik implementiert werden.

(**) Der Puffer könnte als zwei 160-Byte-RAMs oder als zwei Gruppen von sechzehn 80-Bit-Schieberegistern implementiert werden.

Sprites werden normalerweise nicht mit einem Framebuffer gemacht (so wie ich das Wort verstehe). Stattdessen vergleicht man die x- und y-Koordinate mit xmin,ymin und xmax,ymax des Sprites. Wenn die aktuelle Scanposition in das Sprite fällt, wird die relevante Farbe aus dem Spritespeicher ausgegeben.

Wenn Sie versuchen, einen Framebuffer anzuzeigen, fassen Sie sich ein Herz. Dies war mein erstes großes FPGA-Projekt. SDRAM sollte bei 100 MHz kein Problem sein (ich habe das zum ersten Mal vor etwa einem Jahrzehnt gemacht, und Silizium ist jetzt viel schneller), also multiplizieren Sie Ihren 50-MHz-Takt. Das Schreiben Ihres eigenen Controllers wird lehrreich sein :)

Das gibt Ihnen viel Bandbreite zum Spielen, Sie können dann den Puffer verdoppeln, kein Problem. 60Hz VGA benötigt durchschnittlich 18 Megapixel/Sek. Wenn Sie ein 16-Bit breites Gerät haben, haben Sie eine Spitzenbandbreite von 200 MB/s. Selbst wenn Sie es schaffen, nur 50 % effizient zu sein (was machbar sein sollte), sind das 100 Megapixel/s bei 16 Bit pro Pixel oder 50 Megapixel/s bei 32 Bit pro Pixel.

Beispielsweise kann es sein, dass Ihr RAM 60 ns benötigt, um einen Lesevorgang einzurichten, danach aber 8 Wörter in 80 ns platzen lassen kann - das sind 8 Bytes in ~ 140 ns. WENN Sie Ihren RAM dazu bringen können, längere Bursts auszuführen, würde dies dazu beitragen, die Kosten für die Einrichtung des Lesevorgangs zu amortisieren.

Basierend auf Ihrem Kommentar, dass es sich um einen Byte-seitigen RAM handelt, sind das etwas mehr als 50 MByte / Sekunde, nur 16 Megapixel / Sekunde bei 24 Bit pro Pixel :( Sie haben einfach nicht genug Bandbreite, um eine Echtfarbenanzeige zu machen, selbst bei VGA. Sie könnte ziemlich leicht 8 Bit pro Pixel machen, aber das sind nur 2 oder 3 Bit pro Farbe - was für Ihre Anwendung in Ordnung sein könnte, ich weiß es nicht.Oder Sie könnten eine 256-Farben-Nachschlagetabelle wie in den alten Tagen erstellen - danach Wenn Sie aus dem Frame-Puffer lesen, verwenden Sie den Wert, um in einer internen RAM-Uhr nachzuschlagen, um die 24-Bit (oder 18-Bit, die gut in ein BRAM passen würden) Farbe zur Ausgabe an den Monitor zu erhalten.

Die doppelte Pufferung funktioniert immer noch:

Zeigen Sie einen Rahmen von Adresse 0 an (lesen Sie einfach alle Pixel der Reihe nach aus und unterbrechen Sie die Lesevorgänge während der Austastintervalle).

Schreiben Sie Ihren nächsten Frame an einen anderen Ort. Für diese doppelte Pufferung muss Ihr DRAM-Controller zwischen den konkurrierenden Anforderungen der Lese- und Schreibkanäle Prioritäten setzen. Tipp, priorisieren Sie die Lesevorgänge, da sie zeitkritisch sind :)

Ist es im Allgemeinen besser, Lesevorgänge zu priorisieren, oder ist es besser, Daten in einen FIFO zu lesen, Schreibvorgängen Priorität zu geben, wenn sich eine bestimmte Datenmenge im FIFO befindet, und Lesevorgängen Priorität zu geben, wenn der FIFO-Pegel zu niedrig wird? Ich habe festgestellt, dass es zumindest beim Ansteuern von LCDs mit Taktsignal nützlich ist, das Lesetiming ziemlich "locker" zu lassen, vorausgesetzt, alle Daten werden rechtzeitig verschoben.
Danke für deinen Beitrag, vielleicht schaue ich mir das Buffering an. Aber ich denke, ich brauche 2 Takte (1/50 MHz), um Daten zu lesen, und ich habe auch ein 8-Bit breites Gerät.
@supercat: Ja, das ist eine fortschrittlichere Lösung, die möglicherweise erforderlich ist, wenn die Bandbreite erhöht wird.
@Misha: Es dauert eine Weile, einen Datenlesevorgang einzurichten, aber Sie können große Mengen auf einmal lesen.