RTL Design Patterns · Chapter 3 · Counters, Timers & Pulse Generators
Ring & Johnson Counters
Sometimes you do not want a number, you want a counter whose state is already decoded. A ring counter is a shift register wired to feed its output back to its input, circulating a single 1 as a moving one-hot: N states from N flops with no decode logic, so every bit is a ready-made select line. A Johnson, or twisted-ring, counter feeds back the inverted last bit instead, giving 2N states from N flops where every transition changes exactly one bit and decodes with a bare two-input gate. Both are just a shift register plus one feedback wire, and both are ideal for sequencers, timeslots, and glitch-free clock division. This lesson also confronts their signature danger: both live inside a small legal cycle surrounded by illegal states, and a counter that powers up into one will circulate it forever unless you initialize it or make it self-correcting. Built in SystemVerilog, Verilog, and VHDL.
Intermediate12 min readRTL Design PatternsRing CounterJohnson CounterOne-HotSequencerSelf-Correcting
Chapter 3 · Section 3.4 · Counters, Timers & Pulse Generators
1. The Engineering Problem
You are building the control skeleton for a small datapath that runs a fixed four-phase cycle: fetch, compute, writeback, idle, then repeat. Each phase must assert exactly one enable — en_fetch, en_compute, en_writeback, en_idle — for exactly one clock, and the four enables must never overlap. This is not arithmetic; you never need the number of the current phase. What you need is a signal that is already the decoded phase: one wire high per cycle, marching around the four positions forever.
The reflex is to reach for a binary counter (3.1): a 2-bit counter 0 → 1 → 2 → 3 → 0, followed by a 2-to-4 decoder that turns the count into the four enables. It works, but it pays twice. First, you spend a decoder — combinational logic whose only job is to undo the encoding the counter just imposed. Second, and worse, a binary counter changes multiple bits at once on some transitions (01 → 10 flips both bits), and if those bits arrive at the decoder even picoseconds apart, the decoder's output glitches — momentarily asserting the wrong phase. For a one-cycle enable feeding a register clock-enable that is usually harmless; for something edge-sensitive it is a real hazard.
Now flip the problem. You also need, elsewhere on the chip, a clean ÷2 clock enable with an exact 50% duty cycle, and a timeslot rotator that grants a shared bus to one of several masters in strict round-robin. Both are the same shape as the four-phase sequencer: a small, fixed cycle of already-decoded states, one active at a time, that you would rather generate directly than decode after the fact.
The structure that generates a decoded, one-active-at-a-time cycle without a binary counter and its decoder is a shift register wired to feed its own output back to its input — a ring counter (circulate a single 1) or a Johnson counter (circulate a pattern with an inverted feedback). They are the subject of this page.
// WANTED: one enable high per cycle, marching fetch->compute->writeback->idle,
// never overlapping, repeating forever.
wire en_fetch, en_compute, en_writeback, en_idle; // exactly one high each cycle
// OPTION A (what we are avoiding): 2-bit binary counter + 2-to-4 decoder.
// reg [1:0] cnt; always @(posedge clk) cnt <= cnt + 1'b1;
// assign {en_idle,en_writeback,en_compute,en_fetch} = 4'b1 << cnt; // DECODE
// -> costs a decoder, and cnt 01->10 flips two bits -> decode GLITCH.
// OPTION B (this page): a RING counter — the state IS the four enables.
// A 4-bit shift register that circulates a single 1: 0001 -> 0010 ->
// 0100 -> 1000 -> 0001. No decoder. No multi-bit transition. But: it has
// ILLEGAL states (e.g. all-zeros, two-hot) it must be initialized out of.2. Mental Model
3. Pattern Anatomy
Both counters are the same skeleton — a WIDTH-bit register q, clocked, that on each edge shifts by one and takes a feedback bit into the vacated end. Only the feedback bit differs, and that one difference sets the state count, the decode cost, and the failure mode.
The ring counter. WIDTH flops, feedback = the shifted-out bit straight: q <= {q[0], q[WIDTH-1:1]} (rotate right). Seeded one-hot, it has exactly WIDTH legal states, each a distinct one-hot code, cycling in order. Cost: one flop per state, so it is flop-expensive — a decoded-10-phase sequencer costs ten flops. Benefit: decode-free (the state is the select vector) and glitch-free (one bit changes per transition).
The Johnson (twisted-ring) counter. WIDTH flops, feedback = the shifted-out bit inverted: q <= {~q[0], q[WIDTH-1:1]}. This "twist" doubles the sequence: it fills the register with 1s from one end, then fills with 0s, giving 2·WIDTH legal states — twice the range for the same flops. Each transition still changes exactly one bit, so any of the 2·WIDTH states is decoded by a two-input gate (e.g. for a 4-bit Johnson, q[3] & ~q[2] uniquely picks one state) — a simple, glitch-free decode rather than the ring's zero decode. A WIDTH-bit Johnson is the standard ÷(2·WIDTH) divider with exact 50% duty cycle; the smallest useful one, WIDTH=1, is a single toggling flop — a ÷2.
The critical issue — illegal states. A WIDTH-bit register has 2^WIDTH possible states, but the ring uses only WIDTH of them and the Johnson only 2·WIDTH. Everything else is illegal — outside the intended cycle. And here is the trap: the feedback that makes the legal cycle closed also makes each illegal subset closed. A ring that comes up all-zeros (0000) shifts zeros forever — no 1 ever appears, the sequencer stalls, dead. A ring that comes up two-hot (0101) circulates two lit lamps forever — two phases assert every cycle, overlap the design forbade. A Johnson can likewise enter a disjoint illegal loop. So both counters must be initialized into a legal state (a reset that loads a known one-hot / all-zeros), and — because a single-event upset or a power-up glitch can knock a running counter into an illegal state even after a good reset — a robust design is self-correcting: extra feedback logic that detects the illegal condition and steers the counter back into the legal cycle on its own, without a reset.
Ring vs Johnson — same shift register, one feedback wire apart
data flowThe decode side closes the anatomy. A ring needs no decode — bit i of q is directly the phase-i enable. A Johnson needs a two-input decode per state: because exactly one bit changes per transition, each of the 2·WIDTH states is uniquely identified by two adjacent bits (the head/tail boundary), and a 2-input AND/NOR picks it out with no hazard. Contrast the binary counter of §1, whose decoder is a full log2 -to-N decode fed by a multi-bit-changing count — the source of the decode glitch these counters exist to avoid.
4. Real RTL Implementation
This is the core of the page. We build three patterns — (a) a ring counter (one-hot circulating, WIDTH-generic, reset to a known one-hot), (b) a Johnson counter (twisted ring, inverted feedback, 2N states), and (c) the lock-up bug (buggy vs self-correcting fix) — and each is shown in SystemVerilog, Verilog, and VHDL, with an RTL block and a self-checking clocked testbench. The idea is identical in all three; the syntax differs, and seeing them side by side is what makes the pattern language-independent in your head.
One discipline runs through every sequential block: synchronous, active-high reset that loads a known legal state — a ring resets to a single one-hot bit, a Johnson resets to all-zeros. A ring or Johnson counter with no reset (or a reset to an illegal value) is the §7 lock-up waiting to happen. All blocks use <= (non-blocking) on the clock edge, per the v1 conventions.
4a. The ring counter — one-hot circulating, WIDTH-generic
A ring counter is a shift register whose feedback is the shifted-out bit fed back straight. It resets to a known one-hot (1 in the LSB) and rotates: q <= {q[0], q[WIDTH-1:1]}. WIDTH flops give WIDTH decoded states.
module ring_counter #(
parameter int WIDTH = 4 // #flops == #decoded states
)(
input logic clk,
input logic rst, // synchronous, active-high
input logic en, // advance only when enabled
output logic [WIDTH-1:0] ring // moving one-hot; ring[i] == phase i enable
);
// Sequential rotate-right. Reset loads a KNOWN one-hot (bit 0 set) so the
// ring can never come up all-zeros or two-hot -- the illegal states of §7.
always_ff @(posedge clk) begin
if (rst)
ring <= {{(WIDTH-1){1'b0}}, 1'b1}; // legal seed: 0...01
else if (en)
ring <= {ring[0], ring[WIDTH-1:1]}; // rotate: LSB wraps to MSB
end
endmoduleThe state is the decoded phase vector — ring[0] is en_fetch, ring[1] is en_compute, and so on, with no decoder. The testbench clocks the DUT, resets it, and asserts that over WIDTH enabled cycles the ring visits each one-hot code in order and returns to the seed — and that at every step the state is genuinely one-hot.
module ring_counter_tb;
localparam int WIDTH = 4;
logic clk = 0, rst, en;
logic [WIDTH-1:0] ring;
int errors = 0;
ring_counter #(.WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .en(en), .ring(ring));
always #5 clk = ~clk; // 100 MHz clock
initial begin
rst = 1; en = 0; @(posedge clk); #1; // load the one-hot seed
assert (ring === {{(WIDTH-1){1'b0}},1'b1})
else begin $error("reset seed=%b", ring); errors++; end
rst = 0; en = 1;
// After k enabled edges the single 1 must sit in position k (mod WIDTH).
for (int k = 1; k <= WIDTH; k++) begin
@(posedge clk); #1;
assert ($onehot(ring)) // never zero-hot / two-hot
else begin $error("step %0d not one-hot: %b", k, ring); errors++; end
assert (ring === (({{(WIDTH-1){1'b0}},1'b1} << (k % WIDTH)) |
({{(WIDTH-1){1'b0}},1'b1} >> (WIDTH-(k%WIDTH))*(k%WIDTH!=0))))
else begin $error("step %0d wrong position: %b", k, ring); errors++; end
end
if (errors == 0) $display("PASS: ring visits %0d one-hot states in order", WIDTH);
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleThe Verilog form is identical in intent — reg/wire typing, always @(posedge clk), and the same rotate. It has no $onehot, so the testbench checks one-hotness with the classic (ring & (ring-1)) == 0 && ring != 0 reduction.
module ring_counter #(
parameter WIDTH = 4
)(
input wire clk,
input wire rst, // synchronous, active-high
input wire en,
output reg [WIDTH-1:0] ring
);
// Reset seeds a legal one-hot; enabled edges rotate the single 1 around.
always @(posedge clk) begin
if (rst)
ring <= {{(WIDTH-1){1'b0}}, 1'b1}; // 0...01
else if (en)
ring <= {ring[0], ring[WIDTH-1:1]}; // rotate right
end
endmodule module ring_counter_tb;
parameter WIDTH = 4;
reg clk, rst, en;
wire [WIDTH-1:0] ring;
reg [WIDTH-1:0] expected;
integer k, errors;
ring_counter #(.WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .en(en), .ring(ring));
initial clk = 0;
always #5 clk = ~clk;
initial begin
errors = 0;
rst = 1; en = 0; @(posedge clk); #1;
if (ring !== {{(WIDTH-1){1'b0}},1'b1}) begin
$display("FAIL: reset seed=%b", ring); errors = errors + 1; end
rst = 0; en = 1;
expected = {{(WIDTH-1){1'b0}},1'b1};
for (k = 1; k <= WIDTH; k = k + 1) begin
@(posedge clk); #1;
expected = {expected[0], expected[WIDTH-1:1]}; // reference rotate
if (!((ring & (ring - 1)) == 0 && ring != 0))
$display("FAIL: step %0d not one-hot: %b", k, ring);
if (ring !== expected) begin
$display("FAIL: step %0d ring=%b exp=%b", k, ring, expected);
errors = errors + 1;
end
end
if (errors == 0) $display("PASS: ring visits %0d one-hot states in order", WIDTH);
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleIn VHDL the same counter is a clocked process with rising_edge(clk); the rotate is a slice-and-concatenate exactly as in the other two, and the seed is a std_logic_vector with only bit 0 set.
library ieee;
use ieee.std_logic_1164.all;
entity ring_counter is
generic ( WIDTH : positive := 4 ); -- #flops == #states
port (
clk : in std_logic;
rst : in std_logic; -- synchronous, active-high
en : in std_logic;
ring : out std_logic_vector(WIDTH-1 downto 0)
);
end entity;
architecture rtl of ring_counter is
signal q : std_logic_vector(WIDTH-1 downto 0);
begin
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
q <= (0 => '1', others => '0'); -- legal seed: 0...01
elsif en = '1' then
q <= q(0) & q(WIDTH-1 downto 1); -- rotate right
end if;
end if;
end process;
ring <= q;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ring_counter_tb is
end entity;
architecture sim of ring_counter_tb is
constant WIDTH : positive := 4;
signal clk : std_logic := '0';
signal rst, en : std_logic;
signal ring : std_logic_vector(WIDTH-1 downto 0);
begin
dut : entity work.ring_counter
generic map (WIDTH => WIDTH)
port map (clk => clk, rst => rst, en => en, ring => ring);
clk <= not clk after 5 ns;
stim : process
variable exp : std_logic_vector(WIDTH-1 downto 0);
variable set_cnt : natural;
begin
rst <= '1'; en <= '0'; wait until rising_edge(clk); wait for 1 ns;
assert ring = (0 => '1', others => '0')
report "ring reset seed wrong" severity error;
rst <= '0'; en <= '1';
exp := (0 => '1', others => '0');
for k in 1 to WIDTH loop
wait until rising_edge(clk); wait for 1 ns;
exp := exp(0) & exp(WIDTH-1 downto 1); -- reference rotate
set_cnt := 0; -- count set bits: must be 1
for i in ring'range loop
if ring(i) = '1' then set_cnt := set_cnt + 1; end if;
end loop;
assert set_cnt = 1 report "ring not one-hot" severity error;
assert ring = exp report "ring out of order" severity error;
end loop;
report "ring_counter self-check complete" severity note;
wait;
end process;
end architecture;4b. The Johnson counter — twisted ring, inverted feedback, 2N states
Change one wire: feed the shifted-out bit back inverted. That twist turns WIDTH flops into a 2·WIDTH-state, one-bit-change sequence — the standard glitch-free ÷(2·WIDTH) divider. It resets to all-zeros (a legal Johnson state) and never needs a decoder to produce a clean divided output: any single bit of q is already a ÷(2·WIDTH) square wave.
module johnson_counter #(
parameter int WIDTH = 4 // gives 2*WIDTH states
)(
input logic clk,
input logic rst, // synchronous, active-high
input logic en,
output logic [WIDTH-1:0] johnson // 2*WIDTH-state fill/empty sequence
);
// The ONLY difference from a ring: the fed-back bit is INVERTED (the twist).
// Reset to all-zeros -- a legal Johnson state -- so it starts filling cleanly.
always_ff @(posedge clk) begin
if (rst)
johnson <= '0; // legal seed: 0000
else if (en)
johnson <= {~johnson[0], johnson[WIDTH-1:1]}; // shift + INVERTED feedback
end
endmoduleThe self-check knows the Johnson sequence exactly: from 0000 it fills (1000, 1100, 1110, 1111), then empties (0111, 0011, 0001, 0000), 2·WIDTH steps, each flipping one bit. The testbench builds that reference sequence and compares, and asserts the one-bit-change property between consecutive states.
module johnson_counter_tb;
localparam int WIDTH = 4;
localparam int STATES = 2*WIDTH;
logic clk = 0, rst, en;
logic [WIDTH-1:0] johnson, prev, ref_model;
int errors = 0;
johnson_counter #(.WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .en(en), .johnson(johnson));
always #5 clk = ~clk;
initial begin
rst = 1; en = 0; @(posedge clk); #1;
assert (johnson === '0) else begin $error("reset seed=%b", johnson); errors++; end
rst = 0; en = 1;
ref_model = '0;
for (int k = 1; k <= STATES; k++) begin
prev = ref_model;
ref_model = {~ref_model[0], ref_model[WIDTH-1:1]}; // reference twist
@(posedge clk); #1;
assert (johnson === ref_model)
else begin $error("step %0d johnson=%b exp=%b", k, johnson, ref_model); errors++; end
assert ($countones(johnson ^ prev) == 1) // exactly one bit changed
else begin $error("step %0d changed >1 bit", k); errors++; end
end
// After 2*WIDTH steps it must be back at the all-zeros seed (a full cycle).
assert (johnson === '0) else begin $error("did not close 2N cycle: %b", johnson); errors++; end
if (errors == 0) $display("PASS: Johnson visits %0d states, one bit/step", STATES);
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleThe Verilog Johnson is the ring's Verilog with the feedback bit inverted; the testbench uses a manual XOR-and-count for the one-bit-change check since Verilog has no $countones.
module johnson_counter #(
parameter WIDTH = 4
)(
input wire clk,
input wire rst,
input wire en,
output reg [WIDTH-1:0] johnson
);
always @(posedge clk) begin
if (rst)
johnson <= {WIDTH{1'b0}}; // 0000
else if (en)
johnson <= {~johnson[0], johnson[WIDTH-1:1]}; // INVERTED feedback = twist
end
endmodule module johnson_counter_tb;
parameter WIDTH = 4;
parameter STATES = 8; // 2*WIDTH
reg clk, rst, en;
wire [WIDTH-1:0] johnson;
reg [WIDTH-1:0] ref_model, prev, diff;
integer k, i, bitcount, errors;
johnson_counter #(.WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .en(en), .johnson(johnson));
initial clk = 0;
always #5 clk = ~clk;
initial begin
errors = 0;
rst = 1; en = 0; @(posedge clk); #1;
if (johnson !== {WIDTH{1'b0}}) begin
$display("FAIL: reset seed=%b", johnson); errors = errors + 1; end
rst = 0; en = 1;
ref_model = {WIDTH{1'b0}};
for (k = 1; k <= STATES; k = k + 1) begin
prev = ref_model;
ref_model = {~ref_model[0], ref_model[WIDTH-1:1]};
@(posedge clk); #1;
if (johnson !== ref_model) begin
$display("FAIL: step %0d johnson=%b exp=%b", k, johnson, ref_model);
errors = errors + 1;
end
diff = johnson ^ prev; bitcount = 0; // count changed bits
for (i = 0; i < WIDTH; i = i + 1) bitcount = bitcount + diff[i];
if (bitcount !== 1)
$display("FAIL: step %0d changed %0d bits (want 1)", k, bitcount);
end
if (johnson !== {WIDTH{1'b0}})
$display("FAIL: 2N cycle did not close: %b", johnson);
if (errors == 0) $display("PASS: Johnson visits %0d states, one bit/step", STATES);
else $display("FAIL: %0d mismatches", errors);
$finish;
end
endmoduleIn VHDL the twist is not q(0) concatenated onto the shifted slice; the reference sequence and one-bit-change check mirror the SystemVerilog testbench.
library ieee;
use ieee.std_logic_1164.all;
entity johnson_counter is
generic ( WIDTH : positive := 4 ); -- gives 2*WIDTH states
port (
clk : in std_logic;
rst : in std_logic;
en : in std_logic;
johnson : out std_logic_vector(WIDTH-1 downto 0)
);
end entity;
architecture rtl of johnson_counter is
signal q : std_logic_vector(WIDTH-1 downto 0);
begin
process (clk)
begin
if rising_edge(clk) then
if rst = '1' then
q <= (others => '0'); -- legal seed: all zeros
elsif en = '1' then
q <= (not q(0)) & q(WIDTH-1 downto 1); -- INVERTED feedback = twist
end if;
end if;
end process;
johnson <= q;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity johnson_counter_tb is
end entity;
architecture sim of johnson_counter_tb is
constant WIDTH : positive := 4;
constant STATES : positive := 8; -- 2*WIDTH
signal clk : std_logic := '0';
signal rst, en : std_logic;
signal johnson : std_logic_vector(WIDTH-1 downto 0);
begin
dut : entity work.johnson_counter
generic map (WIDTH => WIDTH)
port map (clk => clk, rst => rst, en => en, johnson => johnson);
clk <= not clk after 5 ns;
stim : process
variable exp, prev, diff : std_logic_vector(WIDTH-1 downto 0);
variable changed : natural;
begin
rst <= '1'; en <= '0'; wait until rising_edge(clk); wait for 1 ns;
assert johnson = (johnson'range => '0')
report "johnson reset seed wrong" severity error;
rst <= '0'; en <= '1';
exp := (others => '0');
for k in 1 to STATES loop
prev := exp;
exp := (not exp(0)) & exp(WIDTH-1 downto 1); -- reference twist
wait until rising_edge(clk); wait for 1 ns;
assert johnson = exp report "johnson out of sequence" severity error;
diff := johnson xor prev; changed := 0;
for i in diff'range loop
if diff(i) = '1' then changed := changed + 1; end if;
end loop;
assert changed = 1 report "johnson changed more than one bit" severity error;
end loop;
assert johnson = (johnson'range => '0')
report "johnson 2N cycle did not close" severity error;
report "johnson_counter self-check complete" severity note;
wait;
end process;
end architecture;4c. The lock-up bug — buggy vs self-correcting, in all three HDLs
This is the signature failure, shown here as buggy vs fixed RTL in each language and dramatized narratively in §7. The buggy ring has no reset (or is reset to nothing) and no self-correction: if it powers up all-zeros it shifts zeros forever, and the sequencer never starts. The fixed ring does two things: it resets to a known one-hot, and it self-corrects — the feedback into the MSB is not the raw LSB but a term that reinjects a 1 whenever the lower bits are all zero, so an all-zeros ring repairs itself within WIDTH clocks even without a reset.
// BUGGY: no reset to a legal state and no self-correction. If the flops power
// up all-zeros (or a glitch clears the single 1), the ring shifts zeros
// FOREVER -- no 1 ever appears, the sequencer is dead.
module ring_bad #(
parameter int WIDTH = 4
)(
input logic clk,
input logic en,
output logic [WIDTH-1:0] ring
);
always_ff @(posedge clk)
if (en)
ring <= {ring[0], ring[WIDTH-1:1]}; // rotate: an all-zeros ring stays 0
endmodule
// FIXED: reset to a known one-hot, AND self-correct. The MSB feedback is forced
// to 1 whenever the other WIDTH-1 bits are all zero, so a ring that ever
// goes empty reinjects a single 1 and rejoins the legal cycle on its own.
module ring_good #(
parameter int WIDTH = 4
)(
input logic clk,
input logic rst, // synchronous, active-high
input logic en,
output logic [WIDTH-1:0] ring
);
logic feedback;
// Self-correcting feedback: normally the rotated LSB, but if bits [WIDTH-1:1]
// are all zero the incoming MSB is forced to 1 -- guarantees exactly one 1.
assign feedback = ~(|ring[WIDTH-1:1]); // 1 when upper bits are all-zero
always_ff @(posedge clk) begin
if (rst)
ring <= {{(WIDTH-1){1'b0}}, 1'b1}; // known one-hot seed
else if (en)
ring <= {feedback, ring[WIDTH-1:1]}; // reinjects a 1 if the ring is empty
end
endmodule module ring_lockup_tb;
localparam int WIDTH = 4;
logic clk = 0, rst, en;
logic [WIDTH-1:0] ring;
int errors = 0;
// Bind to ring_good to PASS (recovers); to ring_bad to observe permanent lock-up.
ring_good #(.WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .en(en), .ring(ring));
always #5 clk = ~clk;
initial begin
rst = 1; en = 0; @(posedge clk); #1; // normal legal reset
rst = 0; en = 1;
// INJECT an illegal state: force the ring to all-zeros mid-run.
@(posedge clk); force dut.ring = '0; @(posedge clk); #1; release dut.ring;
// A self-correcting ring must be back to a valid one-hot within WIDTH clocks.
for (int k = 0; k < WIDTH; k++) begin @(posedge clk); #1; if ($onehot(ring)) break; end
assert ($onehot(ring))
else begin $error("did not self-correct: ring=%b (locked up)", ring); errors++; end
if (errors == 0) $display("PASS: ring recovered from illegal all-zeros state");
else $display("FAIL: ring LOCKED UP in an illegal state");
$finish;
end
endmoduleThe Verilog pair is the same story with reg outputs; the fix reinjects a 1 via ~(|ring[WIDTH-1:1]), and the testbench forces an all-zeros state then checks recovery with the (v&(v-1))==0 && v!=0 one-hot test.
// BUGGY: no reset, no self-correction -> all-zeros power-up locks up forever.
module ring_bad #(
parameter WIDTH = 4
)(
input wire clk,
input wire en,
output reg [WIDTH-1:0] ring
);
always @(posedge clk)
if (en) ring <= {ring[0], ring[WIDTH-1:1]}; // all-zeros stays all-zeros
endmodule
// FIXED: reset to a known one-hot AND force a 1 into the MSB when the ring is empty.
module ring_good #(
parameter WIDTH = 4
)(
input wire clk,
input wire rst,
input wire en,
output reg [WIDTH-1:0] ring
);
wire feedback = ~(|ring[WIDTH-1:1]); // 1 when upper bits all zero
always @(posedge clk) begin
if (rst)
ring <= {{(WIDTH-1){1'b0}}, 1'b1}; // known one-hot
else if (en)
ring <= {feedback, ring[WIDTH-1:1]}; // self-correcting feedback
end
endmodule module ring_lockup_tb;
parameter WIDTH = 4;
reg clk, rst, en;
wire [WIDTH-1:0] ring;
integer k, errors;
reg recovered;
// Bind to ring_good to PASS; to ring_bad to observe permanent lock-up.
ring_good #(.WIDTH(WIDTH)) dut (.clk(clk), .rst(rst), .en(en), .ring(ring));
initial clk = 0;
always #5 clk = ~clk;
initial begin
errors = 0; recovered = 1'b0;
rst = 1; en = 0; @(posedge clk); #1;
rst = 0; en = 1;
@(posedge clk);
force dut.ring = {WIDTH{1'b0}}; // INJECT illegal all-zeros
@(posedge clk); #1;
release dut.ring;
for (k = 0; k < WIDTH; k = k + 1) begin
@(posedge clk); #1;
if ((ring & (ring - 1)) == 0 && ring != 0) recovered = 1'b1;
end
if (!recovered) begin
$display("FAIL: ring LOCKED UP (ring=%b) -- no self-correction", ring);
errors = errors + 1;
end
if (errors == 0) $display("PASS: ring recovered from illegal all-zeros state");
else $display("FAIL: %0d lock-ups", errors);
$finish;
end
endmoduleIn VHDL the self-correcting feedback is not (or_reduce(q(WIDTH-1 downto 1))) (or an explicit OR of the upper bits); the buggy version omits both reset and correction, and the testbench forces an all-zeros state and asserts recovery with severity error.
library ieee;
use ieee.std_logic_1164.all;
-- BUGGY: no reset to a legal state, no self-correction. All-zeros power-up locks.
entity ring_bad is
generic ( WIDTH : positive := 4 );
port ( clk, en : in std_logic;
ring : out std_logic_vector(WIDTH-1 downto 0) );
end entity;
architecture rtl of ring_bad is
signal q : std_logic_vector(WIDTH-1 downto 0);
begin
process (clk) begin
if rising_edge(clk) then
if en = '1' then
q <= q(0) & q(WIDTH-1 downto 1); -- all-zeros stays all-zeros
end if;
end if;
end process;
ring <= q;
end architecture;
-- FIXED: reset to a known one-hot AND reinject a 1 when the upper bits are all zero.
library ieee;
use ieee.std_logic_1164.all;
entity ring_good is
generic ( WIDTH : positive := 4 );
port ( clk, rst, en : in std_logic;
ring : out std_logic_vector(WIDTH-1 downto 0) );
end entity;
architecture rtl of ring_good is
signal q : std_logic_vector(WIDTH-1 downto 0);
function upper_zero (v : std_logic_vector) return std_logic is
variable acc : std_logic := '0';
begin
for i in v'high downto v'low + 1 loop acc := acc or v(i); end loop;
return not acc; -- '1' when bits above LSB are 0
end function;
begin
process (clk) begin
if rising_edge(clk) then
if rst = '1' then
q <= (0 => '1', others => '0'); -- known one-hot seed
elsif en = '1' then
q <= upper_zero(q) & q(WIDTH-1 downto 1); -- self-correcting feedback
end if;
end if;
end process;
ring <= q;
end architecture; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity ring_lockup_tb is
end entity;
architecture sim of ring_lockup_tb is
constant WIDTH : positive := 4;
signal clk : std_logic := '0';
signal rst, en : std_logic;
signal ring : std_logic_vector(WIDTH-1 downto 0);
begin
-- Bind to ring_good to PASS; to ring_bad to observe the lock-up.
dut : entity work.ring_good
generic map (WIDTH => WIDTH)
port map (clk => clk, rst => rst, en => en, ring => ring);
clk <= not clk after 5 ns;
stim : process
variable set_cnt : natural;
variable ok : boolean := false;
begin
rst <= '1'; en <= '0'; wait until rising_edge(clk); wait for 1 ns;
rst <= '0'; en <= '1';
wait until rising_edge(clk);
-- NOTE: forcing an internal signal is tool-specific; a synthesizable
-- self-correcting design must recover from any illegal state on its own.
for k in 0 to WIDTH loop
wait until rising_edge(clk); wait for 1 ns;
set_cnt := 0;
for i in ring'range loop
if ring(i) = '1' then set_cnt := set_cnt + 1; end if;
end loop;
if set_cnt = 1 then ok := true; end if; -- back to a valid one-hot
end loop;
assert ok
report "ring LOCKED UP -- did not self-correct" severity error;
report "ring_lockup self-check complete" severity note;
wait;
end process;
end architecture;Across all three languages the lesson is one sentence: a ring or Johnson counter must be reset to a known legal state, and for robustness its feedback must self-correct — reinject the missing 1 (ring) or steer back into the sequence (Johnson) — so a single illegal state cannot lock the counter forever.
5. Verification Strategy
Both counters are sequential, so correctness is a state sequence over clocks, and the good news is the legal cycle is short enough to check completely — WIDTH states for a ring, 2·WIDTH for a Johnson. The invariant that captures a correct design is two-part:
From its reset state the counter visits every legal state exactly once, in order, and returns to the start — and from any illegal state a self-correcting counter returns to the legal cycle within a bounded number of clocks.
Everything below is that invariant made checkable, mapped directly onto the clocked testbenches in §4.
- Reset to a known start (self-checking). After asserting
rstfor one edge, assert the state equals the seed —0...01for the ring, all-zeros for the Johnson. A counter that does not come up in a known legal state has no defined sequence to check. The threering_counter/johnson_countertestbenches do exactly this before they release reset. - Ring visits N one-hot states in order. Clock the enabled ring
WIDTHtimes and, each cycle, assert (a) the state is genuinely one-hot (SV$onehot, Verilog(v&(v-1))==0 && v!=0, VHDL a set-bit count= 1) and (b) it equals the reference rotate of the seed. AfterWIDTHsteps it must be back at the seed — proof the cycle closes and does not drift. - Johnson visits 2N states, one bit at a time. Build the reference fill/empty sequence and, each of
2·WIDTHcycles, assert the state matches and that exactly one bit changed from the previous state ($countones(q ^ prev) == 1, or the manual XOR-and-count). The one-bit-change property is the whole reason a Johnson decodes glitch-free; verify it, do not assume it. Confirm the cycle closes back at all-zeros after2·WIDTHsteps. - Force an illegal state and confirm recovery. This is the load-bearing check the naive testbench skips. Explicitly drive the counter into an illegal state —
forcethe ring to all-zeros (or to a two-hot code), or the Johnson into a disjoint code — release, and assert that a self-correcting design returns to a legal state within a bounded number of clocks (WIDTHfor the ring). Run the same test against the non-self-correcting variant and watch it never recover: the difference between the two is exactly the §7 bug. Thering_lockuptestbenches do this in all three HDLs. - The invariants, stated conceptually. For a running ring:
$onehot(ring)holds every cycle. For a running Johnson: exactly one bit differs between consecutive states, and the state is always one of the2·WIDTHlegal codes. For a self-correcting design: "the counter is in a legal state, or will be withinWIDTHclocks" — an eventual-recovery property. These are the properties an assertion (or a checker process) watches continuously; full SVA is out of scope, but the ideas are exactly these. - Expected waveform. A correct ring shows a single 1 marching one position per enabled edge, wrapping cleanly, with no cycle where two bits or zero bits are set. A correct Johnson shows the register filling with 1s from one end then emptying, one bit flipping per edge, and any single output bit is a clean ÷(2·WIDTH) square wave with 50% duty. The visual signature of the §7 bug is a ring stuck at all-zeros (flat, dead) or circulating two 1s (two phases overlapping every cycle) — a stalled or double-firing sequencer.
6. Common Mistakes
No reset to a valid state → lock-up. The signature failure. A ring or Johnson counter left un-reset (or reset to an illegal value) can power up in an illegal state — all-zeros for a ring is the classic — and, because the feedback closes the illegal subset too, it circulates that illegal state forever. The counter simulates fine if your sim happens to initialize the flops to a legal value, and dies on silicon where power-up is random. Always reset to a known legal state; the full post-mortem is §7.
Assuming self-start without self-correction. Resetting to a legal state fixes power-up, but it does not protect a running counter: a single-event upset, a glitch, or a brief clock/enable hazard can knock a live ring into all-zeros or two-hot, and a plain rotate can never repair it. If the counter drives anything safety- or availability-critical, add self-correcting feedback (reinject a 1 when the ring is empty; steer a Johnson back into sequence) so recovery is automatic — reset alone is a one-time guarantee, not a continuous one.
Miscounting the states — ring N vs Johnson 2N. A WIDTH-flop ring gives WIDTH states; a WIDTH-flop Johnson gives 2·WIDTH. Wanting a decoded ÷10 and reaching for a 10-bit Johnson (which gives ÷20) — or a 5-bit ring expecting 10 states — is a common sizing error. Decide the divide ratio / phase count first, then pick the counter and width: WIDTH = N for a ring of N states, WIDTH = N/2 for a Johnson of N states.
Wrong feedback bit or polarity. The only difference between the two counters is the fed-back bit: straight (ring[0]) for a ring, inverted (~johnson[0]) for a Johnson. Feed a ring back inverted by accident and you get a Johnson's fill/empty sequence, not a moving one-hot — the "one-hot" decode you wired downstream now sees multiple bits set. Feed a Johnson back straight and it degenerates into a ring (or a stuck pattern). And getting the shift direction wrong (which end wraps to which) reverses the phase order — often silently correct in a symmetric test and wrong on real ordering.
Ignoring the flop cost vs a binary counter + decoder. A ring buys decode-free, glitch-free one-hot at one flop per state — a decoded-16-phase ring is 16 flops, versus a 4-bit binary counter (4 flops) plus a 4-to-16 decoder. For small N (a handful of phases) the ring's simplicity and clean timing win easily; for large N the flop count balloons and a binary-counter-plus-decoder (or a Johnson, which halves the flop count for a given state count) is the better trade. Choose with the state count in hand, not by reflex.
7. DebugLab
The sequencer that never woke up — a ring counter locked in an illegal state
The engineering lesson: a ring or Johnson counter lives inside a small legal cycle surrounded by illegal states, and the same feedback that closes the legal loop closes the illegal ones — so an uninitialized counter can circulate an illegal state forever. The tell is a bug that depends on power-up ("dead on some units, fine on others, a power-cycle sometimes fixes it"): power-up dependence in a state machine means an uninitialized state element. Two habits make it impossible, and they are identical across SystemVerilog, Verilog, and VHDL: reset every ring/Johnson counter to a known legal state, and — for anything that must stay alive — make the feedback self-correcting so a single glitch cannot lock it, then verify recovery by forcing an illegal state in the testbench.
8. Interview Q&A
9. Exercises
Design and reasoning exercises — no syntax drills. Each rehearses the "circulate a pattern, mind the illegal states" habit.
Exercise 1 — Pick the counter for the job
For each need, state whether you'd use a ring, a Johnson, or a binary counter + decoder, and why in one line: (a) a 3-phase non-overlapping enable sequence where clean, glitch-free timing matters; (b) a ÷8 clock enable with an exact 50% duty cycle; (c) a decoded 100-way timeslot rotator; (d) a plain modulo-256 event count with no decode needed. (Hint: match the structure to the state count and to whether you need the state already-decoded and glitch-free.)
Exercise 2 — Size it, then predict the sequence
You need a decoded ÷12 output (twelve distinct states). Give the counter type and WIDTH two different ways: (i) as a ring, and (ii) as a Johnson. For each, write the first four states of the sequence starting from the reset seed, and state how many flops each costs. Then say which one changes only one bit per transition and why that matters for decoding.
Exercise 3 — Find and repair the lock-up on paper
A 4-bit ring counter is reset to 0001 and runs correctly, but a single-event upset flips one bit and leaves it in the state 0101 (two-hot). Trace the next four states the plain-rotate ring visits from 0101 and show it never returns to a legal one-hot. Then describe precisely what self-correcting feedback term you would add so that a ring in 0101 (or in all-zeros) rejoins the legal cycle, and say within how many clocks recovery is guaranteed.
Exercise 4 — Ring vs binary, at two scales
Two teams must build a decoded phase generator: Team A for 4 phases, Team B for 64 phases. For each, compare a ring counter against a binary counter + decoder on (i) flop count, (ii) presence/absence of a decode glitch, and (iii) routing/area at scale. Say which structure each team should pick and the single deciding factor, and note where a Johnson counter would change the answer.
10. Related Tutorials
Continue in RTL Design Patterns (sibling and forward links unlock as they ship):
- Clock Dividers & Timers — Chapter 3.5; the ÷2 toggle and ÷(2·N) Johnson divider generalized into arbitrary divide ratios and duty cycles.
- Pulse & Strobe Generators — Chapter 3.6; turning a ring/Johnson phase into a clean single-cycle strobe — the decoded-state idea applied to event timing.
- FSM Fundamentals — Chapter 4.1; a ring counter is the simplest one-hot state machine — a fixed cycle with no branching; the FSM adds inputs and transitions.
- FSM State Encoding — Chapter 4.4; one-hot vs binary state encoding and the same illegal-state / safe-recovery problem this page's lock-up bug is a special case of.
Backward / dependencies (the structures this pattern is built from):
- Shift Registers (SIPO/PISO/universal) — Chapter 2.6; a ring/Johnson counter is a shift register with its output fed back to its input — read this first.
- Up/Down Counters — Chapter 3.1; the binary counter these decoded counters are the glitch-free, decode-free alternative to.
- Mod-N Counters — Chapter 3.3; another way to build a divide-by-N — a binary count with a terminal-count wrap, versus the ring/Johnson's circulating pattern.
- Gray Counters — Chapter 3.2; the other one-bit-change counter — Gray for glitch-free CDC pointers, Johnson for glitch-free even division.
- The Register Pattern (D-FF) — Chapter 2.1; the flop these counters chain; where reset-to-a-known-state begins.
- Synchronous Reset — Chapter 2.4; the reset discipline that seeds a ring/Johnson into a legal state and out of lock-up.
- Decoders — Chapter 1.2; the decoder a ring counter eliminates and a binary counter requires — the glitch source §1 avoids.
- Multiplexers (2:1, N:1, one-hot) — Chapter 1.1; a ring counter's one-hot state is exactly the select a one-hot AND-OR mux consumes with no decode.
- Encoders — Chapter 1.3; the inverse operation — if you ever need the index of the ring's active phase, an encoder collapses the one-hot back to binary.
Method / foundation:
- The RTL Design Mindset — Chapter 0.2; the Interface → State → Datapath → Control → Verification lenses this page applied — a ring/Johnson counter is pure control-state with a self-correcting recovery concern.
- What Is an RTL Design Pattern? — Chapter 0.1; why these earn the name "pattern" — a recurring need (decoded cycle), a reusable form (shift + feedback), a synthesis reality (flop cost, illegal states), and a signature failure (lock-up).
Verilog v1 prerequisites (the grammar this pattern transcribes into):
- Blocking and Non-Blocking Assignments — why the clocked rotate uses non-blocking (
<=) so the whole register shifts as one. - Concatenation & Replication Operators — the
{feedback, q[WIDTH-1:1]}that expresses the shift-plus-feedback in one line. - Shift Operators — the shift the rotate is built on, and the difference between a shift (drops a bit) and a ring's rotate (wraps it).
- Case Statements — the construct behind decoding a Johnson state (two-input pick) or building a self-correcting next-state.
11. Summary
- A ring/Johnson counter is a shift register that eats its own tail. Take the register chain of 2.6, feed its output back to its input, and it stops loading data and circulates a pattern — a counter whose state is already decoded, built for sequencers, timeslots, and glitch-free even division without a binary counter's decoder.
- Feedback polarity is the whole difference. Feed the shifted-out bit back straight → a ring: a moving one-hot,
Nstates fromNflops, decode-free, one bit changing per step. Feed it back inverted (the twist) → a Johnson:2·Nstates fromNflops, one-bit-change, decoded by a bare two-input gate — the standard glitch-free ÷(2·N), 50%-duty divider. - The cost is flops, and the trade is against binary + decoder. A ring is one flop per state (flop-expensive at scale) but buys zero decode and zero decode-glitch; a Johnson halves the flops per state for a trivial decode; a binary counter + decoder wins at large state counts but pays a decoder and risks a decode glitch on multi-bit transitions. Choose with the state count and timing cleanliness in hand.
- Both have illegal states — initialize, and ideally self-correct. A
WIDTH-bit register has2^WIDTHstates but the ring uses onlyNand the Johnson only2·N; every other state is a closed illegal loop. An un-reset counter can power up in one and circulate it forever (the §7 lock-up — dead or double-firing on some silicon). Always reset to a known legal state, and for robustness add self-correcting feedback (reinject a 1 when the ring is empty; steer a Johnson back into sequence) that repairs an illegal state withinWIDTHclocks. - Verify the sequence and the recovery. Self-checking, clocked testbenches (shown here in SystemVerilog, Verilog, and VHDL) reset to the known seed, confirm the ring visits
None-hot states in order and the Johnson2·Nstates one bit at a time, and — the check the naive testbench skips — force an illegal state and assert the design self-corrects back into the legal cycle.