24 / 37 · 실습
실습: 스스로 실패를 보고하는 테스트벤치
DUT·자극·참조 모델·판정을 분리하고 reset·enable·순환을 레이스 없는 관찰 시점에서 검사합니다.
내용은 무료로 볼 수 있습니다. 수강 신청하면 학습 기록을 저장할 수 있어요.
구현과 검사기를 분리합니다
테스트벤치는 자극을 공급하고 기대 동작을 계산하며 불일치를 보고합니다. 합성할 회로가 아니라 시뮬레이션 코드입니다. 이 실습은 매개변수 강의의 modulo_tick을 사용합니다. 그 모듈을 modulo_tick.sv, 다음 완전한 테스트벤치를 tb.sv로 저장하세요.
명세는 동기 reset 우선, enable=0에서 count 유지와 pulse 해제, enable=1에서 modulo N 증가 및 순환 시 pulse=1입니다. DUT 내부 상태를 읽지 말고 정수 참조 count를 사용합니다.
`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활성 에지의 레이스를 피합니다
task는 캡처보다 반 클록 앞선 하강 에지에서 입력을 정하고 다음 상승 에지의 nonblocking 갱신 후 비교합니다. 여기서 #1은 timescale과 10ns 클록을 명시한 교육용 관찰 지연입니다. 물리적인 clock-to-Q·setup·hold 분석이 아닙니다. 재사용 검증 환경에서는 clocking block이나 이벤트 영역 규칙을 더 엄밀하게 사용할 수 있습니다.
첫 step의 reset이 DUT를 알려진 상태로 만듭니다. 참조값 초기화가 DUT까지 초기화하는 것은 아닙니다. !==는 알려진 기대값과 비교할 때 뜻하지 않은 X/Z 출력을 확실한 불일치로 처리합니다.
top을 명시해 컴파일·실행합니다
Icarus Verilog가 설치된 셸에서 다음을 실행합니다.
iverilog -g2012 -s tb -o sim.vvp modulo_tick.sv tb.sv
vvp sim.vvp별도 컴파일 명령에 -P tb.N=1, -P tb.N=2, -P tb.N=5, -P tb.N=8을 넣어 설정 행렬을 확인하세요. Icarus 공식 명령행 문서는 언어 모드와 top 선택 옵션을 설명합니다. 시뮬레이터마다 지원하는 SystemVerilog 범위가 다를 수 있으므로 실제 버전을 기록합니다.
검사기를 의도적으로 실패시킵니다
DUT의 종료 비교를 바꿔 한 에지 일찍 순환하게 하거나 pulse 기본 해제를 제거하세요. 적어도 하나의 시나리오가 timeout 전에 0이 아닌 실패로 종료해야 합니다. 그 뒤 올바른 소스를 복원합니다. 관련 오류를 넣어도 잡지 못하는 검사기의 통과는 약한 근거입니다.
소스 해시·N·정확한 명령·종료 코드·PASS 출력과 의도적 실패의 불일치를 보관하세요. 이 로컬 테스트벤치를 DDS 실행으로 표시하면 안 됩니다. 범용 SystemVerilog의 테스트벤치 지연과 initial 블록은 제한된 단일 클록 제품 실습 인터페이스와 별개입니다.