Testbench Code
`timescale 1ns/1ps
module tb_board_blink_dual;
reg clk;
reg rst;
wire led_fixed;
wire led_cfg;
wire expected_led_fixed;
wire expected_led_cfg;
wire mismatch;
// Top parameters under test: fixed = (3,6); cfg = (CW=3, EVERY=4)
localparam integer TB_CW = 3;
localparam integer TB_EVERY = 4;
board_blink_dual #(.CW(TB_CW), .EVERY(TB_EVERY)) dut (
.clk(clk), .rst(rst), .led_fixed(led_fixed), .led_cfg(led_cfg)
);
// Golden models
reg [2:0] exp_cnt_fixed;
reg exp_led_fixed_q;
assign expected_led_fixed = exp_led_fixed_q;
reg [TB_CW-1:0] exp_cnt_cfg;
reg exp_led_cfg_q;
assign expected_led_cfg = exp_led_cfg_q;
always @(posedge clk) begin
if (rst) begin
exp_led_fixed_q <= 1'b0;
exp_cnt_fixed <= 3'd0;
end else begin
if (exp_cnt_fixed == 6-1) begin
exp_led_fixed_q <= ~exp_led_fixed_q;
exp_cnt_fixed <= 3'd0;
end else begin
exp_cnt_fixed <= exp_cnt_fixed + 3'd1;
end
end
end
always @(posedge clk) begin
if (rst) begin
exp_led_cfg_q <= 1'b0;
exp_cnt_cfg <= {TB_CW{1'b0}};
end else begin
if (exp_cnt_cfg == TB_EVERY-1) begin
exp_led_cfg_q <= ~exp_led_cfg_q;
exp_cnt_cfg <= {TB_CW{1'b0}};
end else begin
exp_cnt_cfg <= exp_cnt_cfg + {{(TB_CW-1){1'b0}},1'b1};
end
end
end
wire mm_fixed = (led_fixed !== expected_led_fixed);
wire mm_cfg = (led_cfg !== expected_led_cfg);
assign mismatch = mm_fixed | mm_cfg;
// Clock
initial begin
clk = 1'b0;
forever #5 clk = ~clk;
end
// VCD: Inputs → Outputs → Expected → Mismatch
initial begin
@(posedge clk);
$dumpfile("tb_board_blink_dual.vcd");
$dumpvars(0,
clk, rst, // 1) Inputs
led_fixed, led_cfg, // 2) Outputs
expected_led_fixed, expected_led_cfg, // 3) Expected Outputs
mismatch // 4) Mismatch
);
end
// Helpers
task apply_cycles; input integer n; integer i; begin for (i=0;i<n;i=i+1) @(posedge clk); end endtask
integer TOTAL_TEST_CASES, TOTAL_PASSED_TEST_CASES, TOTAL_FAILED_TEST_CASES;
// Run a window, count expected toggles for both LEDs
task run_window;
input integer cycles;
input [8*48-1:0] name;
integer i;
reg case_failed;
integer tog_fixed, tog_cfg;
reg prev_fixed, prev_cfg;
begin
case_failed = 0;
tog_fixed = 0; tog_cfg = 0;
prev_fixed = expected_led_fixed; prev_cfg = expected_led_cfg;
for (i=0; i<cycles; i=i+1) begin
@(posedge clk);
if (mismatch) case_failed = 1;
if (expected_led_fixed !== prev_fixed) tog_fixed = tog_fixed + 1;
if (expected_led_cfg !== prev_cfg) tog_cfg = tog_cfg + 1;
prev_fixed = expected_led_fixed; prev_cfg = expected_led_cfg;
end
$display("CASE=%s : toggles_fixed=%0d toggles_cfg=%0d %s",
name, tog_fixed, tog_cfg, case_failed ? "MISMATCH" : "OK");
TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
if (!case_failed) TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
else TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
apply_cycles(1);
end
endtask
// Stimulus: show 4 reset cycles, then several short windows
initial begin
rst = 1'b1;
exp_cnt_fixed = 0; exp_led_fixed_q = 0;
exp_cnt_cfg = 0; exp_led_cfg_q = 0;
apply_cycles(4);
@(negedge clk) rst = 1'b0;
TOTAL_TEST_CASES = 0; TOTAL_PASSED_TEST_CASES = 0; TOTAL_FAILED_TEST_CASES = 0;
run_window(3, "pre_first_toggle"); // no toggle yet for either
run_window(1, "cfg_first_toggle"); // cfg toggles at 4th edge
run_window(2, "gap_until_fixed_toggle");
run_window(1, "fixed_first_toggle"); // fixed toggles at 6th edge
run_window(4, "one_cfg_period"); // cfg toggles once more
run_window(6, "one_fixed_period"); // fixed toggles once more
@(negedge clk) rst = 1'b1; apply_cycles(2); @(negedge clk) rst = 1'b0;
run_window(4, "post_reset_cfg_first_toggle");
run_window(6, "post_reset_fixed_first_toggle");
$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");
$finish;
end
endmodule