Was genau sind Einzelzyklus-Befehlsarchitekturen?

Ich habe den folgenden Text aus Laborarbeit 2 des Computerarchitekturkurses der CMU erhalten. Ich versuche dieses Praktikum eigentlich aus eigenem Interesse selbst zu machen und bin in keiner Weise ein Student der CMU.

Die Maschine hat eine Single-Cycle-Mikroarchitektur: Jeder Befehl benötigt genau einen Zyklus, um ausgeführt zu werden . Abgesehen von der Korrektheit (wie von den Architekturspezifikationen definiert) ist dies die einzige Einschränkung, die wir der Mikroarchitektur der Maschine auferlegen. Solange diese beiden Einschränkungen erfüllt sind (d. h. Korrektheit und Einzelzyklus ), können Sie die Mikroarchitektur beliebig implementieren. Als Orientierungshilfe bieten wir eine abstrakte Beschreibung der Einzelzyklus-Mikroarchitektur, wie wir sie im Unterricht besprochen haben.

  1. Der Architekturzustand der Maschine (ohne Speicher) wird in Registern gespeichert: dem Programmzähler und Mehrzweckregistern
  2. Es gibt eine globale Leitung namens \clock", die mit allen Registern verbunden ist.
  3. Wenn ein Register eine steigende Flanke an der Uhr sieht, erfasst das Register den momentanen "Schnappschuss" der Werte an seinem Eingang. Von da an hält das Register die erfassten Werte und führt sie seinem Ausgang zu.
  4. Die Ausgabe von dem/den Register(n) wird in eine kombinatorische Schaltung eingespeist, die aus Logikgattern (z. B. ADD) besteht. Der Ausgang der Logikgatter wiederum wird als Eingang zu dem/den Register(n) zurückgeführt.
  5. Bei der nächsten steigenden Taktflanke erfasst das Register wieder die Werte an seinem Eingang

.

Mein Zweifel: Es fordert mich auf, eine Einzelzyklusarchitektur zu implementieren, aber die von 1 bis 5 nummerierten Punkte scheinen kein Einzelzyklus zu sein.

Nehmen Sie die Anweisung ADD R1, R2, R3 an. Gemäß den Schritten von 1 bis 5 dauert dies zwei Taktzyklen und nicht 1 Taktzyklus.

  • An der Pos-Flanke eines Taktzyklus werden die Adressregister (Adresse in der Registerdatei) R1 und R2 zwischengespeichert und die Werte in diesen Registern werden zur Addition an die ALU gesendet.

  • In der nächsten Pos-Flanke wird die Ausgabe von ALU in das Register R3 zurückgeschrieben.

Es dauert also eigentlich zwei Zyklen, oder? EWarum wird es dann als Einzelzyklusanweisung bezeichnet?

Wenn Sie es sich ansehen, könnten Sie den Fall annehmen, dass 1 Anweisung 2 Zyklen benötigt, um am Ausgang zu erscheinen. Wenn Sie jedoch mehrere Anweisungen ausführen, ist es effektiv 1 Zyklus pro Anweisung, dh 10 Anweisungen benötigen 11 Zyklen, 100 Anweisungen benötigen 101 Zyklen.
Sie müssen Ihre ALU dazu bringen, das Ergebnis im selben Taktzyklus zu berechnen , damit es im selben Taktzyklus auch in R3 geschrieben wird. Erst dann haben Sie die Single-Cycle-Ausführung erreicht. (dh obwohl die Überlegungen von @ BeB00 interessant sind, sind sie irrelevant)
Eigentlich ist sogar das nicht wirklich der Fall. Sie können niemals direkt auf den Inhalt eines Registers schauen, sondern nur versuchen, den Inhalt durch einen Lesebefehl zu lesen. Wenn Sie Ihren Add-Befehl aktivieren, die CPU einmal takten, dann Ihren Lesebefehl aktivieren und die CPU einmal takten, erhalten Sie das richtige Ergebnis. Edit: Dies ist eine Antwort auf meinen vorherigen Kommentar
@BeB00 das wäre eine Anweisung mit drei Zyklen.
@MarcusMüller Wenn ich aktivieren sage, meine ich, den relevanten Opcode in der Steuerschaltung zu haben
@BeB00 spielt keine Rolle. Die Definition ist klar: Die Anweisung muss beendet sein, bevor die nächste Uhr hereinkommt.
@MarcusMüller In seiner Frage sieht es so aus, als wäre sein Alu kombinatorisch, aber Sie haben Recht, dass alles innerhalb dieses Zyklus passieren muss
@MarcusMüller richtig, also wenn ich aktivieren sage, meine ich nicht Uhr. Wenn ich Uhr sage, meine ich damit, die geladene Operation auszuführen
@MarcusMüller ja, das Ergebnis muss im selben Zyklus in R3 zurückgeschrieben werden, damit es sich um eine Einzelzyklusanweisung handelt. Aber ich brauche eine weitere +ve-Flanke, um das Ergebnis in R3 zu speichern, oder?
@Jsmith mein Punkt ist, dass das Ergebnis, das in R3 gespeichert wird, Teil der nächsten Anweisung ist
@BeB00 Punkt ist, dass Sie R3 frühestens in den Augenblicken nach dem nächsten Taktzyklus lesen , wenn der Wert bereits zwischengespeichert ist. Der Effekt ADD, dass R3 im nächsten Zyklus einen neuen Wert hat, findet also im Strom statt
@MarcusMüller, was ich vorhin gesagt habe
@BeB00 Wenn das richtig ist, was wird der Fall sein, wenn die unmittelbar nächste Anweisung versucht, etwas in das Register R3 zu schreiben? Welches Schreiben findet statt? Das von ADD oder das nächste Schreiben nach R3?
@Jsmith das Add-Schreiben wird stattfinden, da es sofort geschieht. Der nächste Schreibvorgang wird auf dem nächsten Posedge ausgeführt
Auf physikalischer Ebene stelle ich mir vor, dass es Dinge gibt, die eine Race-Bedingung verhindern und dass sich der Registereingang ändert, bevor der Latch auftritt, obwohl ich mir vorstelle, dass die Gate-Fortpflanzungsverzögerung selbst dann verhindern würde, wenn dies nicht der Fall wäre.
Nicht genug, um seine eigene Antwort auszureichen, aber es war wahrscheinlich billiger, einen einzigen Zyklus zu haben ... Jedes Tor / System kann nur einmal pro Zyklus verwendet werden ... MIPS hat auch mehrere Zyklen mit unterschiedlicher Hardware, die beliebter zu sein scheinen

Antworten (6)

Die einzige mir bekannte Möglichkeit, Lese- und Schreibvorgänge im selben Taktzyklus durchzuführen, besteht darin, Registerlesevorgänge bei positiver Flanke und Registerschreibvorgänge bei negativer Flanke (oder umgekehrt) auszulösen und dann Ihre Daten so zu machen Pfadlogik breitet sich vollständig innerhalb einer halben Taktperiode aus. In einer Single-Cycle-Architektur ist es wirklich nur ein semantischer Unterschied, ein bisschen Trickserei.

In ausgefeilteren Taktschemata können Sie tatsächlich mehrere Taktphasen haben, sodass die „Flankentrigger“-Beziehung nicht symmetrisch sein muss, wie ich es beschrieben habe. Wenn Ihr Kern per Pipeline geleitet wird, ist dies tatsächlich aus Gründen der Korrektheit wichtig, damit Sie kein Rennen zwischen Ihren Register-Lese- und Rückschreibphasen haben.

Ich denke, Sie sind zu Recht verwirrt darüber, wie Lese- und Schreibvorgänge in der Registerdatei zusammenhängen, aber das erfordert, dass Sie sich etwas tiefer mit der Implementierung eines Einzelbitregisters auf Transistorebene befassen. Ich denke, Sie werden feststellen, dass es ein Rennen gibt, wenn beide Ereignisse (Lesen und Schreiben) von derselben Taktflanke ausgelöst werden und Ihre kombinatorische Logik eine Änderung propagieren kann, bevor der Registerstatus vollständig verriegelt ist. Schauen Sie sich diese Webseite für eine logische Dekonstruktion eines flankengetriggerten D-Flip-Flops an.

Warnung: Ein-Zyklus- Unterricht kann ein Marketing-Schachzug sein.

Betrachten Sie das Datenblatt für PIC18F4xK22.

An den auffälligsten Stellen sieht man folgendes:

Alle Befehle sind Einzelzyklen, mit Ausnahme von Programmverzweigungen

Hört sich cool an? Aber irgendwo im Dokument vergraben sehen Sie Folgendes:

Ein „Befehlszyklus“ besteht aus vier Q-Zyklen: Q1 bis Q4.

In Wirklichkeit führt Ihr Befehl also 4 Systemtakte aus.

Ich habe das in den Kommentaren gesagt, aber es ist ein bisschen fragmentiert.

Grundsätzlich haben Sie Recht, dass es zwei Taktzyklen dauern würde, um einen ADD-Befehl auszuführen und ihn dann in ein Register zu speichern.

Wenn Sie jedoch die nächste Anweisung ausführen, speichert dies das vorherige Ergebnis im Register. Wenn Sie also zwei ADDs hintereinander ausführen, erhalten Sie das richtige Ergebnis und es dauert nur zwei Zyklen.

Nebenbei bemerkt ist es nur relevant, ein Ergebnis in einem Register zu speichern, wenn Sie eine Anweisung darauf ausführen möchten. Wenn der letzte Befehl in Ihrem Code ein ADD war und Sie danach nicht erneut getaktet haben, enthalten die Register nicht das neueste Ergebnis, aber das spielt keine Rolle, da Sie sie nicht erneut lesen. Sobald Sie eine Anweisung erneut ausgeführt haben, um sie zu lesen, würden sie verriegeln, und dann würde Ihre Anweisung ausgeführt und das richtige Ergebnis geliefert.

Eine Möglichkeit, dies zu vermeiden, wäre, die Register an der fallenden Flanke einrasten zu lassen, aber dies würde Ihre Geschwindigkeit um die Hälfte einschränken und wäre nicht sehr nützlich
Sie haben gesagt, dass zwei aufeinanderfolgende ADD 2 Taktzyklen dauern. Werden es nicht 3 sein? Angenommen, wir haben ADD R1, R2, R3 und dann ADD R4, R5, R6. In Pos-Kante 1 werden R1 und R2 gelesen. In Pos-Flanke 2 wird R3 zurückgeschrieben und R4 und R5 werden gelesen. In Pos-Flanke 3 wird R3 der zweiten Anweisung zurückgeschrieben. Bin ich richtig? (Es macht nichts aus, dass das Ergebnis der ersten Addition in diesem Beispiel nicht verwendet und durch die nächste Anweisung überschrieben wird.)
Ja, aber es wird immer noch als 2 Zyklen betrachtet. Dieser dritte Zyklus, in dem das Rückschreiben stattfindet, kann jede Anweisung sein, einschließlich NOP. Wenn Sie ein Programm schreiben, das 10 Anweisungen lang ist, was auch immer diese Anweisungen waren, das Programm würde korrekt und vollständig laufen, wenn Sie den Prozessor 10 Mal takten.
Okay, ich verstehe, was ist dann mit dem Versuch, R3 sofort nach dem Schreiben von R3 zu lesen? Ich meine so etwas wie ADD R1, R2, R3 und dann ADD R3, R3, R3. Es wird ein Problem geben, wenn R3 gelesen wird, bevor/oder während R3 aus dem vorherigen ADD geschrieben wird, richtig?
Wenn wir also R3 nach ADD R1, R2, R3 lesen wollen, müssen wir ein NOP dazwischen einfügen, sonst ist es ein Fehler?
Nein. Das 1. ADD-Ergebnis wird geschrieben, bevor das 2. ADD erfolgt. Es könnte der Fall sein, dass der kombinatorischen Additionsschaltung für einige Nanosekunden nach Posege 2 aufgrund der Ausbreitungsverzögerung das falsche Ergebnis gegeben wird, jedoch wird dies vor dem Ende des 2. Takts auf das korrekte Ergebnis aktualisiert.
Dies ist tatsächlich eines der Dinge, die die Taktgeschwindigkeit begrenzen. Wenn Sie zu schnell takten, werden Sie dieses falsche Ergebnis einklinken und Probleme verursachen.
@Jsmith: Ja, der Versuch, R3 unmittelbar nach dem Schreiben von R3 zu lesen, ist eine Read-after-Write- Hazard . Am einfachsten ist es, ein NOP dazwischen einzufügen, aber viele CPU-Designer implementieren stattdessen die Datenweiterleitung , damit die Anweisung, die R3 liest, stattdessen aus dem Ausgang der ALU gelesen wird.

Es ist eine Single-Cycle-Architektur! Wenn die Addition r1, r2, r3 in den kombinatorischen Decoder kommt, dekodiert er die Nachricht und teilt der ALU mit, die Addition von r1, r2 durchzuführen, und das Ergebnis ist eine Eingabe in das Register r3. Jetzt kann der Additionswert bei der nächsten steigenden Taktflanke oder bei der fallenden Flanke (bevorzugt) zurückgeschrieben werden. Da das Zurückschreiben den Decoderteil der Mikroarchitektur nicht beeinflusst, wird die nächste Anweisung decodiert und so weiter. Ich hoffe es hilft.

Kein Grund, warum Sie dies nicht in einem einzigen Zyklus implementieren können, zu Beginn des Zyklus geben die Register ihren aktuellen Wert aus, Ihr Befehlsdecoder sieht und addiert, sieht die Eingangsregister und erzeugt kombiniert die Summe dieser Elemente, er sieht auch die Ziel und kombiniert das Ergebnis in den Eingang zu r3 auf der steigenden Flanke, die diesen einzelnen Zyklus beendet, r3 erhält die Summe von r1 und r2. Kein Grund, eine fallende Flanke oder ähnliches zu verwenden.

Wenn sich die Register jetzt in einer Registerdatei befunden hätten, hätten Sie ein viel größeres Problem. Sie können die beiden Eingänge nicht in einer einzigen Uhr lesen, ohne die Definition einer Uhr zu ändern (die Verwendung der fallenden Flanke ist Betrug, der nur die Definition einer Uhr ändert). in zwei Uhren).

Da die Register voneinander getrennt sind und über eigene Ein- und Ausgänge verfügen und der Speicher nicht gezählt wird (manchmal können Sie dies auch noch tun), können Sie problemlos eine Maschine mit einem Zyklus erstellen. Zu Beginn des Zyklus sind die Registerausgänge bereit, kombinatorisch berechnen Sie die Registereingänge vor dem Ende des Zyklus.

Wenn die Register dreistufig mit busA und busB gekoppelt sind, sobald die Opcodes decodiert sind, und diese internen Busse die Operanden an ALU leiten, wobei die ALU-Funktion aus diesem winzigen Teil des Befehlscodes ausgewählt wird und das Zielregister die Der ALU-Ausgang wird an die Eingangspins dieses Registers gelegt, dann der nächste Taktzyklus (alle Taktaktionen erfolgen MCU-weit an derselben Flanke), dann haben Sie eine MCU mit einem einzigen Taktzyklus.

Es liegt an Ihnen, das Tristate-Verhalten zu definieren, um dies zu implementieren.