Altifigence Academy

57 / 63 · Konzept

Zeiger und Grenzregeln einer FIFO mit einem Takt

Zeiger und Belegung einer FIFO mit vier Plätzen sowie simultane Annahme bei empty/full definieren.

Reihenfolge erhalten, ohne alle Daten zu verschieben

FIFOFIFO Warteschlange, die zuerst eingefügte Daten zuerst ausgibt. Voll-/Leerzustände und Regeln gleichzeitigen Lesens/Schreibens gehören zum Vertrag. gibt zuerst eingefügte Daten zuerst aus. Hier werden Lesen und Schreiben im selben Takt entschieden: DEPTH=4, acht Datenbits. rptr zeigt den nächsten Lese-, wptr den nächsten Schreibplatz. Beide sind zwei Bit und laufen von 3 nach 0 um. occupancy zählt 0–4 und braucht drei Bit.

rptr=wptr gilt sowohl bei leer als auch voll. Adressgleichheit allein als empty zu verwenden würde volle Daten verlieren. Wir führen count CC mit empty=(C=0)\mathrm{empty}=(C=0) und full=(C=4)\mathrm{full}=(C=4). Für D=4D=4 gelten:

Waddr=⌈log⁡2D⌉=2Wcount=⌈log⁡2(D+1)⌉=3\begin{aligned} W_{\mathrm{addr}}&=\lceil\log_2 D\rceil=2\\ W_{\mathrm{count}}&=\lceil\log_2(D+1)\rceil=3 \end{aligned}

Grenzregeln trennen Anforderung und Annahme

push und pop sind Anforderungen; p und q ihre tatsächlichen Annahmen. Ohne Reset werden sie aus dem Zustand vor der Flanke berechnet:

q=pop∧(C>0)p=push∧((C<4)∨q)C+=C+p−q\begin{aligned} q&=\mathrm{pop}\land(C>0)\\ p&=\mathrm{push}\land((C<4)\lor q)\\ C^{+}&=C+p-q \end{aligned}

Bei empty und beiden Anforderungen wird nur push angenommen, da kein alter Wert zum Lesen existiert. Neue Daten erscheinen nicht sofort am Ausgang. Bei full werden beide angenommen: Der alte Head wird entnommen und schafft so Platz. full bedeutet hier „vier Werte gespeichert“, kein bedingungsloses Push-Verbot.

Zustand vor FlankeNur pushNur popBeide
C=0Einen speichernAblehnenNur push, C=1
0<C<4C+1C−1Beide, C bleibt
C=4AblehnenC=3Beide, C=4

Grenzen mit Taktschritten prüfen

Fordern Sie anfangs push und pop gemeinsam an: Nur push darf angenommen werden. Füllen Sie dann ohne pop vier Plätze und aktivieren Sie beide mit neuen Daten. occupancy bleibt 4, ausgegeben wird der alte Head und hinten steht der neue Wert.

FIFO taktweise untersuchen

FIFO mit vier Einträgen. Anfangs leer, Lese- und Schreibzeiger stehen auf 0. pop wird angenommen, wenn sie nicht leer ist. push wird angenommen, wenn Platz frei ist oder an derselben Flanke ein pop angenommen wird.

Das Modell zeigt Zustand nach jeder steigenden FlankeSteigende Flanke Übergang des Takts von 0 auf 1. Nicht zu verwechseln mit dem gesamten hohen Pegelintervall CLK=1. Mehr erfahren. Angenommenes pop setzt output valid=1; ohne pop an der nächsten Flanke hält das AusgangsregisterRegister Speichert einen Zustand aus mehreren Bits. Die synchronen Register dieses Kurses übernehmen festgelegte Eingaben an Taktflanken. Mehr erfahren seinen Wert, aber valid wird 0. Alte Bits leerer Plätze werden verborgen. „Von vorn“ initialisiert die gesamte Übung und bildet keinen RTL-SpeicherresetReset Steuerung zur Rückkehr in einen festgelegten Anfangszustand. Synchron/asynchron und Priorität gegenüber anderen Steuerungen müssen definiert werden. Mehr erfahren ab.

RTL einschließlich registriertem Ausgang

SystemVerilog
module fifo4 (
  input  logic       clk, rst, push, pop,
  input  logic [7:0] din,
  output logic [7:0] dout,
  output logic       dout_valid, full, empty,
  output logic [2:0] occupancy
);
  timeunit 1ns;
  timeprecision 1ps;
  logic [7:0] mem [0:3];
  logic [1:0] rptr, wptr;
  logic take_push, take_pop;

  assign empty = (occupancy == 3'd0);
  assign full  = (occupancy == 3'd4);
  assign take_pop  = !rst && pop && !empty;
  assign take_push = !rst && push && (!full || take_pop);

  always_ff @(posedge clk) begin
    if (rst) begin
      rptr <= 2'd0;
      wptr <= 2'd0;
      occupancy <= 3'd0;
      dout_valid <= 1'b0;
    end else begin
      dout_valid <= take_pop;
      if (take_push) begin
        mem[wptr] <= din;
        wptr <= wptr + 2'd1;
      end
      if (take_pop) begin
        dout <= mem[rptr];
        rptr <= rptr + 2'd1;
      end
      case ({take_push, take_pop})
        2'b10: occupancy <= occupancy + 3'd1;
        2'b01: occupancy <= occupancy - 3'd1;
        default: occupancy <= occupancy;
      endcase
    end
  end
endmodule

occupancy wird nicht durch zwei unabhängige ifs erhöht und verringert. Zwei nonblocking-ZuweisungenNonblocking Sequenzielle RTL-Zuweisung mit <=. Nach Auswertung der rechten Seite wird die Aktualisierung geplant, sodass Register derselben Flanke den alten Zustand verwenden. Mehr erfahren an dasselbe Register würden bei Gleichzeitigkeit nur die letzte wirksam lassen. Beide Zeiger bewegen sich jeweils bei Annahme; count wird einmal aus vier Fällen bestimmt. Annahmebedingungen verhindern Unterlauf bei 0 und Überlauf bei 4.

Gleiche Adresse bei voller FIFO lesen und schreiben

Nach A, B, C, D gilt rptr=wptr=0, occupancy=4. Mit pop=push=1, din=E folgt dout=A, dout_valid=1, Inhalt [B, C, D, E]. Beide Zeiger werden 1, count bleibt 4. mem[0] wird E, doch die rechte Seite der nonblocking-Lesezuweisung sieht das alte A. Dieses read-first-Verhalten ist für den Austausch bei full notwendig.

Danach liefert nur pop B und count=3. Bei empty mit din=Z und beiden Anforderungen wird hingegen nur count=1, dout_valid=0. Erst ein weiteres pop liefert Z. Direkte Weitergabe bei empty und Austausch bei full sind getrennte Entscheidungen.

Synchroner active-high-Reset initialisiert Zeiger, count, dout_valid, nicht Speicher oder dout. Nach Reset blockiert empty das Lesen alter Bits; später werden nur neu beschriebene Plätze gelesen. Ein vollständiger Arrayreset ist funktional unnötig. Daten mit dout_valid=0 werden ignoriert. Gültige Daten antworten auf angenommenes pop; ohne separates Ausgangs-ready muss der Verbraucher sie annehmen.

Bei einer Tiefe, die keine Zweierpotenz ist, ist natürlicher Zweibitüberlauf ungeeignet: Tiefe drei muss explizit nach Adresse 2 auf 0 springen. Falls Synthese RAM nutzt, prüfen Sie dessen Old-data-Verhalten bei Kollision. Dieses RTL gilt nur für einen Takt und übernimmt keine Zeigersynchronisation einer asynchronen FIFO.

Selbst ausprobieren

Nach Reset je eine Flanke: (1) push A+pop, (2) push B, (3) push C, (4) push D, (5) push E+pop, (6) pop. Geben Sie Inhalt, occupancy, valid und gültige Ausgabe an. Warum braucht (5) read-first?

Erklärung lesen

(1) [A],1, valid=0. (2) [A, B],2,0. (3) [A, B, C],3,0. (4) [A, B, C, D],4,0. (5) [B, C, D, E],4, valid=1, dout=A. (6) [C, D, E],3, valid=1, dout=B. In (1) fehlt ein alter Head, daher kein pop. In (5) sind rptr=wptr=0: Lesen und Schreiben treffen denselben Platz und müssen das alte A ausgeben. Write-first mit E verletzt FIFO-Reihenfolge und Inhalt. Ausgaben bei valid=0 werden nicht bewertet.

Deine Auswahl gilt für diesen Browser. Du kannst sie jederzeit in der Fußzeile ändern.