Altifigence Academy

57 / 63 · Concetto

Puntatori e politiche di confine della FIFO a clock singolo

Implementare puntatori e occupazione di quattro posti e definire accettazione simultanea a vuoto/pieno.

Conservare l'ordine senza spostare tutti i dati

Una FIFOFIFO Coda che estrae prima i dati inseriti prima. Pieno, vuoto e regole di lettura/scrittura simultanee fanno parte del contratto. estrae prima ciò che è entrato prima. Qui lettura e scrittura si decidono allo stesso clock: DEPTH=4, dati a otto bit. rptr indica la prossima lettura, wptr la prossima scrittura. I due bit di ciascun puntatore consentono il ritorno naturale da 3 a 0. occupancy deve contare 0–4 e richiede tre bit.

rptr=wptr è possibile sia a vuoto sia a pieno. Usare solo l’uguaglianza per empty farebbe perdere i dati di una FIFO piena. Manteniamo CC, con empty=(C=0)\mathrm{empty}=(C=0) e full=(C=4)\mathrm{full}=(C=4). Per D=4D=4:

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}

Ai confini distinguere richiesta e accettazione

push e pop sono richieste; p e q sono le accettazioni effettive. Senza reset si calcolano dallo stato prima del fronte:

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}

A vuoto con entrambe, si accetta solo push: non esiste un vecchio elemento da estrarre. Il nuovo dato non esce subito. A pieno si accettano entrambe perché estrarre la vecchia testa libera spazio. full significa «quattro elementi memorizzati», non un divieto incondizionato di push.

Stato prima del fronteSolo pushSolo popEntrambe
C=0Memorizza unoRifiutaSolo push, C=1
0<C<4C+1C−1Entrambe, C invariato
C=4RifiutaC=3Entrambe, C=4

Verificare i confini avanzando il clock

All'inizio richiedete push e pop insieme: deve passare solo push. Poi riempite quattro posti senza pop e riattivate entrambe con nuovi dati. occupancy resta 4, esce la vecchia testa e resta una nuova coda.

Esaminare una FIFO un clock alla volta

FIFO a quattro elementi. Inizialmente vuota, con puntatori di lettura e scrittura a 0. Accetta pop se non è vuota. Accetta push se c’è spazio o se viene accettato un pop sullo stesso fronte.

Il modello mostra lo stato dopo ogni fronte di salitaFronte di salita Passaggio del clock da 0 a 1, distinto dall’intero intervallo di livello CLK=1. Approfondisci. Un pop accettato pone output valid=1; senza pop al fronte seguente, il registroRegistro Memorizza uno stato di più bit. I registri sincroni del corso acquisiscono gli ingressi specificati al fronte di clock. Approfondisci mantiene il valore ma valid diventa 0. I vecchi bit dei posti vuoti sono nascosti. «Dall'inizio» reinizializza l'intero laboratorio, non simula il resetReset Controllo che riporta allo stato iniziale specificato. Occorre definirne sincronicità e priorità rispetto agli altri controlli. Approfondisci della RAM RTL.

RTL con uscita registrata

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 non usa due if indipendenti per incremento e decremento. Due assegnazioni nonblockingNonblocking Assegnazione RTL sequenziale con <=. Valuta il membro destro e pianifica l’aggiornamento, permettendo ai registri dello stesso fronte di usare lo stato precedente. Approfondisci allo stesso registro farebbero prevalere l'ultima nella simultaneità. I puntatori avanzano ciascuno all'accettazione; count si decide una volta fra quattro casi. Le condizioni impediscono decremento da 0 e incremento da 4.

Leggere e scrivere lo stesso posto a pieno

Dopo A, B, C, D: rptr=wptr=0, occupancy=4. Con pop=push=1, din=E, dopo il fronte dout=A, dout_valid=1 e contenuto [B, C, D, E]. Entrambi i puntatori diventano 1 e count resta 4. mem[0] diventa E, ma il membro destro nonblocking legge il vecchio A. Questo read-first è essenziale alla sostituzione a pieno.

Un successivo solo pop restituisce B e count=3. A vuoto, din=Z con entrambe dà invece count=1, dout_valid=0: occorre un altro pop per vedere Z. Passaggio immediato a vuoto e sostituzione a pieno sono scelte distinte.

Reset sincrono active-high inizializza puntatori, count, dout_valid, non memoria o dout. Dopo reset empty blocca i vecchi dati; si leggeranno solo posti riscritti da nuovi push. Non serve azzerare tutto l'array per il funzionamento normale. Con dout_valid=0 i dati si ignorano. Un'uscita valida risponde a un pop accettato; senza ready d'uscita il consumatore deve riceverla.

Con profondità non potenza di due non basta l'overflow naturale: a profondità tre si torna esplicitamente da indirizzo 2 a 0. Se la sintesi usa RAM, verificatene l'old-data in collisione. Questo RTL è a clock singolo e non realizza sincronizzazione dei puntatori di una FIFO asincrona.

Prova tu

Dopo reset, un fronte ciascuno: (1) push A+pop, (2) push B, (3) push C, (4) push D, (5) push E+pop, (6) pop. Indicate coda, occupancy, valid e dato valido. Perché (5) richiede read-first?

Leggi la spiegazione

(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) non c’è vecchia testa, quindi niente pop. In (5) rptr=wptr=0: lettura e scrittura coincidono e devono restituire il vecchio A. Write-first con E viola ordine e contenuto FIFO. Le uscite con valid=0 non vengono valutate.

La scelta vale per questo browser. Puoi modificarla in qualsiasi momento dal piè di pagina.