Processor Datapath & Pipelining
Level: 5 (Mastery — cross-domain: quantitative analysis, proof, and code) Time limit: 75 minutes Total marks: 60
Notation: A classic 5-stage pipeline has stages IF, ID, EX, MEM, WB. Assume register write occurs in the first half of a clock cycle and register read in the second half (write-before-read in the register file), unless stated otherwise. Show all work.
Question 1 — Deep-pipelining optimum & throughput proof (20 marks)
A combinational datapath performs useful logic work with total logic delay (in ns). We split it into balanced pipeline stages. Each pipeline register adds a fixed overhead (setup + clock-to-Q + skew) per stage. The clock period is therefore
Independently, a fraction of dynamic instructions cause a 1-cycle stall bubble per additional pipeline stage beyond a baseline, modelled as an average CPI penalty that grows linearly with depth:
where (bubbles per instruction per extra stage) captures aggregate hazard cost.
(a) Throughput is instructions/ns. Write explicitly as a function of . (3)
(b) Treating as continuous, prove that has a unique interior maximiser (for suitable positive parameters) and derive a closed-form expression for . State the condition on parameters for to exist. (9)
(c) Using , , , compute the optimal (round to nearest integer), the resulting clock period, CPI, and throughput. Compare against . (5)
(d) Explain physically why real deep-pipeline curves fall off faster than this model predicts. Name two effects not captured by the linear-CPI assumption. (3)
Question 2 — Forwarding logic design & hazard proof (22 marks)
Consider the MIPS-style 5-stage pipeline with an EX/MEM and MEM/WB forwarding network feeding the two ALU operand muxes (ForwardA, ForwardB). Register $0 is hardwired to zero.
(a) Write the complete boolean/RTL conditions for ForwardA = 10 (forward from EX/MEM) and ForwardA = 01 (forward from MEM/WB), including the correct priority and the $0 and RegWrite guards. Justify why EX/MEM must take priority over MEM/WB. (8)
(b) Prove that even with full ALU-to-ALU forwarding, a lw followed by a dependent instruction cannot avoid exactly one stall, whereas an R-type producer followed by a dependent consumer needs zero stalls. Base the proof on the pipeline stage/cycle in which the value first becomes available versus needed. (6)
(c) Give the hazard-detection-unit condition (in terms of ID/EX.MemRead, ID/EX.RegisterRt, and the IF/ID register fields) that inserts the load-use bubble. Explain exactly which pipeline registers are frozen and which control signals are zeroed to create the bubble. (4)
(d) For the code below, list every forwarding path activated and every stall inserted, cycle by cycle. Assume forwarding + hazard detection are present. (4)
lw $t0, 0($s0)
add $t1, $t0, $s1
sub $t2, $t1, $t0
sw $t2, 4($s0)Question 3 — Precise exceptions in a pipeline (18 marks)
(a) Define what it means for exceptions to be precise, in terms of the architectural state visible to the handler. State the two invariants that must hold. (4)
(b) Two instructions can raise exceptions in the same cycle in different stages (e.g. an arithmetic overflow in EX and an instruction-fetch page fault in IF of a younger instruction). Prove that to preserve precise exceptions you must service the oldest faulting instruction and squash younger ones — and explain why an in-order pipeline makes this naturally solvable by deferring exception handling to a single point. (8)
(c) A designer proposes flagging exceptions and carrying them down the pipeline in the pipeline registers, handling them only in the MEM (or WB) stage. Explain the two advantages this "deferred/tagged" approach gives for precision, and identify one subtlety with instructions (like stores or branches) whose side effects must be suppressed before the handling point. (6)
Answer keyMark scheme & solutions
Question 1
(a) Substitute the two models: (3) (1 mark each factor correctly placed; 1 for reciprocal form.)
(b) Maximising ⟺ minimising the denominator (9) Let so . Then (2 marks expansion.) Differentiate: Set : Second derivative for , so this is a minimum of ⇒ unique maximum of . (2 marks: uniqueness + convexity.) Existence condition : , i.e. , and we need (so ). (1 mark.)
(c) With : (5) (2 marks.) At :
- ns
- CPI
- instr/ns.
(2 marks.) At : , CPI, instr/ns. So the optimum roughly doubles throughput vs. 5 stages ( vs ). (1 mark comparison.)
(d) Real curves fall faster because: (3)
- Register overhead does not shrink and eventually dominates (), so most of the cycle is pure latch overhead — diminishing returns are super-linear once .
- Hazard cost is super-linear, not linear: deeper pipelines lengthen the branch-misprediction penalty and load-use distance, so mispredict flush cost grows with depth; also power/clock-skew and jitter worsen. Any two: branch penalty growth, increased forwarding complexity, power density, limits on further logic partitioning. (1.5 each, cap 3.)
Question 2
(a) Forwarding to ALU input A: (8)
ForwardA = 10 (EX/MEM) if:
EX/MEM.RegWrite = 1
AND EX/MEM.RegisterRd ≠ 0
AND EX/MEM.RegisterRd = ID/EX.RegisterRs
ForwardA = 01 (MEM/WB) if:
MEM/WB.RegWrite = 1
AND MEM/WB.RegisterRd ≠ 0
AND NOT( EX/MEM.RegWrite AND EX/MEM.RegisterRd≠0
AND EX/MEM.RegisterRd = ID/EX.RegisterRs ) ← priority guard
AND MEM/WB.RegisterRd = ID/EX.RegisterRs
(RegWrite guard, ≠0 guard, match term, priority-exclusion term — 1 mark each × two cases minus overlap ⇒ 5 marks.) Priority justification (3): if both EX/MEM and MEM/WB write the same register, the instruction in EX/MEM is younger and thus produced the value later in program order; the architecturally correct operand is the most recent write. Forwarding from MEM/WB would deliver a stale value. Hence EX/MEM must win.
(b) Producer availability vs. consumer need (6):
- The ALU consumer needs both operands at the start of its EX stage.
- An R-type producer computes its result at the end of its EX stage. If it is immediately followed by a dependent instruction (distance 1), the producer is in MEM while the consumer is in EX in the same cycle. EX/MEM forwarding delivers the result to the consumer's EX input in that cycle ⇒ 0 stalls. (3)
- A
lwproducer obtains its data only at the end of its MEM stage. A distance-1 consumer would need it at the start of its EX, which is the same cycle as the load's MEM — the data does not exist yet (it appears only at cycle end). No forwarding path can send data backward in time. Inserting one bubble pushes the consumer's EX to the cycle when the load is in WB, and MEM/WB forwarding then supplies it ⇒ exactly one unavoidable stall. (3)
(c) Load-use hazard detection condition: (4)
if ( ID/EX.MemRead = 1
AND ( ID/EX.RegisterRt = IF/ID.RegisterRs
OR ID/EX.RegisterRt = IF/ID.RegisterRt ) )
→ stall
(2 marks.) To create the bubble: freeze the PC and the IF/ID register (they retain their values — instruction re-fetched/re-decoded next cycle), and zero all control signals of the ID/EX register (set the EX/MEM/WB control bits to 0) so a no-op ("bubble") flows down EX/MEM/WB harmlessly. (2 marks.)
(d) Cycle-by-cycle for the given code: (4)
| Instr | notes |
|---|---|
lw $t0 |
produces $t0 in MEM. |
add $t1,$t0,$s1 |
load-use hazard on $t0 ⇒ 1 stall inserted; then $t0 forwarded MEM/WB → EX (ForwardA=01). |
sub $t2,$t1,$t0 |
$t1 from add: EX/MEM→EX (ForwardA=10). $t0 from lw now old enough: MEM/WB→EX (ForwardB=01). |
sw $t2,4($s0) |
$t2 from sub: EX/MEM→EX (ForwardB=10) (into ALU/store data). |
Total: 1 stall (load-use), plus forwarding paths as listed. (2 stall correct, 2 forwards correct.)
Question 3
(a) Precise exception (4): an exception is precise if, when the handler runs, the architectural state is exactly as if instructions executed strictly in program order up to the faulting one and none after. Two invariants: (i) all instructions before the faulting one have completed and committed their state; (ii) the faulting instruction and all instructions after it have committed no state changes. (2 marks each.)
(b) Oldest-first proof (8): Suppose instructions (older, in EX with overflow) and (younger, , in IF with page fault) both fault in one cycle. If we serviced first, we would enter its handler while has not committed and while itself faults — violating invariant (i) that all pre-fault instructions complete, and forcing the handler to see partial/out-of-order state. Moreover should not even execute if faults, since in the sequential model control transfers at and is never reached. Therefore correctness requires servicing the oldest faulting instruction and squashing all younger in-flight instructions ( and beyond). (4 marks.) Why in-order helps (4): in an in-order pipeline, "oldest in flight" is simply "furthest down the pipeline," so program order = pipeline position. By deferring the actual trap to a single commit point (say WB) and comparing exception flags in stage order, the hardware always picks the oldest flagged instruction automatically and flushes everything behind it — no reorder buffer needed. (4 marks.)
(c) Tagged/deferred exceptions (6):
- Advantage 1: Exceptions are handled at one point in a fixed stage, so multiple simultaneous faults are naturally prioritised by pipeline position (oldest = furthest along), guaranteeing invariant (i)/(ii). (2)
- Advantage 2: State updates (register/memory writes) happen at/after that point, so an instruction carrying an exception flag can be turned into a no-op — no younger instruction has committed, and the faulting instruction commits nothing — satisfying precision without rollback. (2)
- Subtlety: instructions with early side effects — a store writes memory in MEM, a branch redirects the PC — must have those effects suppressed if any older instruction is flagged. So the flush/squash logic must reach back and cancel memory writes and PC updates of instructions that are logically younger than an as-yet-unhandled fault. (2)
[
{"claim":"k* = sqrt((1-a)T/(aO)) ~ 27.57 for T=20,O=0.5,a=0.05",
"code":"T,O,a=20,0.5,Rational(1,20); kstar=sqrt((1-a)*T/(a*O)); result = abs(float(kstar)-27.5680975) < 1e-4"},
{"claim":"Throughput at k=28 approx 0.3504 instr/ns",
"code":"T,O,a=20,0.5,0.05; k=28; Tclk=T/k+O; CPI=1+a*(k-1); Th=1/(CPI*Tclk); result = abs(Th-0.35035) < 1e-3"},
{"claim":"Throughput at k=5 approx 0.18519 instr/ns",
"code":"T,O,a=20,0.5,0.05; k=5; Tclk=T/k+O; CPI=1+a*(k-1); Th=1/(CPI*Tclk); result = abs(Th-0.185185) < 1e-4"},
{"claim":"Optimum beats k=5 (higher throughput)",
"code":"T,O,a=20,0.5,0.05; f=lambda k:1/((1+a*(k-1))*(T/k+O)); result = f(28) > f(5)"}
]