23. Divide-by-4 Tick Generator

Generate a one-clock-wide pulse every fourth rising edge of clk. The synchronous active-high reset rst clears state and realigns the tick sequence so the first pulse occurs exactly four clocks after reset deassertion.

Requirements

  • Module name: tick_div4
  • Inputs:
    • clk
    • rst
  • Outputs:
    • tick — 1 for exactly one clk cycle every fourth clock after reset deassertion; otherwise 0

Testbench Code

`timescale 1ns/1ps
module tb_tick_div4;
  reg clk;
  reg rst;

  wire tick;
  wire expected_tick;
  wire mismatch;

  tick_div4 dut(.clk(clk), .rst(rst), .tick(tick));

  reg [1:0] exp_cnt;
  reg       exp_tick_q;
  assign expected_tick = exp_tick_q;

  always @(posedge clk) begin
    exp_tick_q <= rst ? 1'b0 : (exp_cnt == 2'd3);
    exp_cnt    <= rst ? 2'd0 : (exp_cnt + 2'd1);
  end

  assign mismatch = (tick !== expected_tick);

  initial begin
    clk = 1'b0;
    forever #5 clk = ~clk;
  end

  initial begin
    $dumpfile("tb_tick_div4.vcd");
    $dumpvars(0,
      clk, rst,
      tick,
      expected_tick,
      mismatch
    );
  end

  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;
  reg [8*48-1:0] TC_NAME;

  task check_case;
    input [8*48-1:0] name;
    begin
      TC_NAME = name;
      #1;
      $display("CASE=%s : tick=%0b expected_tick=%0b %s",
               TC_NAME, tick, expected_tick, mismatch ? "MISMATCH" : "OK");
      TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
      if (!mismatch) TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
      else begin
        TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
        $display("  FAILED: expected_tick=%0b", expected_tick);
      end
      apply_cycles(1);
    end
  endtask

  initial begin
    TOTAL_TEST_CASES = 0; TOTAL_PASSED_TEST_CASES = 0; TOTAL_FAILED_TEST_CASES = 0;

    rst = 1'b1; apply_cycles(1);
    rst = 1'b0; apply_cycles(1);
    check_case("after_reset_no_tick");

    apply_cycles(2);
    check_case("no_tick_until_4th_edge");

    apply_cycles(1);
    check_case("first_tick_at_4th_edge");

    apply_cycles(3);
    check_case("no_tick_edges_5_to_7");

    apply_cycles(1);
    check_case("second_tick_at_8th_edge");

    rst = 1'b1; apply_cycles(1);
    rst = 1'b0; apply_cycles(1);
    check_case("realign_after_reset");

    apply_cycles(3);
    check_case("tick_again_4_edges_after_realign");

    $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