Altifigence Academy

55 / 63 · Concetto

Memoria sincrona e contratto delle collisioni

Definire risposta registrata, read-first/write-first/no-change e responsabilità distinte di valid e inizializzazione.

Prima dell'indirizzo viene l'istante della risposta

Un contratto di memoria specifica capacità, latenza e collisioni. Costruiamo una memoria con 16 locazioni da otto bit, capace di accettare una lettura e una scrittura a ogni fronte. Condividono il clock ma hanno indirizzi indipendenti. Capacità: 16×8=128 bit; indirizzo: log⁡216=4\log_2 16=4 bit.

Con indirizzo e rd_en stabili prima del fronte, dopo appaiono rd_data e rd_valid registrati: uno stadio di lettura. Un altro always_ff sullo stesso fronte vede ancora il vecchio valore; il registroRegistro Memorizza uno stato di più bit. I registri sincroni del corso acquisiscono gli ingressi specificati al fronte di clock. Approfondisci consumatore riceve la risposta al fronte seguente. Distinguete richiesta e osservazione. Si può accettare una nuova lettura ogni clock: uno stadio di latenza non dimezza il throughputThroughput Numero di operazioni accettate o completate per unità di tempo. Una pipeline può aumentarlo senza ridurre la latenza del singolo lavoro. Approfondisci.

Leggere e scrivere lo stesso indirizzo

Prima del fronte M[3]=8'h25; si legge 3 mentre vi si scrive 8'h9A. Read-first risponde 25 e memorizza 9A; write-first risponde 9A. No-change mantiene il registro d'uscita, che non è necessariamente il precedente contenuto di 3. Sono tre contratti validi distinti, descritti anche nei modi di sincronizzazione RAM AMD.

Qui usiamo read-first. M−M^{-} è l'array prima del fronte, M+M^{+} dopo. Senza reset, per ogni indirizzo aa:

M+[a]={wr_data,wr_en=1∧a=wr_addrM−[a],otherwiseM^{+}[a]=\begin{cases} \mathrm{wr\_data}, & \mathrm{wr\_en}=1\land a=\mathrm{wr\_addr}\\ M^{-}[a], & \text{otherwise} \end{cases}
rd_data+={M−[rd_addr],rd_en=1rd_data−,rd_en=0rd_valid+=rd_en\begin{aligned} \mathrm{rd\_data}^{+}&=\begin{cases} M^{-}[\mathrm{rd\_addr}], & \mathrm{rd\_en}=1\\ \mathrm{rd\_data}^{-}, & \mathrm{rd\_en}=0 \end{cases}\\ \mathrm{rd\_valid}^{+}&=\mathrm{rd\_en} \end{aligned}

Anche in collisione si legge M−M^{-}, non M+M^{+}. Senza richiesta rd_data mantiene il valore.

SystemVerilog
module sync_mem16x8 (
  input  logic       clk, rst,
  input  logic       wr_en, rd_en,
  input  logic [3:0] wr_addr, rd_addr,
  input  logic [7:0] wr_data,
  output logic [7:0] rd_data,
  output logic       rd_valid
);
  timeunit 1ns;
  timeprecision 1ps;
  logic [7:0] mem [0:15];

  always_ff @(posedge clk) begin
    if (rst) begin
      rd_valid <= 1'b0;
    end else begin
      rd_valid <= rd_en;
      if (wr_en)
        mem[wr_addr] <= wr_data;
      if (rd_en)
        rd_data <= mem[rd_addr];
    end
  end
endmodule

Entrambi i membri destri usano valori vecchi; gli aggiornamenti non bloccantiNonblocking Assegnazione RTL sequenziale con <=. Valuta il membro destro e pianifica l’aggiornamento, permettendo ai registri dello stesso fronte di usare lo stato precedente. Approfondisci seguono dopo. Mettere la scrittura prima nel codice non crea write-first. Per farlo si può usare (wr_en && wr_addr==rd_addr) ? wr_data : mem[rd_addr]. Il bypass conserva comunque il registro d'uscita.

Valid e inizializzazione sono promesse diverse

rst è sincrono active-high. Con rst=1 si ignorano entrambe le richieste e si azzera solo rd_valid; array e rd_data non vengono cancellati. Dopo accensione, indirizzi mai scritti non sono definiti: possono apparire X in simulazione e non si può presumere un numero nell'hardware.

rd_valid=1 significa «ciclo di risposta», non «indirizzo inizializzato e dato noto». Leggete solo indirizzi già scritti o mantenete una tabella d'inizializzazione. Anche una lettura read-first insieme alla prima scrittura restituisce il contenuto precedente sconosciuto. Inoltre serve ignorare rd_data con valid=0. Il controllo d’inizializzazione e il controllo di valid (valid guard) non si sostituiscono a vicenda.

Reset non cancella il contenuto già scritto nell'RTL. Se il sistema decide di scartarlo, deve gestire anche la tabella esterna d'inizializzazione. Cancellare dati sensibili è un requisito distinto dal reset di controllo.

Ogni colonna registra un fronte di salitaFronte di salita Passaggio del clock da 0 a 1, distinto dall’intero intervallo di livello CLK=1. Approfondisci. Gli ingressi sono quelli immediatamente precedenti; gli stati indicati con «after» sono quelli subito dopo l’aggiornamento. Le colonne indicano l’ordine dei campioni, non il ritardo fisico di propagazioneRitardo di propagazione Tempo dalla variazione d’ingresso all’assestamento corretto dell’uscita. Equivalenza logica e proprietà temporali sono distinte. Approfondisci. Tutti gli indirizzi sono 3. E0 scrive per la prima volta 25; E1 sovrascrive con 9A e legge. Il trattino in data indica un valore indeterminato; valid=0 non conta come risposta. I dati sono esadecimali.

Leggere il valore precedente nella collisione read-first
Mostra i dati della forma d’onda
Dati della forma d’onda: ogni carattere è un intervallo; il punto mantiene lo stato precedente; p è un ciclo di clock.
SegnaleForma d’ondaValori del bus
edge2345E0 → E1 → E2 → E3
wr_en1.0.
wr_data23..25 → 9A
rd_en01.0
M[3] after23..25 → 9A
rd_data after234.- → 25 → 9A
rd_valid after01.0

Seguire quattro fronti a mano

E0 scrive 25 a 3 con rd_en=0. E1 scrive 9A e legge 3: dopo vale valid=1, data=25. E2 legge senza scrittura e restituisce 9A. E3 con rd_en=0 porta valid=0 e mantiene data=9A. Contare il bus a E3 come nuova risposta elabora due volte lo stesso dato.

Indirizzi diversi sono indipendenti: leggere 7 scrivendo 3 restituisce il vecchio contenuto di 7. La risorsa RAM FPGA inferita e le sue collisioni vanno verificate nei rapporti di sintesi e nella documentazione del dispositivo. Definire la semantica RTL e verificare una realizzazione concreta sono fasi distinte.

Prova tu

M[6]=8'h10. E0 legge 6 e scrive 8'h20; E1 legge senza scrivere; E2 ha rd_en=0. Calcolate data/valid read-first. Cosa cambia con write-first? La prima collisione read-first su indirizzo non inizializzato dà un valore definito?

Leggi la spiegazione

E0: data=10, valid=1, array aggiornato a 20. E1: data=20, valid=1. E2 mantiene data=20 con valid=0: nessuna risposta. Write-first cambia solo E0 in data=20. La prima collisione read-first restituisce il vecchio contenuto non inizializzato, senza atteso numerico definito. valid=1 non garantisce inizializzazione.

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