ISA-Opcodes --- Woher kommen sie?

12

Wenn Ingenieure eine Befehlssatzarchitektur entwerfen, befolgen sie, wenn vorhanden, welche Prozedur oder welches Protokoll, um bestimmte Binärcodes als Anweisungen zu kennzeichnen. Wenn ich beispielsweise eine ISA habe, die besagt, dass 10110 eine Ladeanweisung ist, woher stammt diese Binärzahl? Wurde es aus einer Statustabelle für eine Zustandsmaschine modelliert, die eine Ladeoperation darstellt?

Bearbeiten: Nach mehr Recherche glaube ich, was ich zu fragen versuche, betrifft, wie die Opcodes für die verschiedenen CPU-Anweisungen zugewiesen sind. ADD kann mit einem Opcode von 10011 gekennzeichnet sein. Ein Ladebefehl könnte als 10110 bezeichnet werden. Welcher Denkprozess wird durchgeführt, um diese binären Operationscodes für den Befehlssatz zuzuweisen?

Steven
quelle
8
Monte Dalrymples "Mikroprozessorentwurf mit Verilog HDL" bietet einen sehr detaillierten Entwurfsansatz für die Z80-CPU. Ich denke, Sie werden viel über Ihre Frage erfahren. Es gibt jedoch viele Überlegungen, die für eine bestimmte Auswahl relevant sind, einschließlich der statistischen Analyse anderer Befehlssätze, Compiler-Ausgaben usw. Ich würde jedoch empfehlen, mit diesem Buch zu beginnen. Obwohl es mit einem bekannten Design beginnt, geht er genau darauf ein und ich denke, Sie würden ein paar Dinge aufgreifen. Gutes Buch.
Jonk
Oder fragen Sie sich vielleicht nach dem Design der Ausführungsmaschine und fragen sich, wie die Bits in der Anweisung dazu beitragen könnten? Ich bin mir Ihrer Formulierung nicht sicher.
Jonk
2
Jemand anderes stellt diese Frage. Muss Dienstag sein.
Ignacio Vazquez-Abrams
5
@Steven Denk darüber nach. Wenn Sie eine ISA entwerfen müssten, woran würden Sie denken? Wenn Ihre Anweisungen nicht alle gleich lang wären, wie würden Sie kürzere oder längere Anweisungswörter auswählen, für welche Anweisungen? Wenn Sie eine entwerfen hatte Dekodierstufe , was würden Sie wollen für Ihre ISA aussehen? Ich denke, die Frage ist unnötig weit gefasst (und daher so gut wie unmöglich zu beantworten), aber Sie können sie erheblich verbessern, indem Sie etwas mehr über sie nachdenken und eine präzise Frage stellen, bei der wir nicht ein Buch schreiben müssten, um sie zu beantworten es.
Marcus Müller
3
In den RISC-V-Spezifikationen wird über die Konstruktionsentscheidungen gesprochen, die sie auf allen Ebenen getroffen haben, einschließlich ein wenig über die Codierung von Maschinenanweisungen. (Dies ist ungewöhnlich für ein Prozessorhandbuch. RISC-V ist im Gegensatz zu den meisten anderen eine akademische Übung und eine CPU-Architektur.)
zwol

Antworten:

5

In vielen Fällen ist die Auswahl ziemlich willkürlich oder basiert auf "wo immer es am besten passt", wenn ISAs im Laufe der Zeit wachsen. Der MOS 6502 ist jedoch ein wunderbares Beispiel für einen Chip, bei dem das ISA-Design stark beeinflusst wurde, indem versucht wurde, so viel wie möglich aus den begrenzten Transistoren herauszupressen.

Schauen Sie sich dieses Video an, in dem erklärt wird, wie der 6502 rückentwickelt wurde , insbesondere ab 34:20 Uhr.

Der 6502 ist ein 8-Bit-Mikroprozessor, der 1975 eingeführt wurde. Obwohl er 60% weniger Gates hatte als der Z80, war er doppelt so schnell, und obwohl er eingeschränkter war (in Bezug auf Register usw.), machte er dies mit einem wett eleganter Befehlssatz.

Es enthält nur 3510 Transistoren, die von einem kleinen Team von Personen von Hand herausgezogen wurden , die über einige große Plastikfolien krabbelten, die später optisch verkleinert wurden und die verschiedenen Schichten des 6502 bildeten.

Wie Sie unten sehen können, übergibt der 6502 den Anweisungs-Opcode und die Zeitsteuerungsdaten an den Decodierungs-ROM und übergibt sie dann an eine "Zufallssteuerungslogik" -Komponente, deren Zweck wahrscheinlich darin besteht, die Ausgabe des ROM in bestimmten komplexen Situationen zu übersteuern.

6502 Blockdiagramm

Um 37:00 Uhr im Video sehen Sie eine Tabelle des Decoder-ROM, die zeigt, welche Bedingungen die Eingänge erfüllen müssen, um für einen bestimmten Steuerausgang eine "1" zu erhalten. Sie finden es auch auf dieser Seite .

Sie können sehen, dass die meisten Dinge in dieser Tabelle Xs in verschiedenen Positionen haben. Nehmen wir zum Beispiel

011XXXXX 2 X RORRORA

Dies bedeutet, dass die ersten 3 Bits des Opcodes 011 sein müssen und G 2 sein muss; Das ist alles, was zählt. In diesem Fall wird die Ausgabe mit dem Namen RORRORA auf "true" gesetzt. Alle ROR-Opcodes beginnen mit 011; Es gibt aber auch andere Anweisungen, die mit 011 beginnen. Diese müssen wahrscheinlich von der "Zufallssteuerlogik" herausgefiltert werden.

Grundsätzlich wurden Opcodes so ausgewählt, dass Anweisungen, die dasselbe tun mussten, über ihr Bitmuster hinweg etwas gemeinsam hatten. Sie können dies sehen, indem Sie sich eine Opcode-Tabelle ansehen . Alle ODER-Anweisungen beginnen mit 000, alle Speicheranweisungen beginnen mit 010, alle Anweisungen, die eine Nullseitenadressierung verwenden, haben die Form xxxx01xx. Natürlich scheinen einige Befehle nicht "zu passen", da das Ziel nicht ein vollständig reguläres Opcode-Format ist, sondern ein leistungsfähiger Befehlssatz. Und deshalb war die "Zufallssteuerlogik" notwendig.

Die oben erwähnte Seite besagt, dass einige der Ausgabezeilen im ROM zweimal vorkommen. "Wir gehen davon aus, dass dies geschehen ist, weil sie nicht die Möglichkeit hatten, die Ausgabe einer Zeile dahin zu leiten, wo sie wollten, sodass sie dieselbe Zeile an eine andere stecken Lage wieder. " Ich kann mir vorstellen, dass die Ingenieure diese Tore einzeln von Hand zeichnen und plötzlich einen Konstruktionsfehler bemerken und versuchen, einen Weg zu finden, um einen Neustart des gesamten Prozesses zu vermeiden.

Artelius
quelle
22

Es kommt darauf an, wie alt die ISA ist.

In den Anfängen des Hand-Designs und noch mehr, als CPUs aus diskreter Logik zusammengesetzt wurden, wäre das Logik-Design an erster Stelle gestanden und weitgehend minimiert worden, und dann wären die ISA-Bitmuster die Werte gewesen, die erforderlich waren, um dies minimal zu machen logische Arbeit.

So kann es ein bestimmtes Muster von Steuersignalen geben, die es einigen Multiplexern ermöglichen, den ALU-Ausgang mit dem Eingang der GP-Registerdatei zu verbinden, einige weitere Steuersignale, die die ALU anweisen, UND, ODER usw. zu addieren, zu subtrahieren und einige Adressbits in die Registerdatei. Diese drei Signalgruppen bilden Felder innerhalb des Befehls. Jede Gruppe wird zusammengehalten, und ihre detaillierte Bedeutung ergibt sich aus dem Entwurf für diese Einheit (ALU usw.), aber die Gruppen können in beliebiger Reihenfolge sein, bis Sie den Befehlsdecoder entwerfen. (Der x86 ist alt genug, dass Sie einiges davon erkennen können, wenn Sie an der richtigen Stelle suchen - es war kein völlig neues Design, sondern stammt aus dem älteren 8080)

Spätere ISAs können "aufgeräumt" und regelmäßiger und benutzerfreundlicher gestaltet werden, wobei Hardware zwischen ihnen und den eigentlichen Steuersignalen auf Hardwareebene zu übersetzen ist, manchmal über "Mikrocode". Diese werden als "CISC" oder "Complex Instruction Set Coding" bezeichnet. Das x86 "Rep" -Anweisungspräfix ist ein einfaches Beispiel dafür - es bewirkt, dass die folgende Anweisung mehrmals wiederholt wird, um das Schreiben einer FOR-Schleife zu vermeiden.

Noch später (in den 1980er Jahren) kam eine Bewegung zurück zu einer einfacheren Art der direkten Codierung (RISC - Reduced Instruction Set Coding), die Sie in den ARM-Prozessoren sehen können. Ausschlaggebend dafür war die geringe Größe der ASICs zu dieser Zeit und der Wunsch, 32-Bit-CPUs auf ihnen zu installieren. Daher gab es keine freie Kapazität für komplexe Befehlssatzdecoder, um die gesamte CPU auf etwa 20.000 Gates zu verkleinern. (Es gab auch eine vorübergehende Leistungssteigerung, weil die Leute noch keine Techniken entwickelt hatten, um CISC-Decoder schnell zu machen - das kam 1995 mit dem Pentium Pro)

Und heutzutage spielt es keine Rolle - CPUs lesen mehrere Anweisungen auf einmal und verwenden Millionen von Transistoren, um sie zu dekodieren, neu zu ordnen und so viele wie möglich gleichzeitig auszuführen, um Programme zu beschleunigen, die für die ältesten geschrieben wurden Stil von ISA.

Brian Drummond
quelle
2
Ich bin mir nicht sicher, ob ich CISC wirklich "benutzerfreundlicher" nennen würde. Das mag die ursprüngliche Absicht gewesen sein, aber 30 Jahre später sind sie gewissermaßen das Gegenteil von "einfach zu bedienen" (zumindest im Vergleich zu RISC-ISAs).
Tonysdg
2
Es gibt Aspekte, in denen sie einfacher zu handhaben waren ... entweder die Regelmäßigkeit (Orthogonalität war ein großes Thema), als Compiler noch relativ triviale Programme waren, oder die direkte Unterstützung übergeordneter Operationen, für die der Compiler weniger Übersetzungen benötigt. Aber das ist lange her und jede überlebende CISC hat so viele Ebenen von Revisionen zusätzlich zu ihrem ursprünglichen Befehlssatz. Auch Compiler wurden nicht mehr erkannt - die rund tausend Optimierungsläufe von gcc waren damals undenkbar. Was damals und heute "einfach" war, hat wenig damit zu tun.
Brian Drummond
4
Die Unterscheidung wurde durch neue, noch komplexere Architekturen wie VLIW ersetzt. wirklich der einzige Konsens ist, dass x86 (16 und 32 bit) schwer zu benutzen ist
pjc50
1
@tonysdg: Es gibt schwer zu verwendende RISC und schwer zu verwendende CISC. Ein guter Vergleich von "Programmiererfreundlichkeit" ist der Vergleich von 68k mit ARM. Der ARM wurde für einen Compiler entwickelt, so dass Sie viel Handarbeit leisten mussten, um Daten aus dem RAM abzurufen und zurück in den RAM zu schreiben. Der 68k wurde für Assembler-Programmierer entwickelt und ermöglicht die direkte Verarbeitung von Daten im RAM. Wenn Sie sich 68k ISA ansehen, werden Sie feststellen, dass es bis auf eine Ausnahme dem modernen RISC ISA sehr ähnlich ist - Sie können direkt mit RAM arbeiten, während Sie mit RISC nur mit Registern arbeiten können.
Slebetman
1
Mikrocode ist in erster Linie ein CISC-Attribut. Sie könnten jedoch CISC ohne Mikrocode implementieren: Der Befehlsdecoder wäre komplizierter. Sie sehen auch einige CISCs ab Pentium-Pro, die intern als RISC bezeichnet werden. Übersetzen jeder CISC-Anweisung in eine oder mehrere interne RISC-Operationen: ein anderer Name für Mikrocode (obwohl die Unterscheidungen in superskalaren Ausführungseinheiten verschwimmen)
Brian Drummond
9

Wenn Sie ähnliche Anweisungen gruppieren, entstehen Muster. Dies ist in ARM sehr offensichtlich, wo das ISA-Handbuch tatsächlich anzeigt, welches Bit eines Befehlsworts der Funktion, der Registerauswahl usw. entspricht. Es kann jedoch auch auf X86 geschlossen werden .

Letztendlich geht der "Funktions" -Teil von Opcodes in einen Binär-zu-Eins-Hot-Decoder, der tatsächlich eine bestimmte Funktion oder Sequenz von Pipeline-Operationen aktiviert. Sie beziehen sich normalerweise nicht auf den Inhalt einer Zustandsmaschine, es sei denn, es handelt sich um Befehle variabler Länge, für deren Dekodierung eine Zustandsmaschine erforderlich ist.

pjc50
quelle
Sie sagen im Grunde, dass sie auf die niedrigstmögliche Transistoranzahl auf dem Chip abzielen. Ich stimme voll und ganz der Frage von OP zu, wo sie sich nicht Hunderte zusätzlicher Transistoren für einen ordentlicheren Befehlssatz leisten können. Die Millionen-Transistor-CPUs haben bei weitem keinen Grund, sich darum zu kümmern, aber viele behalten es aus Gründen der Abwärtskompatibilität bei.
Harper - Reinstate Monica
@Harper Es gibt immer noch einen Grund, weil die Transistoren zwar kleiner wurden, aber immer noch eine Größe haben - und die Taktraten in der Zwischenzeit stark gestiegen sind. Ein zu großer Befehlsdecoder kann daher immer noch ein Leistungsengpass sein (einer der Gründe, warum sich viele CPUs dafür entschieden haben, Befehle vorab zu codieren). Es geht nicht (nur) um die Anzahl der Transistoren, sondern mehr um die Taktrate in Kombination mit der Chipfläche. Die Weitergabe von Informationen nimmt noch einige Zeit in Anspruch, und obwohl moderne CPUs nicht mit Lichtgeschwindigkeit laufen, sind sie nicht weit genug vom Tempolimit entfernt, um signifikante Verbesserungen zu erwarten.
Luaan
@Luaan: Eigentlich ist "Was machen wir mit all diesen Transistoren" heutzutage eine echte Frage. Schauen Sie sich alle L2 / L3-Caches an, die heutzutage herumgeworfen werden. Das ist eine stille Einsicht, die wir für all diese Millionen von Transistoren nicht besser gebrauchen können. Die neuesten Xeons widmen mehr als 2 Milliarden Transistoren dem Cache!
MSalters
6

Irgendwann setzte sich jemand und definierte sie.

Ein guter ISA macht den Decoder so einfach wie möglich.

Zum Beispiel könnten Sie mit einem ALU-Befehl einige Bits des Opcodes direkt in die Steuerleitungen der ALU senden lassen.

Ratschenfreak
quelle
Vielen Dank an alle für die hervorragenden Antworten. Sie haben mir alle geholfen, das viel besser zu verstehen.
Steven
4
Es gibt tatsächlich einige andere Faktoren als die Einfachheit des Decoders, die berücksichtigt werden müssen. Abhängig von den Umständen und der beabsichtigten Verwendung können andere (z. B. Codedichte) wichtiger sein als die Einfachheit des Decoders. In einem modernen Prozessor überwiegt die Codedichte wahrscheinlich in den meisten Fällen die Einfachheit des Decoders .
Jerry Sarg
5

Normalerweise teilen Sie Ihren ISA in Funktionsgruppen auf. Es ist sinnvoll (entweder für die Logikoptimierung oder um aufgeräumt zu sein), dass komplementäre Paare durch einen einzelnen Bitwechsel (Laden gegen Speichern) unterschieden werden und dass Sie eine Hierarchie von Bits haben, die sich auf den Decodierungsentscheidungsbaum auswirkt.

Letztendlich hat eine willkürliche Zuweisung von Bits für den Funktionsblock (im Gegensatz zum Platzieren der 'Daten'-Felder in der Anweisung) nur einen geringen Einfluss auf Ihre gesamte Entwurfseffizienz - aber Sie haben viele Möglichkeiten, wie Sie vorgehen müssen Optimieren Sie Ihre ISA-Codierung, je nachdem, was Sie für einen wichtigen Parameter halten.

Sean Houlihane
quelle
1

Befehlskodierung ist ein hässlicher Kompromiss zwischen.

Vereinfachen Sie die Dekodierung. Dazu benötigen Sie einen einfachen Satz von Feldern, von denen jedes separat dekodiert und an einen separaten Teil der Ausführungs-Engine weitergeleitet werden kann.

Packen Sie so viele Funktionen wie möglich in ein begrenztes Anweisungswort. Dies führt zu Dingen wie speziellen Konstantenformaten, die eine Vielzahl gängiger Zahlen codieren können.

Vorwärts- und Rückwärtskompatibilität. Wenn Sie jedem möglichen Opcode eine Funktion zuweisen, haben Sie keinen Raum mehr, um die Architektur später zu erweitern. Wenn Sie eine vorhandene Architektur erweitern, müssen Sie Ihre neuen Anweisungen in die Ersatz-Opcodes einfügen.

Peter Green
quelle
1

Randy Hyde ist exzellent (wenn auch etwas veraltet). The Art of Assembly wird im x86-Befehlssatz ausführlich in Kapitel 3.3.4, Steuergerät und Befehlssätze, und in den folgenden Abschnitten beschrieben .

Programme in frühen Computersystemen (vor Von Neumann) waren oft "fest" in die Schaltung eingebunden. Das heißt, die Verkabelung des Computers hat bestimmt, welches Problem der Computer lösen würde. Man musste die Schaltung neu verkabeln, um das Programm zu ändern. Eine sehr schwierige Aufgabe. Der nächste Fortschritt im Computerdesign war das programmierbare Computersystem, das es einem Computerprogrammierer ermöglichte, das Computersystem unter Verwendung einer Abfolge von Buchsen und Steckerdrähten auf einfache Weise neu zu verdrahten. Ein Computerprogramm bestand aus einer Reihe von Bohrungen (Sockeln), wobei jede Reihe eine Operation während der Ausführung des Programms darstellte. Der Programmierer könnte eine von mehreren Anweisungen auswählen, indem er einen Draht in die bestimmte Buchse für die gewünschte Anweisung einsteckt.

Er zeigt dann recht eingängig und ausführlich, wie die ersten Stecker für die Anweisung stehen, die nächsten Stecker codieren Quelle und Ziel. Natürlich "stopft" heute niemand mehr, aber für die wirklich alten ISAs machen die Bits im Opcode im Grunde den gleichen Job wie die Stecker zuvor.

Am Ende haben Sie ungefähr Folgendes:

Bildbeschreibung hier eingeben

DevSolar
quelle
Vielen Dank für den Link von Hyde! Es ist sehr informativ und er scheint einen exzellenten Unterrichtsstil zu haben.
Steven