Worked examples — Set-associative and fully associative caches
This page is a drill hall. The parent note gave you the machinery — how an address splits into tag, set index and block offset, and how direct-mapped, set-associative, and fully associative caches differ. Here we throw every kind of question at that machinery so no exam scenario surprises you.
Before we start, one promise: every symbol is re-explained the moment it appears, and every number below is machine-checked in the verify block.
The symbols we will reuse
Keep these five letters in mind — every example plugs numbers into exactly these.
The scenario matrix
Think of a cache-mapping problem as a machine with three input dials:
- Associativity dial — how many places a block may go: (direct-mapped), some middle number (set-associative), or every line (fully associative).
- Address dial — a specific memory address, which may land anywhere, including on a boundary (offset ) or right at the last byte of a block.
- Question type dial — "decode the fields", "which set?", "hit or miss?", "how many bits?", or a real-world word problem.
The table below lists every case class this topic can throw. Each later example is tagged with the cell it covers.
| Cell | Case class | What is stressed |
|---|---|---|
| A | (direct-mapped) field decode | the degenerate low end of associativity |
| B | middle (4-way) field decode | the normal set-associative case |
| C | all lines (fully associative) | the degenerate high end — no index field |
| D | Two addresses colliding in a direct-mapped cache | conflict-miss birth |
| E | The same two addresses in a 2-way cache | conflict avoided by associativity |
| F | Offset = 0 (block boundary) vs offset = max (last byte) | degenerate / limiting offsets |
| G | A hit/miss trace with a replacement policy | temporal behaviour over time |
| H | Word problem — pick associativity under a bit budget | design decision |
| I | Exam twist — change block size, watch every field move | sensitivity to one dial |
Quick vocabulary refresh (so nothing is unearned)
Recall Why
and not plain division? answers "how many yes/no bits do I need to name one of things?" ::: Because each bit doubles the count you can name: bits name things, so to name things you need the with , i.e. .
The field ruler below shows this split drawn to scale (using Example B's widths). In every example that follows, mentally lay your address on this ruler: the orange slab on the right eats the low bits (offset), the teal slab in the middle eats the next bits (index), and the plum slab on the left keeps the rest (tag). Notice the caption's reminder that the three widths always sum to .

Example A — Direct-mapped decode (cell A)
Forecast: Guess first — with , how many sets are there? (Hint: as many as there are lines.)
- Lines (here bytes, bytes). Why this step? Each line holds one -byte block; bytes hold blocks, so that's the line count.
- Sets . Why this step? Direct-mapped means one line per set, so sets lines. This is the degenerate low end of the matrix.
- Fields: offset ; index ; tag . Why this step? Fields must sum to the address width; whatever isn't offset or index is tag.
- Decode
0x0000ABCD.- Offset = low 5 bits .
- Index = next 10 bits, i.e. bits .
- Tag = remaining high 17 bits (bits ) . Why this step? We slice from the right so each field keeps its numeric meaning: shift the address right by then mask bits for the index, shift right by for the tag.
Verify: ✓. Offset is a valid byte index () ✓. Line/set is in range ✓. (Numeric split checked in Verify block.)
Example B — 4-way set-associative decode (cell B)
Forecast: More ways than Example A — do you expect more or fewer index bits?
- Lines (here bytes, bytes). Why this step? Same rule as before — capacity divided by block size gives how many blocks fit.
- Sets . Why this step? Four lines share each set, so we need a quarter as many sets as lines. Fewer sets than the direct-mapped case with the same size — index bits shrink, tag bits grow.
- Fields: , , . Why this step? Offset covers the bytes in a block, index names one of sets, tag is the leftover.
- Decode
0x00DEAD00.- Offset = low 6 bits .
- Index = next 8 bits, bits .
- Tag = high 18 bits (bits ) . Why this step? Same right-to-left slicing; only the field widths changed.
Verify: ✓. Set ✓. Offset means the address sits on a block boundary — a cell F limiting case that also happens here (checked below).
Example C — Fully associative decode (cell C)
Forecast: With a block allowed anywhere, what happens to the index field?
- Lines (here bytes, bytes). Why this step? The capacity-over-block rule again; these 256 lines are the total parking spots.
- Sets (all lines in one giant set). Why this step? Fully associative the degenerate high end: no partitioning, so index bits . The index field vanishes.
- Fields: , , . Why this step? Everything above the offset is tag now — the tag must uniquely name the block among all of memory's blocks, because there's no set to narrow the search.
- Decode
0x00DEAD00: offset ; tag address .
Verify: ✓. Compare with Example B on the same address: the 4-way tag was with an 8-bit index . Here the tag swallows those 8 index bits, so the fully-associative tag equals — checked in Verify. This is the story "index bits migrate into the tag."
Example D — Two colliding addresses, direct-mapped (cell D)
Forecast: Two different addresses, same line — good or bad?
- Config: lines, , , , . Why this step? We need the field widths before we can slice either address; capacity/block gives 64 lines, direct-mapped makes 64 sets, so index is 6 bits.
- Index of
0x0120: bits .0x0120; low 4 bits are offset, next 6 bits . Why this step? We shift right past the 4 offset bits and read the next 6 bits — that names the set. - Index of
0x0520: bits .0x0520; next 6 bits as well. Why this step? Same slice on the second address; both give → they map to the same and only line for that set. - Tags differ:
0x0120tag = bits ;0x0520tag . Why this step? Same set, different tag = the two blocks fight for one line.
Result: Accessing them alternately causes each to evict the other every time → an endless string of conflict misses, even though 63 other lines sit empty. This is the parking-lot pain the parent note warned about.
Verify: both indices ✓, tags and (distinct) ✓.
The picture below draws exactly this: a column of 64 lines, only line highlighted, with both address arrows pointing at that single orange slot. The red caption says out loud why the other 63 lines cannot help.

Example E — Same two addresses, now 2-way (cell E)
Forecast: We doubled the choices per set. Does the ping-pong stop?
- Config: , sets, , , . Why this step? Halving the sets shifts the index window: index is now 5 bits (bits ), not 6, and the freed bit joins the tag.
- Index of
0x0120: bits . From0000\,0001\,0010\,0000_2, dropping the 4 offset bits and reading 5 bits gives . Why this step? Same slicing idea, but the window is now 5 bits wide, so we read one bit fewer at the top of the field. - Index of
0x0520: bits . From0000\,0101\,0010\,0000_2, the 5-bit slice is . Why this step? Bit (which differed between the two addresses) is now part of the tag, not the index — so both still share set . - But each set has 2 lines. Both blocks fit side by side: one in way 0, one in way 1. No eviction. Why this step? Associativity gives a second parking spot in the same set, so a shared index no longer forces a fight.
Result: The alternating accesses now both hit after the first two loads. Associativity converted a stream of conflict misses into hits — exactly the sweet-spot argument.
Verify: both indices ✓; two ways available so both resident simultaneously ✓.
Example F — Offset extremes: boundary vs last byte (cell F)
Forecast: Two addresses apart — same block or neighbours?
- , so offset low 6 bits. Why this step? We only need the offset width to test the extremes; six bits count the 64 bytes inside a block.
0x1000: low 6 bits → first byte of its block (the degenerate zero-offset boundary case). Why this step? Offset is the smallest possible offset — the block's starting byte.0x103F: low 6 bits → last byte of the same block (the maximum-offset limiting case). Why this step? Offset is the largest offset, and the bits above bit 5 match0x1000, so tag+index are identical → one line.- Block base address
0x1000; block spans0x1000–0x103F. Why this step? Masking off the low 6 bits of either address gives the same base, confirming a shared block.
Result: A single cache miss on 0x1000 brings in all 64 bytes, so the later access to 0x103F is a hit — this is spatial locality made concrete, and it shows both offset limits ( and ) inside one block.
Verify: offsets and ✓; identical block base (address AND ) ✓.
Example G — Hit/miss trace with LRU (cell G)
Forecast: Five accesses, two homes, LRU deciding evictions. How many hits?
We track the set as [MRU ... LRU] (most-recently-used first).
- A → set empty → MISS, load A. Set:
[A]. Why this step? Cold start — nothing is cached yet, so the first touch of A cannot hit. - B → not present, spare way free → MISS, load B. Set:
[B, A]. Why this step? B is a new block and the set still has a free way, so we load without evicting; B becomes most-recent. - A → present → HIT. Move A to front. Set:
[A, B]. Why this step? A is still resident from step 1; temporal locality pays off, and LRU order updates so A is now most-recent. - C → not present, set full → MISS. LRU is B (at the back) → evict B, load C. Set:
[C, A]. Why this step? Both ways are full, so we must evict the coldest; LRU marks B as least-recently used. - B → not present (we just evicted it!) → MISS, evict LRU = A, load B. Set:
[B, C]. Why this step? B returned one access too late — it was thrown out in step 4, so this is a fresh miss; A is now coldest and gets evicted.
Result: hits (only step 3), misses . Final set [B, C].
Verify: hit count , miss count , final (checked by a small LRU simulator).
The figure walks these five snapshots left to right, colouring HIT green and MISS orange, with the top box always the most-recently-used way — watch B fall to the back and get evicted.

Example H — Word problem: choose associativity under a bit budget (cell H)
Forecast: More associativity → bigger tags. Where does the tag cross 20 bits?
- Lines . Offset . Why this step? We need the line count to know how index bits split among sets; capacity/block gives 512 lines.
- Tag as a function of : sets , index , so Why this step? Each doubling of halves the sets, removing one index bit and adding it to the tag — so tag grows by exactly bit per doubling.
- Evaluate:
- : . ✓
- : . ✓
- : . ✓
- : . ✓ (exactly at budget)
- : . ✗ (over budget) Why this step? We plug each associativity into and compare against the 20-bit ceiling.
- Answer: 8-way is the highest associativity within the 20-bit tag budget.
Verify: ✓ and ✗ (both checked).
Example I — Exam twist: change block size, watch every field (cell I)
Forecast: Doubling the block — which fields grow, which shrink?
- Block 64 B: ; ; ; ; . Why this step? Baseline case: standard field computation for the smaller block.
- Block 128 B: ; ; ; ; . Why this step? Doubling adds one offset bit (each block covers twice the bytes) and halves the line count, removing one index bit. Those two changes are equal and opposite on the tag — so the tag stays put at 18.
- Interpretation:
- Offset: (bigger blocks need one more byte-selector bit).
- Index: (fewer, larger blocks → fewer sets).
- Tag: (unchanged — the shifts cancel).
Result: With cache size and associativity fixed, doubling block size trades one index bit for one offset bit; the tag is invariant. A tidy fact examiners love to test.
Verify: both tag values equal ✓; offset , index ✓.
The last figure stacks the two field rulers so you can see the boundary between index and offset slide left by one bit while the tag edge stays frozen — the arrow marks the single migrating bit.

Wrap-up recall
Recall As associativity rises with size fixed, what happens to index and tag bits?
Index bits shrink by 1 per doubling of ; tag bits grow by 1 per doubling of ; offset unchanged. ::: Because so , and the bit lost from the index is added to the tag.
Recall In Example G, why did B miss on its second appearance?
Because between B's two accesses, block C forced an eviction and LRU chose B (the coldest). ::: B returned one access too late — a textbook LRU thrash.
Related deeper dives: Cache performance metrics (turn these hit/miss traces into AMAT), Write policies (what a store miss does), Cache coherence (multiple caches racing), and the Translation Lookaside Buffer (TLB) which is itself a small fully associative cache.