24 / 37 · Praktikum
Praktik: testbench yang melaporkan kegagalannya sendiri
Pisahkan DUT, stimulus, acuan dan pemeriksaan untuk reset, enable dan putaran tanpa race.
Pelajaran dapat dibaca gratis. Daftar untuk menyimpan progres.
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.
`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
endmoduleHindari 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:
iverilog -g2012 -s tb -o sim.vvp modulo_tick.sv tb.sv
vvp sim.vvpKompilasi 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.