Structural Hazards

Imagine a kitchen with five cooks but only one oven. However well you schedule the cooks, the moment two of them need to bake at the same instant, one must wait — not because of any dependency between their dishes, but because there simply isn't enough hardware to go around. That is a structural hazard: two instructions in the pipeline demand the same physical resource in the same cycle, and one of them has to stall.

Structural hazards are the most primitive of the three hazard families. Unlike a data hazard (about values) or a control hazard (about which instruction is next), a structural hazard is purely about resource contention — not enough units for the work in flight. And the fix is refreshingly blunt: build more hardware, or make everyone wait.

The classic case: one memory, two demands

The textbook structural hazard hides in the pipeline's use of memory. The IF stage reads an instruction from memory every cycle. The MEM stage reads or writes data for loads and stores. In steady state, every cycle has some instruction in IF and another in MEM — so if the machine has a single, unified memory with one access port, those two stages fight over it constantly.

Step through it: in cycle 4 the load lw is in MEM fetching its data while sub is in IF fetching its instruction word — both reach for the one memory port. Something has to give. The elegant fix is the Harvard split: give the machine a separate instruction cache and data cache, each with its own port. Now IF talks to the I-cache and MEM talks to the D-cache, and the collision vanishes. This is exactly why every real CPU has split L1 caches.

Two more resources that run out

Memory is the famous case, but the same pattern recurs whenever a resource is scarcer than the demand:

Scarce resourceThe collisionCure
Single memory portIF (instruction) vs MEM (data) in the same cyclesplit I-cache + D-cache (Harvard)
One register-file write porttwo instructions reaching WB togetheradd write ports; or write in the first half-cycle, read in the second
Non-pipelined multiplier / FPUa multi-cycle unit can't accept a new op each cyclepipeline the unit, duplicate it, or stall the issue

The register-file trick is especially neat: the file is clocked so that writes happen in the first half of the cycle and reads in the second half. An instruction writing back and another reading the same register in the same cycle then see the new value — and the single physical port is time-shared, dodging both a structural and a data hazard at once.

Duplicate or stall? The cost in CPI

Suppose we didn't split the caches. Then every load and store (say 30\% of instructions) would collide with an instruction fetch and stall one cycle, dragging CPI from 1 to 1 + 0.30 \times 1 = 1.30 — a 30\% slowdown for want of a second port. Splitting the cache costs a bit of silicon area but buys that whole 0.30 back. That trade — a little more hardware versus a lot of lost cycles — is the entire decision behind structural hazards.

// Structural hazard: added CPI = fraction stalled x stall cycles. function cpi(base: number, fracStall: number, stallCycles: number): number { return base + fracStall * stallCycles; } const base = 1.0; // ideal pipelined CPI const memOps = 0.30; // ~30% of instructions are loads/stores // Unified memory: every load/store contends with an instruction fetch -> 1 stall each. console.log(`unified memory (1 port): CPI = ${cpi(base, memOps, 1).toFixed(2)}`); // Split I-cache + D-cache: two ports, so IF and MEM never collide -> no stalls. console.log(`split I/D caches (2 ports): CPI = ${cpi(base, 0.0, 1).toFixed(2)}`); const gained = cpi(base, memOps, 1) - cpi(base, 0, 1); console.log(`CPI saved by splitting the cache: ${gained.toFixed(2)}`);

The name comes from the Harvard Mark I (1944), a room-sized electromechanical calculator that stored its program on punched paper tape and its data in separate counters — instructions and data physically apart. The rival von Neumann model put both in one memory, which is what programmers see. Modern chips are sneaky hybrids: the programmer's model is von Neumann (one address space), but down at the L1 cache the hardware is Harvard — separate instruction and data caches — precisely to dodge this structural hazard. Two 80-year-old machines, still arguing inside your laptop.

It's easy to lump all three hazard types together, but the distinction is sharp and worth memorising. A data hazard is about a value not being ready; a control hazard is about not knowing which instruction is next; a structural hazard is about not having enough hardware. Two completely independent instructions — no shared registers, no branch — can still stall if they both need the one multiplier this cycle. If you catch yourself looking for a data dependence to explain a stall and there isn't one, ask whether they're fighting over a resource.