Altifigence Academy

24 / 37 · Travaux pratiques

Travaux pratiques : un banc qui signale ses propres échecs

Séparez DUT, stimuli, référence et vérifications pour tester reset, enable et rebouclage sans course.

Séparer implémentation et vérificateur

Le banc fournit des stimuli, calcule les attentes et signale les différences. C'est du code de simulation, pas un circuit à synthétiser. Sauvegardez modulo_tick de la leçon paramètres dans modulo_tick.sv et ce banc complet dans tb.sv. Reset synchrone prime; sans enable, count reste et pulse s'efface; avec enable, count progresse modulo N et pulse vaut 1 uniquement au rebouclage. Utilisez une référence entière sans lire l'état interne.

SystemVerilog
`timescale 1ns/1ps
module tb #(parameter integer N = 5);
  localparam integer CW = (N <= 1) ? 1 : $clog2(N);
  logic clk = 1'b0;
  logic rst = 1'b0;
  logic enable = 1'b0;
  wire [CW-1:0] count;
  wire pulse;
  integer expected_count = 0;
  logic expected_pulse = 1'b0;
  integer checks = 0;
  modulo_tick #(.N(N)) dut (
    .clk(clk), .rst(rst), .enable(enable),
    .count(count), .pulse(pulse)
  );
  always #5 clk = ~clk;

  task automatic step(input logic reset_value, enable_value);
    begin
      @(negedge clk);
      rst = reset_value;
      enable = enable_value;
      if (reset_value) begin
        expected_count = 0;
        expected_pulse = 1'b0;
      end else begin
        expected_pulse = 1'b0;
        if (enable_value) begin
          expected_count = expected_count + 1;
          if (expected_count == N) begin
            expected_count = 0;
            expected_pulse = 1'b1;
          end
        end
      end
      @(posedge clk);
      #1;
      if (count !== CW'(expected_count) || pulse !== expected_pulse)
        $fatal(1, "Mismatch check=%0d count=%0d expected=%0d pulse=%b expected=%b",
          checks, count, expected_count, pulse, expected_pulse);
      checks = checks + 1;
    end
  endtask

  initial begin
    if (N < 1) $fatal(1, "N must be positive");
    step(1'b1, 1'b1);
    repeat (N-1) step(1'b0, 1'b1);
    step(1'b0, 1'b0);
    step(1'b0, 1'b1);
    step(1'b0, 1'b0);
    repeat (2*N+1) step(1'b0, 1'b1);
    step(1'b1, 1'b1);
    step(1'b0, 1'b0);
    $display("PASS N=%0d checks=%0d", N, checks);
    $finish;
  end
  initial begin
    #10000;
    $fatal(1, "Timeout");
  end
endmodule

Éviter une course au front actif

La tâche change les entrées au front descendant, un demi-cycle avant capture, puis compare après les mises à jour non bloquantes du montant. #1 est un retard d'observation pédagogique avec timescale et horloge de 10ns, pas une analyse physique de clock-to-Q, setup ou hold. Les environnements réutilisables peuvent employer clocking blocks ou discipline des régions d'événements. Le premier reset rend le DUT connu; initialiser la référence ne l'initialise pas. !== signale X/Z devant une attente connue.

Compiler avec top explicite

Dans un shell où Icarus est installé:

text
iverilog -g2012 -s tb -o sim.vvp modulo_tick.sv tb.sv
vvp sim.vvp

Compilez séparément avec -P tb.N=1,-P tb.N=2,-P tb.N=5,-P tb.N=8. La documentation officielle Icarus décrit langage et top. Notez la version employée, car le support SystemVerilog varie.

Provoquer volontairement un échec

Modifiez le comparateur pour reboucler un front trop tôt ou retirez l'effacement par défaut de pulse. Un scénario doit échouer avant timeout avec code de sortie non nul. Restaurez ensuite la bonne source. Un PASS incapable de détecter une mutation pertinente est peu probant. Conservez hash, N, commandes, statuts, PASS et première divergence. Un test local n'est pas une exécution DDS:#delay et initial d'un banc général ne correspondent pas automatiquement à l'interface de laboratoire restreinte à une horloge.

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