In deze aflevering laat ik zien dat niet alleen het programmeren van een simpele microcontroller complex is geworden. Door het uitfaseren van chips die in de jaren tachtig gebruikt werden, zitten we qua hardware ook in een spagaat van complexiteit...
Henk van de Kamer
In nummer 317 en 318 liet ik via een Breaduino – de microcontroller van de Arduino Uno op een breadboard – zien hoe complex het programmeren van een simpele microcontroller ondertussen is geworden. Ik sloot af met de volgende wens:
‘Ik wil daarom een nieuw fundament waarvan ik exact weet hoe elke tussenstap is ontstaan. Uiteraard zodanig dat iedereen dat desgewenst kan reproduceren.’
Die wens voert uiteindelijk terug naar het moment dat een gloednieuwe Commodore 64 op mijn bureau stond. Al vrij snel vroeg ik mij af hoe een ‘dood’ systeem een Basic-prompt op het scherm kan toveren (https://www.hetlab.tk/idee/bootstrap-computer). Met ‘dood’ geef ik aan dat een computer (nog) niet zichzelf kan programmeren om bijvoorbeeld die Basic-prompt te tonen. In essentie is mijn vraag dus hoe een computer de door ons mensen bedachte software ophaalt en vervolgens gaat uitvoeren.
Simpel?
Het antwoord lijkt simpel. Zodra een processor onder spanning wordt gezet, wordt als eerste een circuit geactiveerd die de processor initialiseert. Elke processor heeft een PC-register (Program Counter) die wijst naar een geheugenlocatie waar de volgende instructie opgehaald moet worden. De 6510 – de processor in de Commodore 64 – initialiseert deze met de waarden in geheugenlocatie $fffc en $fffd. In een 80x86 processor is $f000:fff0 hard gecodeerd. Het initialiseren van het PC-register noemen we ook wel de reset vector.
Toen ik dit ‘simpele’ antwoord lang geleden ontdekte, ontstond uiteraard de volgende vraag: waar komen de instructies in die geheugenlocatie vandaan? Of beter: hoe komen ze daar terecht. Wellicht is dit te abstract. Laten we eens teruggaan naar het moment waarop de eerste transistor werd gemaakt. Er waren toen al computers, maar voor dit gedachtenexperiment doen we alsof die nooit hebben bestaan. Want exact dezelfde vragen kunnen we ook over die voorlopers stellen...
RTL processor
Met weerstanden (R) en transistoren (T) kunnen alle logische (L) schakelingen als AND, OR etc. gemaakt worden (https://en.wikipedia.org/wiki/Resistor%E2%80%93transistor_logic). Met die bouwstenen kunnen vervolgens complexere schakelingen gemaakt worden, waaronder dus een processor (https://en.wikipedia.org/wiki/TX-0). In een ver verleden heb ik zo een full adder gemaakt. Ofwel het optellen van twee bits en die schakeling is nog steeds terug te vinden in alle processoren. Laten we voor nu aannemen dat via deze weerstanden en transistoren een werkende processor is gemaakt. Nogmaals, deze processor doet niets vanuit zichzelf. Het zal echter een serie in- en uitgangen hebben die de adres- en databus vormen. Verder is er een ingang die als kloksignaal dient.
Zoals gezegd, zorgt de reset vector voor het initialiseren van het PC-register, waarna de volgende klokpuls de instructie in die geheugenlocatie ophaalt door op de adresbus deze waarde in te stellen en dan te wachten op het antwoord dat via de databus wordt doorgegeven.
Versimpelen
Meestal wordt op de adresbus met de bekende locatie van de reset vector een ROM – Read Only Memory – aangesloten. Echter niets weerhoudt ons ervan om gewoon handmatig de databus van een waarde – of beter: geldige instructie – te voorzien via schakelaars. Direct na het geven van een klokpuls is die informatie naar de eeuwige jachtvelden verdwenen, maar de processor heeft geen enkele mogelijkheid om dat te detecteren. Het gaat dus gewoon braaf die instructie uitvoeren en meestal hebben deze extra informatie nodig. Ofwel het PC-register wordt verhoogd, de adresbus wordt ingesteld, waarna we de databus wederom via schakelaars van een waarde voorzien.
Het negeren van de adresbus, of beter niet zorgen voor geheugen, klinkt gek, maar dit is exact de eerste stap die Ben Eater met een moderne versie van de 6502 – de voorloper van de eerder genoemde 6510 – processor uitvoert (https://www.youtube.com/watch?v=LnzuMJLZRdU). Zijn instructies worden gecodeerd via weerstanden, ofwel de processor ziet een oneindige serie NOP – No OPeration ofwel doe niets – voorbijkomen.
In de volgende filmpjes wordt een EEPROM – een elektrisch wisbare ROM – toegevoegd en die wordt via een andere computer geprogrammeerd. Dat gaat in tegen mijn wens, maar Ben heeft in een eerdere video laten zien dat zo’n EEPROM ook handmatig geprogrammeerd kan worden. In december 2020 (https://www.hetlab.tk/embedded/eeprom-handmatig-programmeren) is het mij gelukt om zo vijf bytes in te voeren.
Handmatige EEPROM programmer |
Klaar?
Ik heb de door Ben gebruikte spullen voor zo’n dertig euro besteld en daarmee zou ik een nieuw fundament kunnen maken. Er is één probleem. De gebruikte EEPROM-chip wordt voor zover ik kan nagaan niet meer gemaakt. Ik heb dus een tiental tweedehands – ze worden van weggegooide elektronica gedesoldeerd – uit China laten komen. Geen idee hoeveel mensen mijn experiment willen nadoen, maar genoemde aantal is waarschijnlijk veel te weinig. Alhoewel ik overtuigd ben dat bovenstaande gaat werken, moet ik een plan B gaan bedenken met modernere spullen...
Flash geheugen
Waarschijnlijk gaan een aantal kenners na het lezen van bovenstaande steigeren. Er worden nog genoeg EEPROMs gemaakt! Inderdaad, al is het lastig om eraan te komen met levertijden van een jaar. Een groter probleem is dat we voor ons experiment een parallelle versie nodig hebben. Deze hebben adres- en datalijnen, ofwel het geheel kan rechtstreeks door een processor worden aangestuurd. Van de ruim vijfduizend exemplaren bij een leverancier waren er slechts vijftien met deze wijze van aansluiten. De meeste gebruiken SPI of I2C en dat betekent een vertaalslag.
Zoals gezegd plan B, want het is duidelijk dat de parallelle EEPROM-versie gaat verdwijnen. Nu is deze de voorloper van het veel modernere flashgeheugen. Ook hier bestaan parallelle exemplaren, maar de meeste zijn niet rechtstreeks op een breadboard te gebruiken. Verder is de kleinste met 128 kilobyte veel meer dan wat we nodig hebben. Op zich zijn deze beperkingen geen probleem. Veel belangrijker is dat flashgeheugen slechts een beperkt aantal keren beschreven kan worden, ofwel niet ideaal voor experimenten. De ATmega328 – de microcontroller op een Arduino Uno – gebuikt intern ook 32 kilobyte aan flashgeheugen en ik heb ondertussen een exemplaar die geen nieuwe programma’s meer kan bewaren. Nu is dit een Chinese kloon, ofwel mogelijk dat de kwaliteit hier een rol speelt?
Controller
Even een kleine zijsprong voordat een lezer gaat opmerken dat sd-kaarten, eMMC, flash drives en ssd zonder problemen vele malen worden volgeschreven. Correct, maar daarvoor is een boekhouding nodig. Ofwel een microcontroller houdt bij hoe vaak een pagina – page in het Engels – is gebruikt. Omdat aanpassen van één byte in zo’n pagina betekent dat deze eerst gewist en vervolgens weer beschreven moet worden, wordt vaak een gewijzigde kopie in een andere pagina gemaakt. Dit noemen we wear leveling en daarmee kan de levensduur enorm verlengd worden, afhankelijk van hoe slim de microcontroller is geprogrammeerd.
Dat laatste maakt ook duidelijk dat ik voor de toekomstige opslag van mijn project geen van de eerder genoemde flash-geheugens kan gebruiken. Of beter: ik moet dan zelf iets in elkaar knutselen met losse flash geheugenchips en een zelf geprogrammeerde microcontroller voor de noodzakelijke wear leveling.
SRAM
Er is een minder fraaie oplossing om de bestelde spullen te gebruiken zonder de EEPROM. Tegenwoordig gebruiken bijna alle computers DRAM – Dynamic RAM – als tijdelijke geheugenchips om programma’s en data te bewaren zolang de computer aanstaat. De genoemde variant verliest vrij snel de informatie, ofwel het complete geheugen moet meerdere keren per seconde ververst worden. Niet handig in het project wat mij voor ogen staat. Nu is er ook SRAM – Static RAM – en zoals de naam aangeeft, blijft de informatie zonder verversing beschikbaar zolang we de computer niet uitzetten. En laat dit nu het geheugen zijn dat Ben in zijn project gebruikt.
Nu ben ik geen expert in het lezen van datasheets, maar volgens mij moet het mogelijk zijn om een SRAM te voorzien van informatie die we via schakelaars invoeren, waarna we de processor starten.
Plan C
Zoals gezegd heeft de ATmega328-microcontroller intern flashgeheugen. Tijdens mijn onderzoek om de EEPROM te vervangen, ontdekte ik dat veel nieuwe implementaties met de oude processoren daar handig gebruik van maken. In de ATmega328 – of een van zijn broertjes – wordt een programma geladen die de klok, adres- en de databus van de gewone processor aanstuurt. De instructies voor deze worden in het resterende deel van het flashgeheugen geplaatst. Als we bedenken dat 32 kilobyte veel meer is dan de grootte van de ROM in de Commodore 64 of de originele IBM model 5150, zal dit werken. In nummer 317 vertelde ik dat een ATmega328 handmatig geprogrammeerd kan worden. Op dit moment twijfel ik echter of dit de juiste weg is omdat we dan twee processoren moeten programmeren.