Eigene CPU bauen

12/09/2010 - 20:15 von Markus Wichmann | Report spam
Hi all,

ich bin mir nicht sicher, ob ich hier richtig bin und könnte andernfalls
einen Hinweis gebrauchen.

Jedenfalls hab ich mich mal auf
<http://web.cecs.pdx.edu/~harry/Relay/index.html> umgesehen und mir
gedacht: Mal sehen, ob ich sowas àhnliches auch hinbekomme.

Kurz: Es soll mal so etwas àhnliches wie ein x86_64 werden, nur ohne die
Fuckups. Meine Frage ist, ob ich die von Zeit zu Zeit auftauchenden
Fragen eventuell hier posten könnte.

Und da hab ich auch schon die erste Frage: Ich habe vor, eine möglichst
allgemeine ALU zu bauen, die dann aber nicht so groß werden soll. Ich
hab mir z.B. schon überlegt, wie ich für den Addierer nicht gleich 64
Volladdierer benutze, sondern einen Flipflop, einen Zàhler, zwei
Multiplexer und einen Demultiplexer, sowie _einen_ Volladdierer benutze,
um trotzdem ein 64-Bit-Ergebnis berechnen zu können. In dem Zusammenhang
bin ich aber über ein Problem gestolpert:

Mein Datenbus wird ja genauso groß, wie ein Register, also 64 Bit. Mein
Input-Bus muss notgedrungen zwei Register aushalten, also 128 Bit. Wie
kriege ich nun Daten möglichst einfach aus einem Register über den
Datenbus in die ALU? Die Register hàngen ja nur direkt am Datenbus.

Ich sollte hinzufügen, dass sich sàmtliche meiner Überlegungen noch auf
Papier befinden, noch nix realisiert ist, ja, noch nichtmal mit Erfolg
gerechnet wird.

Eine Möglichkeit wàre, zwei Register hinzuzufügen, die ich dann Input0
und Input1, oder kurz I0 und I1 nenne, und vor jeder Operation der ALU
erst diese Register befüllen muss. Geht es auch anders? Meine Idee von
einem Register besteht aus 64 Level-0-1-Bit-Speichern (Level-0 = die
Technik, auf deren Grundlage ich dann alles bastle. Auf der Webseite
oben waren das Relais, ich könnte auch TTL-Chips oder FPGA-Chips nehmen.
Oder Hebel und Wellen), die ich alle zusammen ansprechen kann. Mit zwei
Leitungen: Load, zum Löschen der Speicher und anschließendem
Neubeschreiben vom Datenbus, und Select, zum Lesen des Registers auf den
Datenbus. Noch einen Anschluss an den Input-Bus der ALU könnte sich als
Problem erweisen.

Die CPU, die in der oben genannten Webseite beschrieben wird, löste das
Problem übrigens mit impliziten Registern: Die Register B und C sind die
Input-Register und Punkt. Das würde ich gerne vermeiden.

Außerdem wollte ich ja eine Multiplikation auch inkludieren. Also wird
der Output-Bus der ALU ebenfalls zwei Registergrößen umfassen. Und dann
brauche ich eine Möglichkeit, das Ergebnis entsprechend aufzuspalten.
OK, andere Möglichkeit wàre, den Output-Bus ein Maschinenword groß zu
machen, und das Ergebnis zu spalten, sobald das kleinere Maschinenword
bekannt ist. Dann wird das Timing dieser Instruktion aber ein PITA.

Tschö,
Markus
 

Lesen sie die antworten

#1 Jan Seiffert
13/09/2010 - 00:52 | Warnen spam
Markus Wichmann schrieb:
Hi all,

ich bin mir nicht sicher, ob ich hier richtig bin und könnte andernfalls
einen Hinweis gebrauchen.

Jedenfalls hab ich mich mal auf
<http://web.cecs.pdx.edu/~harry/Relay/index.html> umgesehen und mir
gedacht: Mal sehen, ob ich sowas àhnliches auch hinbekomme.




Hmmm, willst du das auch mit Relais machen?
Oder TTL/CMOS?
Oder FPGA?

Das ist erstmal das "wichtigste", denn das setzt irgendwie den Scope/Grenzen des
ganzen.

Kurz: Es soll mal so etwas àhnliches wie ein x86_64 werden, nur ohne die
Fuckups.



Das ist dann schon ein "advanced" ziel. Da du kein grosses Prozessorhaus mit
super-duper Fertigung bist empfiehlt sich ein _moeglichst_ einfacher RISC, das
macht den HW-Aufbau (& Design) einfach.
Das ist dann aber keine tolle CPU, und beinhaltet IMHO seine eigenen Fuck-UPs.

Was glaubst du denn was das tolle an sowas wie ARM, PPC, MIPS oder SPARC ist?
Nicht das sie die absoluten Segensbringer sind (Hier und da ein paar nette Ideen
und Konzepte, aber IMHO auch ein haufen Unsinn aus dem Elfenbeinturm).

Wenn man nicht die High Performance Teile giesst (X Pipeline stufen, caches,
foo, bar), laest sich so ein Teil in null-komma-nix aus wenig VHDL in wenig
Gatter synthetisieren (Das SPARC VHDL ist sogar verfuegbar, wars unter GPL?).

Da kann man die komplexitaet der CPU fast stufenlos verstellen vergleichbar mit:
configure --with-alus=1 --enable-caches=1k --disable-prefetch --with-foo ...
make
make install

Frueher war die Einfachheit dann eben beim Produzieren auch ein riesen Vorteil.
Heutzutage ist das Produzieren nicht das Problem, so das diese Abgespeckten
Designs IMHO eher ihre Nachteile zeigen.

Aber so laesst sich Heutzutage mit geringem Aufwand eine breite Produktpallette
aufstellen, die besser auf den Zielmarkt passt (und natuerlich "gnadenlos" nach
unten skaliert).


Meine Frage ist, ob ich die von Zeit zu Zeit auftauchenden
Fragen eventuell hier posten könnte.




Nun, wir reden hier IMHO gerne ueber den bit und byte fuddel kram, auch mal auf
Hardware ebene.
Denoch muss ich in dem Fall sagen: Wenns ums Gatter biegen geht, bist du mit
einer Elektronik-Gruppe wohl besser bediehnt.
Wenngleich das Instructionset eines solchen home-made Prozessor ein
interressantes Thema sein koennte, was dann in einer Elektronik-Gruppe nicht so
ganz passt...

Was dann ein generelles Problem aufzeigt:
Man macht fuer einen schoen zu programmierenden Prozessor am besten erstmal
einfach das Instruction set (vielleicht mit einem Emulator) und programmiert
auch mal wirklich damit, damit man ein Gefuehl dafuer bekommt (Empfehlung: die
basics der libc implementieren, nicht in einfach das es "geht", sondern in
schoen schnell und optimiert), dann kann man das noch verfeinern, ueber die
"philosophie" des Instruction set nachdenken.

Wie sich das in Hardware giesst ist dann nur noch "runterbeten".

Aber, naja, leider auch nicht so ganz, wie du schon beschrieben hast.
Darum schlagen immer wieder Beschraenkungen der HW in das Instruction set durch
(oder das Instruction set ist da gleich drauf ausgerichtet), das muss sich schon
danach richten was "machbar" ist (oder praktikabel, aber das instruction set
selber sollte es auch sein -> ups)...

[snip]
In dem Zusammenhang
bin ich aber über ein Problem gestolpert:

Mein Datenbus wird ja genauso groß, wie ein Register, also 64 Bit. Mein
Input-Bus muss notgedrungen zwei Register aushalten, also 128 Bit. Wie
kriege ich nun Daten möglichst einfach aus einem Register über den
Datenbus in die ALU? Die Register hàngen ja nur direkt am Datenbus.

Ich sollte hinzufügen, dass sich sàmtliche meiner Überlegungen noch auf
Papier befinden, noch nix realisiert ist, ja, noch nichtmal mit Erfolg
gerechnet wird.

Eine Möglichkeit wàre, zwei Register hinzuzufügen, die ich dann Input0
und Input1, oder kurz I0 und I1 nenne, und vor jeder Operation der ALU
erst diese Register befüllen muss. Geht es auch anders?



Nun, diese Register musst du ja nicht unbedingt "offen" legen, sie sind implizit da.
Du machst den Datenbus 64 bit, und jetzt besteht die ausfuehrung eines Befehls
immer aus diesen schritten:
fetch regx -> i0 or NOP
fetch regy -> i1 or NOP
operation
store res0 -> regz or NOP
Halt eine kleine HW-Statemachine um die ALU drum rum.

Eine andere Moeglichkeit ist: Du hast zwei input operand busse zu 64 bit fuer
op0 & op1.
Beide Busse sind mit allen Registern verbunden (Leitungen sind Billig...), das
richtige Operandregister wird dann jeweils auf den entsprechenden Bus
aufgeschaltet. Bus 0 ist mit dem einen Eingang deiner ALU verbunden, Bus 1 mit
dem anderem.
Hier mal als Schlatplan fuer ein Bit und ein Register

Zu anderen Registern
. .
. .
. .
| |
+-| TriState Treiber |--+
| | |
| | | |
| | EN_0.0 |
| | |
| +| TriState Treiber |--+--| FF | Reg0 Bit0
| |
\\ // |
\ ALU / EN_0.1
\/ ^ ^
Register 0 Bus 1

Die Treiber sind dabei sowas wie ein 74xx244, 74xx376 oder 74xx541 (541 geht am
glattesten auf, da octal).

Du musst beim rueckschreiben der Ergebnisse nur aufpassen: entweder duerfen sich
Operant & Ergebniss-Register nicht ueberlappen, oder da muss noch ein "Puffer"
dazwischen.
Du musst die Operation warsch. sowieso in mehreren Phasen ausfuehren
operanten aufschalten
(setling time)
operation ausfuehren
(ergebniss capturen)
ergebniss zurueck schreiben


Außerdem wollte ich ja eine Multiplikation auch inkludieren. Also wird
der Output-Bus der ALU ebenfalls zwei Registergrößen umfassen. Und dann
brauche ich eine Möglichkeit, das Ergebnis entsprechend aufzuspalten.
OK, andere Möglichkeit wàre, den Output-Bus ein Maschinenword groß zu
machen, und das Ergebnis zu spalten, sobald das kleinere Maschinenword
bekannt ist. Dann wird das Timing dieser Instruktion aber ein PITA.




Was glaubst du warum so viele RISC in ihrer Urform keine Multiplikation enthalten?

Ein Trick ist, das die MUL-Alu doppelt breit ist, um das ganze Ergebniss zu
erzeugen, aber man zwei Befehle hat:
MULHI
MULLO

MULHI gibt die obere haelfte des Ergebniss zuruck, MULLO die untere haelfte.
Und bevor du jetzt sagst, das ist doof: In der Praxis ist das schon ganz gut
gebrauchbar und besser als garnix.
Auch beliebt ist die Sache mit "Register paaren", in x86 in der eax:edx
implementation, in anderen oft in einer "ein grades und ein ungrades sind ein
paar" oder "zwei nebeneinander sind ein paar" (man sagt r4 und es landed in r4 &
r5, teilweise nur grade Register sind fuer den Befehl zulaessig). Das Ergebniss
aus der MUL-Alu rausschreiben passiert dann auch wieder in mehreren schritten.
Mit dem bekifften SPARC multiplication helper faengst du besser erst garnicht an...

Du wirst sowieso schauen muessen:
Einseits willst du "auf Teufel komm raus" sparen (die Instruktionen werden davon
nicht schneller...), andererseits willst du ein recht "komplettes" Instruction
set. Das heisst dann wieder das du das alles in HW giesst (sparen?), oder das du
die ausfuehrung von Microcode vorsiehst (was aber auch die impl. verkompliziert
+ langsamer).
Lies dir mal den vierten Absatz hier durch:
http://en.wikipedia.org/wiki/ARM_ar...SC_Machine:_ARM2
Er beschreibt dieses Problem-Bermudadreieck ganz gut.

Achso:
Bitfriemelinstruktionen (count leading zeros und sowas) nicht vergessen, wenn
man sie braucht und nicht hat, sind sie ein PITA.

Tschö,
Markus



Gruss
Jan

"You can call me Echo"

Ähnliche fragen