56 / 63 · Konzept
Registerdatei mit zwei Leseports, einem Schreibport und Bypass
Unabhängige Leseports und synchrones Schreiben implementieren sowie Bypass, Initialisierung und valid prüfen.
Lektionen sind frei lesbar. Schreibe dich ein, um deinen Fortschritt zu speichern.
Zwei Werte gleichzeitig aus einem Speicher lesen
Für A+B werden beide Operanden im selben Zyklus benötigt. Eine 2-Lese-/1-Schreib-RegisterdateiRegister Speichert einen Zustand aus mehreren Bits. Die synchronen Register dieses Kurses übernehmen festgelegte Eingaben an Taktflanken. Mehr erfahren bietet zwei Leseadressen und eine Schreibadresse. Hier werden acht 16-Bit-Wörter gespeichert, beide Lesepfade sind flankengesteuert registriert. Drei Adressbits, 8×16=128 Bit Speicher. Adresse 0 ist normaler Speicher, kein festes Nullregister.
Jeder Leseport hat eigenes enable und valid. Ein Port darf ruhen, der andere lesen; beide dürfen dieselbe Adresse verwenden. Zwei gleichzeitige Adressen verursachen andere Kosten als mehr Kapazität. Kleine Strukturen können Flipflop-ArraysFlipflop Speichert den Eingang an einer festgelegten Taktflanke bis zur nächsten. Die Eingabe muss Setup und Hold erfüllen. Mehr erfahren mit zwei Auswahlbäumen nutzen. Für RAM können replizierte Speicher mit gemeinsamen Schreibzugriffen nötig sein. Die RTL-Deklaration allein legt die Ressource nicht fest.
Wenn der geschriebene Wert gleichzeitig gelesen wird
Der Vertrag antwortet bei gleicher Adresse und Flanke mit neuen Schreibdaten. Ein Bypass leitet wr_data direkt zum Eingang des Leseregisters, ohne Array-Aktualisierung abzuwarten. Für Port gilt:
Bei ist die Antwort:
bezeichnet den alten Inhalt. muss für jeden Port unabhängig verglichen werden. Bypass auf beide Ausgänge wegen nur einer Kollision verfälscht den anderen Operanden. Ohne wr_en-Prüfung würde bereits eine zufällig gleiche wr_addr den Bypass auslösen, obwohl kein Schreibvorgang stattfindet.
module regfile8x16_2r1w (
input logic clk, rst, wr_en,
input logic [2:0] wr_addr,
input logic [15:0] wr_data,
input logic rd_en0, rd_en1,
input logic [2:0] rd_addr0, rd_addr1,
output logic [15:0] rd_data0, rd_data1,
output logic rd_valid0, rd_valid1
);
timeunit 1ns;
timeprecision 1ps;
logic [15:0] mem [0:7];
always_ff @(posedge clk) begin
if (rst) begin
rd_valid0 <= 1'b0;
rd_valid1 <= 1'b0;
end else begin
rd_valid0 <= rd_en0;
rd_valid1 <= rd_en1;
if (wr_en)
mem[wr_addr] <= wr_data;
if (rd_en0)
rd_data0 <= (wr_en && wr_addr == rd_addr0)
? wr_data : mem[rd_addr0];
if (rd_en1)
rd_data1 <= (wr_en && wr_addr == rd_addr1)
? wr_data : mem[rd_addr1];
end
end
endmoduleBypass ändert die Eingangsauswahl, nicht die registrierte Lesestufe. Adresse und Anforderung liegen vor der Flanke, Antwort danach; der nachfolgende Verbraucher kann sie an der nächsten Flanke erfassen. Kombinatorisches Lesen verändert Pfad und Latenzvertrag und ist keine bloße Codeverkürzung.
Beispiel: Nur ein Port kollidiert
M[2]=12, M[5]=7 seien bereits geschrieben. Vor E0: beide Lese-enables aktiv, rd_addr0=2, rd_addr1=5, wr_en=1, wr_addr=2, wr_data=20. Danach liefert Port 0 per Bypass 20, Port 1 den alten Wert 7. Ihre gültige Summe ist 27, anders als 19 bei read-first ohne Bypass.
E1 liest beidseitig Adresse 2 und schreibt dort 30: beide Antworten 30. E2 hat wr_en=0, wr_addr=2, wr_data=99 und liest 2: beide müssen 30 liefern. Das zeigt Bypass-Fehler durch bloße Adressgleichheit. Bei E3 mit rd_en0=0, rd_en1=1 gelten valid0=0, valid1=1.
Jede Spalte dokumentiert eine steigende FlankeSteigende Flanke Übergang des Takts von 0 auf 1. Nicht zu verwechseln mit dem gesamten hohen Pegelintervall CLK=1. Mehr erfahren. Eingänge gelten unmittelbar davor, Zustände mit „after“ unmittelbar nach der Aktualisierung. Die Spalten zeigen die Reihenfolge der Abtastungen, keine physische LaufzeitLaufzeit Zeit nach einer Eingangsänderung, bis der Ausgang auf dem richtigen Wert stabil ist. Logische Äquivalenz und zeitliche Eigenschaften sind getrennte Aussagen. Mehr erfahren. Vorgefüllt M[2]=12, M[5]=7, beide Lese-enables stets 1, Schreibadresse 2. In der letzten Spalte darf wr_data=99 bei wr_en=0 nicht durchgereicht werden.
Wellenformdaten anzeigen
| Signal | Wellenform | Buswerte |
|---|---|---|
| edge | 234 | E0 → E1 → E2 |
| wr_en | 1.0 | |
| wr_data | 234 | 20 → 30 → 99 |
| rd_addr0 | 2.. | 2 |
| rd_addr1 | 23. | 5 → 2 |
| out0 after | 23. | 20 → 30 |
| out1 after | 23. | 7 → 30 |
Was initialisiert werden muss
Synchroner active-high-Reset verhindert Schreiben und setzt beide valid auf 0. Array und Ausgabedaten bleiben erhalten. Alte Buswerte nach Reset sind bei valid=0 korrekt. Falls das System danach nur neue Arbeit erlaubt, muss deren Initialisierungsfolge separat definiert sein.
Eine erstmals geschriebene Adresse liefert bei gleichzeitiger Leseanforderung dank Bypass einen definierten neuen Wert. Ohne gleichzeitiges Schreiben bleibt eine nie initialisierte Adresse unbestimmt. rd_valid markiert die Antwortzeit und ist kein adressbezogenes initialized-Bit. Die Referenz muss vor normalen Erwartungen prüfen, ob die Adresse bekannt ist, und Bypass gesondert behandeln.
Prüfen Sie vier Lese-enable-Kombinationen, gleiche/verschiedene Adressen, Kollision mit keinem/Port 0/Port 1/beiden, aufeinanderfolgendes Schreiben und Resetpriorität. Mehrere Schreibports liegen außerhalb dieser Lektion; dafür wären zuerst Prioritäten kollidierender Schreibzugriffe festzulegen. Portzahl verändert Funktionsvertrag und Ressourcenbedarf.
Selbst ausprobieren
M[1]=4, M[4]=9. Beide Ports lesen 1 und 4, während 13 nach 4 geschrieben wird. Welche Antworten folgen? Danach wr_en=0, wr_addr=1, wr_data=99, beide Leseadressen=1. Was folgt? Erklären Sie schließlich valid und Verbrauch bei ausschließlich deaktiviertem Port 0.
Erklärung lesen
Zuerst (4,13): Nur Port 1 kollidiert und erhält Bypass. Danach (4,4); wr_en=0 verbietet 99. Zuletzt rd_valid0=0, rd_valid1=1. Port 0 hält Daten, die nicht als Antwort konsumiert werden. Adresse 0 ist wie jede andere beschreib- und lesbar.