digiwleeaLearn
Open the lab →

Synchronous counters

One shared clock, a count-enable chain, and clean cascading

12 min read

A synchronous counter is a counter in which every flip-flop shares one common clock so all bits update on the same edge, and a count-enable AND chain toggles bit k only when the enable and every lower bit are 1; a carry-out lets counters cascade, a direction control makes it count up or down, and a decoded synchronous clear or load makes it count through a chosen modulus.

The counter lesson built two counters: a ripple counter, where each stage's output clocks the stage above it, and a program counter, a synchronous up-counter with an adder bolted on. This lesson stays with the synchronous family and fills in the machinery a real counter carries: a count enable, a carry-out for chaining, an up or down direction, and a clean way to count to any modulus. It leans on the toggle flip-flop from flip-flop types (hold when T = 0, flip when T = 1) and on the clocked state machine idea (one shared clock stepping a register).

One clock for every bit

The word *synchronous* means one thing here: every flip-flop is driven by the same clock. One clock wire fans out to all of them, and combinational logic in front of each bit decides whether that bit should toggle on the coming edge. Compare that with a ripple counter, where the system clock reaches only bit 0 and each stage's own output becomes the *clock* of the next stage. The two structures count the same 0, 1, 2, 3, ... sequence, but they behave very differently in time.
  • Clock. Ripple: the clock drives bit 0 only, and each stage's output clocks the stage above it. Synchronous: one clock wire reaches every flip-flop at once.
  • When bits move. Ripple: one bit at a time, the change rippling upward from bit 0. Synchronous: all bits together, on the single edge.
  • Settling. Ripple: the top bit lags by the sum of every stage's delay (about n flip-flop delays for n bits), and the outputs pass through wrong codes on the way (the glitch detail is in the counter lesson). Synchronous: one flip-flop delay plus the enable-logic delay, then every bit is valid at the same instant.
  • Cost. Ripple: the fewest gates (just the toggle flip-flops). Synchronous: extra toggle-enable logic in front of each bit, in exchange for clean, glitch-free timing.

The count-enable AND chain

A synchronous counter needs a rule that says *which* bits toggle on the next edge, and the rule is exact: **bit k toggles when the count enable and all bits below it are 1**. Bit 0 toggles on every enabled edge; bit 1 toggles when bit 0 is 1; bit 2 toggles when bits 0 and 1 are both 1; and so on. That is just a binary carry reaching column k, spelled out as an AND. The master count enable EN gates the whole chain: with EN = 0 no bit toggles, so the counter freezes and holds its value.
T0 = EN , T1 = EN Q0 , T2 = EN Q0 Q1 , ... , Tk = EN Q0 Q1 ... Q(k-1)
There is a tidy way to build that chain without a separate wide AND for every bit: carry the AND term along one stage at a time. Call the enable arriving at bit k its enable-in e_k, start with e_0 = EN, and pass e_{k+1} = e_k AND Q_k up to the next stage. Then T_k = e_k. Each stage adds just one 2-input AND. This chain looks like the ripple carry inside an adder, but it is combinational: it settles within one clock period, before the next edge, so all the flip-flops still fire together off the one clock. (A very wide chain adds delay and caps the clock rate, which is why big counters sometimes compute the enables with parallel look-ahead ANDs instead of a long serial chain.)

A worked 3-bit synchronous counter

Take a 3-bit counter. Its toggle inputs are T0 = EN, T1 = EN AND Q0, and T2 = EN AND Q1 AND Q0. Add one more useful output, the ripple-carry-out RCO = EN AND Q2 AND Q1 AND Q0, which is high only when the counter is full (111) *and* enabled. Trace every count with EN = 1, then two rows with EN = 0 to see it hold:
ENQ2 Q1 Q0T2 = EN·Q1·Q0T1 = EN·Q0T0 = ENRCO = EN·Q2·Q1·Q0next
10000010001
10010110010
10100010011
10111110100
11000010101
11010110110
11100010111
11111111000
00110000011
01110000111
A 3-bit synchronous up-counter with enable and carry-out. With EN = 1 the toggle-enables follow the AND rule and the count climbs 0 to 7 then wraps; every bit that changes changes on the same edge. RCO fires once, at 111, the count where the counter is full. With EN = 0 (last two rows) all toggle-enables are 0, so the counter holds its value no matter how many edges pass.
Read the 011 row: both lower bits are 1, so T2, T1, and T0 are all high and the count steps cleanly to 100 in one edge, not through the intermediate codes a ripple counter would flash. Read the EN = 0 rows: the enable zeroes the whole chain, so the counter simply waits. That single enable line is what lets you pause a counter and, next, chain counters together.

Cascading: the ripple-carry-out

To build a wider counter from narrow ones, wire the low counter's RCO into the high counter's EN, and give both counters the same clock. Now the high counter is enabled only on the one edge where the low counter is full and rolls over. Two 4-bit synchronous counters chained this way make an 8-bit synchronous counter: the low nibble counts 0 to 15, and on the edge it wraps 1111 to 0000 its RCO is high, so the high nibble increments on that very edge.
This is still fully synchronous, even though the signal is called ripple-carry-*out*. RCO feeds the next stage's enable (a data input the AND chain reads), never its clock. Both counters fire off the one shared clock, so there is no cumulative clock skew, unlike a ripple counter where a stage's output really is the next stage's clock. Same word, different wire: carry into the enable, not into the clock.
Try it
A 4-bit synchronous up-counter has its RCO wired to the count-enable of a second 4-bit synchronous counter, both on the same clock, both starting at 0. How many clock edges until the high counter first reads 1? And why is this cascade still called synchronous even though we used a ripple-carry-out?

Counting up or down

Counting down flips the rule: **bit k toggles when all bits below it are 0**, because a binary borrow reaches column k exactly when everything under it has run out. So a down-counter's chain uses the *complements* of the lower bits. Trace EN = 1 counting down from 000: 000, 111, 110, 101, 100, 011, 010, 001, 000. At 000 every lower bit is 0, so all three bits toggle and it wraps straight to 111; after that only the bits whose lower neighbours are all 0 flip.
An up/down counter carries both rules and a direction line DIR (say DIR = 1 counts up, DIR = 0 counts down) that selects which one feeds each toggle input:
Tk = EN [ (DIR Q0 ... Q(k-1)) (¬DIR ¬Q0 ... ¬Q(k-1)) ]
Bit 0 has no lower bits, so its AND terms are empty (= 1) and T0 = EN in both directions: bit 0 always toggles when enabled. For the cascade signal, an up-counter exports a carry (EN AND all bits 1, high at the top count), and a down-counter exports a borrow (EN AND all bits 0, high at 000 just before it wraps to all 1s). Feed whichever one into the next stage's enable, exactly as before.

Counting to N: mod-N synchronously

A plain 3-bit counter wraps after 2^3 = 8 values. To count modulo N (through N states 0 to N - 1, then wrap), detect the last value N - 1 and use a synchronous clear or load so the wrap happens on the next edge, not the instant the value appears. For a mod-6 counter (0 through 5), the last value is 5 = 101. An AND of Q2 and Q0 is high at 101, and among the six counts the counter actually visits (0 to 5) only 5 sets both, so that one line flags the terminal count. Wire it to a synchronous clear, or to a load whose data input is 0: on the next edge the count goes cleanly 5 to 0, giving exactly six states 0, 1, 2, 3, 4, 5.
The counter lesson built its mod-N the simpler way, by detecting N itself and clearing asynchronously, which works but lets the value N flash for one instant before it is wiped. Detecting N - 1 and clearing synchronously, as here, never shows an out-of-range count: the clear is just another input to the flip-flops and only takes effect at the edge. Same modulus, cleaner timing.
The same synchronous load input that resets a mod-N counter also lets it start anywhere: drive the load data with a preset value and pulse load, and the count begins there. That is the exact load path that turns the program counter into a jump-capable counter: count normally, or load a target address when a control line says so.
Spot the fault
Q11Q00T2 (should be Q1·Q0)1
Look at T2
Wrong logic level
T2 is high even though Q0 is 0. Bit 2 must toggle only when all lower bits are 1 (T2 = EN AND Q1 AND Q0), but here it was wired to Q1 alone, forgetting Q0. At count 010 bit 0 and bit 2 both toggle, so the count jumps to 111 instead of stepping to 011. Fix: AND in every lower bit, not just the one immediately below.
Common mistakes. Get the toggle condition right: bit k toggles when all lower bits are 1 (up) or all 0 (down), not just the single bit beneath it (the fault above). Gate the whole chain with the count enable: forget it and the counter free-runs and cannot be paused or cascaded. The enable AND chain is combinational and must settle inside one clock period, so a very wide serial chain limits the maximum clock rate (use parallel look-ahead ANDs for wide counters). When cascading, feed the carry-out into the next stage's enable, never its clock, or you have quietly rebuilt an asynchronous ripple counter. And mind the mod-N off-by-one: detect N - 1 and clear or load on the next edge for a glitch-free wrap; detecting N with an async clear lets N flash for an instant (see the counter lesson).
Synchronous timing is why real systems count this way. Every bit changes off one clock edge with a single, bounded settling delay, so a moment after the edge the whole count is valid and glitch-free, and you can decode it, compare it, or feed it to an address bus with no hazards. That predictability is what lets a synchronous counter drive memory addresses, act as a timer, and sequence a CPU's fetch-decode-execute without ever showing a wrong intermediate value. With the enable, carry-out, direction, and modulus in hand, the next step is the memory a counter's address selects.

Frequently asked

What is a synchronous counter?

A synchronous counter is a counter in which every flip-flop shares one common clock, so all bits update on the same edge. Combinational logic in front of each bit decides whether it toggles: bit k toggles when the count enable and all lower bits are 1. Because there is no clock rippling from stage to stage (unlike a ripple counter), the outputs step atomically from one valid count to the next with no glitches.

How does the count-enable AND chain decide which bits toggle?

Each bit has a toggle input T_k = EN AND Q0 AND Q1 AND ... AND Q(k-1): bit 0 toggles on every enabled edge, and bit k toggles only when the enable EN and every lower bit are 1 (a binary carry reaching column k). The chain is usually built one stage at a time, e_{k+1} = e_k AND Q_k, so each bit adds just one AND gate. Holding EN = 0 zeroes every toggle input, which freezes the counter.

What is a ripple-carry-out and how do you cascade counters with it?

The ripple-carry-out RCO = EN AND (all bits 1) is high for the single count where a counter is full and enabled. To make a wider counter, wire the low counter's RCO into the high counter's enable and give both the same clock: the high counter then advances only on the edge the low counter rolls over. It stays synchronous because RCO drives an enable input, not a clock, so no clock skew accumulates.

How do you build a mod-6 (or mod-N) synchronous counter?

Detect the last value N - 1 and use a synchronous clear or load so the wrap happens on the next clock edge. For mod-6, detect 5 = 101 (an AND of Q2 and Q0, since among counts 0 to 5 only 5 sets both) and load 0 on the next edge, giving states 0, 1, 2, 3, 4, 5. Detecting N - 1 and clearing synchronously is glitch-free, unlike detecting N with an asynchronous clear, which lets N flash for an instant.

Every lesson here builds toward one thing: a working CPU, from the transistor up.

Open the free lab →
Found this useful? Share itShare on X