Verilog · 2026-06-16 · FPGA · RTL · clock · CDC

Clock divider with 50% duty (Verilog)

Dividing a clock sounds trivial until you need an odd ratio at a clean 50% duty — a single posedge counter can only give you (N±1)/2 high time. The fix is to run one counter on the rising edge and one on the falling edge and AND them: each is high for the same number of clocks, but the falling-edge copy is shifted half a cycle, so the overlap lands at exactly 50%. This module does that for odd ratios and the simple toggle for even ones, and also exposes a clock-enable strobe — which, on a real FPGA, is what you usually want.

module clk_gen #(
  parameter integer DIV = 4              // output = clk / DIV  (DIV >= 2)
)(
  input  wire clk,
  input  wire rst_n,
  output wire clk_en,                    // 1-cycle strobe every DIV clocks (preferred)
  output wire clk_out                    // divided clock, 50% duty (route via a global buffer)
);
  localparam integer W    = (DIV <= 2) ? 1 : $clog2(DIV);
  localparam integer HALF = DIV / 2;     // even path: toggle every HALF clocks
  localparam integer THR  = (DIV + 1) / 2;

  // ---- clock-enable strobe: one pulse per DIV input clocks ----
  reg [W-1:0] cnt;
  always @(posedge clk or negedge rst_n)
    if (!rst_n)
      cnt <= 0;
    else
      cnt <= (cnt == DIV - 1) ? 0 : cnt + 1'b1;

  assign clk_en = (cnt == DIV - 1);

  // ---- even path: a toggle flop every HALF clocks -> exact 50% ----
  reg [W-1:0] eCnt;
  reg         eQ;
  always @(posedge clk or negedge rst_n)
    if (!rst_n) begin
      eCnt <= 0;
      eQ   <= 1'b0;
    end
    else if (eCnt == HALF - 1) begin
      eCnt <= 0;
      eQ   <= ~eQ;
    end
    else
      eCnt <= eCnt + 1'b1;

  // ---- odd path: posedge & negedge counters, each high for THR clocks;
  //      their AND is high for exactly DIV/2 -> 50% duty on an odd divisor ----
  reg [W-1:0] pCnt, nCnt;
  always @(posedge clk or negedge rst_n)
    if (!rst_n) pCnt <= 0; else pCnt <= (pCnt == DIV - 1) ? 0 : pCnt + 1'b1;
  always @(negedge clk or negedge rst_n)
    if (!rst_n) nCnt <= 0; else nCnt <= (nCnt == DIV - 1) ? 0 : nCnt + 1'b1;

  wire pQ = (pCnt < THR);
  wire nQ = (nCnt < THR);

  assign clk_out = (DIV % 2 == 0) ? eQ : (pQ & nQ);
endmodule

Use the enable, not the divided clock

On an FPGA, clk_out is a derived clock built in the logic fabric — and for odd ratios it is even combinational (pQ & nQ). Driven straight into a flip-flop’s clock pin it has skew and can glitch. Two correct ways to use this block:

  • Preferred — clk_en. Keep every register on the original clk and qualify them with if (clk_en). There is one clock domain, the static-timing analyser sees everything, and there is no second clock tree to skew. This is the “stable” path: you get a slow update rate without a slow clock.
  • If you truly need a clock (an external device wants a divided clock pin, say), route clk_out through a global clock buffer (BUFG on Xilinx, a clock-capable route on others) and constrain it with create_generated_clock. Never let a fabric-divided net clock internal logic directly. For a real frequency synthesis use the PLL/MMCM, not a divider.

Testbench (self-checking)

Measure the divided period and duty from edge timestamps — for an even (DIV=4) and an odd (DIV=5) ratio — and check the enable strobe spacing. Build and run with iverilog -g2012 -o sim design.v tb.v && vvp sim.

`timescale 1ns/1ps
module tb;
  localparam real CLKP = 10.0;          // 100 MHz input clock

  reg clk = 0, rst_n = 0;
  always #(CLKP / 2) clk = ~clk;

  // one even divider (DIV=4) and one odd divider (DIV=5)
  wire en4, out4, en5, out5;

  clk_gen #(.DIV(4)) even_div (
    .clk     (clk),
    .rst_n   (rst_n),
    .clk_en  (en4),
    .clk_out (out4)
  );

  clk_gen #(.DIV(5)) odd_div (
    .clk     (clk),
    .rst_n   (rst_n),
    .clk_en  (en5),
    .clk_out (out5)
  );

  integer pass = 0, fail = 0;

  // period + duty of a divided clock from three edge timestamps
  task check_clk (input integer div, input real rise0, input real fall, input real rise1);
    real period, duty;
    begin
      period = rise1 - rise0;
      duty   = 100.0 * (fall - rise0) / period;
      if ((period == div * CLKP) && (duty > 49.9) && (duty < 50.1)) begin
        pass = pass + 1;
        $display("  PASS  DIV=%0d  period=%0.0fns  duty=%0.1f%%", div, period, duty);
      end
      else begin
        fail = fail + 1;
        $display("  FAIL  DIV=%0d  period=%0.0fns  duty=%0.1f%%", div, period, duty);
      end
    end
  endtask

  // spacing of the clk_en strobe (should be DIV input clocks)
  task check_en (input integer div, input real t0, input real t1);
    begin
      if (t1 - t0 == div * CLKP) begin
        pass = pass + 1;
        $display("  PASS  DIV=%0d  clk_en every %0.0fns", div, t1 - t0);
      end
      else begin
        fail = fail + 1;
        $display("  FAIL  DIV=%0d  clk_en spacing=%0.0fns", div, t1 - t0);
      end
    end
  endtask

  real e0, e1, e2, o0, o1, o2, s0, s1;
  initial begin
    repeat (2) @(posedge clk);
    rst_n = 1;

    @(posedge out4); e0 = $realtime; @(negedge out4); e1 = $realtime; @(posedge out4); e2 = $realtime;
    check_clk(4, e0, e1, e2);

    @(posedge out5); o0 = $realtime; @(negedge out5); o1 = $realtime; @(posedge out5); o2 = $realtime;
    check_clk(5, o0, o1, o2);

    @(posedge en5); s0 = $realtime; @(posedge en5); s1 = $realtime;
    check_en(5, s0, s1);

    $display("  ==== %0d passed, %0d failed ====", pass, fail);
    $finish;
  end
endmodule
  PASS  DIV=4  period=40ns  duty=50.0%
  PASS  DIV=5  period=50ns  duty=50.0%
  PASS  DIV=5  clk_en every 50ns
  ==== 3 passed, 0 failed ====

Usage

  • DIV sets the ratio (f_out = f_clk / DIV). The 50% logic handles any DIV >= 2, even or odd; pick the actual frequency with the frequency ↔ period tool.
  • For a runtime ratio, register DIV and reload the counters at clk_en so the change is glitch-free — same shadow-register idea as the PWM generator.
  • Feeding logic in another clock domain? Don’t divide and ride — cross with a proper synchronizer or FIFO (see metastability & CDC).