the tag/index/offset split
When the post office sorts a letter, it reads the address in pieces with different jobs: the city decides which sorting centre, the street picks the route, and the house number is the final delivery within the street. A memory address fed to a cache is read the same way, chopped into three fields, each with a distinct job: the offset, the index, and the tag. Understanding this split is the key that makes every cache organization click into place.
Read from the low (rightmost) bits up: the OFFSET is the bottom bits that select which byte within a cache line (a 64-byte line needs 6 offset bits, since 2^6 = 64). Above it, the INDEX selects which set (or, in a direct-mapped cache, which single slot) the block maps to; if the cache has 2^k sets you need k index bits. The remaining high bits are the TAG, stored alongside the line and compared on lookup to confirm that the line currently sitting in that set really is the block you want, not some other block that happens to share the same index. Lookup is therefore: use the index to find the set, compare the tag(s) to detect a hit, then use the offset to pick the byte.
This three-way split is why caches are fast: the index is just some bits of the address, so finding the candidate set takes no search at all, and only the tag must be compared. It also explains the whole design space at a glance. More lines per set (higher associativity) means more tags compared in parallel and fewer index bits. A bigger line means more offset bits. Two different addresses with the same index but different tags are exactly the blocks that collide and cause conflict misses in a low-associativity cache. The honest subtlety to remember: which bits become index versus tag is a design choice, and a poor choice can make access patterns collide more — this is why some caches hash the index bits to scatter collisions.
A 32-bit address into a cache with 64-byte lines and 256 sets: bits [5:0] are the offset (64 bytes/line), bits [13:6] are the index (256 = 2^8 sets), bits [31:14] are the 18-bit tag. Lookup: index -> set, compare tag, offset -> byte.
An address is read as tag | index | offset: index finds the set, tag confirms the block, offset picks the byte.
The offset bits NEVER select a set — they only pick a byte inside a line, which is why blocks are line-aligned. A frequent beginner error is using too many or too few bits for one field; the three field widths must sum to the full address and each is fixed by the cache's geometry.