PhD: Ja, herzlich willkommen.
Heute zeige ich euch, wie ich in Factorio eine CPU gebaut habe.
Im Talk werden wir uns aber auch anschauen, wie ihr einfach eine CPU selbst bauen könnt.
Hier seht ihr erst mal eine Demo, wie man in Factorio Minesweeper spielen kann.
Nicht Minecraft, Minesweeper.
Ja, also ihr seht, an der CPU selbst ist auch ein Display und eine
Tastatur angeschlossen und das ganze Programm von Minesweeper habe ich in
Assembly geschrieben, dann auf die CPU geladen und genau, ist ein bisschen
langsamer als euer Minesweeper vielleicht, aber da muss man mit leben.
Um das Ganze erstmal abzukürzen, drücken wir mal auf dieses ominöse Feld.
Ah, okay, schade.
Okay, nun können Sie sich jetzt die Frage stellen, wie kam
ich auf diese sehr gute Idee, eine CPU in Factorio zu bauen?
Naja, wie so vieles hat alles angefangen mit Minecraft.
Ich habe da als Jugendlicher viel Minecraft gespielt und auch schon damals hat
mich eigentlich das Redstone besonders fasziniert und mit diesen Tutorials aus
dem Jahr 2011 von damals habe ich dann auch kleinere Projekte gebaut und einfach
dadurch spielerisch irgendwelche Grundlagen von Digital -Schaltungen kennengelernt.
Also hier in dem Tutorial wird ein Sieben -Segment -Display gebaut.
Und was mich vor allem fasziniert hat, waren die simplen
Grundbausteine, die man damals in Minecraft nur hatte.
Es gab ein OR -Gate und NOT -Gate und trotzdem konnte
man daraus jegliche komplizierte Hardware bauen.
Allerdings war dies wegen den einfachen Bausteinen zum Teil auch sehr mühsam
und es führte mich jetzt zur aktuelleren Frage, ob es in einem anderen Spiel,
was ich sehr gerne spiele, nämlich Factorio, vielleicht etwas einfacher wäre.
Hier hat man nämlich etwas komplexere Grundbausteine zur Verfügung und
kann somit auch möglicherweise kompliziertere Architekturen konstruieren.
Nun habe ich schon von Factorio gesprochen, vielleicht kurz eine Umfrage im Saal.
Wer von euch hat denn schon mal Factorio gespielt?
Und wer hat es geschafft, eine Rakete zu bauen und das Spiel zu gewinnen?
Nicht schlecht, das haben nicht alle geschafft.
Vielleicht darf ihr schon mal Applaus.
Aber für die zwei Leute, die es nicht wissen, was es ist.
Also Factorio ist ein Fabriksimulationsspiel.
Die Story ist, wir sind gestrandet auf einem fremden Planeten
und müssen eine Rakete bauen, um wieder davon wegzukommen.
Allerdings ist es direkt nicht möglich.
Also ihr kennt es vielleicht, ich kann jetzt auch nicht direkt eine Rakete bauen.
Stattdessen muss man also erstmal eine Fabrik aufbauen, Forschung
betreiben, Produktionsketten aufbauen, um damit am Ende genug
Ressourcen zur Verfügung zu haben, um eine Rakete bauen zu können.
Und für diese gesamte Fabrik haben sich die
Entwickler von Factorio ein Steuerungssystem überlegt.
Wäre gut, wenn man das ein bisschen angucken kann.
Um es mal ein Beispiel geben zu können.
Hier so sieht ein Greifer aus, der Items von A nach B legen kann.
Und wir können zum Beispiel ein Transportband befüllen, das sich hier im Kreis dreht.
Die Factorio -Spieler wissen schon, das nennt man ein sogenanntes Sushi -Belt.
Und wenn die Greifers aber einfach mehr Items da drauf
liegen, dann führt das zur Überfüllung und zum Stillstand.
Und Stillstand wollen wir natürlich vermeiden,
denn wir wissen ja alle, the factory must grow.
Okay, deshalb müssen wir unseren Greifer steuern.
Der soll also aufhören, neue Items auf das Förderband zu
legen, wenn zum Beispiel 30 von der Sorte schon dort liegen.
Wie machen wir das am besten? Wir verwenden dafür sogenannte Signalkabel.
Die sind einfach Kabel, die Signale zwischen Komponenten in Factorio
übertragen können, zum Beispiel von diesem Transportband zum Greifer.
Und der Greifer, der bekommt jetzt die Information,
wie viele Items auf dem Transportband aktuell liegen.
Und wir können nun eine Einschaltbedingung festlegen.
In dem Fall sollen wir sagen, wenn Eisen kleiner
gleich 30 ist, dann soll er eingeschaltet sein.
Ansonsten soll er eben ausgeschaltet sein.
Jetzt können wir unser Signal einfach durchschleifen zum zweiten
Greifer und das gleiche dort einstellen, aber diesmal eben für Kupfer.
Und dann, wenn wir das gut haben, dann können wir mal ausprobieren,
was passiert, wenn wir jetzt unsere Items an die Greifer anschließen.
Jetzt sehen wir, aha, sie legen auch wieder Items
drauf, aber wichtig, sie hören irgendwann auf.
Das heißt, es dreht sich weiter.
Dieses Kabel, das wir gerade gebaut haben, ist vergleichbar
mit so einem DMX -Kabel in der Veranstaltungstechnik.
Wir haben ganz viele kleine Stränge und jeder einzelne
Strang kann ein Signal von einer Sorte übertragen.
Gut, das ist schon mal ganz cool, es funktioniert, aber
was machen wir nun mit komplizierteren Bedingungen?
Angenommen, wir haben noch einen dritten Greifer und der soll jetzt
angeschaltet sein, wenn Stahl weniger als 5 ist, aber die Summe von
Eisen und Kupfer größer sein soll als 40. Wir sehen schon, für so
Bedingungen benötigen wir logische und arithmetische Operationen.
Dafür gibt es sogenannte Combinators.
Der erste Combinator ist der Arithmetic Combinator und was der
macht, er kann einfach zwei Signale miteinander verrechnen.
Zum Beispiel hier können wir unsere Eisen und Kupfer nehmen und die Werte zusammenrechnen.
Aktuell liegen ja 30 Items drauf von jeder Sorte und die Summe
von 60 können wir in ein neues Signal speichern, das auch
virtuell sein kann, in dem wir dieses Signal S für die Summe.
Gut, außerdem haben wir noch eine zweite Bedingung, dass Stahl kleiner als 5 sein
soll und das können wir in einem Decider Combinator beides zusammen festlegen.
Erstmal sagen wir, unsere Summe soll größer als 40 sein und
unser Stahl soll kleiner sein als 5 und das Ganze verknüpfen
wir eben noch mit so einem AND, damit das funktioniert.
Als Output können wir dann ein weiteres Signal erzeugen,
das sagen, okay, ein grünes Checkmark, dafür super.
So, wenn die Bedingungen erfüllt sind, dann wollen wir dieses Signal erzeugen.
Und jetzt können wir dieses grüne Checkmark -Symbol einfach zum Greifer
hier geben und sagen, der soll aktiv sein, wenn das größer ist als
0. Und damit funktioniert das auch und wir schaffen, was wir wollen.
Wir legen genau fünf Stahlbarren hier drauf und mehr auch nicht.
Das heißt, zusammenfassend haben wir einmal den Arithmetic
Combinator, der für simple Rechenoperationen zuständig ist.
Wir haben den Decider Combinator, der Signale miteinander vergleichen
kann und diese Vergleiche auch noch Bool-sc hen zusammen verknüpfen kann.
Einen dritten Combinator ist der Constant Combinator, der einfach
nur konstante Inputsignale sendet, die man einmal festlegen kann.
Und das sind alle Grundbausteine, aus denen meine CPU aufgebaut ist.
So, jetzt haben wir schon eine Vorstellung, was es für Factorio gibt und
dann sollen wir kurz einen Überblick geben, was ich damit gebaut habe.
Es kommen jetzt zwei etwas technischere Slides, danach
erkläre ich aber im Detail, wie so eine CPU aufgebaut ist.
Also, unsere CPU ist eine Five -Stage -Pipelined -Architektur.
Sie hat 32 General -Purpose -Register, einige Kilobyte an RAM
und Hard -Drive und verfügt über einen Interrupt -Handler.
Außerdem besticht sie mit einer 700 -Hertz -Taktfrequenz.
Ist jetzt nicht ganz der State -of -the -Art, das
schauen wir uns später auch nochmal genauer an.
Wir haben ein Character -Display, ein Color -Display und noch Tastatur
und sogar einen Random -Number -Generator, den wir angeschlossen haben.
Und auch von der Software -Seite, damit das alles funktioniert, habe ich einen
Assembler in Python geschrieben und auch noch einen Simulator der kompletten CPU
mit leicht höherer Taktfrequenz, sodass man eben zum Debuggen das besser machen kann.
In Assembly selbst habe ich einmal FactOS, ein Kommandozeilen -Umgebungsprogramm
geschrieben und ein Dateisystem und damit auch mehrere Minigames gebaut.
Das Konzept der CPU ist ein RISC -V Instruction Set.
Für alle, die nicht kennen, RISC -V ist eine Open -Source -Befehlssatz -Architektur
und das ist für mich sehr praktisch, weil ich damit einfach genau ins Manual
schauen konnte, wie die das dann gemacht haben und das einfach dann kopiert habe.
Und was auch nett ist, dass RISC -V an Befehle generell 32 -Bit -Wörter verwendet.
Das passt perfekt in Factorio, denn alle Signale, die wir
bisher gesehen haben, waren auch signed Integer mit 32 -Bit.
Das heißt, wir nehmen einfach das als unsere Wortlänge.
Außerdem haben wir eine Memory -Map -Peripherie und
insgesamt benutzen wir eine Big -Endian -Architektur.
Und das ist eigentlich das Wichtigere.
Wir bekennen uns ganz eindeutig zum Dezimalsystem, denn alle Signale,
die wir irgendwo im Spiel sind, sind nur im Dezimalsystem angezeigt.
Das habe ich jetzt einfach auch so übernommen.
Also mein Assembler kann auch kein Hexadezimal einlesen.
Da müssen wir einfach jetzt durch.
Jetzt kommen wir zur eigentlichen Frage, wie funktioniert ein Computer?
Also ein Herzstück, Computer, kommt von Compute, ist eine ALU, eine Arithmetic Logical Unit.
In dieser Einheit können wir eben Inputs miteinander
verrechnen und damit neue Outputs berechnen.
Die müssen ja aber irgendwie miteinander gespeichert und irgendwie geladen werden.
Das heißt, dafür verwenden wir ein Register -File.
Das ist ein kleiner, schneller Speicher für Daten, direkt mit der ALU verbunden.
Zum Beispiel, wenn wir die Register T0 und T1 miteinander addieren
wollen, dann laden wir an den Read -Port A das Register T0, an B das
T1, stellen die ALU auf Plus, bekommen das Ergebnis und über den Write
-Back -Port setzen wir den zu A0 und speichern das Ergebnis da ab.
Das allein reicht aber noch nicht, um zum Beispiel die Instruktion,
dass wir A0 einfach inkrementieren wollen, implementieren zu müssen.
Ja, dann brauchen wir eine Immediate Value, also diese 1.
Das heißt, unser untere Port der ALU wird mit einem Multiplexer versehen
und der wählt eben eine der beiden Inputs aus, den er zu ALU weiterleitet.
Wir brauchen noch mehr Speicher.
Arrays für andere Datenstrukturen wollen wir im RAM speichern
und dazu wollen wir Werte aus dem RAM laden und speichern.
In meinem Fall berechnet die ALU die RAM -Adresse und das Ergebnis wird
dann ebenfalls mit einem Multiplexer wieder zum Register weitergeleitet und
der Read -Output vom Register auf der B -Seite wird zum Speichern verwendet.
Okay, jetzt wissen wir so, was für Komponenten es vielleicht in der CPU gibt.
Wie kann man jetzt einen Befehl damit ausführen?
Also ein Befehl ist ja die kleinste elementare Anweisung, die eine CPU direkt ausführen
kann und in dem Fall besteht sie aus fünf Stages, wo der Befehl drin sein kann.
Im Instruction Fetch laden wir unseren aktuellen Befehl erst aus dem Speicher.
In Decode lesen wir den ein und verstehen, was wir eigentlich überhaupt tun müssen.
In Execute führen wir den Befehl aus der ALU aus und berechnen zum Beispiel
die Adresse, die wir im Memory Access dann für den Speicherzugriff verwenden
und im Register Writeback schreiben wir die Werte zurück in das Register File.
Sieht schon ungefähr ähnlich aus, bis wir vielleicht die
ersten beiden Schritte, wie wir es gerade gesehen haben.
Um das Ganze jetzt mal ein bisschen plastischer zu machen:
Hier ist die Factorio CPU und quasi die Platine und um das mal ganz so nochmal
durchzugehen, wie ein Befehl seinen Ablauf hat, starten wir im Program Counter.
Das ist ein spezielles Register, das immer die aktuelle Adresse
des Befehls, das wir ausführen wollen, beherbergt und diese Adresse
geben wir jetzt erstmal zum RAM, um unseren Befehl zu laden.
Der geladene Befehl wandert dann hier vorne in ein sogenanntes Pipeline Register.
Über Pipelining rede ich auch gleich noch.
Wir können uns jetzt gerade erstmal merken, nach jeder Stage werden alle
Ergebnisse, die wir haben, erstmal zwischengespeichert in einem Register.
Gut, mit unserem geladenen Befehl müssen wir die nur dekodieren.
Wir haben ja, wie gerade schon gesehen, unseren Ablauf, wie unser
Befehl vielleicht auszusehen hat und können zum Beispiel die richtigen
Adressen der Register laden oder Immediate Values dekodieren.
Diese ganzen Informationen, zum Beispiel welche Register
wir haben, geben wir an unser Register -File und dann wird
das alles zur nächsten Pipelining -Stage weitergeleitet.
Der nächste Schritt ist die ALU oder die Execute Unit.
Eben hier haben wir unsere ALU.
Normale ALUs kann eben Plus und Minus, bitweise logische, boolsche Operationen durchführen.
Meine ALU kann auch Integer -Division und Exponentiation.
Das liegt einfach daran, dass die Arithmetic -Combinators
das schon nativ in Factorio implementiert haben.
Das heißt, es wäre Quatsch, das jetzt nicht zu benutzen.
Das bedeutet aber im Umkehrschluss auch, dass ich nicht
den perfekten RISC -V -Standard implementieren konnte.
Außerdem können wir auch springen und Befehlsprungbedingungen einführen.
Das passiert in der Execute- Unit Logic und anschließend unser Befehl,
oder unser Ergebnis von der ALU, geben wir ins nächste Pipeline -Register.
Gut, wenn wir zum Beispiel eine Adresse geladen haben, die
wir nun im RAM oder woanders zugreifen wollen, dann haben wir
unseren Memory -Controller, der an den Main -Bus angeschlossen ist.
Und dieser Bus ist zum Beispiel eben angeschlossen an den angesprochenen RAM.
Wir haben auch noch ein Hard -Drive oder unsere
Kernel ROM, wo unser Betriebssystem drauf ist.
Aber da wir Memory -Mapped sind, haben wir unsere
Peripherie auch direkt an diesem Bus angeschlossen.
Und das Ergebnis, nachdem wir das zum Beispiel geladen
haben, wandert dann ins nächste Pipeline -Register.
Okay, wir sind quasi durch. Im letzten Schritt passiert nicht mehr viel.
Einfach die Ergebnisse dieses Befehls werden wieder an die Register weitergeleitet.
Gut, ich habe gerade eben schon über Pipelining es angedeutet.
Wenn wir einen Befehl in Instruction Fetch haben und diesen dann zu
Instruction Decode übergeben, wäre es ja eigentlich sinnvoll, einfach
schon den nächsten Befehl in Instruction Fetch beginnen zu lassen.
Und das können wir auch weiterspielen.
Sobald wir in Execute sind, kann der nächste Befehl in Instruction Fetch starten.
Ich habe das erstmal für drei Befehle hier simuliert.
Das heißt, alle wandern hier durch und jeder wird am Ende erfüllt.
Und wir können aber damit theoretisch bis zu fünf Befehle gleichzeitig ausführen.
Das ist natürlich schön.
Dadurch bekommen wir sehr viel mehr Instructions pro Sekunde.
Das Problem ist, es funktioniert halt leider nicht direkt.
Wenn wir uns zum Beispiel solche Pseudo -Code
anschauen, hier haben wir zwei Register S0 und S1.
Die wollen wir miteinander addieren und in T0 speichern.
Und anschließend wollen wir T0 plus S2 rechnen.
Wenn wir uns mal anschauen, unser blauer Befehl, der jetzt gerade in Execute
ist, der braucht den Wert von T0, damit er einen richtigen Wert berechnen kann.
T0 selbst ist aber noch in der Memory Access vom vorherigen Befehl
und dementsprechend noch gar nicht zum Register weitergeleitet.
Das heißt, wir haben leider nicht viele korrekte Befehle, die wir
ausführen und wir müssen irgendwie schauen, dass wir das beheben.
Eine Lösung, die ich auch hier implementiert habe, ist sogenanntes Forwarding zu verwenden.
Das heißt, die Logik, die Hardware sieht, oh, wir haben hier eine Abhängigkeit
vom Register T0 und der Wert, der in Memory Access ja schon berechnet
ist von T0, wird einfach direkt an die Execute -Schiene weitergeleitet.
Das ist auch der komplizierteste Part von der gesamten
CPU, weil es sehr viele Bedingungen gibt und auch sehr viel
mehr Probleme, die ich jetzt nicht alle ansprechen kann.
Okay, wie kann man so eine CPU in Factorio eigentlich genau umsetzen?
Ein Konzept, das man dazu benötigt, ist das Konzept eines GameTicks.
Factorio hat 60 GameTicks pro Sekunde, das bedeutet 60 Updates,
die irgendwie im Spiel gemacht werden und jeder Combinator
benötigt immer ein GameTick, um seine Berechnung durchzuführen.
Und interessanterweise können wir diese Anzahl an GameTicks
pro Sekunde auch ein bisschen steuern über unseren Map -Editor.
So sieht das aus, wenn wir 60 Updates pro Sekunde haben und wenn wir aber unsere
Spielgeschwindigkeit auf ein Viertel verringern, sieht das so aus, aber eigentlich
interessant ist natürlich, wenn wir das auch ein bisschen beschleunigen können.
Insgesamt können wir das Spiel innerhalb von dem Spiel in dem Game Editor auf
bis zu 64 Mal beschleunigen und erhalten damit 3700 Updates pro Sekunde circa.
Das trifft sich gut.
Unser Pipeline -Stage braucht 5 GameTicks zum Bearbeiten von einer Stage.
Das heißt, damit erreichen wir ungefähr 700 GameTicks pro Sekunde.
Zum Vergleich dazu, eine sequentielle CPU, wenn wir also kein Pipeline verwenden,
braucht ungefähr 20 GameTicks pro Befehl für eine ähnliche Architektur.
Also wir sind quasi ganz gut dabei mit unseren 700 GameTicks.
Das ist eigentlich gar nicht so schlecht.
Das Problem ist eher, wenn man sich fragen könnte, ja gut, kann man einfach
noch mehrere CPUs daneben setzen, also eine Dual -Core, eine Quad -Core machen.
Dann haben wir ein Problem, dass wir sehen, die
Instruktionen pro Kern pro Sekunde werden wieder weniger.
Das liegt nicht daran, dass die Hardware irgendwie
langsamer ist, das ist ja genau identisch.
Das Problem ist, dass mein Computer es nicht
mehr schafft, so viel gleichzeitig zu simulieren.
Also wir kommen an die Grenze, was halt eben simulierbar ist.
Vielleicht war Factorio eben doch nicht die beste Idee, um
darin eine CPU zu bauen, aber es ist immerhin anschaulich.
Okay, das heißt, wir haben so einen Sweet -Spot zwischen, wie
kompliziert wollen wir unsere Hardware bauen und wie groß wollen wir
werden, um möglichst viele Instruktionen pro Sekunde zu erhalten.
Schauen wir uns doch mal an, wie zum Beispiel ein Register in Factorio implementiert ist.
Nur mal zur Erinnerung, wie funktioniert so ein Register?
Wir haben einen Daten -Input und wenn wir einen Puls geben, dann wird dieses
angeliegene Datensignal im Register gespeichert und dauerhaft ausgegeben.
Und egal, welches neue Daten -Input dann anliegt, der State bleibt gleich.
Erst sobald man eben nochmal einen Puls gibt, wird der Wert aktualisiert.
Wie man das jetzt in Factorio implementiert, ist ein bisschen
anders, als man es in regulärer Hardware vielleicht kennt.
Was wir im Grunde genommen haben, ist so ein Kreislauf, wo der
Wert, der jetzt gerade aus diesem Addierer rauskommt, direkt an den
Addierer selbst zurückgeschleift wird und dadurch nicht verloren geht.
Wir haben also so eine Art Rückkopplung.
Aber wenn wir das so haben, dann können wir diesen Befehl nie ändern.
Wir müssen ihn irgendwie wieder rauslöschen.
Und deswegen ist unser Clock -Signal über diese Schaltung einfach
dafür zuständig, den alten Wert zu löschen und einen Puls zu geben
mit dem neuen Signal, dass dieser nun in den Addirer reinkommt.
Das ist jetzt ein bisschen komplizierter, muss auch sich ein bisschen damit beschäftigen.
Aber wie man das in Factorio aussieht, hier links haben wir unser
Datensignal, das ist jetzt gerade mit dem Symbol D und hat einen Wert von 11.
Unser Clock -Signal ist einfach eine Uhr.
Und wenn wir unser Datensignal verbinden, dann bauen wir erstmal unser And -Gate,
dass wir sagen, das Datensignal soll nur durch, wenn Clock größer Null ist.
Und anschließend bauen wir unseren Kreislauf, wo unser Register gespeichert ist.
Die Leute in Factorio reden da von einer sogenannten Memory -Cell, dass
wir eben unseren Output vom rechten Decider wieder zurück zum Input geben.
Und nun müssen wir noch die Bedingungen geben, dass der alte Wert ja gelöscht werden soll.
Das heißt, wir sagen, er wird durchgelassen, wenn der Puls gleich Null ist.
Aber sobald der Eins ist, wird der alte Wert eben gelöscht.
Ja, ist jetzt ein bisschen schnell, da muss man
sich, glaube ich, selbst mal mit dran rumspielen.
Aber das funktioniert jetzt genau, wenn wir jetzt den Wert
hier zum Beispiel auf 44 am Input ändern und dann unseren
Clock geben, dann wird der Wert eben auch aktualisiert.
Gut, ein weiteres Problem, das man sich angucken kann, ist unsere RAM.
Also erstmal in unserem RAM selbst, hier sind überall
kleine Register, wie wir sie gerade gesehen haben, drin.
Insgesamt habe ich da jetzt 64 Register eingebaut.
Das ist aber ein bisschen wenig. 64 Register, ja, 64 Wörter.
Wir hätten eigentlich schon ganz gerne so 10 .000 Wörter, die wir abspeichern wollen.
Na gut, da können wir die triviale Lösung machen. Just download more RAM.
Also einfach, ja, mehr RAM -Dinger bauen. Das funktioniert aber nicht.
Das wird zu langsam und es skaliert nicht gut und es ist auch nicht so elegant.
Die andere Lösung ist eher, dass wir, oder das ich erkannt habe, in einem
Register können wir eigentlich mehrere Signale gleichzeitig speichern.
Also wir müssen nicht nur ein Signal anlegen, wir haben über
256 Signale in Factorio und so wird ein Register stattdessen zu
einem RAM -Block, den wir blockweise lesen und schreiben können.
Und um das mal ein Beispiel zu geben, dafür haben wir unseren Memory Controller.
Angenommen, wir wollen jetzt an Adresse 14 .850 speichern,
dann müssen wir das eben erstmal binär verstehen.
Okay, das sind irgendwie zwei Byte Zahlen und das ergibt
dann eine Block -ID von 58 und eine Signal -ID von 2.
Das heißt, speichern wir jetzt einen Wert dort, dann lesen wir
erst den kompletten Block, wir updaten dann an Signal 2 unseren
neuen Wert und speichern dann den kompletten Block wieder im RAM.
Das schaffen wir gerade so noch, wenn man es geschickt implementiert in den
fünf GameTicks, das ist also gut und damit haben wir eben sehr viel größere
Speicherzugriffe und können auch ernsthafte Programme daran speichern.
Kommen wir auch mal zum Programmieren, wie können wir so eine CPU programmieren?
Mein Talk war jetzt eher dafür ausgelegt, ein bisschen mehr die
Hardware zu zeigen, deswegen werde ich hier es nur ein bisschen verkürzt
darstellen, aber wir haben verschiedene Befehlskategorien, wir haben
schon Register, Registerbefehle gesehen und Register Immediate Befehle.
Dann haben wir eben den Load Word und den Store Word Befehl,
das ja typisch für eine Load- Store Architektur wie RISC -V ist.
Hier können wir auch direkt unsere Peripherie eben beeinflussen und schließlich
haben wir auch Jumps für die Leute, die irgendwie höhere Sprachen kennen,
das ist einfach ein go to Befehl, aber das ist halt alles, was wir haben.
Also BGT steht für Branch Greater Than, das heißt, wenn
T0 größer gleich T2 ist, dann springen wir zur Marke End.
Eine wichtige Instruktion ist der Ec all oder Environment Call
und das ist für die Interaktion mit IO -Komponenten zuständig.
Wir haben ein Register A7, wo wir eine Zahl reinspeichern können und dann
abhängig davon, welche Zahl das eben ist, wird ein E call durchgeführt, der aber
natürlich nicht vom Himmel fällt, den muss man also erstmal implementieren.
Ich habe eigentlich auch mehr Zeit damit verbracht, Assembler für
die CPU zu schreiben, als die eigentliche CPU in Factorio zu bauen.
Das heißt, da hat man irgendwie ganze System Calls, die man implementieren muss.
Gut. Wie baut man jetzt so ein Programm und kann das dann in Factorio ausführen?
Als Eingabe haben wir einfach eine Textdatei und als Ausgabe
eine Liste von Befehlen, als alles 32 -Bit -Wörter sind.
Ganz grob, Assembler funktioniert in dem Fall so, wir entfernen erstmal allen
Whitespace, alle Kommentare, das ist alles schön ist, tokenizen das, ersetzen dann
Pseudo -Instructions, wir haben solche Instructions wie Load Immediate, das ist
jetzt keine echte Instruction, die kann man aber einfach durch ein "Add Immediate
plus Null" zum Beispiel ersetzen und außerdem müssen wir alle Labels ersetzen.
Wir haben ja irgendwelche Sprungmarken, wie wir gerade gesehen haben
mit End, die müssen wir durch relative Sprungadressen ersetzen.
Das ist eigentlich sehr nett, weil dadurch unser Code
positionsunabhängig ist und überall ausführbar ist und dann müssen
wir eben jede einzelne Instruktion in einen Maschinencode umwandeln.
Das heißt, wenn wir so ein Beispiel haben, hier ist jetzt ein Fibonacci
-Zahlenprogramm, das Fibonacci -Zahlen einfach ausgibt, das Pendant dazu in
Python, das seht ihr mal hier, das ist jetzt nicht so schwer, aber was ich
auf ein paar Dinge aufmerksam machen will, eine For -Loop implementieren wir
im Grunde genommen so, dass wir am Anfang der Loop eben einen Equal Condition
machen und aus der Loop rausspringen, wenn wir fertig sind und ansonsten
unseren Counter um eins erhöhen und dann wieder zum Anfang zurückspringen.
Für die Leute, die jetzt Assembly programmieren, das ist jetzt nichts Neues und
irgendwelche E calls implementieren wir oder Prints implementieren wir als E calls,
indem wir die entsprechenden Argumente in A0 und A7 laden und dann Ecall ausführen.
Und wenn wir das dann assemblieren, dann bekommen wir auch so einen schönen
Maschinencode, der jetzt da ist und jetzt fragen, ja gut, was machen wir denn damit?
Können wir irgendwie den auch jetzt in Factorio reinladen?
Und die Antwort ist ja und vermutlich Leute, die
Factorio spielen, ahnen das schon, was wir haben.
Mein Assembler gibt einen ganz langen, ominösen String aus und wenn wir diesen
String als Blueprint in Factorio eingeben, dann bekommen wir ein Blueprint.
Also für Leute, die nicht kennen, Blueprint ist einfach
eine gespeicherte Folge von Blöcken, die man in Factorio einfügen kann
und insbesondere kann man den mit Python auch, ja, ich würde nicht sagen
entschlüsseln, das ist einfach nur ein ZIP File,
aber man kann das eben manipulieren und dann zum Beispiel hier so Signale
in einen Constant Combinator reingeben und diese Signale, die ihr hier seht,
sind eben genau unser Maschinencode, den wir von der, von der, gerade eben vom
Assembler gesehen haben. Okay. Und jetzt wollen wir unsere,
unser Programm mal ausführen. Wir haben jetzt unser Betriebssystem, das R0,
der Befehl ist, der das Programm in ROM 0 ausführt und ja, funktioniert auch.
Wir bekommen alle Fibonacci -Zahlen, also die ersten 10, die wir jetzt
im Programm auch haben wollen. Soweit mal die Toolchain.
Nun wollte ich noch ein bisschen zeigen, was ich implementiert habe.
Das Ziel war es, einen nutzbaren Computer zu haben. Das heißt, was dachte ich,
was brauche ich als erstes? irgendwie mal ein Dateisystem. In dem Fall haben wir
Chest 32, das 128 Blöcke hat mit jeweils 256 Wörter. Wörter. Es hat eine relativ
einfache Ordnerstruktur, es hat keine Metadaten, keine Benutzer,
keine Schreibleserechte, was auch immer. Acht Zeichen pro Dateiname
müssen genügen. Und dann haben wir natürlich noch einen Kommando -Zeilen -Umgebung,
das interaktive Kommandos eben entgegennehmen kann. Ist ein bisschen
an Linux angelehnt, aber auch nicht komplett. Okay, um eine Demo davon zu geben,
wir schalten die CPU mal ein und können jetzt zum Beispiel mal lesen,
was im Ordner Binary drinsteht und sehen jetzt folgende Dateien hier. Okay,
lass mal die Datei "TITLE" ausführen. Dann machen wir das mal und sehen,
er muss kurz den RAM laden. Okay. Jetzt sehen wir den Titel, das ist genau,
einfach genau der Titel, den ihr am Anfang gesehen habt. Das heißt,
das Programm spuckt einfach den Titel aus. Oder um jetzt mal das Color Display
zu zeigen, können wir mal Snake ausführen. Dann, da dauert es ein bisschen länger,
bis das alles in RAM geladen ist, aber irgendwann leert es auch hier und dann,
genau, haben wir unsere Pfeiltasten und können da Snake spielen.
Es ist auch die maximale Geschwindigkeit, wie schnell das simuliert werden kann.
Also schneller geht das nicht. Naja, ein Proof of Concept. *applause* Okay,
und damit bin ich schon am Ende. Ich sehe schon, ich habe mich ein bisschen
zu sehr beeilt, aber da können wir auch kurz darüber reden,
warum man das eigentlich Ganze machen will. Also, diese Frage,
wie funktioniert ein Computer und warum das alles funktioniert,
wie es funktioniert, hat mich eigentlich schon immer fasziniert
und ich wollte schon immer Dinge von Grund auf verstehen. Deswegen habe ich,
glaube ich, auch angefangen, erst mal Mathe zu studieren und ja,
das fand ich einfach eine sehr wichtige Sache. Aber außerdem mochte ich
auch die Challenge, was ist halt möglich und wo sind die Grenzen?
Was kann man ein bisschen Engineering betreiben, um möglichst effizient
was zu erreichen? Die CPU, die ihr gerade gesehen habt, war auch nicht die erste,
also das war Version 3 und generell deswegen, warum ich mich für den Talk
entschieden habe, ich möchte eigentlich meine Faszination mit dem ganzen Thema,
mit anderen Menschen teilen und ich glaube, das ist hier ein ganz guter Ort.
Ein Ausblick, super gerne, danke. *applause* Als Ausblick, es wäre super cool,
wenn man einen Compiler dafür schreiben müsste. Das Problem ist,
damit kenne ich mich jetzt gar nicht aus. Ich habe irgendwie angefangen,
so ein tausendseitiges Buch zu lesen, aber es hat noch nicht
ganz gereicht mit der Zeit. Das heißt, wenn ihr Experten wie hier Lust haben,
mit mir da was zusammen zu machen, bin ich ganz Ohr. Super cool wäre auch
ein GPU für das Farbdisplay, das noch ein bisschen schneller machen kann,
das ist relativ langsam aktuell und auch cool wäre noch mehr IO mit anderen
Factorio -Konzepten. Das ist ein sogenannter Beltprinter und damit, wenn man sowas
zum Beispiel anzeigen könnte, wäre das ja auch ganz cool. Ich möchte mich erstmal
bedanken an 9R vom Hardware -Content -Team für den besten Support. Mein Vater,
der mich für die Informatik begeistert hat und natürlich meine Frau,
die mich bei all meinen verrücktesten Ideen immer unterstützt. Wenn ihr Lust habt,
*applause* wenn ihr denkt, hey, das ist cool, da will ich noch mitmachen,
dann macht doch gerne mit. Wir haben jetzt gleich im Anschluss um 20 .30 Uhr
eine Self -Organized Session, in dem wir idealerweise eure eigenen Programme
für die CPU programmieren könnt und dann auch ausführen könnt.
Ihr bräuchtet halt dafür Factorio, aber das ist eh ein gutes Spiel,
kann ich einfach so empfehlen. Ist auch kein Problem in dem Raum hier
und ansonsten, wenn ihr irgendwie einfach nur das Projekt auschecken wollt,
hier ist der Code vom Assembler und auch allen Assembly -Programmen.
Ein bisschen Doku ist auch da, das kann aber noch verbessert werden.
Aber ja, ansonsten vielen Dank fürs Zuhören und ich bin gern für Fragen offen.
*applause*
Ich bin davon ausgegangen, dass es Fragen gibt. Es gibt auch schon die erste.
Fangen wir doch mal mit Mikrofonnummer 1 an, bitte.
Ja, danke. Ich wollte Fragen gehabt du einen YouTube -Kanal hast.
Noch nicht, aber ich glaube, ich wollte mal ein YouTube -Video darüber machen,
vermute ich dann auch auf Englisch, aber ich habe jetzt nicht vor,
einen YouTube -Kanal aufzubauen.
Questioner: Okay, danke.
Herald: Mikrofonnummer 3.
Wie viele Jahre, bis du Doom in Factoriol implementiert hast?
Ach, es gibt sogar schon erste Sachen in Factorio, das ist jetzt nicht
auf der CPU, aber es sind sehr spannende Raytracing -Algorithmen,
die in Factorio implementiert sind. Ich habe leider den Namen vergessen,
aber wenn du es schreiben willst, mach gerne, ich freue mich drauf.
Questioner: Danke.
Herald: Nummer 4.
Jo, danke für den Talk. Mich hätte jetzt interessiert, du hast dich ja auch
mit den Grenzen und Möglichkeiten beschäftigt, sagen wir mal,
wir nehmen einen noch besseren Computer, wo das draufläuft und noch ein paar Mods,
die irgendwie die optimieren. Ist es möglich, in Factorio einen PC zu bauen,
auf dem Factorio läuft?
Ich denke doch, auf jeden Fall. Also man muss klarstellen, was ist,
also das komplette Spiel wird leider nicht funktionieren, aber man kann bestimmt
einzelne Komponenten, wie man meint, Ressourcen, legt die auf Förderbänder,
das wäre schon ziemlich cool, aber aktuell bräuchte man auf jeden Fall
einen Compiler dafür, ich will nicht der Mensch sein,
der wie Roller -Coaster -Tycoon das in der Assembly programmiert und ja,
aber dann auf jeden Fall.
Zusatzfrage, nimmst du Hardware -Spenden entgegen?
PhD: Nein.
Herald: Nummer zwei, bitte.
Ja, die Frage mit Doom hat man mir leider weggenommen,
aber mich würde doch mal interessieren, wie lange hat denn das gedauert
jetzt insgesamt irgendwie? There must have no life.
Nein, also ich habe letztes Jahr angefangen für im Dezember für Advent of Code,
fand ich das lustig, mal versuchen in Advent of Code,
also das ist so ein Programm, wo man irgendwie kleine
Coding -Challenges machen kann, damit das in Factorio zu lösen,
das hat nicht geklappt, das habe ich dieses Jahr mit der besseren CPU
ein bisschen programmiert, es hat halb geklappt, aber genau, so ein gutes Jahr
saß ich jetzt daran, aber im Sommer habe ich auch nicht ganz so viel gemacht.
Questioner: Okay, danke.
Wir haben eine Frage aus dem Internet
und das Internet möchte gerne wissen, ob die CPU auch irgendeinen Mehrwert bietet
für das Spiel, also irgendeinen Vorteil, kann das irgendeinen Nutzen haben?
Ich glaube, nicht so ganz das Problem ist, vor allem, wenn man das
in normaler Geschwindigkeit und nicht auf 64 -facher Beschleunigung ausführt,
was ja eigentlich zugegeben die normale Geschwindigkeit ist, wie man normalerweise
Factorio spielt, sie dann schon relativ langsam ist, also ihr habt
zwölf Instruktionen pro Sekunde dann, das ist nicht so schnell,
man kann vermutlich vielleicht sowas machen, dass man so eine Crafting -CPU
daran anschließt und dann irgendwelche Items basierend auf Demand craften kann,
das geht bestimmt, aber ich glaube nicht, ich glaube, alle Logik, die man braucht
für Factorio, sind schon in den Combinator selbst drin, dann kann man das
seinen eigenen Hardware -Computer vermutlich bauen.
Herald: Nummer eins,
ja, wie lange würde man die CPU bauen müssen, wenn man nicht
den Debug -Modus einschaltet, sondern die Ressourcen wirklich meint?
Ja, also ich habe, wie ihr habt schon gesehen, das war alles jetzt
im Creative -Modus, dadurch, dass sie jetzt fertig ist, kann man sie einfach
als Blueprint nehmen und eure Survival -Welt packen und dann funktioniert sie dort,
also sie benutzt keine Mods, außer eben, ihr habt es vielleicht gesehen,
paar Quality -of -Life Mods, das heißt, die Ressourcen selbst sind sehr wenig,
aber das Ganze zum Entwickeln, ich habe es in Survival angefangen,
aber das hat dann sehr schnell gemerkt, dass es dann wenig Spaß macht
und dann bin ich es dann auch, ja.
Herald: Nummer drei.
Vielen Dank für den Talk. wenn du größtenteils mit, das RISC -V -Instruction -Set
benutzt, was hat dich davon abgehalten, einen RISC -V Compiler zu benutzen?
Ja, gute Frage, also das können wir, also ist mir leid, genau, also generell
ist es so, dass es halt eben nicht perfekt eins zu eins gemappt ist,
weil ein paar andere Sachen da sind, die RISC -V nicht hat, aber man könnte einfach
die ja nicht implementieren in den Compiler, da würde es schon gehen. Ich glaube,
der Hauptpunkt war, ich habe einfach wenig Ahnung davon und vielleicht kann man
das einfach, ja, dann bestimmt machen, aber ich bin nicht so Hardware -nah und ich,
ja, ich habe vor allem, ja, *applause* vielleicht die zweite Motivation war auch,
in dem Projekt wollte ich eigentlich versuchen, jeden Code selbst zu schreiben
und keinen fremden Code zu verwenden und so gucken, wie weit man damit kommt.
So ein bisschen die Simulation stellt euch vor, ihr seid jetzt, es ist 1960,
ihr habt den ersten Computer und wollt den jetzt programmieren, aber jetzt, genau,
das macht sehr viel Sinn, dass man das einfach vielleicht mal da auf den Compiler
verwendet.
Wir hatten ja früher gar nichts.
Ja, eben.
Nummer vier, bitte.
Also, ich finde das erstmal richtig cool, weil ich habe das in der Uni gehabt
und mir dachte so, what the fuck, was wollt ihr mit mir mit diesem Zeug da?
Ich finde das cool, dass man das auch mal in Factorio sieht. Eine Frage,
die mir halt ein bisschen ein Ding ist, hast du Branch Prediction Buffer
und Branch History Tables, hast du das gemacht für die Pipelining?
Genau, also was das so ist, ich weiß nicht, wie viel Zeit wir haben,
ihr könnt auch gerne gleich zur Self- Organized Session noch die technischeren
Fragen mitbringen, aber generell ist es so, dass wir keine wirkliche
Branch Prediction durchführen können, oder die Branch Prediction ist,
wir nehmen immer nicht den Branch. Und das liegt einfach daran,
dass wir so schnell, wir haben eigentlich nicht genug Zeit,
also wir haben eigentlich nur ein Stage, bevor wir wissen, dass es ein Befehl ist
und im nächsten, also dass es ein Branch Befehl ist und im nächsten Stage
wird der Branch schon entschieden und genommen. Das heißt, dazwischen ist einfach
gar keine viele Stages, das heißt, es macht einfach keinen Sinn,
der Branch Prediction einzubauen.
Passt, danke.
PhD: Gerne.
Jetzt ist mir der Mensch von Mikrofon 2 weggelaufen, ich bin traurig.
Dann machen wir weiter mit 3.
Ja, also danke für den Talk. Ich wollte fragen, gibt es dieses Jahr
so wie letztes Jahr einen Factorio -Server für den 39C3 und wenn ja,
wird diese CPU dort gebaut?
Also, ich weiß es nicht, ich bin nicht so in der Community,
wenn jemand da was hat, dann soll doch gerne, kommt doch gerne vorbei
zur Self -Organized Session, dann kriegen wir das bestimmt hin.
Herald: Nummer 4.
Hi, sehr cooles Projekt, but can it run Crisis?
Also, gleiche Frage, ich glaube, man kann relativ viel,
wir haben halt ziemlich wenig, also Snake war ja schon an der Grenze,
also damit irgendwas flüssig läuft, ist alles sehr langsam
und man hat auch Probleme, man kann eben nicht beliebig
viel Hardware dran stellen, das heißt, wir bräuchten auch eine halbwegs gute GPU,
um sowas, irgendwelche Sachen da laufen zu lassen, die irgendwie Richtung
Grafik erfordern und dann haben wir auch schon wieder weniger Ressourcen
für die CPU, also ja, aber ich bin gerne, also zeigt gerne, was ihr könnt,
also ich glaube, das Feld ist offen, würde ich sagen.
Questioner: Dankeschön.
Das Internet ist nochmal neugierig.
Genau, das Internet ist neugierig und frag dich mal, wie sieht es eigentlich
mit Secure Computing aus, also wäre es möglich, das Ganze zu verschlüsseln,
die Laufbänder?
Ich habe mich jetzt zugeben, diesmal nicht mit Verschlüsselung beschäftigt,
einfach weil ich der einzige User bin, der in der Welt ist. Da gibt es nicht,
Eve ist einfach nicht da, also es ist, naja, aber klar, man könnte auch
auf jeden Fall sich damit beschäftigen, ich, ja, ich glaube, ich habe ja irgendwie
vorhin gelernt eben, dass es erst ab 1961 Passwörter gibt, ich glaube,
von der Taktgeschwindigkeit sind wir noch ungefähr im Jahr 1943, heißt,
da darf man auch keine Security haben.
Wenn wir kurze Fragen und Antworten haben, dann gehen sich noch zwei Fragen aus.
Drei.
Ist irgendeine Implementation für LTN oder Ähnliches geplant? Nicht,
dass ich wüsste, nein, leider nicht. Aber nur zum Wissen.
Herald: Nummer zwei.
Ich wollte einfach nur kurz fragen, wie viele Stunden hat man in Summe
schon in Factorio, also hast du schon in Summe drauf, beziehungsweise,
wenn es so indiskret ist, wie viel ist in diesem eigentlichen Save
gerade an Stunden drauf?
Ja, der Save ist spannend, weil der natürlich das 64 -fach beschleunigt
ist die ganze Zeit, aber die echten Stunden, ich denke, sie belaufen so 200
bis 400, aber wie gesagt, ich glaube, ich habe mehr Zeit
mit der Assembly -Programmierung verbracht als mit der Implementierung und genau,
aber ja, sonst an CPU, an Laufzeit von Factorio weiß ich nicht genau.
Eine ganz schnelle auf Nummer 4 bitte noch.
Brauchst du jetzt eine Pause von Factorio oder kannst du es immer noch sehen
für ein paar hundert Stunden?
Also ich glaube, eine Pause wäre irgendwann mal angemessen, ich will noch
ein YouTube -Video drüber machen und dann denke ich, ist das Projekt
von meiner Seite erstmal so ein bisschen abgeschlossen, aber ich bin gespannt,
was man noch zusammen erreichen kann bisher, weil ich alles sehr alleine
und dann vielleicht kann meine Community noch was Cooles erreichen.
Und damit sind wir auch schon am Ende der Zeit für diesen Talk. Und ein riesen