Altifigence Academy

24 / 37 · Praktikum

Praktik: testbench yang melaporkan kegagalannya sendiri

Pisahkan DUT, stimulus, acuan dan pemeriksaan untuk reset, enable dan putaran tanpa race.

Pisahkan implementasi dan pemeriksa

Testbench menyediakan stimulus, menghitung harapan dan melaporkan beda. Ini kode simulasi, bukan rangkaian untuk sintesis. Simpan modulo_tick dari pelajaran parameter sebagai modulo_tick.sv dan testbench lengkap ini sebagai tb.sv. Reset sinkron menang; tanpa enable, count ditahan dan pulse dibersihkan; diaktifkan, count bertambah modulo N dan pulse=1 hanya saat putaran. Gunakan acuan integer, tidak membaca keadaan internal.

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

Hindari race pada tepi aktif

Task mengubah masukan pada tepi turun, setengah siklus sebelum tangkapan, dan membandingkan setelah pembaruan nonblocking tepi naik. #1 adalah delay pengamatan edukatif dengan timescale dan clock 10ns, bukan analisis fisik clock-to-Q, setup atau hold. Lingkungan pakai ulang bisa menggunakan clocking blocks atau disiplin wilayah kejadian. Reset awal menjadikan DUT diketahui; menginisialisasi acuan tidak menginisialisasi DUT. !== melaporkan X/Z terhadap harapan diketahui.

Kompilasi dengan top eksplisit

Pada shell dengan Icarus terpasang:

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

Kompilasi terpisah dengan -P tb.N=1,-P tb.N=2,-P tb.N=5,-P tb.N=8. Dokumentasi resmi Icarus menjelaskan bahasa dan top. Catat versi karena dukungan SystemVerilog bervariasi.

Sengaja buat gagal

Ubah pembanding agar kembali satu tepi terlalu cepat atau hapus pembersihan default pulse. Sebuah skenario harus gagal dengan status bukan nol sebelum timeout. Pulihkan sumber benar setelahnya. PASS yang tak dapat mendeteksi mutasi relevan adalah bukti lemah. Catat hash, N, perintah, status, PASS dan perbedaan pertama. Jangan sebut test lokal sebagai eksekusi DDS:#delay dan initial pada testbench umum tidak sama dengan antarmuka laboratorium terbatas satu clock.

Pilihan berlaku di peramban ini. Ubah kapan saja di bagian bawah halaman.