Testbench Code
`timescale 1ns/1ps
module tb_piso4;
// Inputs
reg CLK, RST, LOAD;
reg [3:0] D;
// Output
wire serial_out;
// DUT
piso4 dut(.CLK(CLK), .RST(RST), .LOAD(LOAD), .D(D), .serial_out(serial_out));
// Expected outputs (for VCD only expected_* signals)
reg expected_serial_out;
reg [3:0] expected_SR;
// Mismatch
wire mismatch = (serial_out !== expected_serial_out);
// Counters / caps
integer TOTAL_TEST_CASES = 0;
integer TOTAL_PASSED_TEST_CASES = 0;
integer TOTAL_FAILED_TEST_CASES = 0;
integer VCD_MAX_CASES = 32;
integer ERROR_MAX_CASES = 32;
integer printed_rows = 0;
integer printed_errs = 0;
integer i;
// 50% duty-cycle clock (10 TU period)
initial begin
CLK = 1'b0;
forever #5 CLK = ~CLK;
end
// Scoreboard: mirror the "no-repeat LSB" DUT behavior
always @(posedge CLK or posedge RST) begin
if (RST) begin
expected_SR <= 4'b0000;
expected_serial_out <= 1'b0;
end else if (LOAD) begin
expected_SR <= D; // hold serial_out on load
expected_serial_out <= expected_serial_out;
end else begin
expected_serial_out <= expected_SR[0]; // emit LSB
expected_SR <= {1'b0, expected_SR[3:1]};
end
end
// VCD: Inputs -> Outputs -> Expected -> mismatch
initial begin
$dumpfile("tb_piso4.vcd");
$dumpvars(0,
tb_piso4.CLK,
tb_piso4.RST,
tb_piso4.LOAD,
tb_piso4.D,
tb_piso4.serial_out,
tb_piso4.expected_serial_out,
tb_piso4.mismatch
);
$dumpon;
end
task log_case;
begin
TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
if (!mismatch) begin
TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
if (printed_rows < VCD_MAX_CASES) begin
$display(" LOAD=%b D=0x%1h | SO=%b EXP=%b | mismatch=%0d",
LOAD, D, serial_out, expected_serial_out, mismatch);
printed_rows = printed_rows + 1;
end
end else begin
TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
if (printed_errs < ERROR_MAX_CASES) begin
$display("ERR: LOAD=%b D=0x%1h => SO=%b (exp %b)",
LOAD, D, serial_out, expected_serial_out);
printed_errs = printed_errs + 1;
end
end
if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
end
endtask
// Apply LOAD/D on negedge; log after the following posedge(s)
task load_word;
input [3:0] word;
begin
@(negedge CLK);
LOAD = 1'b1; D = word;
@(posedge CLK); #1; log_case(); // load edge (SO holds)
@(negedge CLK);
LOAD = 1'b0; // begin shifting next edge
end
endtask
task shift_n;
input integer n;
integer k;
begin
for (k=0; k<n; k=k+1) begin
@(posedge CLK); #1; log_case();
end
end
endtask
initial begin
// Init & reset
RST = 1'b1; LOAD = 1'b0; D = 4'h0;
expected_SR = 4'h0; expected_serial_out = 1'b0;
@(posedge CLK); #1; log_case(); // during reset
RST = 1'b0;
@(posedge CLK); #1; log_case(); // after reset release
// Directed cases
load_word(4'b1011); // expect stream: 1,1,0,1 on next 4 edges
shift_n(4);
load_word(4'b0100); // expect: 0,0,1,0
shift_n(4);
// Randomized cases
for (i=0; i<6; i=i+1) begin
load_word($random);
shift_n(4);
end
// Summary
$display("------------------------------------");
$display("TOTAL_TEST_CASES=%0d", TOTAL_TEST_CASES);
$display("TOTAL_PASSED_TEST_CASES=%0d", TOTAL_PASSED_TEST_CASES);
$display("TOTAL_FAILED_TEST_CASES=%0d", TOTAL_FAILED_TEST_CASES);
$display("ALL_TEST_CASES_PASSED=%s", (TOTAL_FAILED_TEST_CASES==0) ? "true" : "false");
#2 $finish;
end
endmodule