Hallo,
Dabei habe ich mich hauptsächlich mit einem Problem beschäftigt, das bei RISC CPU's auftritt, nämlich die Befehlslänge.
Ja, das ist wohl wahr. Aber es gibt auch interessante Lösungen dafür, schau Dir mal den Itanium an. Auch das Problem mit der bedingten Ausführung ist dort auf sehr interessante Art und Weise gelöst.
(eigentlich könnte hier meine Antwort schon zu Ende sein aber ich hoffe man nimmt es mir bei diesem Thema nicht übel wenn ich das doch mit ein paar Buchstaben mehr erkläre)[...]
Naja, ganz grob entspricht das meiner Möglickeit 2. Allerdings halte ich so einen Itanium-Befehlssatz für überflüssig, weil diese Art von Parallelität nur selten vorkommt (entweder will man SIMD oder MISD.) Für SIMD ist der Befehlssatz zu mächig und damit zu teuer (Man nutzt die Möglichkeiten nicht richtig aus). MISD lässt sich, da Abhängigkeiten zwischen den Paralellbefehlen verbotenen sind, nicht schnelle als bei einem konservativen Befehlssatz lösen. (Ein MISD Prozessor wäre auch mal interessant.)
(Auch wenn die z.B. bei ARM vorgestellten Möglichkeiten schon gut sind.)
Ja, der Weg von ARM ist interessant aber er hat auch einen eklatanten Nachteil, er erfordert das der Code immer auch lesbar ist (es werden ja ganz normale Speicherzugriffe nur eben mit IP-relativer Adressierung benutzt). Aus Gründen der Sicherheit ist es aber besser wenn Code nicht lesbar sondern nur ausführbar ist.
Ansonsten gibt es für Deine Version mit den 32Bit Befehlen noch eine Möglichkeit 4:
Um ein NOP zu kodieren sollten nicht mehr als 8 Bit nötig sein so das in jedem NOP immer 24 Bit an Daten enthalten sein können. Wenn Du nun einen Load-Immediate-Befehl baust der selber noch 16 Bits Daten enthält (die restlichen 16 Bits sollten doch reichen um den Befehl ansich und das Zielregister zu kodieren) dann könnte der im Decoder um 2 Takte verzögert werden (es käme eben für 2 Takte kein Befehl aus dem Decoder raus) und der Decoder würde intern aus diesem Befehl und den 2 folgenden NOPs insgesamt einen Load-Immediate mit vollen 64Bit Nutzdaten bauen. (wenn einer der beiden folgenden Befehle kein NOP ist gibt es eben eine Illegal-OpCode-Exception) Das hätte auch den Vorteil das nichts schlimmes passiert wenn mal einer der beiden Daten-NOPs angesprungen wird und auch der Disassembler (im Debugger) hätte keine Probleme was sinnvolles anzuzeigen. Ein weiterer Vorteil ist das diese Variante allein im Decoder passiert und Dein eigentlicher CPU-Kern davon nicht betroffen ist, spätestens wenn Du irgendwann doch mal das simple In-Order-Pipeline-Konzept verlassen möchtest wird es Dir sehr entgegen kommen Dich im CPU-Kern auf die eigentliche Arbeit konzentrieren zu können.
Das Problem ist, das man dadurch einen großen Teil des möglichen Befehlssatzes blockiert. Bisher hab ich mich außerdem nie mit Befehlen die mit doppelter Datenabhängigkeit funktionieren beschäftigt. Die von dir beschriebene Möglichkeit hatte ich auch schon mal, da sie z.B. bei ARM schon vorbereitet wurde. Die never Kondition entspricht quasi deinem nop. Ein weiterer Nachteil deines Vorschlags ist, das man spezielle Befehle hierfür benötigt. Möglichkeit 3 zeichnete sich ja gerade dadurch aus, das man die Immideate wie ein Register nutzten konnte, weil die Daten allein über einen Registerzugriff aktiviert werden. Bei Dissassemblen hab ich eigentlich keine Schwierigkeiten. Der Dissassembler dekodiert dann halt so was wie add r1, r2, nir(0xABCDEF01) und setzt den abhängigen Befehl in Klammern, oder so.
Das mit dem Skip-Befehl ist keine so schlechte Idee aber auch hier solltest Du das gleich im Decoder lösen und dem CPU-Kern nur Befehle übergeben bei denen die Ausführungsbedingung integraler Bestandteil ist. Es sollte auch möglich sein das sich dieses Condition-Präfix mit jedem Befehl kombinieren lässt, damit lassen sich wieder viele Sprünge vermeiden und das bringt einiges an Performance und spart oft auch Code. [...]
Das mit der universellen Zuordnung hatte ich eh so vorgesehen (es hieß ja nicht skip next branch). Überhaupt hab ich mir überlegt, ob ich nicht zwei Dekodierstufen einfügen soll. Das macht die Sache einfacher.
... solltest Du Dir mal anschauen wie das bei Alpha gelöst ist.[...]
Die Alpha Methode wäre natürlich auch ne Möglichkeit, allerdings ist die Nutzung eines beliebigen Register natürlich mit viel notwendiger Information verbunden. Dafür ist diese Möglichkeit ziemlich nah an dem System, das auch viele Programmiersprachen anwenden. (von FreeBasic weiß ich, das ein if ausgeführt wir, wenn der Ausdruck ungleich null ist. In C ist es glaub ich genauso.)
Grundsätzlich gibt es drei Konditionsmöglichkeiten.
A) Keine Konditionellen Befehle außer bei Sprüngen. Pro. Keinen Speicherbedarf im Opcode Kontra: Piplineunfreundlich
B) ARM-artige Konditionen: Pro: mächtig Kontra: Wenig Erweiterungsmöglichkeiten, Koste viel Speicher im Opcode
C) Alpha-artige Konditionen: Pro: Flexible Konditionsverwaltung Kontra: Externen Vergleichsoperatoren nötig. Koste viel Speicher im Opcode und Register
D) if TRUE Flag set Kondition: Pro: Wenig Speicher im Opcode Kontra: Externe Unterstützung nötig.
Ich persönlich könnte mir also eine Möglichkeit vorstellen, bei der der einzelne Befehl keine Kondition trägt. Sprünge hätten dann sowas wie eine erweiterte Alpha-artige Kondition und Skip-Präfixe ein leistungsfähiges Konditionssystem.
Darüber hinaus gibt es natürlich noch Möglichkeiten, den Aufbau zu erweitern, indem man konditionelle Sprungbefehle oder Befehlspräfixe einführt.
Auch ich habe mir über die beiden Punkte (Befehlsgröße und bedingte Ausführung) einige Gedanken gemacht und (wie ich meine) auch ein paar interessante Lösungsmöglichkeiten gefunden, das ist ganz am Anfang dieses Threads recht gut beschrieben.
Du weißt aber schon, das du den Decoder damit ziemlich auf blähst, da du eigentlich 3 Instructionsets verwendest. Ich würde den Dekoder in verschiedene Teile aufteilen. Du brauchtest also 1 Meta-Decoder (Trennt das Pakete auf) + 2 60bit-Decoder + 3 42bit-Decoder (39bit+3bit Condition) + 6 20bit Decoder = 12 Teildecoder. Da eigentlich alle Teildecoder das gleiche machen, hättest du ziemlich viele Einzellschaltungen. (zum Vergleich ARM brauch für seine Schaltung nur ein ARM und einen Thumb decoder.) Ich würde mir deshalb überlegen, ob du nicht die kurze Befehle auf den langen Abbilden kannst, z.B. indem du konstante Bits einfügst. Außdem würde ich mich fragen, ob du wirklich 6 20bit Anweisungen simultan ausführen musst, oder ob es reicht, immer nur 3 Befehle gleichzeitig zu bearbeiten und die Pipline entsprechend zu modifizieren, oder die Piplinegeschwindigkeit zu halbieren.
Darüber hinaus sollte dir klar sein, das es schwierig ist für einen VLIW Befehlssatz Code zu schreiben, weil das Design nicht der traditionellen Logik entspricht, weshalb das Compiler Backend ziemlich kompliziert wird. Assemblerprogrammierung für eine solche Plattform ist sogar noch schwieriger. Auch die interne Logik wird aufgebläht, weil viele Baugruppen mehrfach auftreten. Von der Theorie her, mag solch ein Befehlssatz ja interessant sein, in der Praxis implementiert man aber lieber SIMD Instruktionen.