Wie kann man den Opcode für eine CPU effizient entwerfen?

Ich baue eine einfache 16-Bit-CPU in Logisim und habe die ALU bereit und die Opcodes, die ich haben möchte. Nun fällt es mir wirklich schwer, die richtige Codierung für die Befehle zu finden, damit die verschiedenen Teilschaltungen (z. B. Logik, Arithmetik) nicht alle Steuerleitungen (die die Codierung aufbauen) als Eingang benötigen, sondern so wenige wie möglich. Gibt es Strategien oder Methoden, die bei einem effizienten Opcode-Design helfen?

thx im vorraus

Bauen Sie zuerst Ihre ALU und sehen Sie, welche Steuerkabel sie benötigt. Verbinden Sie sie dann direkt mit dem Register "aktuelle Anweisung". Gleiches gilt für die Speicherzugriffssteuerlogik und alle anderen Hauptklassen von Opcodes. Verwenden Sie dann die verbleibenden Bits, um auszuwählen, welche Teilschaltung aktiviert wird.
Es gibt auch das Originalpapier von Ken Chapman über seine programmierbare 8-Bit-Zustandsmaschine KCPSM alias PicoBlaze. Es beschreibt, wie er Anweisungen auswählt und die ISA gestaltet. dc.uba.ar/materias/disfpga/2010/c2/descargas/…

Antworten (6)

Ich denke, es ist ein guter Ansatz, einige andere Befehlssätze zu studieren.

Ein kleiner wäre der MSP430 von TI, es ist ein 16-Bit-Prozessor mit etwa 22 Befehlen.

http://www.physics.mcmaster.ca/phys3b06/MSP430/MSP430_Instruction_Set_Summary.pdf

Sie können sich auch die Atmel-AVRs ansehen, die auch einen recht kleinen Befehlssatz haben.

In einem kleinen Projekt von mir habe ich versucht, einen einfachen 32-Bit-Prozessor in VHDL mit einem kleinen Befehlssatz (14 Befehle) zu entwickeln:

http://www.blog-tm.de/?p=80

Aufgrund meiner derzeitigen Freizeit ist es noch nicht ganz fertig. Die Anweisungen sind implementiert, aber zwei sind nicht getestet und möglicherweise fehlen einige Status-Flags.

Aber ich habe nichts gefunden, wo ich sehen könnte, was die tatsächliche Codierung ist und warum sie so gewählt wurde.
Die aktuelle Codierung finden Sie im Repo: github.com/TM90/MISC_Processor/raw/master/Documentation/… . Der Grund, warum ich diese Kodierungen so gewählt habe, dass die Logik im Befehlsdekodierer minimal wurde.

Studieren (aber nicht replizieren) Sie den ARM-Ansatz für die Anweisungscodierung. Es ist stark präfixorientiert (wie der von Dzarda empfohlene Huffman-Baum-Ansatz) und sehr einheitlich in Bezug darauf, wo sich der Registerauswahlteil der Anweisung befindet.

Der einfallslose, aber zuverlässige Ansatz besteht darin, alle vorhandenen Steuersignale aufzuzählen , die wahrscheinlich mehr als 16 Bit umfassen, und dann zu versuchen, eine Logikminimierung im Karnaugh-Map-Stil durchzuführen.

Ich verstehe nicht ganz, was du mit Steuersignalen meinst.
Was ich am ARM gefunden habe und mag, ist das Bedingungsfeld, das werde ich einschließen.
Steuersignale sind die Eingänge zu den verschiedenen Multiplexern und ermöglichen, dass Daten zwischen den Teilen der CPU geleitet werden.
Für eine 16-Bit-Architektur halte ich die Befehlscodierung von ARM nicht für angemessen. Mayby thumb2 ist besser. Aber ich mag die MIPS-Art der Kodierung, einfach und leicht verständlich, wenn auch etwas verschwenderisch

Einmal habe ich versucht, in Logisim eine 4-Bit-CPU mit einem Kern mit 8-Bit-Befehlslänge zu erstellen. Es endete mit einer einfachen Zustandsmaschine, wirklich mehr als einer CPU.

Zufällige Dinge zu suchen

  • Huffman-Bäume
  • Kodierung mit fester Länge oder variabler Länge?
  • Ist es ein von Neumann-Design mit einem einzigen Adressraum oder ein Harvard-Stil mit separaten Daten/Programmen?

Ausgezeichnetes Video auf Computerphile über Huffman-Bäume:

https://www.youtube.com/watch?v=umTbivyJoiI

Die Huffman-Codierung funktioniert nicht für eine Codierung mit fester Länge, oder?
@Benjoyo Ich kann mir ein Szenario vorstellen, in dem die Ersatzbits für Variationen der am häufigsten verwendeten Anweisungen verwendet werden, wodurch mehr Funktionalität bereitgestellt wird.
Aber ich verstehe nicht, was für eine Optimierung das bringt. Es hilft mir nicht beim Schaltungsdesign. Was ist das Ziel bei der Verwendung von Huffman-Codierung für Opcode?

Die ISA, die ich für den Unterricht geschrieben habe, hatte einmal einen 4-Bit-Operationscode wie folgt: 1XXX ALU instructions 01XX jump, jump register, call etc 001X branch not equal, branch equal zero 000X 0 - load, 1 - store

Anstatt der optimalste zu sein, ist dies einer der einfacheren Stile zum Konstruieren / Entwerfen von Gattern, da das Eingangssignal eines einzelnen Bits vollständig steuern kann, welcher logische Pfad genommen wird. Alternativ können Sie Ihre am häufigsten verwendeten Symbole Huffman- codieren und mit Nullen auffüllen, um einen Operationscode mit fester Länge zu erhalten.

Diese Art der Optimierung ist das, wonach ich im Moment suche. Aber ich habe einen 5-Bit-Opcode und kämpfe damit, die Befehle so zu gruppieren, dass sie in der Schaltung sinnvoll sind.
@Benjoyo Sie könnten eine Menge mehr ALU-Anweisungen mit dem oberen Bitsatz haben. Außerdem war meine Sprungbedingungsabdeckung ziemlich schwach und die meisten normalen Zweige würden zwei Anweisungen erfordern. Im Allgemeinen dachte ich an die Kategorien: Mathematik / Kontrolle / Gedächtnis

Eine Sache, die Sie berücksichtigen müssen, ist, ob Sie irgendeine Form von Mehrwort-Anweisungen zulassen oder alles, was wie eine Mehrwort-Anweisung "fungieren" kann; Wenn Sie dies tun, sollten Sie dann überlegen, ob Sie zusätzliche Anweisungswörter nach der Hauptanweisung verwenden oder Wörter voranstellen möchten. Das Zulassen von Präfixen und Folgewörtern kann die Komplexität der Unterbrechungsbehandlung erhöhen, aber es kann die Notwendigkeit vermeiden, selten verwendete Anweisungen in denselben Opcode-Bereich wie häufig verwendete einzufügen.

Wenn Befehle im Zyklus abgerufen werden, bevor sie ausgeführt werden, könnte man einen "bedingten Verzweigungs"-Befehl haben, der entweder bewirkt, dass das nächste Befehlswort übersprungen wird, oder dessen Inhalt direkt in den Programmzähler übertragen wird; Ein solches Design könnte die Interrupt-Sequenzierung etwas komplexer machen, aber es könnte die Notwendigkeit verringern, einen großen Teil des Opcode-Speicherplatzes für "Verzweigungs-", "Sprung"- und "Aufruf"-Befehle zu verwenden, während es einen viel größeren Bereich von Verzweigungsbedingungen zulässt als sonst möglich wäre. Da eine ausgeführte Verzweigung im Allgemeinen einen toten Zyklus nach der Ausführung des Befehls selbst erfordert, unabhängig davon, woher die Adresse stammt, kostet es keine zusätzlichen Kosten, wenn die Adresse aus dem folgenden Wort stammt, das abgerufen wurde, aber nicht ausgeführt wird Zeit.

Auch wenn das Verschieben der Zieladresse aus den Verzweigungsbefehlen den Opcode-Platz, den sie verschlingen, verringert, ist ein 16-Bit-Opcode-Format immer noch ziemlich eng. Die Verwendung von Präfixanweisungen kann dabei helfen. Wenn man beispielsweise 32 Register haben möchte, würde die unabhängige Angabe jedes Registers als Quelle1, Quelle2 und Ziel 15 Bits im Opcode erfordern, was eine satte Gesamtzahl von zwei Anweisungen ermöglicht. Nicht sehr nützlich. Andererseits wäre es schön, jedes der 32 Register für jeden der drei Operanden verwenden zu können. Man könnte die beiden Ziele ausgleichen, indem jede ALU-Operation, der kein Präfix vorausgeht, acht Bits verwendet, um zwei Eins-aus-sechzehn-Registerauswahlen zu treffen, aber eine ALU-Operation, die unmittelbar auf ein Präfix folgt, einige Bits im Präfix verwendet mit acht aus der folgenden Anweisung, um eine unabhängige Auswahl beider Quellen und des Ziels aus dem vollständigen Satz von 32 zu ermöglichen. Befehle, die die oberen Register verwenden, würden zwei Wörter/Zyklen anstelle von einem benötigen, aber in einigen Fällen könnte sich ein solcher Kompromiss durchaus lohnen. Die größte Schwierigkeit bei der Verwendung von Präfixen besteht darin, dass man entweder verhindern muss, dass zwischen einem Präfix und der nächsten Anweisung ein Interrupt auftritt, oder aber sicherstellen muss, dass, wenn dort ein Interrupt auftritt, die Anweisung nach dem Präfix immer noch die richtigen Register verwendet [z -Zählersicherungslogik speichert die Adresse des letzten ausgeführten Nicht-Präfix-Befehls]. aber in einigen Fällen könnte sich ein solcher Kompromiss durchaus lohnen. Die größte Schwierigkeit bei der Verwendung von Präfixen besteht darin, dass man entweder verhindern muss, dass zwischen einem Präfix und der nächsten Anweisung ein Interrupt auftritt, oder aber sicherstellen muss, dass, wenn dort ein Interrupt auftritt, die Anweisung nach dem Präfix immer noch die richtigen Register verwendet [z -Zählersicherungslogik speichert die Adresse des letzten ausgeführten Nicht-Präfix-Befehls]. aber in einigen Fällen könnte sich ein solcher Kompromiss durchaus lohnen. Die größte Schwierigkeit bei der Verwendung von Präfixen besteht darin, dass man entweder verhindern muss, dass zwischen einem Präfix und der nächsten Anweisung ein Interrupt auftritt, oder aber sicherstellen muss, dass, wenn dort ein Interrupt auftritt, die Anweisung nach dem Präfix immer noch die richtigen Register verwendet [z -Zählersicherungslogik speichert die Adresse des letzten ausgeführten Nicht-Präfix-Befehls].

Die Verwendung von Anweisungen mit mehreren Wörtern erschwert einige Aspekte des Entwurfs, verringert jedoch möglicherweise die Notwendigkeit, andere schwierige Entscheidungen zu treffen.

Dieser Typ hat die besten Details zum Verständnis der Festverdrahtung des fest codierten Teils eines Decoders, der die Steuerleitungen für eine fest codierte CPU erklärt: http://minnie.tuhs.org/CompArch/Tutes/week03.html Wie Sie sehen können, Ihre Die Wahl der Opcodes wirkt sich wirklich darauf aus, wie komplex die Dekodierungslogik ist.