Ein DIR, das den Bildschirm löscht: die Jagd auf eine geteilte Sperre
Auf dem Neo6502 löschte im Hercules-Modus der erste DIR-Befehl nach dem Start den Bildschirm. Zwölf Korrekturen, elf durch Messung widerlegt, vier falsch kalibrierte Instrumente — und am Ende eine SDK-Sperre, die sich beide Kerne teilten und den Video-Interrupt um eine einzige Zeile verzögerte. Eine Zeile genügte.
Der Neo6502 verbindet einen 65C02 mit einem RP2040: Letzterer übernimmt alles Übrige, einschließlich der DVI-Videoausgabe, über die Bibliothek PicoDVI von Luke Wren. Unsere Firmware Trinity ergänzt einen «Hercules»-Modus: 720 × 350 monochrom innerhalb eines 720 × 480-Signals mit 60 Hz — ein nativer Modus, eine Bildschirmzeile pro Bildzeile, während der Modus 320 × 240 jede Zeile verdoppelt und damit doppelt so viel Zeit hat.
Der Fehler war reproduzierbar und absurd: MODE 1 funktionierte, und dann löschte das erste DIR das Bild. Das zweite lief unbemerkt durch. Die Tastatur reagierte während der Störung weiter. Nur ein Moduswechsel brachte das Bild zurück.
1. Was die Messung widerlegt hat, eine Hypothese nach der anderen
Der Unterschied zwischen beiden Befehlen war der FatFs-Cache: kalt liest der erste zweiundzwanzig Sektoren vom USB-Stick; warm fast nichts. Die Plattenlast war der Auslöser. Blieb die Frage, wie sie die Anzeige zerstörte.
Zuerst verdächtigten wir Speichermangel, dann zu wenige Puffer, dann die Busarbitrierung, dann einen Monitor, der die Synchronisation verliert. Jede Erklärung wurde von der nächsten Messung widerlegt, und zwei darauf gelieferte Korrekturen verschlimmerten den Fehler, bevor sie zurückgenommen wurden:
- Vier Zeilenpuffer statt zwei: aus dem schwarzen Bild wurde eine vollständige Blockade beider Kerne. Ein Nachbarprojekt, pico-pacPlus, dokumentiert das Gegenteil unserer Erwartung: «deeper line pools widen the window».
- Dem Videokern Vorrang auf dem Speicherbus geben: das Bild erlosch schon beim Eintritt in den Hercules-Modus, ohne einen einzigen Plattenzugriff.
- Den Takt von 270 auf 250 MHz senken, mit einem 720 × 540-Timing bei 50 Hz aus dem Fork ikjordan/PicoDVI. Keine Wirkung. Und doch war das die richtige Spur: wenn Bremsen nichts ändert, ist es kein Durchsatzproblem.
Wir probierten auch ein VGA-Timing 720 × 400 bei 70 Hz — das des Textmodus der MDA-Karten, genau 720 Pixel breit. Der Monitor meldete endlich einen Modus, was er bei 720 × 480 nie getan hatte (ein Timing für Fernseher). Doch das Bild kam schlecht heraus, und die Spur ging in Reserve.
2. Das Instrument verfälscht die Messung, viermal
Die Debug-Schnittstelle der Firmware hat nie funktioniert: die Leitung lag nicht am Erweiterungsstecker. Die gesamte Instrumentierung lief über die SWD-Sonde, die den Speicher des RP2040 im Betrieb liest. Wir schrieben ein Werkzeug, das auf der Maschine tippt, indem es direkt in die Tastaturwarteschlange der Firmware schreibt, und die Zähler ausliest — brauchbar bei schwarzem Bild.
Dieses Instrument hat uns viermal belogen, und jede Lüge kostete eine Hypothese:
- Den zweiten Kern an die Sonde zu hängen lässt den Encoder auf 15 Bilder pro Sekunde fallen und fabriziert eine Störung, die es nicht gibt. Eine ganze Messreihe war zu verwerfen.
- Ein schlecht kalibrierter Überlaufzähler stand im Normalbetrieb bei 96 000 pro Sekunde: er ignorierte, dass jeder DMA-Kanal einmal pro Block seiner eigenen Spur auslöst.
- Der Latenzzähler maß die vertikale Austastlücke — 1 462 µs, immer derselbe Wert, im Ruhezustand wie unter Last. Es musste nach Dauer gefiltert werden, nicht nach Zeilennummer, denn die Firmware verschiebt diesen Zähler beim Start absichtlich um zwei Zeilen.
- Ein
resetüber die Sonde hinterlässt die Platine in einem Zustand, in dem der Hercules-Modus beide Kerne blockiert. Mehrere Messungen wurden so genommen, ohne diesen Faktor zu kontrollieren.
Angenommene Regel: für jede Messung von einer Spannungsunterbrechung ausgehen, nie von einem Software-Reset. Und nie zwei Versuche vergleichen, die nicht vom gleichen Zustand starten.
3. Was die Blackbox zeigte
Alle Messungen entstanden danach, wenn die Störung schon saß. Also setzten wir einen Flugschreiber ein: genau im Moment, in dem die Abweichung erkannt wird, und vor jeder Reparatur, kopiert die Firmware den vollständigen Zustand der sechs DMA-Kanäle und der Video-Zustandsmaschine.
Das Ergebnis war eindeutig. Für jede der drei TMDS-Spuren nutzt PicoDVI einen Daten-Kanal und einen Steuer-Kanal, der ihn umprogrammiert. Während der Störung trugen zwei Datenkanäle die Konfiguration einer anderen Spur — falsches Anforderungssignal, falsche FIFO. Zwei Kanäle schrieben in dieselbe FIFO, ein dritter bekam nichts mehr. Die Blocklisten im Speicher waren dagegen unversehrt: der Fehler lag nicht in den Daten, sondern in ihrem Laden.
Ein Foto des Bildschirms vervollständigte das Bild besser als zehn Messungen: nach einem DIR stand der Text doppelt, in einer blauen und einer gelben Kopie, etwa vierzig Pixel auseinander. Gelb = Rot + Grün: die anderen beiden Kanäle liefen untereinander noch gleich, nur der blaue verschob sich. Blau ist die Synchronisationsspur, die einzige mit vier Steuerblöcken, wo die anderen zwei tragen. Eine Verschiebung um einen Rang bewegt sie nicht gleich weit: die drei Spuren laufen auseinander.
4. Den Fehler harmlos machen, bevor man ihn versteht
Daraus entstand eine Korrektur, die die Ursache nicht behebt: allen Spuren gleich viele Blöcke geben, indem die Austastlücke der beiden Datenspuren genauso zerteilt wird wie die der Synchronisationsspur. Ein Überlauf verschiebt sie dann alle mit derselben Phase, und das Bild bleibt aus einem Stück.
Auf der Platine, mit vier DIR hintereinander: der Überlauf trat weiter auf (fünfzehnmal), aber die drei Spuren bewegten sich gemeinsam — null Phasenversatz — und der Text war scharf. Der Fehler war unsichtbar geworden.
Das ist nützlich, aber nicht befriedigend. Die eigentliche Frage blieb: warum lagen die Steuerkanäle eine Runde vorn?
5. Die Ursache: eine Sperre, die sich beide Kerne unwissentlich teilten
Die endlich richtig gefilterte Latenzmessung ergab eine bescheidene und entscheidende Zahl. Zwischen zwei aktiven Zeilen liegt das nominale Intervall bei 32 µs. Während des DIR war der schlechteste Wert 63 µs — also eine einzige Zeile Verzug, niemals mehr. Keine lange Blockade, kein kritischer Abschnitt von hunderten Mikrosekunden: eine Zeile.
Und eine Zeile genügt. Während dieses Verzugs beenden die Datenkanäle ihren Block und verketten zu ihren Steuerkanälen, die dann von selbst die Liste durchlaufen — ohne dass jemand darum bittet. Sie liegen eine Runde vorn, die Spuren verschieben sich, und der Rest folgt.
Blieb, was den Interrupt aufhielt. Die Antwort lag in den Sperrnummern. PicoDVI schützt seine vier Warteschlangen mit Spinlocks, die über next_striped_spin_lock_num() beschafft werden. Diese SDK-Funktion verteilt die Sperren 16 bis 23 der Reihe nach an alle Aufrufer — Warteschlangen, Mutexe, Alarm-Pools und was Bibliotheken sonst nutzen. Auf der Platine ausgelesen: PicoDVI hatte die Nummern 18 und 19, die FatFs und der USB-Stack vom anderen Kern aus nehmen konnten.
Das entscheidende Detail steht in der SDK-Dokumentation: spin_lock_blocking beginnt damit, die Interrupts abzuschalten auf dem wartenden Kern. Als der Anwendungskern die Sperre während eines Plattenschubs hielt, wartete der Videokern darauf — blind. Sein Zeileninterrupt kam eine Zeile zu spät.
Das SDK dokumentiert den Kompromiss ohne Umschweife: diese Sperren sind geteilt, und die Nummern innerhalb eines Bereichs zu verteilen «verringert die Wahrscheinlichkeit», dass zwei Nutzungen auf dieselbe fallen. Wahrscheinlichkeit, keine Garantie.
6. Die Korrektur passt in eine Zeile
// vorher
dvi_init(&dvi0, next_striped_spin_lock_num(), next_striped_spin_lock_num());
// nachher
static int slTmds = -1, slColour = -1;
if (slTmds < 0) { slTmds = spin_lock_claim_unused(true); slColour = spin_lock_claim_unused(true); }
dvi_init(&dvi0, slTmds, slColour);
Reservierte Sperren, die niemand sonst nehmen kann (Nummern 24 und 25, außerhalb des geteilten Bereichs). Auf der Platine gemessen, kaltes DIR über zweiundzwanzig Sektoren, zwei Versuche, einer davon nach einer Spannungsunterbrechung:
| vorher | nachher | |
|---|---|---|
| Kanalüberläufe | 9 bis 15 | 0 |
| Intervall zwischen zwei Zeilen | 63 µs | 33 µs (der Nominalwert) |
| verspätete Zeilen während des DIR | +10 bis +16 | +2 |
| Bild | schwarz oder doppelter Text | einwandfrei |
Der Fehler tritt nicht mehr auf. Das ist kein Umweg mehr.
7. Warum es niemand anders gesehen hat
Wir haben nachgesehen, wie die anderen PicoDVI-Projekte die Bibliothek initialisieren: das Original, der Fork ikjordan, der Fork xep80, pico-pacPlus. Alle übernehmen das Muster des Ursprungsbeispiels, next_striped_spin_lock_num() — und es passt ihnen. Der Kompromiss des SDK zeigt sich nur in einer besonderen Lage, die drei Bedingungen zusammenbringt:
- anhaltende Ein- und Ausgabe auf dem Anwendungskern — ein Schub von Sektoren, kein einzelner Lesezugriff;
- ein nativer Videomodus, ohne Zeilenverdopplung und damit ohne Aufholreserve;
- die Zuteilung, die beiden Subsystemen dieselbe Sperre gibt.
Emulatoren, die Programme von einer SD-Karte laden, bringen die ersten beiden zusammen. Wir wissen nicht, ob andere auf diesen Fall gestoßen sind: man braucht eine Sonde und einen Zähler für den Vorlauf der DMA-Kanäle, um ihn zu sehen, und wir haben ihn nirgends beschrieben gefunden. Genau darum gibt es diesen Artikel — wenn jemandem die Signatur bekannt vorkommt, erspart sie ihm die zwei Tage, die sie uns gekostet hat.
8. Was diese Jagd hinterlässt
Zwölf Korrekturen, elf widerlegt. Vier falsche Messungen, die neu kalibriert werden mussten. Und zwei Dinge, die den Fall wirklich geöffnet haben, keines davon aus einer Messung: ein Foto des Bildschirms, das zeigte, dass sich nur eine Spur verschob, und das Auslesen der Sperrnummern, das die Ursache lieferte.
Eine Korrektur, die an ihren eigenen Zählern gemessen wird, ist eine nicht geprüfte Korrektur. Zweimal in derselben Nacht standen alle Anzeigen auf Grün, während der Bildschirm nichts zeigte.
Die Signatur dieses Fehlers ist es wert, behalten zu werden, denn sie ist typisch für den RP2040: eine Verzögerung von genau einer Zeiteinheit des Echtzeitsystems — hier einer Zeile — unabhängig von der Taktfrequenz. Es ist kein Durchsatzproblem: es ist jemand, der eine Sperre hält.
Quellen & Links
- Wren6991/PicoDVI — die ursprüngliche Bibliothek, DVI per Software auf dem RP2040.
- ikjordan/PicoDVI — Fork mit 50-Hz-Modi und Breiten von 720 Pixeln.
- pico-pacPlus — verworfene Puffer, Stillstand beider Kerne, rote Linien: ein benachbarter Fehler, dokumentiert.
- pico-sdk — hardware_sync — die „striped“-Sperren und ihr Kompromiss, in den Kommentaren des SDK selbst.
- neo6502.com — die Platine von Olimex, Firmware von Paul Robson.