Ich arbeite an einer digitalen Schaltung mit diskreten Komponenten, um ein 640x480-VGA-Display in einem 80x30-Textmodus anzusteuern.
Bei einem 640 x 480-Display beträgt der Pixeltakt 25,175 MHz, was einer Periode von etwa 40 ns entspricht. Ich verstehe nicht, wie ich so oft ein neues Pixel für das Display bereitstellen soll.
Die grundlegende Architektur für meine Schaltung ist wie folgt:
Der Binärzähler für horizontale Pixel zählt bei 25,175 MHz bis 800 (640 sichtbare Pixel + 160 für Veranda, Synchronisation, Veranda). Bei 800 den vertikalen Zeilenzähler erhöhen (und bei 525 Zeilen zurücksetzen)
Leiten Sie unter Verwendung der horizontalen und vertikalen Position die x, y-Koordinate des aktuellen Zeichens ab.
Indizieren Sie mithilfe der x-, y-Koordinate des Zeichens in den Videospeicher, um das ASCII-Zeichen abzurufen.
Verwenden Sie das ASCII-Zeichen, um im Zeichen-ROM zu indizieren und ein Bitmuster für das Zeichen zu erhalten
Verwenden Sie das Parallel-Seriell-Schieberegister, um eine 8-Pixel-Zeichenzeile in einzelne Bits mit Pixeltaktfrequenz umzuwandeln
Wenn Sie der Kette folgen, lautet sie: Zähler -> RAM -> ROM -> Parallel zum seriellen Schieberegister
Unter Verwendung der schnellsten Komponenten, die ich finden kann, summieren sich die Ausbreitungsverzögerungen und die Zugriffszeit auf ungefähr 15 ns + 20 ns + 70 ns + 15 ns = 120 ns, viel mehr als die 40 ns-Periode für 25 MHz.
Bei noch höheren Auflösungen und Bildwiederholraten können Sie Pixeltakte weit über 100 MHz haben, was einer Periode von 10 ns entspricht.
Wie ist es möglich, alle 10 ns neue Pixel für das Display bereitzustellen, wenn nur die Zugriffszeit für RAM / ROM bereits deutlich darüber liegt, ohne auch nur alle anderen Signale in Ihrem System zu berücksichtigen?
quelle
Antworten:
Es gibt zwei Hauptgründe, warum Sie dies als Herausforderung empfinden.
Erstens verwenden Sie ältere und diskretere Teile (Integration in geringerem Maßstab), als dies im Zeitalter von VGA der Fall gewesen wäre.
Aber als nächstes verwenden Sie sie auf atypische Weise. Insbesondere bedeutet Ihr Ansatz nicht
pipelined
, dass Sie bei der Bestimmung Ihres Intervalls und damit der Rate mehrere Verzögerungen addieren müssen .Im Gegensatz dazu versuchen synchrone digitale Designs, die versuchen, Geschwindigkeit zu erreichen, so wenig wie möglich zwischen Registern zu tun.
Während sich die Details wahrscheinlich ein wenig unterscheiden würden, würde es grob gesagt ungefähr so funktionieren:
Wenn Sie eine Aufgabe wie diese aufschlüsseln, erhalten Sie nur eine kombinatorische Verzögerung plus eine gewisse Ausbreitungsverzögerung sowie Register-Setup- und Haltezeiten, die zwischen die Uhren passen müssen.
Ein auf diese Weise erstelltes Design benötigt viele Takte, um eine Ausgabe zu erzeugen - die Latenz ist tatsächlich höher als bei einem rein kombinatorischen Design. Es erzeugt jedoch bei jedem Zyklus eines viel schnelleren Takts eine neue korrekte Ausgabe.
Und hey, es ist ein Video, es spielt keine Rolle, ob die CRT ein Dutzend Pixel hinter dem Pixelzähler zeichnet - das berücksichtigen Sie natürlich beim Timing der Synchronisationssignale, damit sie im Vergleich zu den tatsächlichen Daten korrekt sind kommt aus dem DAC.
In der Praxis funktionieren fast alle komplexen digitalen Systeme auf diese Weise, da dies eine großartige Idee ist - bis eine Pipeline-CPU auf eine Abhängigkeit von einem früheren Rechenergebnis oder einem bedingten Zweig stößt ... Dann werden die Dinge interessant, wenn sie darüber sprechen in der nächsten Vorlesung einer Klasse für digitale Systeme - aber zum Glück ist Ihre VGA-Situation viel einfacher, insbesondere wenn Sie sich noch keine Gedanken über Zerreißeffekte machen, wenn sich der Zeichenpuffer während des Zeichnens des Bildschirms ändert.
Wenn Sie dies erstellen möchten, tun Sie dies praktisch in einem FPGA. Dadurch werden Ihnen ziemlich viele synchrone Speicher aufgezwungen, wenn Sie interne Speicher verwenden, oder synchrone E / A-Register, wenn Sie externen Speicher verwenden. Wenn Sie einen Fehler machen, müssen Sie nur mit den Daumen drehen, während er neu kompiliert wird, anstatt einen langen Tag mit der Neuverdrahtung zu verbringen .
quelle
Sie vergessen, dass ein Grafikadapter niemals nur ein einzelnes Pixel zeichnen würde - sondern zumindest eine vollständige Scanlinie. Dies wäre also ein vollständig pipelinisierbares Problem.
Vergessen Sie auch nicht, dass es bisher fünf Jahrzehnte Videoproduktionshardware gibt. Ihr Problem wird normalerweise mit einem speziellen RAM-Typ gelöst, in den Sie Ihre Buchstaben an einem Port rendern und der nacheinander in einen Videosignal-DAC ausgelesen wird. Diese Hardware ist viel, viel schneller als das, was Sie sich ansehen.
Nein, warum würdest du das tun? Sie würden Ihr Zeilenpixel einfach in einen zusammenhängenden Speicherbereich einfügen und linear an Ihren DAC ausgeben. Wenn es sich um eine CPU / MCU-Implementierung handelt, würden Sie dies nicht einmal Ihrer CPU überlassen, sondern einer programmierten DMA-Einheit nichts anderes zu tun, als einen Wert nach dem anderen zu nehmen und ihn beispielsweise an einen parallelen Datenport ohne CPU-Kerninteraktion auszugeben.
Ah, Sie möchten im laufenden Betrieb rendern - gute Wahl, aber ungewöhnlich bei modernen RAM-Kosten. Stattdessen würden Sie das Zeichen einfach vorher in einen Frame-Puffer rendern oder, wenn Ihr Gerät extrem schlank ist, die Zeichenzeile direkt an den DAC weiterleiten (siehe meine DMA-Erklärung oben).
quelle
Ganz abgesehen vom Pipelining (was genau das ist, was Sie tun sollten), fehlt Ihnen etwas Wichtiges ...
Das Parallel-In- und Serial-Out-Schieberegister taktet zwar mit ungeraden 25 MHz, aber wenn Ihre Zeichen beispielsweise 8 Pixel breit sind, liegt der Eingang bei nur ~ 3,2 MHz, was für die LS-Serie der VGA-Ära dennoch leicht zu erreichen ist Sie müssen das nächste Byte bereit haben, wenn das Schieberegister mit dem aktuellen beendet ist (hier kommt die Pipeline ins Spiel).
Erzeugen Sie einen Pixeltakt mit ~ 25 MHz und einen Speichertakt mit 1/8 davon, um den Textpuffer und das CG-ROM anzusteuern, und leiten Sie dann das Speicher- und CG-ROM-Zugriffsmaterial weiter.
Ein weiterer Trick: Die Textpufferausgabe wird für jede Zeile innerhalb einer bestimmten Textzeile wiederholt. Sie können also die 80 Byte Text in einen Ringpuffer takten und dann den RAM für die nächsten 7 Zeilen nicht mehr lesen (unter der Annahme einer 8) Zeilenzeichen), damit Sie den Speicher für die CPU freigeben können, wobei 80 Byte RAM an der Seite des Objekts erforderlich sind.
quelle
Das funktioniert also offensichtlich nicht. Du brauchst eine Pipeline.
1) Speichern Sie die Zeichen zusammenhängend im Speicher. Beginnen Sie oben links.
2) Rufen Sie während des Austastintervalls ein Zeichen ab. Rufen Sie weiterhin Zeichen in der Speicherreihenfolge ab.
3) Pipeline jedes decodierte Zeichen plus Zeilenindex in das ROM.
4) Pipeline der ROM-Ausgabe in einen Puffer.
5) Den Puffer in ein Schieberegister leiten. Lesen Sie die Pixel kontinuierlich in Intervallen von 40 ns aus.
(Das bedeutet, dass Sie alle 320 ns ein neues Zeichen in das Schieberegister laden müssen, was möglicherweise sogar möglich ist, ohne den gesamten Rest des Systems zu pipelinen.)
6) Kehren Sie während der horizontalen Austastung entweder zum Zeilenanfang zurück oder fahren Sie mit dem nächsten Zeichen fort (dh dem Beginn der nächsten Zeile).
Bonusfunktion: Da Sie nur alle 320 ns ein Zeichen benötigen, können Sie auch ein Zeichen + Farbpaar lesen und entweder MSDOS- oder Spectrum-Farbzeichen verwenden.
quelle