Structural Hazards

Imagine a five-stage pipelined processor executing one instruction per cycle. This is a dream throughput: one instruction retired every clock tick. Now suppose the instruction fetch stage and the memory-access stage both need memory. They need it in the same cycle. If there is only one memory port, one of them must wait. This is a structural hazard: two instructions, marching through the pipeline in lockstep, collide over a shared piece of hardware. Structural hazards matter because pipelining assumes every stage is free every cycle. That freedom must hold for the instruction sitting in it. Real chips have finite budgets of transistors, wires, and ports. A single-ported register file, one multiplier, one memory interface, or one bus can be shared across stages. Once several in-flight instructions need the same unit simultaneously, something must give. Either the pipeline must stall, or the hardware must be redesigned to avoid the conflict. Unlike Data Hazards and Forwarding, which arise from values flowing between instructions, structural hazards arise purely from hardware occupancy. They would exist even if every instruction were computing something totally unrelated to its neighbors. This article builds on The Pipelined Datapath, where we assumed each stage had its own dedicated resources. Here we relax that assumption. We ask: what happens when resources are shared? How does the pipeline detect and resolve the conflict? What does it cost in cycles and complexity? We derive the stall logic from first principles. We walk through the classic combined-memory example with a full cycle-by-cycle trace. We also connect the ideas to modern designs. These include multi-ported caches, banked register files, and superscalar issue logic. Such designs exist specifically to eliminate these hazards. By the end, you will be able to identify structural hazards in a pipeline diagram. You will compute the exact throughput penalty they impose. You will also explain the two standard fixes: duplicate the resource, or stall the pipeline until the resource frees up. This sets up the more subtle hazards — data and control — that dominate real-world pipeline design.

Key takeaways
  • A structural hazard occurs when two instructions in a pipeline need the same hardware unit in the same cycle.
  • The classic example: a single unified memory serving both instruction fetch (IF) and data memory access (MEM) at once.
  • Detection is purely a matter of resource occupancy tracking — no data values are involved, unlike data hazards.
  • Resolution options: stall (insert a bubble) or duplicate/pipeline the resource (e.g., separate I-cache and D-cache).
  • Stalling costs cycles but preserves correctness; over-provisioning hardware costs area/power but preserves throughput.
  • Real processors eliminate the most common structural hazards by design — split caches, multi-ported register files, multiple ALUs — reserving stalls for rarer conflicts.
The core insight

A structural hazard is a scheduling conflict, not a data conflict: it says "this wire or unit is busy," and the only fixes are to wait or to build more wires.

The idea

A pipeline overlaps the execution of multiple instructions, each occupying a different stage at each clock cycle. This overlap is only valid if every stage's hardware is available to every instruction that needs it. A structural hazard occurs when the pipeline schedule would require two (or more) instructions, in different stages, to use the same physical resource — a memory port, a functional unit, a register-file port, a bus — during the same clock cycle. The hardware cannot serve both requests simultaneously, so naively continuing would produce incorrect behavior (one access silently overwriting or ignoring the other) or is simply electrically impossible (one wire cannot carry two values at once). The canonical resource is memory. In a five-stage MIPS/RISC-V-style pipeline (IF, ID, EX, MEM, WB), the IF stage of instruction i+3 wants to read an instruction word from memory in the same cycle that the MEM stage of instruction i wants to read or write a data word. If instruction and data memory are the same physical memory with one port, both accesses cannot happen in that cycle. Other classic structural hazards include: a single non-pipelined multiplier that ties up the EX stage for several cycles while later instructions pile up behind it; a single write port on the register file when two instructions in WB in the same cycle both need to write back (rare in a scalar in-order pipeline, but common in superscalar designs); and a single shared bus between the pipeline and a peripheral or memory controller. It is essential to distinguish structural hazards from data and control hazards. A data hazard exists because an instruction needs a value produced by another instruction that has not been computed or written back yet — the hardware could serve both requests, but the data is not ready (this leads to Data Hazards and Forwarding). A control hazard exists because the pipeline does not yet know which instruction to fetch next (this leads to control-flow resolution, branch prediction, and delayed branches). A structural hazard exists because the hardware itself is double-booked, independent of what values are involved. This makes structural hazards, in one sense, the simplest hazard to reason about: you can detect them by looking only at a table of which stage uses which resource in which cycle, with no attention to instruction semantics at all.

How it works

The standard way to reason about structural hazards is to build a reservation table: a grid whose rows are hardware resources and whose columns are pipeline stages (equivalently, relative time steps after an instruction enters the pipeline). Each entry marks whether an instruction occupies that resource that many cycles after it starts. Two instructions separated by k cycles collide on resource R if their reservation-table rows for R overlap when shifted by k. For the concrete unified-memory example, define resource `MEM`. Instruction i uses `MEM` in its IF stage (cycle t) and again in its MEM stage (cycle t+3, since IF, ID, EX, MEM are stages 1–4). Instruction i+3, entering the pipeline three cycles after instruction i, wants to use `MEM` for its IF stage at cycle t+3 — exactly the cycle that instruction i needs `MEM` for its MEM stage. Collision. The pipeline control logic must detect this collision every cycle and, if found, freeze the earlier stage (or the later stage, depending on priority policy) by inserting a bubble — a cycle in which no useful work advances, functionally equivalent to a `nop` injected into the pipeline.

Detection and stall algorithm

``` // Runs once per clock cycle, in hardware, for every resource R // that can be shared across stages. function detect_and_resolve_structural_hazard(cycle_t, pipeline_state): for each shared_resource R in ResourceList: requesters = [] for each stage S in PipelineStages: instr = pipeline_state[S] // instruction currently in stage S if instr != BUBBLE and stage_needs_resource(S, instr, R): requesters.append((S, instr)) if len(requesters) > 1: // Conflict: resource R is wanted by more than one stage this cycle. winner = pick_priority(requesters) // policy: e.g., "later pipeline stage wins" for (S, instr) in requesters: if (S, instr) != winner: stall_stage(S) // freeze this stage's instruction in place insert_bubble(downstream_of(S)) // send a NOP forward instead // Stages not stalled proceed normally this cycle. ``` ``` // Concrete instantiation for the IF/MEM shared-memory hazard, // as implemented by the pipeline's hazard-detection unit. function memory_hazard_check(IF_instr, MEM_instr): if IF_instr requires memory read and MEM_instr requires memory read or write: // give MEM (older instruction) priority — it must complete, it's further along stall(IF_stage) // freeze PC, freeze IF/ID latch contents insert_bubble(ID_stage) // ID stage receives a NOP this cycle // else: no conflict, both proceed ``` The priority rule "older instruction wins" is not arbitrary: an in-order pipeline must retire instructions in program order, and letting a younger instruction "jump ahead" of an older one on a shared resource would either violate program order or require complex out-of-order bookkeeping. Stalling the younger instruction (freezing IF, keeping the same PC, holding the IF/ID latch) and inserting a bubble one stage downstream is the standard, easily verified fix.

Why splitting memory works

The dominant real-world fix is not to stall at all, but to make the conflict structurally impossible: give instruction fetch and data access separate physical memories (or separate cache ports), so `MEM_instruction` and `MEM_data` are different resources that can never collide. This is why virtually every real pipelined CPU has a split L1 cache — separate instruction cache (I-cache) and data cache (D-cache) — even though a unified L1 would sometimes have better hit rates. The throughput gain from eliminating a hazard that would otherwise fire on nearly every load/store instruction vastly outweighs the modest capacity loss from splitting one cache into two smaller ones.

Figure
Reservation-table view of the classic IF/MEM structural hazard, and its cure
Unified Memory: Structural Hazard Instr Cyc1 Cyc2 Cyc3 Cyc4 Cyc5 Cyc6 i1 (LW) IF ID EX MEM WB i2 IF ID EX MEM WB i3 IF ID EX MEM i4 IF? ID i1.MEM and i4.IF both want the one memory port in Cycle 4 — CONFLICT Fix A: Stall (bubble) i4 — stalled — IF ID bubble Cost: +1 cycle per conflicting instruction pair Fix B: Split I-cache / D-cache I-cache (IF) D-cache (MEM) Separate ports ⇒ IF and MEM never compete ⇒ zero stalls, every cycle full

Complexity & correctness

Cost of a structural hazard (stall model). Suppose a resource conflict fires with probability p per eligible instruction (e.g., every load/store instruction, if memory is unified) and each conflict costs exactly one bubble cycle. For n instructions with a fraction f being loads/stores, the total stall cycles are ``` StallCycles ≈ f · n · p ``` and the effective cycles-per-instruction (CPI) becomes ``` CPI_effective = 1 + f · p ``` instead of the ideal CPI of 1 for a fully pipelined, hazard-free design. This is a linear, per-instruction overhead — not asymptotic complexity in the algorithmic sense, but the relevant "complexity" metric for a hardware structure: cycles per instruction and hardware area/ports. For the unified-memory hazard specifically, every load and store instruction that overlaps with any IF in the same cycle triggers a stall, so in the worst case (steady stream of loads/stores with instruction fetch every cycle) p → 1, giving up to a 33%–50% CPI penalty in a naive 5-stage pipe, depending on instruction mix — a severe cost, which is exactly why real designs eliminate it structurally rather than tolerate it.

Cost of the fix (duplication model). Splitting into I-cache and D-cache costs extra hardware area (two tag arrays, two sets of sense amps, etc.) roughly proportional to the number of caches added, a constant-factor hardware cost, but reduces the stall probability p to 0 for that hazard. The trade is: spend area once, save cycles forever — a classic space/time trade that is almost always favorable once a hazard is common enough (loads and stores are typically 25–40% of dynamic instructions in real workloads).

Correctness argument. The pipeline is correct with respect to a structural hazard if and only if the reservation-table conflict is always detected before the conflicting cycle executes, and the resolution (stall or duplicate) always removes the double-use in that cycle without altering any other instruction's stage assignment. We can state this as an invariant maintained every cycle: > Invariant: At the end of every clock cycle, for every shared resource R, at most one non-bubble instruction is occupying R. The detection algorithm above checks this invariant proactively, before the cycle commits: it inspects, for the upcoming cycle, which instructions in which stages would use R, and if more than one would, it prevents all but one (the priority winner) from advancing into that use this cycle by stalling and substituting a bubble. Because a bubble by construction uses no resource, inserting a bubble can never itself create a new conflict. By induction over cycles — if the invariant holds at the start of cycle t (trivially true at t=0, an empty pipeline) and the detection/stall logic prevents any violation during cycle t, then it holds at the start of cycle t+1 — the invariant holds for all cycles, so the pipeline never actually double-drives a shared resource. This is precisely why structural-hazard logic is simpler to verify than data-hazard forwarding logic: it is a pure occupancy/scheduling argument with no dependence on data values, addresses, or control flow, only on which stage each instruction currently sits in.

Honesty about trade-offs. Stalling is simple to implement and verify but throws away cycles permanently; there is no way to "recover" a bubble later (in a strictly in-order pipeline). Duplicating hardware avoids stalls entirely but costs silicon area, power, and sometimes cycle time (a bigger, more complex register file with more ports can have a longer access time, which can lengthen the clock period for every instruction, not just the conflicting ones). Real designs choose per-resource: memory is almost always split (cheap fix, huge win), but a full crossbar giving every functional unit unlimited register-file ports would be prohibitively expensive, so real superscalar machines instead limit issue width or use banked/multi-ported register files as a compromise, accepting occasional structural stalls when contention is rare enough not to matter.

Worked example

Consider this instruction sequence running on a 5-stage pipeline (IF, ID, EX, MEM, WB) with a single unified memory (one port, shared by IF and MEM): ``` I1: LW R1, 0(R2) ; load I2: ADD R3, R1, R4 I3: SUB R5, R6, R7 I4: OR R8, R9, R10 ``` Without any hazard, instruction k would be in IF at cycle k, ID at cycle k+1, EX at cycle k+2, MEM at cycle k+3, WB at cycle k+4. I1's MEM stage falls in cycle 4. I4's IF stage, absent any stall, would also fall in cycle 4 (since I4 is the 4th instruction, IF at cycle 4). Both want the single memory port in cycle 4 — a structural hazard.

Cycle12345678
I1 (LW)IFIDEXMEMWB
I2 (ADD)IFIDEXMEMWB
I3 (SUB)IFIDEXMEMWB
I4 (OR)stallIFIDEXMEM→WB

At cycle 4, I1 is in MEM (needs the memory port) and, without intervention, I4 would be trying to enter IF (also needs the memory port). The hazard-detection unit sees this collision one cycle ahead, stalls I4's IF (freezes the PC, keeps fetching nothing useful — a bubble propagates into ID), and lets I1 win the memory port since it is older. I4 then successfully fetches in cycle 5, one cycle later than the ideal schedule, and every later stage of I4 is pushed back by exactly one cycle. I2 and I3, already past their IF stage, are entirely unaffected — the stall is localized to the instructions still contending for the resource.

Tally: 4 instructions, ideal completion at cycle 8 (I4's WB) with no hazard would have been cycle 7 — one extra cycle, exactly matching the prediction `StallCycles = 1 conflict × 1 bubble`. CPI over this window is `8 cycles / 4 instructions = 2.0` locally, though amortized over a longer program with only occasional such collisions, the steady-state CPI approaches `1 + f·p` as derived above.

Second case — the fix in action. Rerun the identical instruction sequence on a machine with a split I-cache/D-cache. Now I1's MEM access goes to the D-cache and I4's IF access goes to the I-cache — different physical ports, no conflict.

Cycle1234567
I1 (LW)IFIDEXMEMWB
I2 (ADD)IFIDEXMEMWB
I3 (SUB)IFIDEXMEMWB
I4 (OR)IFIDEXMEM→WB

All four instructions complete by cycle 7 with zero bubbles — the ideal `n + 4` cycle schedule for an n-instruction, 5-stage pipe. This is the direct, measurable payoff of eliminating the structural hazard by hardware duplication rather than tolerating it with stalls.

Common pitfalls & traps

PitfallWhy it happensFix
Confusing structural hazards with data hazardsBoth can "look like" a stalled pipeline in a diagramAsk: is a value missing (data hazard) or a unit double-booked (structural)? Check the reservation table, not the data dependencies.
Assuming splitting caches always helpsSplitting can reduce total cache capacity and hurt hit rate for instruction- or data-heavy codeModel both effects: stall-cycle savings vs. miss-rate cost; in practice the stall savings dominate for shared L1, which is why splits are near-universal there.
Forgetting that a stall must propagate downstreamFreezing one stage without inserting a bubble in the next stage corrupts the pipeline (same instruction "processed twice" or a slot left undefined)Every stall of stage S must pair with a bubble injected into stage S+1 on the same cycle.
Wrong priority policy (younger instruction evicts older)Naively picking "whichever request arrived last"Always give priority to the older instruction (the one further along the pipeline) to preserve program order in an in-order machine.
Ignoring multi-cycle functional unitsA non-pipelined divider/multiplier occupying EX for several cycles is a structural hazard too, not just the classic memory exampleTrack occupancy of every shared unit across all cycles it is busy, not just single-cycle stages.
Treating structural hazard rate as workload-independentStall probability p depends heavily on instruction mix (load/store frequency)Profile realistic workloads before deciding whether a resource needs duplicating.

Pioneers & history

The formalization of pipeline hazards — structural, data, and control — as a taxonomy for reasoning about pipelined machine design was crystallized in the classic RISC pipeline literature of the early 1980s, building on decades of mainframe and supercomputer pipelining practice.

Origins · 1 minJohn L. Hennessy and how this was discovered
JL
John L. Hennessy
born 1953 · Co-developed the RISC pipeline model (Stanford MIPS) and, with David Patterson, formalized the structural/data/control hazard taxonomy used throughout modern computer architecture education and industry.

History
1964
— IBM System/360 Model 91 introduces instruction pipelining and out-of-order execution at industrial scale.
1967
— Instruction Level Parallelism and pipeline interlocks studied formally in CDC and IBM technical reports.
1981
— Stanford MIPS project (Hennessy et al.) develops a RISC pipeline explicitly designed to minimize structural and data hazards.
1985
— Patterson and Hennessy popularize the structural/data/control hazard classification in RISC architecture courses.
1990
s — Split I-cache/D-cache becomes near-universal in commercial pipelined microprocessors specifically to eliminate the memory structural hazard.

Where this matters

Every modern pipelined CPU core — from embedded microcontrollers to high-end server chips — is designed around eliminating the most common structural hazards by construction. The near-universal split L1 instruction/data cache exists directly because of the IF/MEM conflict described here; a unified L1 would need a second read/write port or would stall on essentially every load and store. Multi-ported register files in superscalar and out-of-order processors (Intel's Core and Xeon lines, AMD's Zen, Arm's Cortex-A and Neoverse families) are engineered with enough read/write ports to let several instructions in flight access operands simultaneously, precisely to avoid structural stalls in the decode/writeback stages when issue width is greater than one. GPUs, which issue many threads' worth of instructions in parallel, replicate ALUs and memory banks partly to avoid the same class of contention at massive scale, and memory-bank conflicts in GPU shared memory are a direct descendant of the structural-hazard idea. Understanding structural hazards is also the natural stepping stone to Data Hazards and Forwarding (where the conflict is over values, not hardware) and to Control Hazards (where the conflict is over which instruction to fetch), the other two legs of the classic pipeline hazard taxonomy taught alongside The Pipelined Datapath.

Ask the tutor: How would the reservation-table method change if a functional unit (like a multiplier) takes 4 cycles instead of 1, and how many structural stalls would a back-to-back sequence of multiply instructions incur?

Is a structural hazard the same thing as a pipeline bubble?

No. A bubble is the mechanism used to resolve many hazards, including structural hazards, but it is not the hazard itself. A structural hazard is the underlying condition — two instructions competing for one physical resource in the same cycle. The bubble is the response: a cycle of no-op inserted to delay one of the contenders until the resource is free. The same bubble mechanism is also used to resolve data hazards (when forwarding is insufficient) and control hazards (when a branch outcome isn't known yet), so seeing a bubble in a pipeline diagram doesn't by itself tell you which type of hazard caused it — you need to check whether the conflict was over hardware, a value, or control flow.

Why don't all processors just add unlimited ports to every shared resource and avoid stalls completely?

Because ports cost area, power, and often cycle time. A register file with more read/write ports needs more wiring per cell, larger multiplexers, and typically a longer or more complex access path, which can slow down the clock for every instruction, not just the ones that would have conflicted. Engineers instead do a cost-benefit analysis per resource: memory is split into I-cache/D-cache because loads and stores are extremely frequent, making the stall cost high and the duplication cost comparatively low. But a fully non-blocking crossbar connecting every execution unit to every register-file port in a wide superscalar core would be enormous, so designers accept some structural stalls (or clever partial solutions like banking) where full duplication isn't worth it.

Can structural hazards occur in a single-issue, strictly in-order pipeline, or only in superscalar designs?

They occur in both, but are far more common and severe in superscalar and out-of-order designs. Even a simple single-issue 5-stage pipeline has the classic IF/MEM hazard if memory is unified, and can have hazards on a shared multiplier or divider that takes multiple cycles. Superscalar processors multiply the opportunities for conflict: with multiple instructions issuing per cycle, register-file ports, functional units, and cache ports must all be provisioned (or arbitrated) for several simultaneous requesters, so structural-hazard-avoidance hardware (extra ports, replicated units, arbitration logic) becomes a much larger fraction of the design effort.

How does a structural hazard differ from resource contention in operating systems or databases?

The underlying idea — two consumers wanting one resource at once — is the same conceptually, but the timing and stakes differ enormously. In a pipeline, the "consumers" are hardware stages operating on a fixed, synchronous clock, and the conflict must be detected and resolved within a single cycle (often less than a nanosecond), using dedicated combinational logic wired directly into the datapath. In OS or database resource contention (locks, mutexes, disk I/O queues), conflicts are resolved by software schedulers over microseconds to milliseconds, using queues, priorities, and sometimes blocking system calls. The pipeline case is a much lower-level, hardware-timed instance of the same general resource-scheduling problem.

Does a structural hazard ever improve performance, or is it always purely a cost?

It is always purely a cost in terms of raw throughput — a structural hazard, by definition, forces at least one instruction to wait that would otherwise have proceeded. However, tolerating some structural hazards can be a net design win if the alternative (duplicating the resource) would cost more in area, power, or clock speed than the stalls cost in lost cycles. So while the hazard itself never speeds anything up, choosing to accept it rather than eliminate it can be the economically correct engineering decision, especially for resources that are rarely contended in typical workloads.

How does a compiler or programmer interact with structural hazards, if at all?

Mostly not directly — structural hazards are resolved entirely in hardware by the pipeline's control logic, transparently to software. Unlike some data hazards (which compilers can sometimes mitigate through instruction scheduling to reduce stalls before forwarding was invented) or control hazards (which compilers influence through branch placement and prediction hints), structural hazards depend on hardware resource layout that software cannot see or reorder around in a general-purpose ISA. The main exception is performance-tuning at a very low level (e.g., avoiding known bank-conflict patterns in GPU kernels), where programmers indirectly avoid hardware resource contention by choosing memory access patterns carefully.

What's the relationship between structural hazards and pipeline depth?

Deeper pipelines (more stages) generally have more opportunities for structural hazards to arise, because more instructions are in flight simultaneously, increasing the chance that some pair of them wants the same unit in the same cycle. But deeper pipelines also often specialize stages more finely, which can reduce sharing (e.g., separate stages purely for address generation vs. memory access). The net effect is design-specific: pipeline depth increases contention opportunities in absolute terms, but careful stage design and resource duplication can keep the actual stall rate low even in very deep, high-frequency pipelines like those in modern high-performance CPUs.

Before this

Where this leads