Altifigence Academy

58 / 63 · Concept

Vérifier FIFO et mémoires avec un scoreboard indépendant

Écrire un scoreboard sans pointeurs du DUT et planifier limites, reset, validité et lectures non initialisées.

Construire la réponse autrement que le DUT

Le vérificateur compare le contrat au DUT. Copier ses rptr et wptr pourrait reproduire la même erreur. Un scoreboardScoreboard État de référence d'un environnement de vérification, comparant dans l'ordre données attendues et sorties réelles. Il est calculé indépendamment des états internes de l'implémentation. indépendant conserve seulement l'ordre logique du FIFOFIFO File où la première donnée entrée sort en premier. Les règles à plein, à vide et en lecture/écriture simultanée font partie du contrat. En savoir plus dans une queue SystemVerilog : ajout en fin pour push accepté, retrait en tête pour pop accepté.

Croisez vide/intermédiaire/plein avec aucune requête/push/pop/deux. Ajoutez rebouclages répétés, pops consécutifs et reset avec données restantes. Consignez la couverture, le seed aléatoire et les entrées d'échec pour reproduire les échecs.

Calculez l'acceptation attendue depuis l'ancienne taille NN de la queue, pas les sorties full ou occupancy du DUT.

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

Retirez l'ancien head avant d'ajouter la nouvelle donnée pour préserver le head lors d'un échange à plein. Comparez dout seulement si q=1q=1 attendu.

Banc de test complet

Ce banc se compile avec fifo4 de la leçon précédente. Il utilise délais et queue pour vérifier. Les entrées changent au front descendant ; 1ps après le front montant, on compare 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 du DUT.

SystemVerilog
module tb_fifo4;
  timeunit 1ns;
  timeprecision 1ps;
  typedef logic [7:0] word_t;
  logic clk=0, rst=1, push=0, pop=0;
  word_t din=0, dout;
  logic dout_valid, full, empty;
  logic [2:0] occupancy;
  word_t model[$], expected;
  bit expect_pop, expect_push;
  fifo4 dut (.*);
  always #5ns clk = ~clk;

  always @(posedge clk) begin
    if (rst) begin
      model.delete();
      expect_pop = 0;
    end else begin
      expect_pop = pop && (model.size() != 0);
      expect_push = push && ((model.size() < 4) || expect_pop);
      if (expect_pop) expected = model.pop_front();
      if (expect_push) model.push_back(din);
    end
    #1ps;
    assert (dout_valid === expect_pop)
      else $fatal(1, "valid mismatch");
    if (expect_pop)
      assert (dout === expected)
        else $fatal(1, "data mismatch");
    assert (int'(occupancy) === model.size())
      else $fatal(1, "count mismatch");
    assert (empty === (model.size() == 0))
      else $fatal(1, "empty mismatch");
    assert (full === (model.size() == 4))
      else $fatal(1, "full mismatch");
  end

  task automatic step(input bit r, p, o, input word_t d);
    @(negedge clk);
    rst=r; push=p; pop=o; din=d;
  endtask

  initial begin
    step(0,0,1,0);
    step(0,1,1,8'hA0);
    for (int i=1; i<4; i++)
      step(0,1,0,word_t'(160+i));
    step(0,1,0,8'hEE);
    step(0,1,1,8'hB0);
    repeat (5) step(0,0,1,0);
    repeat (3) begin
      for (int i=0; i<4; i++) step(0,1,0,word_t'(i));
      repeat (4) step(0,0,1,0);
    end
    step(0,1,0,8'h5A);
    step(1,1,1,8'hFF);
    step(0,0,1,0);
    repeat (100)
      step(0,bit'($urandom_range(1,0)),bit'($urandom_range(1,0)),
           word_t'($urandom));
    repeat (4) step(0,0,1,0);
    @(negedge clk);
    $display("PASS");
    $finish;
  end
endmodule

Le premier front montant a rst=1 et vide DUT et modèle. === compare strictement même les X. Avec valid=0, dout non initialisé n'est pas comparé ; un valid lui-même à X fait échouer le test. Les quatre derniers pops vérifient toutes les données restantes. PASS est le message obtenu après exécution réelle avec toutes les assertions satisfaites ; la présence du code ne constitue pas cette preuve.

Avant d'ajouter B0, gardez l'ancien head A0 comme attendu. On compare l'ordre logique des données, pas des pointeurs ou cases physiques.

Ordre du scoreboard pour des requêtes simultanées à plein
  1. Ancienne file: [A0, A1, A2, A3]
  2. Valeur attendue pour pop: Conserver l'ancienne tête A0
  3. Appliquer push: [A1, A2, A3, B0]
  4. Comparaison des réponses: Si valid=1, dout=A0

Vérifier le scoreboard par les limites

Avec [A0,A1,A2,A3], un push seul de EE est rejeté car la taille vaut 4. Au prochain échange simultané, conservez expected=A0, puis ajoutez B0 pour former [A1,A2,A3,B0]. Si le DUT sort immédiatement B0, l'assertion de données échoue. À vide, une double requête donne expect_pop=0,expect_push=1 : stockez sans comparaison de sortie. Pendant reset, ignorez les demandes, videz la queue et vérifiez valid=0.

Pour la mémoire, utilisez une référence par adresse et une table initialized. En read-first, capturez l'attendu avant de modifier la référence écrite. Pour le bypass du banc de registresRegistre 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, sélectionnez wr_data en collision puis appliquez l'écriture de référence. Précisez si les lectures non initialisées sont interdites ou indéfinies ; n'inventez pas une réponse 0. Une mémoire adressable exige son propre vérificateur.

Injectez séparément des défauts : nouvelle donnée sortie lors d'un pop à plein, compte incrémenté en échange simultané, valid actif sur pop à vide. Chacun doit faire échouer le contrôle correspondant. Ces essais et la couverture par limite révèlent les lacunes du test. Placement des RAM et timing relèvent de vérifications de synthèse et temporelles distinctes.

Essayez vous-même

Un scoreboard ajoute d'abord push, puis utilise la nouvelle taille pour autoriser pop. Quelle erreur fait-il à vide avec les deux demandes ? Quand garder l'attendu à plein ? Évaluez aussi la règle imposant dout=0 après chaque reset.

Lire l’explication

À vide, ce scoreboard croit pouvoir retirer immédiatement l'élément ajouté et produit expect_pop=1. L'acceptation doit utiliser la taille avant le front : seul push est accepté, valid=0. À plein, sauvegardez puis retirez l'ancien head avant d'ajouter la nouvelle donnée. Reset n'impose pas dout=0 : vérifiez valid=0 et queue/occupation vides. Comparez la donnée si expect_pop=1 et valid du DUT séparément, sinon un valid toujours nul pourrait passer.

Votre choix s’applique à ce navigateur. Modifiez-le à tout moment en bas de page.