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 originalclkand qualify them withif (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_outthrough a global clock buffer (BUFGon Xilinx, a clock-capable route on others) and constrain it withcreate_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
DIVsets the ratio (f_out = f_clk / DIV). The 50% logic handles anyDIV >= 2, even or odd; pick the actual frequency with the frequency ↔ period tool.- For a runtime ratio, register
DIVand reload the counters atclk_enso 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).