Posts mit dem Label VGA werden angezeigt. Alle Posts anzeigen
Posts mit dem Label VGA werden angezeigt. Alle Posts anzeigen

Samstag, 8. Februar 2014

VGA Karte mit CPLD

Ich wollte schon seit geraumer Zeit eine kleine VGA-Karte bauen. Nun habe ich einen ersten funktionierenden Prototypen. Herzstück ist ein XC95144XL - CPLD von Xilinx. Dieser kommt im TQFP-Gehäuse mit mindestens 100 Pins und fine-pitch, eine neue Herausforderung für meine Lötfähigkeiten :-)

Glücklicherweise hat das Löten problemlos geklappt- mit viel Flußmittel und Entlötlitze.
VGA-Board

Der entlötete IC war ein AVR ATmega128, mit dem ich den CPLD ansteuern wollte. Geplant war eine Daten-/Adressbus-Schnittstelle wie beim Motorola M68008-Prozessor. Das bedeutet, der Grafik-RAM wird mit einem 17-Bit breiten Adressbus, einem 8-Bit breiten Datenbus, einem /CS und R/W-Signal angesteuert.

Aus mir unbekannten Gründen ließ sich der AVR nicht programmieren, obwohl mein AVR-Dragon den Chip über die ICSP-Schnittstelle (der Wannenstecker auf der Platine) erkannt hat.

Mein Plan, den CPLD über die 68k-Schnittstelle anzusprechen, war also gescheitert. Aus dem CPLD-Verilog-Code habe ich die Schnittstelle rausgeschmissen und stattdessen eine simple SPI-Schnittstelle implementiert, worüber sich nun Daten in den Grafik-RAM schieben lassen. Den PIC32 auf dem roten Digilent-Board verwende ich, um Daten per Software-SPI in den CPLD zu schieben (die drei dünnen Kupferlackdrähte, die an den Pads des AVR aufgelötet sind).

Das SPI-Protokoll besteht aus 17+8 = 25 Bits. Die oberen 17 Bits sind Adressbits, die unteren 8 sind die Datenbits für die Pixelfarbe. Bei low-activen Chipselect shiftet der PIC32 die Adress- und Pixel-Bits in ein internes CPLD-Datenregister. Bei der dann folgenden steigenden /CS-Flanke legt der CPLD im Schreibzyklus die Daten- und Adress-Bits auf den Grafik-RAM-Bus und schreibt so die Daten in den RAM.

Pro Pixel müssen also 25 Bits in den CPLD geschoben werden. Bei einer Auflösung von 320x200 sind 64000 Pixel für ein vollständiges Bild zu schreiben, es müssen also 64000*25 Bits geschoben werden. Das begrenzt daher die Geschwindigkeit, mit der der RAM beschrieben werden kann, recht stark. Hinzu kommt, dass der CPLD mit 25 MHz getaktet ist, und daher nur SPI-Taktraten kleiner 12 MHz schaffen kann.

Prinzipielle Beschreibung

Die Grafikkarte besteht aus diesen ICs:

  • CPLD XC95144XL
  • 128 kByte SRAM AS7C31025C-10 (10 ns schnell)
  • Oszillator 25 MHz
Für ein VGA-Signal der Auflösung 640x400 benötigt man einen Pixel-Takt von 25,175 MHz (VGA-Timings siehe hier), eine Frequenz von 25 MHz tut es aber auch. Für jeden Pixel (bei 8-Bit-Farben) muss ein Byte aus dem SRAM ausgelesen werden, wofür bei der Frequenz also 40 ns zur Verfügung stehen. Das ist schon recht sportlich, die SRAMs, die man bei Reichelt findet, schaffen 55 ns oder 70 ns. Bei TME.EU habe ich aber schnellere SRAMs gefunden.

Um die Speicher- und Taktanforderungen zu senken, habe ich die Auflösung auf 320x200 halbiert. Im Prinzip verdoppel ich die Pixel horizontal und vertikal. Dadurch habe ich die doppelte Zeit, um den RAM auszulesen und nur ein Viertel des Speicherbedarfs.

Nur den RAM auszulesen, reicht aber ja nicht aus. Der Speicher muss auch beschrieben werden können. Dazu verdoppel ich den Takt, mit dem der RAM angesprochen wird. In "geraden Takt" wird der RAM ausgelesen, um die Daten aus dem Bildschirm anzuzeigen. Im ungeraden Takt können die Daten, die vom PIC32 reingeshiftet wurden, in den RAM geschrieben werden.

Testbild

Schaltung

Schaltung VGA-Karte

VGA ist analog. Daher müssen aus einer 8-Bit-Farbe drei analoge Farbkanäle (RGB) erzeugt werden. Die 8 Bits eines Farb-Bytes werden als RRRGGGBB-Farbe interpretiert. Für den roten und grünen Kanal werden drei Bits verwendet, für den blauen zwei.

Pixeldaten und RAM-Adressierung

Die begrenzte Makrozellen- und Registerzahl des CPLDs zwingen zu einer etwas verschwenderischen RAM-Belegung. Eine Bildschirmzeile besteht aus 320 Pixeln, was 320 Byte im RAM entspricht. Die naheliegenste Adressierung eines Bytes in Abhängigkeit der x-y-Koordinate wäre ja
addr = y*320 + x
Eine Multiplikation mit 320 lässt sich in dem CPLD nicht realisieren, bzw. ich wüsste nicht wie :-)
Eine Multiplikation mit 512 hingegen ist schnell gemacht, denn das entspricht einem Bit-Shift nach links um 9 Stellen. Und das ist mit nem CPLD überhaupt kein Problem. Eine Bildschirmzeile belegt daher 512 Pixel, wovon nur die ersten 320 Pixel belegt sind. Die hinteren 192 Pixel können zwar geschrieben werden, werden aber nicht dargestellt.

Palettenkompatible Bilddaten erzeugen

Das digitale RRRGGGBB-Farbsignal mit den dazugehörigen Widerständen gibt die darstellbaren Farben auf dem Bildschirm vor. Die R-Bits sind die niederwertigen Bits, die B-Bits sind die höchstwertigen. Ein Byte mit Wert 0 entspricht Schwarz, 255 ist weiß. Mit C# lässt sich ein Bild mit einer beliebigen Farbpalette und Farbtiefe in ein kompatibles Format mit ähnlichen Farben erzeugen. Folgender C#-Code erzeugt Byte-Daten mit dem gewünschten Pixelformat:

Bitmap bild = new Bitmap("sample.bmp");                         
Byte[] picdat = new Byte[bild.Width * bild.Height];             
for (int y = 0; y < bild.Height; y++)                           
{                                                               
for (int x = 0; x < bild.Width; x++)                    
{                                                       
Color col = bild.GetPixel(x, y);                
int r = col.R / 32;                             
int g = col.G / 32;                             
int b = col.B / 64;                             
byte rgb = (byte)( r | (g << 3) | (b << 6) );   
picdat[x + y * bild.Width] = rgb;               
}                                                       
}                                                               


Verilog-Code

Die CPLD-Sourcen findet man hier.

Ausblick

Mein langfristiges Ziel ist es noch weiterhin, einen kompletten kleinen Homebrew-Computer zu bauen. Das größte Problem war immer, ein sinnvolles Display an meine Systeme anzubinden. LCD-Displays in Smartphone-Größe sind mir zu klein und etwas farbig sollte es auch sein. Diese Grafikkarte erfüllt nun meine wichtigsten Anforderungen.
Auf meinem Wunschzettel stehen Sprites, um schnelle Animationen zu ermöglichen und Hardware-Cursor für einen Mauszeiger und Tastatur-Cursor. Das lässt sich aber wohl nicht mit so einem kleinen CPLD realisieren. Der Sprung zu einem FPGA ist da nur logisch, aber auch gleich nochmal ne Runde anspruchsvoller.

Sonntag, 5. Februar 2012

Extboard und VGA-Versuche

Gestern habe ich meine Z80-Erweiterungsplatine in Auftrag gegeben. Wie schon geschrieben, wird darauf ein AY-3-8912 zur Sounderzeugung mit Verstärker für direkten Lautsprecheranschluss sein und eine IDE-Schnittstelle (wie bereits für das alte Z80-Board implementiert).
Ein dsPIC30F6011 wird für die Anbindung von PS/2-Maus und Tastatur sorgen. Da noch so viel Platz auf der Platine zur Verfügung stand, habe ich mich entschlossen, mich mal an Ethernet zu probieren. Microchip hat den ENC28J60 im Programm, den angeblich weltweit kleinsten Ethernet-Controller. Über SPI wird der an den dsPIC angebunden. Microchip bietet auch gleich einen kompletten TCP/IP-Stack für deren µCs an. So ein Stack ist ziemlich groß, daher habe ich einen der größten dsPICs gewählt, damit neben dem TCP/IP-Stack noch ausreichend Platz für weiteren Code bleibt.
Das Extboard
Sollte alles wie geplant klappen, implementiert mein System dann Maus, Tastatur, IDE, Sound, RS232 und Ethernet. Natürlich ist dann erst einmal viel Software zu schreiben, aber dann fängt der Spaß ja erst richtig an :-)

Nun fehlt nur noch eine "Grafikkarte". Dazu hatte ich vor ein paar Monaten mal eine kleine Testplatine mit einem XC9572 CPLD erstellt, diese lag aber nur unbestückt im Karton herum. Nun habe ich sie bestückt und den CPLD mit Anregungen von http://www.fpga4fun.com/PongGame.html programmiert.

VGA mit CPLD

Die beiden Wannensteckerbuchsen sind zur Anbindung meines alten Z80-Boards. Der unbestückte IC-Sockel kann einen RAM-Baustein (gedacht als Grafikspeicher) aufnehmen. So sieht das Testbild aus, dass der CPLD erzeugt:

VGA-Output
Die Auflösung ist 640x480 bei 8 Farben. Ein paar Verilog-Code-Anpassung sind noch nötig, da das Bild sehr weit nach links verschoben ist. Das lässt sich aber einfach durch das V-Sync-Signal beeinflussen.

Schwieriger wird es, wenn der CPLD die Grafikdaten aus dem RAM holen muss, und den RAM-Zugriff des Z80 koordinieren muss. Ob der CPLD das alles aufnehmen kann?