Skip to content

RTL Design Patterns · Chapter 3 · Counters, Timers & Pulse Generators

Mod-N & Programmable Counters

A plain binary counter only wraps at a power of two, but real designs need to divide by arbitrary N all the time, like a divide-by-1000 tick or a baud-rate generator. A mod-N counter is just a counter with a compare-and-reset added: advance every cycle, and when the count reaches its terminal value, force it back to zero. The whole design rests on one small decision, and the terminal value is N minus 1, not N. Getting that off by one turns a divide-by-N into a divide-by-N-plus-1, the most common counter bug in the field. You build a fixed modulus set by a parameter, a runtime-programmable modulus with a guard for when N shrinks, and the signature off-by-one, each proven with a clocked self-checking testbench in SystemVerilog, Verilog, and VHDL.

Foundation12 min readRTL Design PatternsMod-N CounterProgrammable CounterClock DividerBaud GeneratorOff-By-One

Chapter 3 · Section 3.2 · Counters, Timers & Pulse Generators

1. The Engineering Problem

You have a free-running counter — the up-counter from 3.1 — and it works: every clock it adds one, and when it hits its maximum it wraps back to zero. The catch is where it wraps. A WIDTH-bit counter is a WIDTH-bit ripple of binary arithmetic, so it wraps only when it overflows, and that happens at exactly 2^WIDTH: a 3-bit counter wraps at 8, a 10-bit counter at 1024, a 16-bit at 65536. The wrap point is a power of two, and nothing else.

But almost nothing a real system needs to divide by is a power of two. You have a 50 MHz reference clock and you need a 1 ms system tick — that is a divide by 50,000. You need to generate 9600 baud for a UART from that same 50 MHz clock — that is a divide by 5208 (and a fraction). You are building a video timing generator and each line is 800 pixel-clocks long — divide by 800. You need a refresh request every 7.8 µs — some awkward integer of cycles. In every one of these the requirement is identical and it is not a power of two: count a fixed number of cycles N, then wrap — for arbitrary N.

You cannot get there by choosing WIDTH, because WIDTH only buys you powers of two. What you need is a counter that ignores the natural overflow point and instead wraps at a point you choose: it counts 0, 1, 2, …, N-1 and then returns to 0, so its period is exactly N cycles, for any N you name. That is the mod-N counter — "mod-N" because the count is always the cycle number taken modulo N. And frequently N is not even fixed at design time: a baud generator must support 9600, 19200, 115200; a programmable timer takes its period from a software-written register. So the same structure must also work when N is a runtime input, retuned on the fly. This page builds both, and the reason it is Chapter 3 is that this one compare-and-reset is the atom underneath every clock divider, every timer, and every pulse generator in the chapters that follow.

the-need.v — WIDTH gives you powers of two; the system needs arbitrary N
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // A plain up-counter wraps ONLY at 2^WIDTH — a power of two:
   reg [9:0] count;                       // 10 bits => wraps at 1024, period 1024
   always @(posedge clk) count <= count + 1'b1;   // 0..1023, then 0 — never /1000
 
   // But the system asks for a /50000 tick, a /5208 baud divisor, a /800 line...
   //   none of these is a power of two, so WIDTH alone cannot produce them.
 
   // The mod-N counter: count 0..N-1 and wrap, for ANY N (period == N):
   //   advance every cycle; when count reaches N-1, force it to 0 next cycle.
   //   N can be a PARAMETER (fixed divisor) or an INPUT (programmable divisor).
   //   The whole pattern this page builds — and the terminal value is N-1, not N.

2. Mental Model

3. Pattern Anatomy

The whole structure is the 3.1 counter plus two additions. Walk the datapath from the register outward.

The count register. Exactly the register from 3.1: a WIDTH-bit flop that holds the current count, updated on the clock edge, cleared by reset, and (optionally) frozen when an enable en is low. Its width must be large enough to hold N-1, i.e. WIDTH = ceil(log2(N)) for a static N, or wide enough for the largest programmable N you will ever load. Nothing about the register is new; the mod-N behaviour lives entirely in what feeds its input.

The terminal-count comparator. A combinational compare of count against the terminal value N-1. Because you are testing for a single exact value, this is an equality comparator (count == N-1), not a magnitude comparator — the cheapest possible compare, one XNOR-reduce, from 1.5. Its output is the wrap signal: high for exactly one cycle each period, on the cycle the counter holds its maximum value. When N is a parameter, N-1 is a constant and the comparator degenerates to a handful of gates on the bits that must be one; when N is an input, N-1 is computed live and the comparator is a full equality compare against a runtime value.

The wrap-to-zero mux. A 2:1 mux on the register's next-value input, steered by wrap. When wrap is low, the next value is count + 1 (advance, the normal counter behaviour). When wrap is high, the next value is forced to 0. This is where the counting sequence is closed into a loop: instead of running on to N and beyond, it snaps back to the start. The wrap pulse is also the natural tick / strobe output of the block — one clock wide, once per N cycles — which is exactly the divided signal a clock divider or baud generator hands downstream.

The period is N cycles; the terminal value is N-1. These are the two invariants to hold in your head, and they are different numbers. The counter visits the N states 0 … N-1; the largest state is N-1; the period — the number of cycles from one wrap to the next — is N. The compare is on the terminal value (N-1); the divide ratio is the period (N). Confusing the two is the bug in §6 and §7.

Static N vs programmable N. With a static modulus, N is a parameter (SV/Verilog) or generic (VHDL): the terminal value N-1 is a compile-time constant, the divisor is fixed in silicon, and the comparator is minimal. With a programmable modulus, N arrives as an input (often latched into a configuration register written by software): the terminal value is mod_n - 1, computed at runtime, and the divisor can be retuned live — the pattern behind a baud-rate generator that switches between 9600 and 115200, or a timer whose period software sets.

What happens when N changes mid-count. A programmable N introduces one hazard the static version never has: if software writes a smaller N while the counter is already above the new N-1, an exact-equality compare (count == mod_n - 1) will never fire — the counter has already passed the value it is waiting for, so it runs all the way up to overflow before it ever wraps. The counter is "stuck" for one very long period. The fix is a >= guard: wrap when count >= mod_n - 1, so a count that is already at or beyond the (new, smaller) terminal value wraps immediately on the next edge. This is the one place a magnitude compare earns its extra area — precisely because a live N can move the target underneath a count that has already gone past it.

Mod-N counter — the 3.1 counter plus a compare-and-reset

data flow
Mod-N counter — the 3.1 counter plus a compare-and-resetcount registerWIDTH-bit flop, holds 0..N-1+1 adderthe normal advance: count + 1compare == N-11.5 EQUALITY compare on terminal valuewrap-to-0 muxwrap ? 0 : count+1 → back into regN: param or inputstatic divisor vs programmable (>= guard)tick / strobewrap pulse: 1 cycle every N — the divided signal
The register and the +1 adder ARE the 3.1 counter. Mod-N adds only two blocks: an equality comparator that fires when count reaches the terminal value N-1, and a 2:1 mux that steers 0 (instead of count+1) back into the register on that cycle. N is the comparator's other input — a compile-time constant (static divisor) or a runtime value (programmable divisor, which needs a >= compare so a shrinking N still wraps). The compare pulse doubles as the tick/strobe: one clock wide, once per N cycles — the divided output a clock divider or baud generator hands downstream. The period is N; the terminal value is N-1. Same idea in SystemVerilog, Verilog, and VHDL.

Two closing notes on hardware reality. First, the comparator being equality (not magnitude) is a deliberate area win: a fixed mod-N only needs to detect one value, so it costs a few gates, not a full magnitude compare — reserve >= for the programmable case where a live N genuinely demands it. Second, everything here is a synchronous wrap: the compare is combinational but the reset-to-zero happens on the clock edge through the register, so the counter has no combinational loop and no glitch — the tick is a clean, registered, one-cycle pulse.

4. Real RTL Implementation

This is the core of the page. We build three patterns — (a) a static mod-N counter (parameter N, wrap on count == N-1), (b) a programmable mod-N counter (N as an input, with a >= guard so a shrinking N re-wraps cleanly), and (c) the off-by-one bug (== N buggy vs == N-1 fixed) — and each is shown in SystemVerilog, Verilog, and VHDL, with a synthesizable RTL block and a clocked, self-checking testbench that generates a clock and asserts the period is exactly N and the sequence is 0 … N-1. The idea is identical across the three languages; seeing them side by side is what makes the pattern language-independent.

One discipline runs through every RTL block, inherited from 3.1: the counter is a synchronous design with an explicit reset — <= (non-blocking) on the clock edge, an active-high synchronous rst that clears the count to 0, and an en that freezes it when low. The mod-N behaviour is layered on top as the next-value selection; the reset/enable semantics are unchanged from the plain counter, because a mod-N counter is a plain counter.

4a. Static mod-N counter — parameter N, wrap on count == N-1

Start with the fixed divisor: N is a parameter, so the terminal value N-1 is a compile-time constant. The counter advances on en, the comparator fires when count reaches N-1, and the wrap mux steers 0 back in. tick is the one-cycle strobe — the divided output.

mod_n.sv — static mod-N counter (parameter N, terminal = N-1)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n #(
       parameter int N     = 10,                       // the modulus — fixed divisor
       localparam int WIDTH = (N > 1) ? $clog2(N) : 1  // wide enough to hold N-1
   )(
       input  logic             clk,
       input  logic             rst,                   // synchronous, active-high
       input  logic             en,                    // advance only when high
       output logic [WIDTH-1:0] count,                 // current count, 0 .. N-1
       output logic             tick                   // 1-cycle strobe once per N
   );
       // wrap fires on the TERMINAL value N-1 (not N). It is an EQUALITY compare
       // against a compile-time constant, so it costs only a few gates. This pulse
       // is both the wrap trigger AND the divided output (tick).
       wire wrap = (count == WIDTH'(N - 1));
 
       always_ff @(posedge clk) begin
           if (rst)         count <= '0;               // sync reset to 0
           else if (en) begin
               if (wrap)    count <= '0;               // reached N-1 → snap to 0
               else         count <= count + 1'b1;     // otherwise advance
           end
       end
 
       // tick is high for exactly the cycle the counter holds N-1 (period == N).
       assign tick = en & wrap;
   endmodule

The compare is on N-1 and tick marks the terminal cycle, so between two ticks there are exactly N cycles. The clocked, self-checking testbench proves precisely that: it generates a clock, watches the counter for several full periods, and asserts (1) the count sequence is 0, 1, …, N-1, 0, … with no value ever reaching N, and (2) the number of cycles between two ticks is exactly N — not N+1. It runs the check for several N, including a non-power-of-two and N=1.

mod_n_tb.sv — clocked self-check: sequence is 0..N-1, period is EXACTLY N
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_tb;
       localparam int N     = 5;                        // non-power-of-two on purpose
       localparam int WIDTH = (N > 1) ? $clog2(N) : 1;
       logic clk = 0, rst, en;
       logic [WIDTH-1:0] count;
       logic tick;
       int   errors = 0;
 
       mod_n #(.N(N)) dut (.clk(clk), .rst(rst), .en(en), .count(count), .tick(tick));
 
       always #5 clk = ~clk;                            // 100 MHz clock
 
       // Reference model: the count must equal (cycles mod N) exactly.
       int expect_count = 0;
       int last_tick_cycle = -1, cyc = 0;
 
       initial begin
           rst = 1; en = 0; @(posedge clk); #1;
           rst = 0; en = 1;                             // release reset, start counting
           for (cyc = 0; cyc < 4*N + 3; cyc++) begin
               @(posedge clk); #1;                      // sample after the edge settles
               // (1) count must never reach N, and must track cycles mod N:
               assert (count === WIDTH'(expect_count))
                   else begin $error("cyc=%0d count=%0d exp=%0d", cyc, count, expect_count); errors++; end
               assert (count < WIDTH'(N))               // must visit 0..N-1 only
                   else begin $error("count %0d reached N=%0d", count, N); errors++; end
               // (2) period between ticks must be EXACTLY N:
               if (tick) begin
                   if (last_tick_cycle >= 0)
                       assert (cyc - last_tick_cycle == N)
                           else begin $error("period=%0d exp=%0d (off-by-one?)", cyc-last_tick_cycle, N); errors++; end
                   last_tick_cycle = cyc;
               end
               expect_count = (expect_count + 1) % N;   // advance reference
           end
           if (errors == 0) $display("PASS: mod_n N=%0d visits 0..N-1, period == N", N);
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

The Verilog form is identical in intent — reg/wire typing, always @(posedge clk), and the constant terminal value N-1. The self-check uses if (…) $display("FAIL") instead of assert, and measures the tick-to-tick period the same way.

mod_n.v — static mod-N counter in Verilog (parameter N, wrap on N-1)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n #(
       parameter N     = 10,
       parameter WIDTH = 4                              // must satisfy 2**WIDTH >= N
   )(
       input  wire             clk,
       input  wire             rst,                     // synchronous, active-high
       input  wire             en,
       output reg  [WIDTH-1:0] count,
       output wire             tick
   );
       // Equality compare against the constant terminal value N-1 (not N).
       wire wrap = (count == (N - 1));
 
       always @(posedge clk) begin
           if (rst)          count <= {WIDTH{1'b0}};    // sync reset
           else if (en) begin
               if (wrap)     count <= {WIDTH{1'b0}};    // reached N-1 → wrap to 0
               else          count <= count + 1'b1;     // advance
           end
       end
 
       assign tick = en & wrap;                         // 1-cycle strobe, period N
   endmodule
mod_n_tb.v — clocked self-check; measures tick-to-tick period == N
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_tb;
       parameter N     = 5;
       parameter WIDTH = 3;
       reg clk, rst, en;
       wire [WIDTH-1:0] count;
       wire tick;
       integer errors, cyc, expect_count, last_tick_cycle;
 
       mod_n #(.N(N), .WIDTH(WIDTH)) dut
           (.clk(clk), .rst(rst), .en(en), .count(count), .tick(tick));
 
       initial clk = 0;
       always #5 clk = ~clk;                            // clock generator
 
       initial begin
           errors = 0; expect_count = 0; last_tick_cycle = -1;
           rst = 1; en = 0; @(posedge clk); #1;
           rst = 0; en = 1;
           for (cyc = 0; cyc < 4*N + 3; cyc = cyc + 1) begin
               @(posedge clk); #1;
               // count must track cycles mod N and never reach N:
               if (count !== expect_count[WIDTH-1:0]) begin
                   $display("FAIL: cyc=%0d count=%0d exp=%0d", cyc, count, expect_count);
                   errors = errors + 1;
               end
               if (count >= N) begin
                   $display("FAIL: count %0d reached N=%0d", count, N);
                   errors = errors + 1;
               end
               // period between ticks must be EXACTLY N:
               if (tick) begin
                   if (last_tick_cycle >= 0 && (cyc - last_tick_cycle) !== N) begin
                       $display("FAIL: period=%0d exp=%0d (off-by-one?)", cyc-last_tick_cycle, N);
                       errors = errors + 1;
                   end
                   last_tick_cycle = cyc;
               end
               expect_count = (expect_count + 1) % N;
           end
           if (errors == 0) $display("PASS: mod_n visits 0..N-1, period == N");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

In VHDL the modulus is a generic, the register update is inside a clocked process guarded by rising_edge(clk), and arithmetic goes through numeric_std (unsigned). The terminal value N-1 is again a compile-time constant, and to_unsigned(N-1, WIDTH) builds the comparison constant at the right width.

mod_n.vhd — static mod-N counter in VHDL (generic N, numeric_std)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   entity mod_n is
       generic (
           N     : positive := 10;                          -- the modulus (fixed divisor)
           WIDTH : positive := 4                            -- must hold N-1
       );
       port (
           clk   : in  std_logic;
           rst   : in  std_logic;                           -- synchronous, active-high
           en    : in  std_logic;
           count : out std_logic_vector(WIDTH-1 downto 0);
           tick  : out std_logic
       );
   end entity;
 
   architecture rtl of mod_n is
       signal cnt  : unsigned(WIDTH-1 downto 0) := (others => '0');
       signal wrap : std_logic;
   begin
       -- Equality compare against the constant terminal value N-1 (not N).
       wrap <= '1' when cnt = to_unsigned(N-1, WIDTH) else '0';
 
       process (clk)
       begin
           if rising_edge(clk) then
               if rst = '1' then
                   cnt <= (others => '0');                  -- sync reset
               elsif en = '1' then
                   if wrap = '1' then
                       cnt <= (others => '0');              -- reached N-1 → wrap to 0
                   else
                       cnt <= cnt + 1;                      -- advance
                   end if;
               end if;
           end if;
       end process;
 
       count <= std_logic_vector(cnt);
       tick  <= en and wrap;                                -- 1-cycle strobe, period N
   end architecture;
mod_n_tb.vhd — clocked self-check; assert period = N (severity error)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   entity mod_n_tb is
   end entity;
 
   architecture sim of mod_n_tb is
       constant N     : positive := 5;                      -- non-power-of-two
       constant WIDTH : positive := 3;
       signal clk   : std_logic := '0';
       signal rst   : std_logic := '1';
       signal en    : std_logic := '0';
       signal count : std_logic_vector(WIDTH-1 downto 0);
       signal tick  : std_logic;
   begin
       dut : entity work.mod_n
           generic map (N => N, WIDTH => WIDTH)
           port map (clk => clk, rst => rst, en => en, count => count, tick => tick);
 
       clk <= not clk after 5 ns;                           -- clock generator
 
       stim : process
           variable exp_count       : integer := 0;
           variable cyc             : integer := 0;
           variable last_tick_cycle : integer := -1;
       begin
           wait until rising_edge(clk); wait for 1 ns;
           rst <= '0'; en <= '1';                           -- release reset, start
           for c in 0 to 4*N + 2 loop
               wait until rising_edge(clk); wait for 1 ns;
               -- count must track cycles mod N and stay below N:
               assert to_integer(unsigned(count)) = exp_count
                   report "count mismatch (mod-N sequence wrong)" severity error;
               assert to_integer(unsigned(count)) < N
                   report "count reached N — should visit 0..N-1 only" severity error;
               -- period between ticks must be EXACTLY N:
               if tick = '1' then
                   if last_tick_cycle >= 0 then
                       assert (cyc - last_tick_cycle) = N
                           report "period /= N (off-by-one in terminal compare?)" severity error;
                   end if;
                   last_tick_cycle := cyc;
               end if;
               exp_count := (exp_count + 1) mod N;
               cyc := cyc + 1;
           end loop;
           report "mod_n self-check complete (period = N, visits 0..N-1)" severity note;
           wait;
       end process;
   end architecture;

4b. Programmable mod-N counter — N as an input, with a shrink guard

Now make the modulus a runtime value: mod_n is an input (in a real design, the output of a software-written configuration register). The structure is the same compare-and-reset, but two things change. First, the terminal value is now mod_n - 1, computed live. Second — the load-bearing detail — the compare must be >=, not ==: if software writes a smaller mod_n while the counter is already above the new terminal value, an exact-equality compare would never match and the counter would run all the way to overflow before wrapping. The >= guard makes a count that is already at or beyond the new terminal value wrap on the very next edge.

mod_n_prog.sv — programmable mod-N (runtime N, >= shrink guard)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_prog #(
       parameter int WIDTH = 16                         // holds the largest legal mod_n
   )(
       input  logic             clk,
       input  logic             rst,                    // synchronous, active-high
       input  logic             en,
       input  logic [WIDTH-1:0] mod_n,                  // RUNTIME modulus (>= 1)
       output logic [WIDTH-1:0] count,
       output logic             tick
   );
       // >= (not ==): if mod_n was just SHRUNK below the current count, an exact
       // equality would never fire and the counter would run to overflow. The
       // magnitude compare guarantees a count at/above the NEW terminal wraps next
       // edge. Terminal value is mod_n - 1 (never mod_n).
       wire wrap = en & (count >= (mod_n - 1'b1));
 
       always_ff @(posedge clk) begin
           if (rst)         count <= '0;
           else if (en) begin
               if (wrap)    count <= '0;                // at/past terminal → wrap to 0
               else         count <= count + 1'b1;      // advance
           end
       end
 
       assign tick = wrap;                              // strobe once per mod_n cycles
   endmodule

The self-checking testbench for the programmable version does everything 4a's did — sequence and period for a couple of static values of mod_n — and then adds the distinguishing test: it changes mod_n mid-count (both larger and, critically, smaller than the current count) and checks the counter re-wraps cleanly at the new modulus within one period, never getting stuck running to overflow.

mod_n_prog_tb.sv — self-check incl. mid-count N change (shrink → clean re-wrap)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_prog_tb;
       localparam int WIDTH = 16;
       logic clk = 0, rst, en;
       logic [WIDTH-1:0] mod_n, count;
       logic tick;
       int   errors = 0, gap = 0, last = -1, cyc = 0;
 
       mod_n_prog #(.WIDTH(WIDTH)) dut
           (.clk(clk), .rst(rst), .en(en), .mod_n(mod_n), .count(count), .tick(tick));
 
       always #5 clk = ~clk;
 
       // Check the period between ticks equals the CURRENT mod_n (after settling).
       task check_period(input int m);
           last = -1; cyc = 0;
           repeat (3*m + 2) begin
               @(posedge clk); #1;
               assert (count < mod_n)
                   else begin $error("count %0d >= mod_n %0d", count, mod_n); errors++; end
               if (tick) begin
                   if (last >= 0) begin
                       gap = cyc - last;
                       assert (gap == m)
                           else begin $error("period=%0d exp=%0d", gap, m); errors++; end
                   end
                   last = cyc;
               end
               cyc++;
           end
       endtask
 
       initial begin
           rst = 1; en = 0; mod_n = 8; @(posedge clk); #1;
           rst = 0; en = 1;
           check_period(8);                             // /8 divider
           mod_n = 3;                                   // SHRINK below current count
           check_period(3);                             // must re-wrap cleanly at 3
           mod_n = 5;                                   // grow again
           check_period(5);
           if (errors == 0) $display("PASS: programmable mod-N re-wraps at each new modulus");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

The Verilog programmable counter is the same >= guard with reg/wire typing; the testbench measures the tick-to-tick period and re-runs it after writing a smaller mod_n.

mod_n_prog.v — programmable mod-N in Verilog (>= shrink guard)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_prog #(
       parameter WIDTH = 16
   )(
       input  wire             clk,
       input  wire             rst,
       input  wire             en,
       input  wire [WIDTH-1:0] mod_n,                   // runtime modulus (>= 1)
       output reg  [WIDTH-1:0] count,
       output wire             tick
   );
       // >= guard: a mod_n shrunk below the current count still wraps next edge.
       wire wrap = en & (count >= (mod_n - 1'b1));
 
       always @(posedge clk) begin
           if (rst)          count <= {WIDTH{1'b0}};
           else if (en) begin
               if (wrap)     count <= {WIDTH{1'b0}};    // at/past terminal → wrap
               else          count <= count + 1'b1;
           end
       end
 
       assign tick = wrap;
   endmodule
mod_n_prog_tb.v — self-check; change mod_n mid-count, re-verify period
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_prog_tb;
       parameter WIDTH = 16;
       reg clk, rst, en;
       reg  [WIDTH-1:0] mod_n;
       wire [WIDTH-1:0] count;
       wire tick;
       integer errors, cyc, last, gap, i;
 
       mod_n_prog #(.WIDTH(WIDTH)) dut
           (.clk(clk), .rst(rst), .en(en), .mod_n(mod_n), .count(count), .tick(tick));
 
       initial clk = 0;
       always #5 clk = ~clk;
 
       // measure tick-to-tick period over ~3 periods and compare to m
       task check_period; input integer m; begin
           last = -1; cyc = 0;
           for (i = 0; i < 3*m + 2; i = i + 1) begin
               @(posedge clk); #1;
               if (count >= mod_n) begin
                   $display("FAIL: count=%0d >= mod_n=%0d", count, mod_n);
                   errors = errors + 1;
               end
               if (tick) begin
                   if (last >= 0) begin
                       gap = cyc - last;
                       if (gap !== m) begin
                           $display("FAIL: period=%0d exp=%0d", gap, m);
                           errors = errors + 1;
                       end
                   end
                   last = cyc;
               end
               cyc = cyc + 1;
           end
       end endtask
 
       initial begin
           errors = 0;
           rst = 1; en = 0; mod_n = 8; @(posedge clk); #1;
           rst = 0; en = 1;
           check_period(8);                             // /8
           mod_n = 3;                                   // SHRINK below current count
           check_period(3);                             // must re-wrap cleanly
           mod_n = 5;
           check_period(5);
           if (errors == 0) $display("PASS: programmable mod-N re-wraps at each modulus");
           else             $display("FAIL: %0d mismatches", errors);
           $finish;
       end
   endmodule

In VHDL the runtime modulus is a std_logic_vector port converted to unsigned; the guard is the same >= on the terminal value mod_n - 1. The testbench drives a smaller mod_n mid-run and asserts the period follows.

mod_n_prog.vhd — programmable mod-N in VHDL (>= guard, numeric_std)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   entity mod_n_prog is
       generic ( WIDTH : positive := 16 );
       port (
           clk   : in  std_logic;
           rst   : in  std_logic;                           -- synchronous, active-high
           en    : in  std_logic;
           mod_n : in  std_logic_vector(WIDTH-1 downto 0);  -- runtime modulus (>= 1)
           count : out std_logic_vector(WIDTH-1 downto 0);
           tick  : out std_logic
       );
   end entity;
 
   architecture rtl of mod_n_prog is
       signal cnt   : unsigned(WIDTH-1 downto 0) := (others => '0');
       signal wrap  : std_logic;
       signal m_u   : unsigned(WIDTH-1 downto 0);
   begin
       m_u  <= unsigned(mod_n);
       -- >= (not =): a mod_n shrunk below the current count still wraps next edge.
       wrap <= '1' when (en = '1' and cnt >= (m_u - 1)) else '0';
 
       process (clk)
       begin
           if rising_edge(clk) then
               if rst = '1' then
                   cnt <= (others => '0');
               elsif en = '1' then
                   if wrap = '1' then
                       cnt <= (others => '0');              -- at/past terminal → wrap
                   else
                       cnt <= cnt + 1;                      -- advance
                   end if;
               end if;
           end if;
       end process;
 
       count <= std_logic_vector(cnt);
       tick  <= wrap;
   end architecture;
mod_n_prog_tb.vhd — self-check; change mod_n mid-count, assert new period
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   entity mod_n_prog_tb is
   end entity;
 
   architecture sim of mod_n_prog_tb is
       constant WIDTH : positive := 16;
       signal clk   : std_logic := '0';
       signal rst   : std_logic := '1';
       signal en    : std_logic := '0';
       signal mod_n : std_logic_vector(WIDTH-1 downto 0) := std_logic_vector(to_unsigned(8, WIDTH));
       signal count : std_logic_vector(WIDTH-1 downto 0);
       signal tick  : std_logic;
   begin
       dut : entity work.mod_n_prog
           generic map (WIDTH => WIDTH)
           port map (clk => clk, rst => rst, en => en, mod_n => mod_n, count => count, tick => tick);
 
       clk <= not clk after 5 ns;
 
       stim : process
           variable last : integer := -1;
           variable cyc  : integer := 0;
 
           procedure check_period(m : integer) is
               variable gap : integer;
           begin
               last := -1; cyc := 0;
               for i in 0 to 3*m + 1 loop
                   wait until rising_edge(clk); wait for 1 ns;
                   assert to_integer(unsigned(count)) < m
                       report "count reached/passed modulus" severity error;
                   if tick = '1' then
                       if last >= 0 then
                           gap := cyc - last;
                           assert gap = m
                               report "programmable period /= modulus" severity error;
                       end if;
                       last := cyc;
                   end if;
                   cyc := cyc + 1;
               end loop;
           end procedure;
       begin
           wait until rising_edge(clk); wait for 1 ns;
           rst <= '0'; en <= '1';
           check_period(8);                                 -- /8
           mod_n <= std_logic_vector(to_unsigned(3, WIDTH)); -- SHRINK below current count
           check_period(3);                                 -- must re-wrap cleanly
           mod_n <= std_logic_vector(to_unsigned(5, WIDTH));
           check_period(5);
           report "programmable mod_n self-check complete" severity note;
           wait;
       end process;
   end architecture;

4c. The off-by-one bug — == N (buggy) vs == N-1 (fixed), in all three HDLs

Pattern (c) is the signature failure, shown here as buggy vs fixed RTL in each language and dramatized in §7. The bug is the same everywhere: the terminal-count compare is written against N instead of N-1. It looks right — "wrap when the count reaches N" reads naturally — but it makes the counter visit 0 … N (that is N+1 states), so the period becomes N+1, and a divider built for /N silently divides by /(N+1). It passes a casual eyeball; it fails on a scope at speed.

mod_n_bug.sv — BUGGY (== N, period N+1) vs FIXED (== N-1, period N)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // BUGGY: compare against N. The counter reaches N before wrapping, so it visits
   //        0..N (N+1 states) and the period is N+1 — a /N divider divides by N+1.
   module mod_n_bad #(
       parameter int N = 10,
       localparam int WIDTH = $clog2(N + 1)              // note: needs a bit MORE to hold N
   )(
       input  logic clk, rst, en,
       output logic tick
   );
       logic [WIDTH-1:0] count;
       always_ff @(posedge clk) begin
           if (rst)                     count <= '0;
           else if (en) begin
               if (count == WIDTH'(N))  count <= '0;     // BUG: == N → wraps one late
               else                     count <= count + 1'b1;
           end
       end
       assign tick = en & (count == WIDTH'(N));          // fires every N+1 cycles
   endmodule
 
   // FIXED: compare against the TERMINAL value N-1. The counter visits 0..N-1
   //        (N states), the period is exactly N, and WIDTH need only hold N-1.
   module mod_n_good #(
       parameter int N = 10,
       localparam int WIDTH = (N > 1) ? $clog2(N) : 1
   )(
       input  logic clk, rst, en,
       output logic tick
   );
       logic [WIDTH-1:0] count;
       always_ff @(posedge clk) begin
           if (rst)                       count <= '0;
           else if (en) begin
               if (count == WIDTH'(N-1))  count <= '0;   // FIX: == N-1 → period N
               else                       count <= count + 1'b1;
           end
       end
       assign tick = en & (count == WIDTH'(N-1));        // fires every N cycles
   endmodule
mod_n_bug_tb.sv — measures the period; N-1 gives N, == N gives N+1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_bug_tb;
       localparam int N = 4;                             // expect period 4
       logic clk = 0, rst, en, tick;
       int   errors = 0, cyc = 0, last = -1, gap;
 
       // Bind the GOOD DUT to PASS; bind mod_n_bad to watch the period come out N+1.
       mod_n_good #(.N(N)) dut (.clk(clk), .rst(rst), .en(en), .tick(tick));
 
       always #5 clk = ~clk;
 
       initial begin
           rst = 1; en = 0; @(posedge clk); #1;
           rst = 0; en = 1;
           for (cyc = 0; cyc < 4*N + 4; cyc++) begin
               @(posedge clk); #1;
               if (tick) begin
                   if (last >= 0) begin
                       gap = cyc - last;
                       // The whole bug: a correct /N counter has period N, not N+1.
                       assert (gap == N)
                           else begin $error("period=%0d exp=%0d — off-by-one!", gap, N); errors++; end
                   end
                   last = cyc;
               end
           end
           if (errors == 0) $display("PASS: period == N (terminal compare is N-1)");
           else             $display("FAIL: %0d — divider is /(N+1), not /N", errors);
           $finish;
       end
   endmodule

The Verilog buggy/fixed pair is the same story with reg outputs. Measuring the tick-to-tick period is what exposes it: the buggy version reads N+1 where the spec says N.

mod_n_bug.v — BUGGY (== N) vs FIXED (== N-1) in Verilog
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   // BUGGY: wrap on == N → visits 0..N (N+1 states) → period N+1 (a /(N+1) divider).
   module mod_n_bad #(
       parameter N = 10,
       parameter WIDTH = 4
   )(
       input  wire clk, rst, en,
       output wire tick
   );
       reg [WIDTH-1:0] count;
       always @(posedge clk) begin
           if (rst)                 count <= {WIDTH{1'b0}};
           else if (en) begin
               if (count == N)      count <= {WIDTH{1'b0}};   // BUG: == N
               else                 count <= count + 1'b1;
           end
       end
       assign tick = en & (count == N);                       // every N+1 cycles
   endmodule
 
   // FIXED: wrap on == N-1 (the terminal value) → visits 0..N-1 → period N.
   module mod_n_good #(
       parameter N = 10,
       parameter WIDTH = 4
   )(
       input  wire clk, rst, en,
       output wire tick
   );
       reg [WIDTH-1:0] count;
       always @(posedge clk) begin
           if (rst)                 count <= {WIDTH{1'b0}};
           else if (en) begin
               if (count == N - 1)  count <= {WIDTH{1'b0}};   // FIX: == N-1
               else                 count <= count + 1'b1;
           end
       end
       assign tick = en & (count == N - 1);                   // every N cycles
   endmodule
mod_n_bug_tb.v — self-checking; period == N passes, period N+1 fails
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   module mod_n_bug_tb;
       parameter N = 4;
       parameter WIDTH = 4;
       reg clk, rst, en;
       wire tick;
       integer errors, cyc, last, gap;
 
       // Bind to mod_n_good to PASS; to mod_n_bad to see period = N+1.
       mod_n_good #(.N(N), .WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .en(en), .tick(tick));
 
       initial clk = 0;
       always #5 clk = ~clk;
 
       initial begin
           errors = 0; last = -1;
           rst = 1; en = 0; @(posedge clk); #1;
           rst = 0; en = 1;
           for (cyc = 0; cyc < 4*N + 4; cyc = cyc + 1) begin
               @(posedge clk); #1;
               if (tick) begin
                   if (last >= 0) begin
                       gap = cyc - last;
                       if (gap !== N) begin
                           $display("FAIL: period=%0d exp=%0d — off-by-one (== N vs == N-1)", gap, N);
                           errors = errors + 1;
                       end
                   end
                   last = cyc;
               end
           end
           if (errors == 0) $display("PASS: period == N (terminal compare is N-1)");
           else             $display("FAIL: %0d — divider is /(N+1), not /N", errors);
           $finish;
       end
   endmodule

In VHDL the identical off-by-one appears when the compare uses to_unsigned(N, WIDTH) instead of to_unsigned(N-1, WIDTH). The buggy compare must also widen the register to hold N, a telltale that something is off.

mod_n_bug.vhd — BUGGY (= N) vs FIXED (= N-1) in VHDL
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   -- BUGGY: compare against N. Counter reaches N → visits 0..N (N+1 states) →
   --        period is N+1, so a /N divider actually divides by N+1.
   entity mod_n_bad is
       generic ( N : positive := 10; WIDTH : positive := 4 );
       port ( clk, rst, en : in std_logic; tick : out std_logic );
   end entity;
   architecture rtl of mod_n_bad is
       signal cnt : unsigned(WIDTH-1 downto 0) := (others => '0');
   begin
       process (clk)
       begin
           if rising_edge(clk) then
               if rst = '1' then
                   cnt <= (others => '0');
               elsif en = '1' then
                   if cnt = to_unsigned(N, WIDTH) then      -- BUG: = N
                       cnt <= (others => '0');
                   else
                       cnt <= cnt + 1;
                   end if;
               end if;
           end if;
       end process;
       tick <= '1' when (en = '1' and cnt = to_unsigned(N, WIDTH)) else '0';
   end architecture;
 
   -- FIXED: compare against the terminal value N-1 → visits 0..N-1 → period N.
   entity mod_n_good is
       generic ( N : positive := 10; WIDTH : positive := 4 );
       port ( clk, rst, en : in std_logic; tick : out std_logic );
   end entity;
   architecture rtl of mod_n_good is
       signal cnt : unsigned(WIDTH-1 downto 0) := (others => '0');
   begin
       process (clk)
       begin
           if rising_edge(clk) then
               if rst = '1' then
                   cnt <= (others => '0');
               elsif en = '1' then
                   if cnt = to_unsigned(N-1, WIDTH) then    -- FIX: = N-1
                       cnt <= (others => '0');
                   else
                       cnt <= cnt + 1;
                   end if;
               end if;
           end if;
       end process;
       tick <= '1' when (en = '1' and cnt = to_unsigned(N-1, WIDTH)) else '0';
   end architecture;
mod_n_bug_tb.vhd — self-checking; assert period = N (severity error)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;
 
   entity mod_n_bug_tb is
   end entity;
 
   architecture sim of mod_n_bug_tb is
       constant N     : positive := 4;
       constant WIDTH : positive := 4;
       signal clk  : std_logic := '0';
       signal rst  : std_logic := '1';
       signal en   : std_logic := '0';
       signal tick : std_logic;
   begin
       -- Bind to mod_n_good to PASS; to mod_n_bad to observe period = N+1.
       dut : entity work.mod_n_good
           generic map (N => N, WIDTH => WIDTH)
           port map (clk => clk, rst => rst, en => en, tick => tick);
 
       clk <= not clk after 5 ns;
 
       stim : process
           variable last : integer := -1;
           variable cyc  : integer := 0;
           variable gap  : integer;
       begin
           wait until rising_edge(clk); wait for 1 ns;
           rst <= '0'; en <= '1';
           for c in 0 to 4*N + 3 loop
               wait until rising_edge(clk); wait for 1 ns;
               if tick = '1' then
                   if last >= 0 then
                       gap := cyc - last;
                       assert gap = N
                           report "period /= N — off-by-one (= N instead of = N-1)" severity error;
                   end if;
                   last := cyc;
               end if;
               cyc := cyc + 1;
           end loop;
           report "mod_n_bug self-check complete (period should be N)" severity note;
           wait;
       end process;
   end architecture;

Across all three languages the lesson is one sentence: the terminal value of a mod-N counter is N-1, so the wrap compare is count == N-1 — comparing against N makes the period N+1 and quietly turns a /N divider into a /(N+1) one.

5. Verification Strategy

A mod-N counter is sequential, so its correctness is a behaviour over time, not a truth table — which means the testbench must be clocked: generate a clock, release reset, and sample the counter after each edge with the self-checking discipline from 3.1. Two invariants capture a correct mod-N counter, and everything below makes them checkable, mapping directly onto the testbenches in §4:

(1) The counter visits exactly the states 0, 1, …, N-1 and never reaches N; (2) the period between two wraps (two ticks) is exactly N cycles — not N+1.

  • Sequence check against a reference (self-checking). Keep a software reference expect_count that advances (expect_count + 1) mod N every cycle, and after each clock edge assert count === expect_count and count < N. This proves the counter visits 0 … N-1 and never overshoots to N — the direct check that the terminal value is N-1. The three §4a testbenches do exactly this: SV assert (count === expect_count), Verilog if (count !== …) $display("FAIL"), VHDL assert … severity error.
  • Period-is-exactly-N check (the off-by-one guard). Record the cycle number of each tick; when the next tick fires, assert the gap is == N. This is the single most important check on the page because it catches the signature bug: a == N compare produces a gap of N+1, and only a period measurement — not an eyeball of the waveform — reliably exposes it. This is what turns "looks like it counts" into "provably divides by N."
  • Sweep several N, including a non-power-of-two and N=1. A mod-N counter that passes at N=8 can still be wrong at N=5 (where the N-1 terminal is not all-ones and a sloppy wrap shows up) or at N=1 (the degenerate case: terminal value is 0, so the counter should wrap every cycle and tick every cycle — a period of 1). Re-run the sequence-and-period checks for N = 2, 3, 5, 8 and the N=1 corner; the non-power-of-two is where the terminal-value discipline earns its keep.
  • Programmable-N: change N mid-count and check clean re-wrap. For the programmable variant, drive a new mod_n while the counter is running — both larger and, crucially, smaller than the current count — and check the counter re-wraps at the new modulus within one period. The smaller case is the one that exposes a missing >= guard: with a pure == compare a shrunk mod_n is never matched and the counter runs to overflow (a period of 2^WIDTH instead of the new N). The §4b testbenches do this with a check_period task called after each write.
  • Assert the divided strobe period. The tick output is the divided signal, so its period is the divide ratio. State it as an invariant: "tick is high for exactly one cycle every N cycles," and verify it with the same tick-to-tick gap measurement. This is the property a downstream clock divider or baud generator depends on — if the strobe period is N+1 instead of N, the derived frequency is wrong even though the RTL "runs."
  • Expected waveform. A correct run shows count ramping 0 → 1 → … → N-1 and snapping to 0 on the next edge, with tick pulsing high for exactly the cycle count == N-1, once every N cycles. The visual signature of the off-by-one bug is a ramp that goes one step too far (to N) before snapping — and a tick-to-tick spacing of N+1 clocks instead of N. Recognizing "the ramp reached N" on a waveform is how you spot the bug by eye once you know to look.

6. Common Mistakes

The terminal-compare off-by-one — count == N instead of count == N-1. The signature mod-N bug and the reason this pattern gets its own page. Because the states are 0 … N-1, the largest value the counter holds is N-1, so that is what the wrap compare must test. Writing count == N lets the counter reach N before wrapping — it visits N+1 states, the period becomes N+1, and a /N clock divider actually divides by N+1. It simulates "fine" (it still counts and wraps), passes a casual eyeball, and produces a subtly wrong frequency that only shows up on a scope or as protocol errors at speed (a UART running at a slightly wrong baud, a tick that is 0.1% slow). Always compare against the terminal value N-1; measure the period in verification to catch it.

A programmable N shrinking below the current count with a pure == compare. When mod_n is a runtime input and software writes a smaller value while the counter is already above the new mod_n - 1, an exact-equality compare will never match — the counter has already passed the value it waits for, so it runs all the way to 2^WIDTH before wrapping (a single enormous period, effectively a stuck counter). The fix is a magnitude guard: wrap when count >= mod_n - 1, so a count at or beyond the new terminal wraps on the next edge. This is the one place a >= (not ==) is required, and it exists only because a live N can move the target underneath the count.

The N=0 and N=1 edge cases. N=0 is meaningless (there is no such thing as modulo-zero) and, worse, mod_n - 1 underflows to all-ones for an unsigned modulus — so guard against mod_n == 0 in a programmable design (treat it as illegal, or clamp to 1) rather than letting the terminal value wrap to the maximum. N=1 is legal but degenerate: the terminal value is 0, so the counter should wrap every cycle (tick high every cycle, period 1) — verify this corner explicitly, because a count == N-1 written assuming N >= 2 can misbehave when N-1 is zero.

Using a magnitude compare where equality suffices — wasted area. For a static mod-N (fixed N), the terminal value is a constant and you only need to detect one exact value, so an equality compare (count == N-1) is correct and cheap — a few gates. Reaching for a magnitude comparator (count >= N-1) there is pure waste: it synthesizes a full carry-chain compare to solve a problem equality already solves. Reserve >= for the programmable case, where a shrinking runtime N genuinely needs it; do not pay for magnitude compare when the modulus is fixed.

7. DebugLab

The baud rate that was almost right — a /N divider that divided by N+1

The engineering lesson: the terminal value of a mod-N counter is N-1, not N — the period is the count of states visited, and an off-by-one there is a frequency error, not a functional one. That is what makes it so dangerous: the RTL still "works" (it counts, it wraps, it ticks), so it sails through a functional eyeball and even a same-vs-same loopback; the damage is a divide ratio that is exactly one too large, which surfaces only against a correct reference, at speed, or on long transfers. Two habits make it impossible, and they are identical across SystemVerilog, Verilog, and VHDL: always trigger the wrap on count == N-1 (the largest of the N states 0 … N-1), and verify by measuring the period — assert the tick-to-tick gap is exactly N, because a divide ratio is a number of cycles, and the only way to prove it is to count them.

8. Interview Q&A

9. Exercises

Design and reasoning exercises — no syntax drills. Each rehearses the "terminal value is N-1, period is N" habit.

Exercise 1 — Design a /10 counter, then predict its width

You need a decade (mod-10) counter that emits one tick per 10 clocks. (a) What value must the wrap compare test for, and what sequence of counts does the register hold? (b) What is the minimum register WIDTH? (c) A colleague builds it comparing count == 10 "because it's a /10 counter" — what is the actual period their divider produces, and how many states does the counter visit? (d) State the one-line self-checking assertion that would have caught their bug, and explain why an eyeball of the waveform would not.

Exercise 2 — Baud divisor from a 50 MHz clock

A UART on a 50 MHz clock must generate 9600 baud using a 16× oversampling tick (the sample strobe runs at 16 × baud). (a) Compute the ideal divisor N = f_clk / (16 × 9600) — is it an integer? (b) Choose the nearest integer N and state the actual oversample-tick frequency and the resulting baud-rate error in percent. (c) Now the same UART must also support 115200 baud — compute that N. (d) Given both rates are needed at runtime, argue why the modulus should be a programmable input rather than a parameter, and name the one extra piece of logic the programmable version needs that the fixed one does not.

Exercise 3 — The shrink hazard on paper

A programmable mod-N counter uses an exact-equality wrap: wrap = (count == mod_n - 1). It is currently running with mod_n = 12 and the count happens to be 9. Software now writes mod_n = 5. (a) Trace what the counter does on the next several edges — does it ever wrap at 5? (b) What is the actual period until the next wrap, in terms of the register width? (c) Rewrite the wrap condition to fix it, and explain in one line why your fix makes the counter recover within one clock. (d) Why does the static (parameter-N) version never suffer this hazard, so it can safely keep the cheaper == compare?

Exercise 4 — The N=1 corner and the N=0 trap

(a) For a mod-1 counter, what is the terminal value N-1, how often should tick fire, and what count sequence does the register hold? (b) Explain why a count == N-1 compare written assuming N ≥ 2 still behaves correctly for N=1 — or, if it does not in your design, what to add. (c) For a programmable counter, explain what mod_n - 1 evaluates to when software writes mod_n = 0 on an unsigned modulus, why that is dangerous, and one way to guard against it (reject, or clamp to 1).

Continue in RTL Design Patterns (sibling and forward links unlock as they ship):

  • Up/Down Counters — Chapter 3.1; the plain count register + enable + reset this pattern extends. A mod-N counter is a counter with a compare-and-reset bolted on — start here if the base counter is not yet familiar.
  • Gray-Code Counters — Chapter 3.4; the sibling variant where the sequence changes (one-bit-per-step, for safe CDC) rather than the wrap point.
  • Clock Dividers & Timers — Chapter 3.3; the direct forward application — a clock divider is a mod-N counter whose tick is the divided output, extended to duty cycle and timers.
  • Pulse & Strobe Generators — Chapter 3.5; the mod-N tick generalized into one-shot, periodic, and delayed strobe generation.

Builds on (in-track):

  • Comparators — Chapter 1.5; the equality (and magnitude) compare that fires the wrap — count == N-1 for static N, count >= mod_n - 1 for the programmable shrink guard.
  • Multiplexers — Chapter 1.1; the 2:1 mux on the register's next-value input that steers 0 (wrap) versus count + 1 (advance).
  • The Register Pattern (D-FF) — Chapter 2.1; the clocked count register at the heart of every counter.
  • Register with Enable & Load — Chapter 2.2; the en gating that freezes the count, and the load semantics a programmable modulus register uses.
  • Synchronous Reset · Asynchronous Reset — Chapter 2; the reset-to-zero discipline the counter inherits.
  • Shift Registers — Chapter 2.3; a related sequential primitive whose length, like a mod-N period, is a deliberate count.

Method / overview:

  • The RTL Design Mindset — Chapter 0.2; the Interface → State → Datapath → Control → Verification lenses this page applies (a mod-N counter is state + a tiny bit of control).
  • What Is an RTL Design Pattern? — Chapter 0.1; why compare-and-reset earns the name "pattern" — a recurring need, a reusable form, a synthesis reality, and a signature off-by-one failure.

Verilog v1 prerequisites (the grammar this pattern transcribes into):

11. Summary

  • A mod-N counter is a counter plus a compare-and-reset. The register and +1 adder are the plain counter (3.1); mod-N adds only an equality comparator (count == N-1, a 1.5 comparator) and a 2:1 mux that steers 0 back into the register when the comparator fires. Advance, advance, advance, snap to zero — with a period of N cycles, for any N.
  • The terminal value is N-1, not N. The counter visits the N states 0 … N-1; the largest is N-1, so that is what the wrap compares against. The period (divide ratio) is N; the terminal value is N-1 — two different numbers, and confusing them is the signature bug.
  • Static vs programmable modulus. A parameter/generic N folds N-1 into a compile-time constant (fixed divisor, cheap equality compare); an input N compares against a live mod_n - 1 (programmable divisor). The programmable version needs a >= shrink guard so a modulus rewritten smaller than the current count still wraps on the next edge instead of running to overflow.
  • The off-by-one is a frequency error, not a functional one. Comparing count == N instead of count == N-1 makes the period N+1, so a /N divider divides by /(N+1). The RTL still counts and ticks — it passes a casual eyeball and a same-vs-same loopback — but the derived clock/baud is subtly wrong and fails at speed (the §7 DebugLab). Watch the N=1 corner (wrap every cycle) and the N=0 trap (mod_n - 1 underflows).
  • Verify by measuring the period. A clocked, self-checking testbench must assert both that the counter visits 0 … N-1 (never N) and that the tick-to-tick gap is exactly N — the divide ratio is a number of cycles, so the only proof is to count them. Sweep several N (incl. a non-power-of-two and N=1), and for programmable N change the modulus mid-count and confirm a clean re-wrap — self-checking testbenches shown here in SystemVerilog, Verilog, and VHDL.