Testbench Code
`timescale 1ns/1ps
module tb_shift_reg_bidirectional;
// 1) Inputs
reg CLK, RST, DIR, serial_in;
// 2) Outputs
wire [3:0] Q;
// 3) Expected outputs (prefixed "expected_")
reg [3:0] expected_Q;
// 4) Mismatch (HIGH when outputs != expected)
wire mismatch;
// DUT
shift_reg_bidirectional dut(.CLK(CLK), .RST(RST), .DIR(DIR), .serial_in(serial_in), .Q(Q));
// Free-running 50% duty-cycle clock (10 time-unit period)
initial begin
CLK = 1'b0;
forever #5 CLK = ~CLK;
end
// Scoreboard mirrors DUT (async reset + edge shift)
always @(posedge CLK or posedge RST) begin
if (RST)
expected_Q <= 4'b0000;
else if (DIR)
expected_Q <= {expected_Q[2:0], serial_in};
else
expected_Q <= {serial_in, expected_Q[3:1]};
end
// 4-state aware compare
assign mismatch = (Q !== expected_Q);
// Counters / limits
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 i;
// VCD — order: Inputs -> Outputs -> Expected -> mismatch; start at #0
initial begin
$dumpfile("tb_shift_reg_bidirectional.vcd");
$dumpvars(0,
tb_shift_reg_bidirectional.CLK,
tb_shift_reg_bidirectional.RST,
tb_shift_reg_bidirectional.DIR,
tb_shift_reg_bidirectional.serial_in,
tb_shift_reg_bidirectional.Q,
tb_shift_reg_bidirectional.expected_Q,
tb_shift_reg_bidirectional.mismatch
);
$dumpon;
end
// Log one test result and cap VCD rows
task log_case;
begin
TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
if (!mismatch) begin
TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
if (TOTAL_TEST_CASES <= VCD_MAX_CASES)
$display(" DIR=%b serial_in=%b | Q=0x%1h EXP=0x%1h | mismatch=%0d",
DIR, serial_in, Q, expected_Q, mismatch);
end else begin
TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
if (TOTAL_FAILED_TEST_CASES <= ERROR_MAX_CASES)
$display("ERR: DIR=%b serial_in=%b => Q=0x%1h (exp 0x%1h)",
DIR, serial_in, Q, expected_Q);
end
if (TOTAL_TEST_CASES == VCD_MAX_CASES) $dumpoff;
end
endtask
// Async reset pulse
task async_reset;
begin
RST = 1'b1;
@(posedge CLK); #1; log_case(); // scoreboard/DUT both jump to 0 immediately; sample next edge
RST = 1'b0;
@(posedge CLK); #1; log_case();
end
endtask
// One capture: drive on negedge, sample after next posedge
task shift_step;
input dir_v;
input sin_v;
begin
@(negedge CLK);
DIR = dir_v;
serial_in = sin_v;
@(posedge CLK); #1; log_case();
end
endtask
initial begin
// Known start via reset
DIR = 1'b0; serial_in = 1'b0; expected_Q = 4'hx; RST = 1'b0;
async_reset();
// Directed sequence
shift_step(1'b1, 1'b1); // left insert 1
shift_step(1'b1, 1'b0); // left insert 0
shift_step(1'b0, 1'b1); // right insert 1
shift_step(1'b0, 1'b1); // right insert 1
// Randomized mix
for (i = 0; i < 12; i = i + 1)
shift_step($random, $random);
// 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