The program counter
Counting through addresses, and the counters behind it
A counter is a clocked circuit that advances through a fixed sequence of values one step per clock edge; a ripple (asynchronous) counter chains toggle flip-flops so each stage clocks the next, a synchronous counter clocks every flip-flop from one common clock so all bits update together, and a program counter is the synchronous counter a CPU uses to hold the address of the next instruction.
A CPU runs a list of instructions stored in memory, and it needs to remember where it is in that list. That bookkeeping is the program counter (PC): a register holding the address of the next instruction. Most of the time it just needs to step to the next address, which is one more than the current one. So a program counter is an 8-bit register with an adder bolted on to add 1 to itself each clock. It is one member of a whole family of circuits called counters, and this lesson builds the program counter first, then steps back to see how counters in general are made.
Register plus incrementer
Take the register's output
Q (the current count), run it into one input of an 8-bit adder, tie the adder's other input to 0, and tie its carry-in to 1. The adder now computes Q + 1. Feed that sum back into the register's data input D and hold the write-enable high. On every clock edge the register loads Q + 1, so the stored value climbs 0, 1, 2, 3, .... The register and adder form a loop, but it is a safe one: the register only samples at the edge, so between edges the Q + 1 value is stable (the same reasoning that made feedback safe in the register bit).on each rising edge: PC = PC + 1
- Wire the register output
Qinto the adder inputA. - Tie the adder's
Binput to0and its carry-in to1, so it outputsA + 1. - Feed the adder's sum back into the register's
Dinput. - Hold the register's write-enable high so it loads the incremented value every clock.
- Add a reset that forces
Dto0, so the counter can start cleanly from address 0.
PC8): a register that adds 1 to itself each clock. RST clears it to 0; the outputs Q0-Q7 are the current address. Before the first clock the outputs read Z; open it in the lab, pulse reset, then run the clock and watch the value count up 0, 1, 2, 3.Try it
In the lab, pulse
RST then clock the program counter five times. What address does it show? Why is it safe for the register's output to feed an adder whose result loops back to the register's input?Answer
Build it in the lab ↗After reset (
0) and five clocks it reads 5: each edge loads PC + 1, so it counts 0, 1, 2, 3, 4, 5. The feedback loop is safe because the register only samples on the edge: between edges the PC + 1 value is stable, so the loop cannot race, the edge just rewrites the next value. Same reasoning as the register bit feeding Q back to itself.Two ways to build a counter: ripple vs synchronous
The
PC8 you just built is one kind of counter, a synchronous one: all eight bits share a single clock, and the adder computes the whole next value Q + 1 in one shot, so every bit updates on the same edge. There is an older, simpler kind too, the ripple (or asynchronous) counter, and comparing the two is the fastest way to understand what a counter really is.Both are built from toggle (T) flip-flops. Recall from flip-flop types that a T flip-flop with its input held
1 flips its output on every clock edge (Q+ = Q XOR T), so a toggling stage is a divide-by-2: its output is a clean square wave at half the frequency coming in. That single fact is the whole trick behind counting in binary.A ripple counter just chains those toggling stages: the system clock drives bit 0, and each stage's own output becomes the *clock* for the next stage up. Bit 0 flips every clock, bit 1 flips each time bit 0 rolls over from
1 back to 0, bit 2 flips each time bit 1 rolls over, and so on. Read the bits together and they count 0, 1, 2, 3, ... in binary, because a binary digit flips exactly when the digit below it wraps. It is called *ripple* because a change starts at bit 0 and ripples upward, one flip-flop at a time.A synchronous counter instead feeds one common clock to *every* flip-flop at once, and puts combinational logic in front of each bit to decide whether it should toggle this edge. The rule that makes it count up is exact: **bit k toggles precisely when all the bits below it are
1**, because that is the moment a binary carry reaches column k. The PC8 obeys this rule implicitly, its adder is just that carry logic spelled out for all eight bits.bit k toggles when Q0 ∧ Q1 ∧ ... ∧ Q(k-1) = 1
Written as toggle inputs for a 3-bit synchronous counter, that is
T0 = 1 (bit 0 always toggles), T1 = Q0, and T2 = Q0 AND Q1. Trace it and you get the same 0 to 7 sequence, but every bit that changes changes on the *same* edge:| count now (Q2 Q1 Q0) | T2 = Q1·Q0 | T1 = Q0 | T0 = 1 | count next |
|---|---|---|---|---|
| 000 | 0 | 0 | 1 | 001 |
| 001 | 0 | 1 | 1 | 010 |
| 010 | 0 | 0 | 1 | 011 |
| 011 | 1 | 1 | 1 | 100 |
| 100 | 0 | 0 | 1 | 101 |
| 101 | 0 | 1 | 1 | 110 |
| 110 | 0 | 0 | 1 | 111 |
| 111 | 1 | 1 | 1 | 000 |
011 both lower bits are 1, so all three toggle-enables are high and the count jumps cleanly to 100; at 111 it wraps to 000.So why prefer synchronous? In the ripple counter the bits do not settle together: bit 1 cannot move until bit 0's edge has propagated through it, bit 2 waits on bit 1, and so on, so an n-bit ripple counter's top bit lags the clock by n flip-flop delays. During that brief ripple the output passes through wrong intermediate codes. Counting
0111 up to 1000, a ripple counter momentarily shows 0110, 0100, 0000 as the carry chews upward, before landing on 1000. Anything reading the count combinationally at that instant sees a glitch. A synchronous counter has no such window: every bit is timed by the one clock, so the outputs step atomically from one valid count to the next.Counting to N: mod-N counters
A plain n-bit counter wraps after
2^n values (0 to 2^n - 1). Often you want a different cycle length, a mod-N counter that runs 0, 1, ..., N-1 and then returns to 0. The recipe is to let the counter increment normally but watch for the terminal count with a small detector: the instant the count reaches N, the detector clears the counter back to 0, so N never sticks and the visible cycle is exactly N states.For a mod-6 counter (
0 through 5), the terminal value is 6 = 110. A single AND of the two high bits Q2 and Q1 goes high only at 110 (no smaller count sets both), so wiring that line to the counter's clear forces 110 straight back to 000. The stable sequence is 0, 1, 2, 3, 4, 5, six states. The same idea gives a mod-10 counter, the BCD (binary-coded decimal) counter that holds one decimal digit: it counts 0000 to 1001 (0 to 9), and a detector on 1010 (= 10, caught by Q3 AND Q1) clears it, so one such counter per digit counts in decimal.The same terminal detector is how a counter becomes a timer: pick
N so the counter's clear pulse fires once every N clocks, and you have divided a fast clock down to a slow, precise tick (one per second, say). Every digital clock and stopwatch is a chain of mod-N counters counting seconds, minutes, and hours.Counters as frequency dividers
Because each bit toggles half as often as the bit below it, a counter is also a frequency divider. Bit 0 runs at half the clock, bit 1 at a quarter, bit 2 at an eighth, and in general **bit k of the count is the clock divided by
2^(k+1)**. So the *top* bit of a k-bit counter is the clock divided by 2^k. Feed a 4-bit counter a 16 kHz clock and its bits come out at 8 kHz, 4 kHz, 2 kHz, and 1 kHz: the top bit is 16 kHz / 2^4 = 1 kHz, a clean divide-by-16. Tapping different bits gives you a whole set of slower clocks for free from one fast one.frequency of bit k = clock / 2^(k+1) , so top bit of k bits = clock / 2^k
Common mistakes. Do not decode a ripple counter's outputs at full speed: while the carry ripples upward the outputs flash through wrong codes, so a decoder watching them can twitch (synchronous counters avoid this). Mind the mod-N off-by-one: detecting
N gives N states 0 to N-1, not N + 1, and the detect-then-clear trick lets the terminal value N appear for one glitchy instant (detecting N-1 and loading 0 on the next edge is the clean, glitch-free version). Get the toggle condition right: bit k toggles when all lower bits are 1, not just the single bit beneath it. And, as the fault below shows, a synchronous reset held high forever freezes the count at its reset value: pulse it once, then release it.Counting up handles the normal case: do the next instruction. But programs also jump (loops, branches, calls), which means loading the PC with an address that did not come from
+1. That is just the register's existing load path: route a target address into D and enable a load instead of the increment. A program counter is therefore a synchronous up-counter with a load path, count or load picked by a control line, which is precisely a multiplexer in front of D.The program counter says *where* the next instruction lives. The thing it indexes into is memory: a bank of storage the PC's address selects. Next we build that memory, then use the PC to walk through it.
Spot the fault
RESET1CLK1Q after 5 ticks0
Look at RESET
Wrong logic level
The count never leaves
0 no matter how many clock edges pass. RESET is stuck at 1, so every edge clears the register back to zero before the +1 can stick. A synchronous reset must be pulsed once and then released to 0; hold it high and the counter is frozen at its reset value.Frequently asked
What is a program counter?
A program counter (PC) is a register that holds the address of the next instruction to fetch. It is wired to an incrementer so it advances by
+1 on every clock edge, walking through memory 0, 1, 2, 3, ..., and it can also be loaded with a new address to jump.What is the difference between a ripple counter and a synchronous counter?
A ripple (asynchronous) counter chains toggle flip-flops, each stage clocked by the stage below it, so a change ripples up one flip-flop at a time and the top bit lags by several gate delays (the outputs glitch through wrong codes while it settles). A synchronous counter clocks every flip-flop from one common clock, with logic that toggles bit k when all lower bits are
1, so every bit updates together with no ripple and no glitch. Synchronous is faster and clean but needs more gates; ripple is simplest.What is a mod-N counter, and how does a BCD counter work?
A mod-N counter cycles through exactly
N values (0 to N-1) then wraps to 0. You build it by detecting the terminal count and clearing the counter back to 0 at that moment. A BCD (binary-coded decimal) counter is the mod-10 case: it counts 0000 to 1001 (0 to 9), and a detector on 1010 (= 10) resets it, so one counter holds one decimal digit.How does a counter divide a clock's frequency?
Each bit of a binary counter toggles half as often as the bit below it, so bit k comes out at the clock divided by
2^(k+1), and the top bit of a k-bit counter is the clock divided by 2^k. A 4-bit counter on a 16 kHz clock therefore produces 8 kHz, 4 kHz, 2 kHz, and 1 kHz on its four bits: a set of slower clocks derived from one fast one.How does a program counter handle jumps?
A jump loads the PC with a target address instead of letting it increment. That target arrives on the register's existing data input
D, so a control line picks count (PC + 1) versus load (the jump address): exactly a multiplexer in front of D.What is the difference between a program counter and an instruction register?
The program counter holds *where* the next instruction is (an address) and increments each cycle. The instruction register holds *what* the current instruction is (the byte fetched from that address) so the control unit can decode it.
Every lesson here builds toward one thing: a working CPU, from the transistor up.
Open the free lab →