Draft preview. Lessons are being written and reviewed.

Digital Design / Module 5: Counters and frequency dividers

27. Up, down and loadable counters

Write the most-used sequential block.

Lecture coming soon

Lab: Counter lab

The idea

A counter is a register plus an adder, and it may be the most-used sequential block in chips. It handles timers, addresses, baud rates, and any "wait N cycles" requirement.

Mod-N counters

Binary counters wrap naturally at 2^N. To count to anything else, reset at N-1.

module mod10 (
  input            clk, rst, en, up, load,
  input      [3:0] d,
  output reg [3:0] q,
  output reg       tc
);
  always_ff @(posedge clk) begin
    tc <= 1'b0;
    if (rst)       q <= 4'd0;
    else if (load) q <= d;
    else if (en) begin
      if (up) begin
        q  <= (q == 4'd9) ? 4'd0 : q + 4'd1;
        tc <= (q == 4'd9);
      end else begin
        q  <= (q == 4'd0) ? 4'd9 : q - 4'd1;
        tc <= (q == 4'd0);
      end
    end
  end
endmodule

Sizing

A count up to N-1 needs $clog2(N) bits. Mod-10 needs four flops, and six of the sixteen possible values are never used.

Terminal count

Other logic often needs a one-cycle pulse at the end of the count. If you compute it combinationally from the counter bits, it can while those bits change, and it adds delay at the start of the next path. A registered terminal count avoids both, though you must account for the extra cycle.

Timer math

To divide 100 MHz down to a 1 kHz tick, count from 0 to 99,999, and pulse once per wrap. That takes 17 bits, because 2^17 is 131,072. Use the pulse as an enable for slower logic, and keep everything on the fast clock.

Common mistake

An off-by-one in the wrap. A mod-10 counter resets at 9, not 10. Also watch the width: q + 1 on a 4-bit q at 15 wraps to 0 in a 4-bit assignment, but in a wider comparison, it becomes 16. Width rules matter here.

Key ideas

  • Add one on enable, then define wrap-around.
  • Mod-N counters reset at N-1.
  • Terminal count is often a registered pulse.

Your exercise

Write a mod-10 up/down counter with load.

Find the bug

This code has one deliberate bug. Read the symptom, then click the line you think is wrong.

What goes wrong: A mod-10 counter should show 0 to 9. This one reaches 10 and then wraps, giving 11 states.

Interview questions

coreHow many flops for a mod-10 counter?

basicFrom a 50 MHz clock you need a one-pulse-per-second tick. What does the counter count to, and how many bits does it need?

basicIn a counter written as if (rst) ... else if (load) ... else if (en) ..., what happens when rst and load are both high, and why?

coreThis is meant to be a mod-10 counter. What sequence does it actually produce, and how do you fix it?

always @(posedge clk)
  if (rst) q <= 4'd0;
  else     q <= (q == 4'd10) ? 4'd0 : q + 4'd1;

coreIn this mod-10 counter, tc is a registered terminal-count pulse. On which value of q is tc high, and why does that matter to the logic that uses it?

always @(posedge clk) begin
  tc <= 1'b0;
  if (rst) q <= 4'd0;
  else if (en) begin
    q  <= (q == 4'd9) ? 4'd0 : q + 4'd1;
    tc <= (q == 4'd9);
  end
end

corew is a 4-bit reg holding 4'hF. Which of these two comparisons is true, and why do they differ?

reg [3:0] w = 4'hF;
// A: (w + 4'd1 == 4'd0)
// B: (w + 1'b1 == 0)

coreWrite a 4-bit counter that counts up and stops at 15 instead of wrapping. What is the key difference from a normal counter?

always @(posedge clk)
  if (rst)             q <= 4'd0;
  else if (q != 4'hF)  q <= q + 4'd1;

advancedThe mod-10 up counter has a load input, and someone loads the value 12. What does the counter do next, if it compares only with 9 to wrap?

advancedA 64-bit free-running counter must run at a clock frequency where a full 64-bit increment does not meet timing. How can you build it?

Practise these 9 questions with animations