Altifigence Academy

24 / 37 · Übung

Übung: eine Testbench, die Fehler selbst meldet

Trennen Sie DUT, Stimuli, Referenz und Prüfungen für Reset, enable und Überlauf ohne Wettlaufbedingungen.

Implementierung und Prüfer trennen

Die Testbench liefert Stimuli, berechnet Erwartungen und meldet Unterschiede. Sie ist Simulationscode, keine zu synthetisierende Schaltung. Speichern Sie modulo_tick aus der Parametereinheit in modulo_tick.sv und diese vollständige Testbench in tb.sv. Synchroner Reset hat Vorrang; ohne enable bleibt count und pulse wird gelöscht; mit enable erhöht sich count modulo N, pulse gilt nur beim Überlauf. Verwenden Sie eine ganzzahlige Referenz statt internen DUT-Zustand.

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

Wettlauf an der aktiven Flanke vermeiden

Der Task ändert Eingänge an der fallenden Flanke, einen halben Zyklus vor Erfassung, und vergleicht nach nichtblockierenden Aktualisierungen der steigenden Flanke. #1 ist bei angegebenem timescale und 10ns-Takt eine didaktische Beobachtungsverzögerung, keine physikalische Prüfung von clock-to-Q, setup oder hold. Wiederverwendbare Umgebungen können clocking blocks oder geordnete Ereignisregionen nutzen. Der erste Reset macht den DUT bekannt; die Initialisierung der Referenz initialisiert ihn nicht. !== erkennt X/Z gegenüber bekannten Erwartungen.

Mit ausdrücklichem Top kompilieren

In einer Shell mit installiertem Icarus:

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

Kompilieren Sie getrennt mit -P tb.N=1,-P tb.N=2,-P tb.N=5,-P tb.N=8. Die offizielle Icarus-Dokumentation beschreibt Sprache und Top. Protokollieren Sie die verwendete Version, weil SystemVerilog-Unterstützung variiert.

Absichtlich scheitern lassen

Ändern Sie den Vergleich für einen um eine Flanke zu frühen Rücksprung oder entfernen Sie das standardmäßige Löschen von pulse. Ein Szenario muss vor Timeout mit Status ungleich Null scheitern. Stellen Sie danach die korrekte Quelle wieder her. Ein PASS, das relevante Mutationen nicht erkennt, ist schwacher Nachweis. Dokumentieren Sie Hash, N, Befehle, Status, PASS und erste Abweichung. Eine lokale Prüfung ist keine DDS-Ausführung: #delay und initial einer allgemeinen Testbench entsprechen nicht automatisch der eingeschränkten Labor-Schnittstelle mit einem Takt.

Deine Auswahl gilt für diesen Browser. Du kannst sie jederzeit in der Fußzeile ändern.