55 / 63 · Concept
Mémoire synchrone et contrat de collision lecture/écriture
Définir réponse enregistrée et modes de collision, en séparant validité et initialisation des adresses.
Les leçons sont gratuites. Inscrivez-vous pour enregistrer vos progrès.
Avant l'adresse, définir l'instant de réponse
Une mémoire exige de spécifier capacité, latence de lecture et collisions. Construisons seize mots de huit bits, acceptant une lecture et une écriture au même front montant. Les requêtes partagent l'horloge mais leurs adresses sont indépendantes. Capacité : 16×8=128 bits ; adresse : bits.
Stabilisez adresse et rd_en avant le front : rd_data et rd_valid enregistrés apparaissent après. C'est un étage de lecture enregistrée. Un autre always_ff lisant ces sorties au même front voit leur ancienne valeur ; le registreRegistre Circuit stockant plusieurs bits d'état. Les registres synchrones du cours capturent leur entrée définie sur un front d'horloge. En savoir plus consommateur les reçoit au front suivant. Marquez séparément requête et observation de réponse. Une requête peut être acceptée chaque cycle : un étage de latence ne divise pas le débitDébit Nombre d'opérations acceptées ou terminées par unité de temps. Un pipeline peut l'augmenter sans réduire la latence d'une opération individuelle. En savoir plus par deux.
Lire et écrire la même adresse
Supposons M[3]=8'h25 avant le front et une lecture de 3 simultanée à l'écriture de 8'h9A. read-first répond 25 puis stocke 9A ; write-first répond 9A ; no-change conserve le registre de sortie, qui ne contient pas forcément l'ancienne valeur de l'adresse 3. Ce sont trois contrats normaux distincts, également décrits dans les modes RAM AMD.
Nous choisissons read-first. Avec avant et après le front, pour toute adresse hors reset :
Même en collision, la lecture utilise , pas . Sans demande de lecture, rd_data est conservé.
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
endmoduleLes membres droits lisent les anciennes valeurs avant les mises à jour non bloquantesAffectation non bloquante Affectation <= du RTL séquentiel : le membre droit est évalué puis la mise à jour planifiée, permettant aux registres du même front de calculer à partir de l'ancien état. En savoir plus. Placer l'écriture avant la lecture ne transforme donc pas le contrat en write-first. Pour celui-ci, utilisez explicitement (wr_en && wr_addr==rd_addr) ? wr_data : mem[rd_addr]. Le contournement conserve le registre de sortie.
Validité et initialisation sont deux contrats distincts
rst est synchrone actif haut. À rst=1 au front, les deux requêtes sont ignorées et seul rd_valid est mis à 0 ; ni tableau ni rd_data ne sont effacés. Le contenu d'une adresse jamais écrite depuis la mise sous tension n'est pas défini : X peut apparaître en simulation et aucune valeur physique précise ne peut être supposée.
rd_valid=1 indique une période de réponse, pas une adresse initialisée. L'utilisateur doit lire seulement des adresses déjà écrites ou maintenir une table d'initialisation. Une lecture read-first au front de la toute première écriture retourne encore l'ancien contenu indéfini. Il faut également ignorer rd_data lorsque valid=0. Cette garde et l'initialisation des adresses ne se remplacent pas.
Le reset n'efface pas le stockage : les valeurs déjà écrites restent dans le RTL. Si le système décide de les abandonner après reset, il doit aussi gérer sa table d'initialisation. Effacer des informations sensibles est une exigence distincte du reset de contrôle.
Chaque colonne observe un front montantFront montant Instant où l'horloge passe de 0 à 1, distinct du niveau désignant tout l'intervalle CLK=1. En savoir plus : entrées avant, états after après. L'alignement n'est pas un retard physiqueRetard de propagation Temps nécessaire après un changement d'entrée pour que la sortie se stabilise à sa valeur correcte. Équivalence logique et caractéristiques temporelles sont distinctes. En savoir plus. Toutes les adresses valent 3 : E0 écrit 25, E1 le remplace par 9A en lisant. Les tirets sont indéfinis ; valid=0 ne compte pas comme réponse. Les données sont hexadécimales.
Voir les données du chronogramme
| Signal | Forme d'onde | Valeurs du bus |
|---|---|---|
| edge | 2345 | E0 → E1 → E2 → E3 |
| wr_en | 1.0. | |
| wr_data | 23.. | 25 → 9A |
| rd_en | 01.0 | |
| M[3] après | 23.. | 25 → 9A |
| rd_data après | 234. | - → 25 → 9A |
| rd_valid après | 01.0 |
Suivre quatre fronts à la main
E0 écrit 25 à l'adresse 3, avec rd_en=0. E1 écrit 9A et lit 3 : après E1, valid=1,data=25. E2 relit sans écrire : data=9A. E3 avec rd_en=0 donne valid=0 et conserve data=9A. Compter cette dernière valeur comme une réponse nouvelle traiterait deux fois la même donnée.
Avec des adresses différentes, lecture et écriture sont indépendantes : lire 7 tout en écrivant 3 retourne l'ancien contenu de 7. Pour un FPGA réel, vérifiez dans la synthèse et la documentation la ressource RAM obtenue et sa politique de collision. Définir le RTL et valider un composant concret restent deux étapes distinctes.
Essayez vous-même
M[6]=8'h10 est initialisé. E0 lit 6 et écrit 8'h20 ; E1 relit sans écrire ; E2 a rd_en=0. Donnez data/valid après chaque front en read-first. Que change write-first ? La première collision sur une adresse jamais écrite a-t-elle une valeur définie ?
Lire l’explication
E0 : data=10,valid=1, et tableau mis à 20. E1 : data=20,valid=1. E2 conserve data=20 mais valid=0, donc aucune réponse. En write-first, seul E0 devient data=20. Une collision read-first lors de la première écriture retourne l'ancien contenu non initialisé : aucune valeur précise n'est attendue. valid=1 ne garantit pas l'initialisation.