8-Bit-Breadboard-Computer auf Basis einer 6502-CPU - wie hoch kann ich die Taktfrequenz schrauben?
Bisherige Artikel dieser Serie - hier könnt ihr nochmal alle Grundlagen nachlesen, falls ihr jetzt erst einsteigt:- Digitale Logik und Logikgatter einfach erklärt
- Verwendung des 555-Timer als Taktgeber / Clock
- Das Clock-Modul: Taktgeber für unseren Breadboard-Computer
- Speichertypen und Zugriff auf Speicher
- Erste Schritte mit der CPU
- Eine echte WDC W65C02-CPU
- Das Speicher-Modul: Anbindung von RAM und ROM
- Erstes Programm in Maschinensprache: RAM-Test
- Das Sniffer-Modul: Ein Arduino/STM32 zeigt an, was auf dem Bus los ist.
- Erstes Ausgabegerät: Adressierung und Ausgabe auf 8 LEDs
- Programmiersprache-Evolution: von Maschinensprache zu Assembler
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 1: Taktungsprobleme
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 2: 20 Nanosekunden, die nicht sein sollten
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 3: Assembler-Programme
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 4: neue Adressierungsarchitektur mit 74HC688 und 74HC138
- 3-fach 7-Segment-Anzeige als dezimale Ausgabe, Teil 5: Programmierung in Assembler
- 3x16 Zeichen LCD DOGM163 als Textausgabe-Gerät, Teil 1: Breadboard-Aufbau und erste Programmversion
- 3x16 Zeichen LCD DOGM163 als Textausgabe-Gerät, Teil 2: Debugging
- Clock-Modul upgraden: höherer Takt (2 KHz) und Frequenzgenerator (bis 160 KHz)
- Erstes Eingabe-Gerät: ein 4x4 Keypad als Tastatur (wird mit 65C22 abgefragt)
- Erweiterung um eine Debug-Anzeige des Prozessorstatus und der Register auf dem LCD
- Die Tastatur spinnt: Hardware-Debugging, Fake-6522 aus China und mehr
- Assembler-Programmierung: Dezimal Hexadezimal Binär Konverter und Spiel
- Ein Arduino ATmega328p dient dem Breadboard Computer als CoProzessor
- Integration eines CoProzessors auf Arduino-Basis
- Assembler-Programmierung: weitere Funktionen des CoProzessors auf Arduino-Basis abfragen
- Selektives Interrupt Handling über den VIA 6522
Aber rekapitulieren wir erst einmal den Stand der Dinge und der jetzigen Takt-Steuerungsmöglichkeiten auf dem Breadboard:
- manueller Takt
Mit dem grünen Button oben links können wir den Prozessor einen einzelnen Takt geben. Das macht nur beim Debuggen an einer bestimmten Stelle im Programm Sinn. Ansonsten würde wir eine sprichwörtliche Ewigkeit brauchen, bis allein die Initialisierungssequenz durchgelaufen ist. - automatischer Takt
Mit einem Schalter können wir zwischen manuellem Takt via Taster und automatischem Takt bis 2 KHz wählen. Durch eine RGB-LED wird angezeigt, welche Takt gewählt ist: grün für Einzeltakt und blau für Automatiktakt. Die Generierung des Taktsignals erfolgt durch eine entsprechende NE555-Schaltung. In der ersten Version konnte wir mit einem Potentiometer die automatische Taktfrequenz zwischen 3 und 500 Hertz einstellen.
Später kam ein das Debug-Modul, genannt Sniffer hinzu, das der 6502 CPU auf die Finger schaute und Adress- und Datenbus und die deassemblierten OpCopdes via eines STM32 auf einem Display ausgab. Als wir feststellten, dass der Sniffer bis 2 KHz zurückkommt, ohne ins Stolpern zu geraten, haben wir die NE555 Schaltung für 2 KHz angepasst. Die automatische Mindestfrequenz stieg damit auf 7 Hz, was zum Mitlesen noch ausreichend langsam ist. Und wir haben ja immer noch unseren manuellen Takt. - automatischer Takt via Frequenzgenerator
Als nächste Erweiterung kam dann ein Frequenzgenerator hinzu, mit dem ein Takt frei zwischen 1 Hertz und 160 Kilohertz einstellbar war. Das Einstellen ging zwar nicht so schnell wie mit dem Poti, dafür aber sehr genau und halt mit höheren Frequenzen. Über 2 KHz ist der Sniffer allerdings nicht mehr verwendbar. Also bekam auch der einen Ein/Aus-Schalter, um Strom zu sparen. Genau wie die Frequenzgenerator selbst, wenn der nicht in Verwendung sein würde.
Aber wir haben auch noch nicht wirklich so richtig etwas berechnet, was längere Rechenzeit in Anspruch nehmen würde. Wollte wir Pi auf 100 Stellen ausrechnen, müssten wir sicher eine lange Zeit warten.
Darum will ich heute mal schauen, wie hoch ich die Frequenz drehen kann, bevor der Breadboard Computer aussteigt. Das ist sicherlich nicht bei allzuhohen Frequenzen der Fall, denn die Breadboards bestehen aus einzelnen dicht nebeneinander liegenden Metallklammern, die empfänglich sind für Einstrahlungen und die eine gewisse eigenen Kapazität und Widerstand haben.
Die 6502 CPU und der 6522 VIA von WDC können bis zu 14 MHz getaktet werden. Das ist schon mal sehr viel. Ist die Frage, ob die Logik-Chips zur Gerätansteuerung da noch mitkommen. Und natürlich der Computer selbst mit seines Breadboards und langen Leitungen.
Oszillatoren
Um noch höhere Taktfrequenzen könnten man sich einen sündhaft teuren Frequenzgenerator kaufen oder es machen wie ich. Ich habe mir in MegaHertz-Schritten einen Satz von Oszillatoren gekauft. Da kostet das Stück ca. 1 Euro, also habe ich mich mit Werten zu 1, 2, 4, 6, 8, 10, 14 und 16 MHz eingedeckt. 16 MHz wäre schon über den 14 MHz, die die CPU maximal kann, aber einen Versuch ist es eventuell wert.
Ein Oszillator ist ein schwingender Quartz samt der erforderlichen Schaltung, um daraus ein auf- und abschwellendes Signal zu machen. Das hat man alles zusammen in ein kompaktes Gehäuse, meist in einem rechteckigen Blechgehäuse eingebaut. Ein Oszillator hat drei aktive Anschlüsse: VCC, GND und Signal. Der vierte Anschluss dient rein der mechanischen Befestigung.
Mein 8 MHz-Oszillator liefert das folgende Signalbild:

Eine Wellenlänge dauert laut Oszilloskop 125 Nanosekunden, das sind eine Achtel Mikrosekunden, was 8 Millionen Schwingungen pro Sekunde entspricht. Stimmt exakt. Über die Wellenform lässt sich diskutieren. Ich würde das eine mit Sinuskurven emulierte Rechteckkurve nennen. Die ist allemal gut genug zur Taktung unserer CPU.
Nachdem ich das bisherige Takt mit 160 KHz über den Frequenzgenerator ausgereizt habe, platziere ich den ersten Oszillator mit 1 MHz und einen weiteren Schalter auf dem Breadboard, mit dem ich zwischen Frequenzgenerator und Oszillator umschalten kann. Langsam wird es kompliziert mit den ganzen Schaltern. Vielleicht sollte ich mir eine Gedächtnisstütze zeichnen...

1: Ein/Ausschalter Sniffer
2: Ein/Ausschalter Frequenzgenerator
3: Umschalter zwischen manueller Takt und NE555-Takt (7 Hz bis 2 KHz)
4: Umschalter zwischen NE555-Takt (7 Hz bis 2 KHz) und Frequenzgenerator-Takt (1 Hz bis 160 KHz)
5: Umschalter zwischen Frequenzgenerator-Takt (1 Hz bis 160 KHz) und festem Oszillator-Takt (1 Mhz bis 16 Mhz)
6: RGB-LED Anzeige manueller Takt (grün) oder NE555-Takt (blau)
7: Taster zum manuellen Takt (ein Druck entspricht einem Takt)
8: Poti zum einstellen des NE555-Taktes (ganz links:7 Hz; ganz rechts: 2 KHz)
9: blaue LED zur Anzeige des NE555-Taktes
10: weiße LED zur Anzeige des gewählten Ausgangstaktes
Software-Einstellungen
Damit das Timing stimmen kann, muss in der Software die Taktfrequenz eingegeben werden. Nach den dort festgelegten Paramteern richten sich dann die delay-Routinen, die zukünftigt benutzt werden müssen, damit das Timing bei der angegebenen Taktfrequenz passt.Die Definition der Taktfrequenz erfolgt über folgende Zeilen am Anfang des Source-Codes:
; ?? KHz / 8 = ?? NopsPerMs ; wieviele NOPs braucht es, um eine Millisek. voll zu machen?
TaktfrequenzKHz:
ASCII 8000
Byte 0
; wieviele NOPs braucht es, um eine Millisek. voll zu machen?
NopsPerMs: equ 250 ; TaktfrequenzKHz / 8 (250 = 2 MHz)
NopsPerMsMult: equ 4 ; TaktfrequenzMHz / 2 und NopsPerMs = #250
; und bei höheren Taktraten: wieviele NOPs pro 10 Mikrosek. (CentiMilliSek.) (min. 1)
NopsPerCMs: equ 10 ; NopsPerMs * NopsPerMsMult / 100
Die Angabe TaktfrequenzKHz ist rein kosmetischer Natur. Sie wird ggf. im LCD angezeigt. Wichtig für die richtigen Pausen und das richtige Timing ist der Wert NopsPerMs. Dieser bestimmt, wieviele NOPs-OpCodes es braucht, damit eine Millisekunde in der verwendeten delay-Routine vergeht. In der Routine läuft eine Schleife.
delayMs: ; NopsPerMs setzen. Dann gilt: delay (ms)
phx
phy
sta millisecs
ldy #NopsPerMsMult ; Wie oft muss die Schleife durchlaufen werden
; (weil 250 NOPs nicht mehr reichen; alles > 2 MHz)
delayMsMult:
lda millisecs
delayMs2:
ldx #NopsPerMs ; NopsPerMs mal warten, 1 ms verstreichen lassen
delayMs3:
NOP ; Zeit verschwenden, 1 NOP = 2 Taktzyklen
dex
bne delayMs3
dec A
bne delayMs2
lda #NopsPerMsMult ; keinen Mult verwendet
beq delayMsDone
dey ; Mult (min. 2) verwendet, dann runterzählen
bne delayMsMult
delayMsDone:
ply
plx
rts
Bei 255 ist bei NopsPerMs allerdings Schluss, denn das ist ein 1-Byte-Wert. Und da dieser Wert der Taktfrequenz in KHz durch 8 entsprechen muss, entspricht ein NopsPerMs-Wert von 250 gleich 8 x 250 KHz = 2000 KHz.Über 2 MHz reicht der einfacher NopsPerMs-Wert nicht mehr aus und wir müssen die Schleife nochmals wiederholen. Dazu dient der NopsPerMsMult-Wert. Wenn NopsPerMs auf 250 steht und wir eine vielfache von 2 MHz haben, stellen wir hier einfach die Taktfrequenz in MHz durch 2 ein. Das wird auch für die höchstmöglichen Taktfrequenzen ausreichend sein.
Außerdem gibt es noch den Wert NopsPerCMs. CMs steht für Centi-Millisekunden, also für 10 Mikrosekunden. Dieser Wert ist für eine weitere delay-Scheife nützlich, die in 10 µs Intervallen funktioniert. Das Minimum ist damit dann nicht mehr eine Millisekunde, sondern 10 Mikrosekunden. Das ist sinnvoll für die Pausen zwischen den LCD-Befehlen, die ja 20 µs sein müssen. Hier immer 1 ms nehmen zu müssen, wäre Zeitverschwendung und damit Geschwindigkeitseinbuße. Sinnvoll wird der Einsatz der delayCMs erst bei höheren Taktfrequenzen, bei dem der NopsPerMsMult-Wert über dem Minimum von 1 liegt, also bei Frequenzen über 2 MHz.
delayCMs: ; NopsPerCMs setzen. Dann gilt: delayMicroseconds*10 (CMs)
; CMs steht für CentiMilliSek = Hunderstel Millisek.
phx
delayCMs2:
ldx #NopsPerCMs ; NopsPerCMs mal warten, 1 ms verstreichen lassen
delayCMs3:
NOP ; Zeit verschwenden, 1 NOP = 2 Taktzyklen
dex
bne delayCMs3
dec A
bne delayCMs2
plx
rts
Herantasten an die Maximalfrequenz
Jedesmal, wenn ich also einen neuen Oszillator auf das Board stecke, muss ich den Assembler-Source anpassen, ihn neu übersetzen lassen und neu aufs EPROM brennen.Bei der Maximalfrequenz des Frequenzgenerators von 160 KHz funktioniert alles wunderbar. Danach setze ich den ersten Oszillator mit einem Megahertz ein und auch damit funktioniert der Breadboard Computer nach Anpassung des Source-Codes zur Zufriedenheit.
Weiter geht es mit dem 2 MHz , dem 4 Mhz und dem 8 Mhz Oszillator. Auch das macht der Breadboard Computer alles klaglos mit. Was ich schon erstaunlich finde bei der Qualität der Breadboards und der langen und vielen Kabel. Wir haben hier ja keine professionelle Platine mit auf Kürze optimierte Leiterbahnen vor uns liegen. Dennoch befinden wir uns mit der Taktfrequenz schon über der eines Commodore Amiga mit ca. 7.1 MHz.
Bei 10 MHz steigt der Computer allerdings aus. Auf dem LCD wird nichts mehr ausgegeben und auch die Initialisierung der 7-Segment-Anzeigen schlägt wohl fehl, denn manchmal bleiben nach einem Reset Reste darauf stehen, obwohl die Initialisierungssequenz diese eigentlich löschen sollte.
Kein Wunder, dass mein Test mit dem 14 MHz Oszillator auch fehl schlägt. Das wäre die Frequenz, die der 6502 und der 6522 maximal vertragen würden laut Spezifikation.
Aber ich glaube, mit den 8 MHz kann ich sehr zufrieden sein. So viel hätte ich eigentlich nicht erwartet. Hat sich die Mühe beim Drahtbiegen und Kabel verlegen also doch gelohnt. Ich glaube, mit freien Verdrahtung mit Jumper-Kabeln könnte ich nicht eine so hohe Frequenz erreichen. Wahrscheinlich wäre ich vor lauter Kabelgewirr auch nie so weit gekommen, so viele Dinge auf/im dem Breadboard Computer unterzubringen, sondern wäre vorher an der Fehlersuche verzweifelt, weil ich irgendwann mal aus Versehen an einem Kabel hängengeblieben und das aus dem Breadboard gerupft hätte.
Was noch nicht ganz perfekt funktioniert, ist das KeyPad, das prellt ab und zu mal. Aber ich glaube, dass kann ich softwaretechnisch lösen.
Und dann wäre da natürlich immer noch das Problem mit dem CoProzessor, der nicht so will, wie ich das will. Irgendwas hakt in dem Arduino ATmega328P und das lässt sich nicht so einfach finden. Da muss ich wohl noch ganze Testreihen durchführen, bis das Teil stabil läuft. Aber zum einmal Zufallszahl, Temperatur, Luftfeuchtigkeit, Datum oder Zeit abzurufen, dafür taugt es ja jetzt schon. Man sollte es halt nur nicht in einer Dauerschleife tun. Trotzdem bleibe ich an dem Problem natürlich dran.
Video
Die Tests mit den unterschiedlichen Frequenzen habe ich natürlich wieder in einem Video dokumentiert. Viel Spaß beim Schauen.