Why the pipeline hates branches: static prediction and the delay slot

After this lesson you will count what a branch costs in the 5-stage pipeline, pick the static scheme that fits a loop or an if, and fill a delay slot legally — the convention every VLIW exercise of the exam is built on.

Take the loop of the June 2025 paper, Question 5. The BNE at its bottom runs five times: four jumps back, one fall-through. A predictor with no memory at all — it cannot even remember what happened last time — gets this branch right four times out of five.

Point the same predictor at the loop of the 2025 midterm, whose exit test BEQ R1, R2, DONE sits at the top, and it is wrong 100 times out of 101. The only difference between the two branches is the sign of one number inside the instruction.

By the end of the lesson you will know which number, why the basic 5-stage pipeline throws away three cycles on every branch, and why every VLIW exercise of the exam says "branch solved in ID stage, 1 cycle delay slot".

A branch answers two questions, and both come late

You know from your first architecture course what a conditional branch does: BNE R1, R2, LOOP compares two registers and continues at the label if they differ, at PC + 4 otherwise. The notes write RISC-V beq x1, x3, L1; this notebook writes branches as the exam papers do, without the dollar sign of the older papers ($R1, $F2). Here is the loop of the June 2025 paper in that spelling (the paper prints ADDI.U for ADDUI and ADDI.D for FADD):

INIT: ADDUI R1, R0, 0
      ADDUI R2, R0, 20
LOOP: LD    F0, 0(R1)
      FADD  F2, F0, F0
      SD    F2, 0(R1)
      ADDUI R1, R1, 4
      BNE   R1, R2, LOOP

What makes a branch special is who is waiting for its answer: the fetch unit needs the next PC every cycle, and a branch delivers it in two parts, both late.

Branch outcome (BO in the notes) — one bit: taken (T, continue at the label) or not taken (NT, continue at PC + 4). Branch target address (BTA) — where a taken branch continues: PC + offset, the offset being the sign-extended immediate of the instruction. A scheme that knows "taken" but not "where" still cannot fetch.

In the basic pipeline of the notes (pp. 30–31) the ALU compares the registers and adds PC + offset in EX, and the PC is written at the end of ME: for three cycles after the branch is fetched, fetch does not know where to go. That gap is the control hazard, a "pipeline hazard due to uncertainty in branch outcome during instruction fetch" in the notes' summary box (p. 32), caused by every instruction that modifies the PC, jumps included. Fetch can only stall until the PC is known, or fetch and flush: guess, keep going, throw the guessed instructions away if the guess was wrong.

cycle 12345678910 BNE I5 I6 IFIDEXMEWB IFIDEXMEWB IFIDEXMEWB PC written at ME

Before: outcome in EX, PC written in ME — 3 bubbles

cycle 12345678910 BNE I5 I6 IFIDEXMEWB IFIDEXMEWB IFIDEXMEWB two cycles saved

After: outcome and target in ID — 1 bubble

Fig. 1. Fetch stalled until the PC is known (the highlighted cell); I5 is fetched in the cycle after it. Left: the notes' Figure 10 (p. 34). Right: the diagram of p. 36.

Read the penalty off the figure: the next PC is known at the end of stage r of the branch (ME is stage 4, ID is 2), so the fetch slots of cycles 2 … r are lost.

Branch penalty = (stage at whose end the PC is written) − 1: three cycles with the PC written in ME, one cycle with the branch solved in ID.

On the notes' four-instruction sequence (p. 34), counted as the formula sheet counts, cycles = IC + 4 + stalls:

Four instructions, one of them a branch (notes pp. 34, 36, 40).
PC written at the end ofFetch doesStallsCycles = IC + 4 + stalls
MEwaits34 + 4 + 3 = 11
IDwaits14 + 4 + 1 = 9
IDguesses right04 + 4 + 0 = 8
IDguesses wrong, flushes14 + 4 + 1 = 9

Early branch evaluation: move the comparison into ID

Nothing forces the comparison to wait for EX: BNE needs an equality test, a cheap tree of XOR gates, and its target is PC-relative, so an adder can produce PC + offset right after decoding. The notes (p. 35) list what the ID stage gains — comparison and addition logic, a multiplexer with control signals to write the PC, wider data paths for the offset and the register values — and both answers are ready at the end of ID: the right panel of Fig. 1.

IF ID EX ME PC instr. memory register file offset = + branch outcome target address ALU moved from EX into ID
Fig. 2. Early branch evaluation, after the notes' Figure 11 (p. 35): the comparator and the adder sit in ID, and their results go straight back to the PC.

The price: a branch that is hungry for its operands

The comparator in ID wants its operands one stage earlier than an ALU would. If the instruction right before the branch produces one of them, forwarding cannot help — the producer is still in EX when the branch asks in ID — so the branch waits one cycle, two after a load. Hence the sentence of every VLIW exercise of the three papers, "1 Integer ALU with 1 cycle latency to next Int/FP/L/S & 2 cycle latency to next Branch": the loop counter must be updated at least one instruction before the branch that tests it.

      ADDUI R1, R1, 4      # produces R1 in EX
      BNE   R1, R2, LOOP   # needs R1 in ID: one stall cycle
An ALU instruction immediately before the branch that reads its result, branch solved in ID.
C1C2C3C4C5C6C7
ADDUIIFIDEXMEWB
BNEIFstallIDEXMEWB

One independent instruction between them removes the stall. Early resolution buys a short penalty and a hungrier branch: both halves are exam material.

Stall or flush: the CPI formula

Stalling pays the penalty on every branch; a guessed fetch pays it only when wrong. The derivation turns that into the one formula of this lesson that is not on the formula sheet.

  1. The formula sheet's cycle count for IC instructions on the 5-stage pipeline (4 = the pipeline fill):

    \[ \text{cycles} = \text{IC} + 4 + \text{stalls} . \]
  2. A fraction \(f_b\) of the instructions are branches; each costs \(r - 1\) cycles when fetch guessed wrong, with probability \(p_{\text{mis}}\):

    \[ \text{stalls}_b = \text{IC} \cdot f_b \,(r - 1)\, p_{\text{mis}} . \]
  3. Divide by IC; for a long program the fill term \(4 / \text{IC}\) vanishes:

    \[ \text{CPI} = 1 + f_b\,(r - 1)\,p_{\text{mis}} . \]
  4. Stalling is "always wrong", \(p_{\text{mis}} = 1\). A predictor pays only when wrong: always not taken has \(p_{\text{mis}} = p_T\), the taken fraction; always taken has \(1 - p_T\).

  5. With \(f_b = 0.2\), \(p_T = 0.6\) and the branch solved in ID: stall 1.20, always not taken 1.12, always taken 1.08. With the PC written in ME: 1.60, 1.36, 1.24 — a machine built for CPI = 1 runs at 1.6 before a single cache miss.

\[ \text{CPI} = 1 + f_b \cdot \text{penalty} \cdot p_{\text{mis}} \] \[ \text{penalty} = r - 1 \]
\(f_b\)
fraction of executed instructions that are branches (about one in five in integer code)
\(r\)
stage at whose end the next PC is written: 2 for ID, 4 for ME
\(p_{\text{mis}}\)
probability that fetch went the wrong way: 1 when stalling, \(p_T\) for always not taken, \(1 - p_T\) for always taken

One branch, three ways to pay

The diagram shows BNE R1, R2, LOOP of the loop above and the three instructions fetched after it. When the branch is taken the correct continuation is I0 LD, I1 FADD, I2 SD at LOOP; when it falls through it is I5, I6, I7, whatever follows the loop. The highlighted cell is where the next PC is finally known; orange cells are stall bubbles; crossed cells are instructions fetched on the wrong path and flushed. The CPI readout uses the two sliders.

  1. Before touching anything: with fetch stalled and the PC written in ME, how many empty fetch slots do you expect? Now switch the resolution to EX and then to ID. What rule links the lost cycles to the stage?
    What you should see

    Three bubbles, then two, then one: the lost cycles are always the resolution stage minus one, because the first correct fetch can only happen in the cycle after the PC is written. Nothing else about the diagram changes.

  2. Keep ID and choose "predict not taken". The branch is taken, so the guess is wrong: predict how many cycles are lost compared with stalling. Then flip the switch so the branch is not taken.
    What you should see

    Wrong guess: exactly one cycle, the same as stalling — the I5 fetched in cycle 2 is crossed out and the LD is fetched in cycle 3. Right guess: zero lost cycles, I5 simply continues. Prediction never makes a single branch cheaper than stalling; it makes the average cheaper, because you pay only when wrong.

  3. Set \(p_T = 0.8\) (four taken branches out of five, like the loop). Which policy gives the lowest CPI, and does the answer change when you move \(p_T\) to 0.2?
    What you should see

    With \(p_T = 0.8\) "predict taken" wins (\(p_{\text{mis}} = 0.2\)); with \(p_T = 0.2\) "predict not taken" wins. Stalling never depends on \(p_T\): its CPI stays at \(1 + f_b \cdot \text{penalty}\). The whole of the next section is about choosing the guess that matches the branch.

Guessing with no memory: the four static schemes

Branch prediction is speculation: guess the outcome, fetch along the guessed path, flush and restart if the guess was wrong. One rule keeps it safe (p. 37): "the processor must avoid updating its internal state (registers, memory, etc.) until the branch outcome is known with certainty". With the branch solved in ID that is free, since the wrongly fetched instruction is still in IF; the out-of-order machines of lesson 17 need a reorder buffer for it.

A static scheme guesses the same way for a given branch every time — a fixed hardware rule or a compiler hint; "static" means nothing is learned at run time, not "the compiler decides". Dynamic schemes (lessons 5 and 6) learn from the branch's history. The notes' four static rules, each with its cost and its failure case:

Always not taken, always taken — and why one of them needs a table

Branch always not taken costs nothing: keep fetching PC + 4 "without any need to determine or access the Branch Target Address" (p. 39). When the branch turns out taken, the one instruction fetched after it becomes a NOP and fetch restarts at the target — one cycle in the ID-resolved pipeline (the notes' Figure 12, left panel below). The right bet for an if whose else path is rare — the notes' example (p. 39) is if (z > 0) w = x; else w = y; with z usually positive, so the branch to the else part is usually not taken — and the wrong one for every loop-closing branch.

Branch always taken mirrors it in direction but not in cost: to fetch the target in the next cycle you must know it while the branch is still in IF, before decoding — and in IF nothing can be computed about a branch, only remembered, indexed by its PC. That memory is the Branch Target Buffer, "a specialized cache in the processor designed to predict the target address of a taken branch instruction before the branch condition is actually resolved" (p. 41): key, the address of the branch; value, the predicted target. In the notes' Example 1 (p. 42) the entry 0x100 → 0x80 sends fetch to 0x80 as soon as the branch at 0x100 is fetched; if the branch is then not taken, the instruction from 0x80 is flushed and fetch resumes at 0x104 — again one cycle. On a miss the processor "might default to sequential execution (PC + 4) or wait for the BTA to be calculated". The BTB is lesson 5; here, remember that without it always taken has nothing to fetch, since the target is known no earlier than the outcome.

cycle 12345678 BNE I5 LD FADD IFIDEXMEWB IF IFIDEXMEWB IFIDEXMEWB flushed restart from the target

Predicted not taken, actually taken (notes' Fig. 12)

cycle 12345678 BNE LD I5 I6 IFIDEXMEWB IF IFIDEXMEWB IFIDEXMEWB flushed restart from I5

Predicted taken via the BTB, actually not taken (notes' Fig. 13, corrected)

Fig. 3. The two mispredictions of the ID-resolved pipeline: the price is always the one fetch slot used while the branch sat in ID; only the flushed instruction and the restart address differ.

Backward taken, forward not taken

The two uniform rules are each right on one kind of branch, so combine them with the one bit that tells the kinds apart: the direction. BTFNT predicts a backward branch (target below the current PC) as taken, because backward branches close loops and a loop of N iterations takes its bottom branch N − 1 times; a forward branch as not taken, because forward branches usually guard an else path or an error handler that is rarely entered. The notes (p. 44) call it "just a comparison of the target address vs the current PC"; in hardware it is the sign bit of the offset. It is not adaptive — a forward branch taken 90 % of the time is mispredicted 90 % of the time. And the sign bit is only known after decoding, when the ID-resolved pipeline knows the outcome anyway: in the course's pipeline the guess pays off only if it is stored, with the target, in the BTB entry of the branch, so that it is available in IF. The tables of this lesson assume that.

LOOP: LD F0, 0(R1) FADD F2, F0, F0 SD F2, 0(R1) ADDUI R1, R1, 4 BNE R1, R2, LOOP offset < 0 : backward predict taken LOOP: BEQ R1, R2, DONE ADD R5, R5, R4 ADDUI R1, R1, 1 J LOOP DONE: ... offset > 0 : forward predict not taken
Fig. 4. The two loops of the hook: the June 2025 BNE jumps back (taken 4 times in 5), the midterm's BEQ jumps forward (taken once in 101). BTFNT reads the sign of the offset and gets both right.

Profile-driven prediction

The fourth scheme lets the compiler decide per branch, from evidence: run the program on representative inputs, record how often each branch was taken, and encode the verdict as a hint bit in the instruction, 1 if usually taken, 0 if not. The notes' pattern (p. 45), T T T T T T T T NT NT NT, gives the hint "taken" and about 27 % mispredictions if future runs look like the profile. The processor only reads a bit; the costs are a profile-compile cycle, a profile that goes stale when the inputs change, and an instruction format with a spare bit — RISC-V has none, so compilers there lay the code out so that the frequent path falls through.

The notes' Table 2 (p. 47), with the delayed branch of section 4 included:

The notes' Table 2 (p. 47), lightly reworded.
TechniquePrediction sourceComplexityRiskBest for
Always not takenassume PC + 4very lowmispredicts backward branchesif-then-else
Always takenassume the jump to the BTAmoderate (BTB)mispredicts forward branchesloops (backward branches)
BTFNTdirection of the branchlowerrors in irregular control flowmixed code
Profile-drivendata of prior runshigh (compile time)profile mismatchstable, performance-tuned code
Delayed branchcompiler-scheduled slothigh (compiler)wasted work if the slot cannot be filledRISC pipelines such as MIPS

Two branches, three schemes, one bit

The top strip is the backward loop branch, executed N times: taken on every iteration but the last (T = taken, N = not taken). The bottom strip is a forward if-branch executed 20 times, taken with the probability you choose. Under each outcome row, the guess of every scheme; an orange cell is a misprediction. The readouts count them.

  1. Before moving anything, predict the number of mispredictions of "always not taken" and "always taken" on a loop of N = 5. Then set N = 20. Which scheme's count stays at 1 whatever N is?
    What you should see

    Always not taken is wrong N − 1 times (4, then 19): every iteration but the exit. Always taken and BTFNT are wrong exactly once, at the exit, for any N — a loop branch is the easiest branch in the world for a scheme that says "taken".

  2. Now the if-branch: with the taken probability at 0.1, which scheme is worst, and by how much? Raise the probability past 0.5 and watch the ranking.
    What you should see

    At 0.1 "always taken" is wrong about 9 times in 10 — the exact opposite of its loop performance — while "not taken" and BTFNT are wrong about 1 in 10. Past 0.5 the ranking flips: a forward branch that is usually taken fools BTFNT exactly as badly as it fools "always not taken".

  3. Look at the "both branches" readout. Can either uniform rule ever beat BTFNT on the two branches together?
    What you should see

    Only when the if-branch is taken more often than not. Below that, BTFNT equals the better of the two uniform rules on each branch — it is "always taken" for the loop and "always not taken" for the if, chosen by the sign of the offset. That one bit is the whole trick.

Putting numbers on it: the two exam loops

The exam asks for a table and a rate, so do both loops of the hook by hand once. The June 2025 BNE executes with R1 = 4, 8, 12, 16, 20 against R2 = 20: five executions, T T T T NT. The midterm BEQ executes with R1 = 0 … 100 against R2 = 100: 101 executions, NT 100 times, then T at the exit.

Mispredictions of the static schemes, branch solved in ID. Loop A: the June 2025 BNE, backward, 5 executions. Loop B: the midterm BEQ, forward, 101 executions. The last row is what the 2025 paper actually asked (lesson 5).
SchemeLoop A (backward BNE)Loop B (forward BEQ)
stall, no guesspays on all 5: 5 cyclespays on all 101: 101 cycles
always not taken4 of 5 = 80 %1 of 101 ≈ 1 %
always taken, BTB hit1 of 5 = 20 %100 of 101 ≈ 99 %
BTFNT1 of 5 = 20 %1 of 101 ≈ 1 %
profile hint (T for A, NT for B)1 of 5 = 20 %1 of 101 ≈ 1 %
2-bit BHT from Strongly NT3 of 5 = 60 %—

To turn a rate into cycles, count. The 2025 program executes 2 + 5 × 5 = 27 instructions; with branch stalls only (the body's load-use hazard is left out on purpose), stalling costs 27 + 4 + 5 = 36 cycles (CPI 1.33), always not taken 35 (1.30), always taken 32 (1.19). In the formula sheet's loop notation, m = 5 and k = 1 stall per iteration give CPI∞ = (m + k) / m = 1.2 for the stalled loop; a correctly predicted branch has k = 0.

The 2025 paper's table has columns "1st … 5th iteration" and rows "Prediction State", "Prediction", "Branch Outcome". For a static scheme the state row is constant: write the prediction row, the outcome row T T T T NT, mark the mismatches, then the rate. A bare "80 %" loses the motivation point.

The delay slot: the bubble becomes a contract

The delayed branch stops guessing and changes the rules: the instruction right after the branch — the branch delay slot — "is always executed, regardless of whether the branch is taken or not" (p. 46), and the branch takes effect only after it. The bubble gets a meaning and the compiler gets the job of filling it; if it finds nothing it writes a NOP, which is exactly the stall you were trying to avoid.

The notes' Figure 14 (p. 46) on the widget's loop: the slot instruction (FMUL F4, F4, F0) is fetched while the branch is in ID; the branch steers only the fetch of cycle 3.
C1C2C3C4C5C6C7C8
BNEIFIDEXMEWB
FMUL (slot)IFIDEXMEWB
LD at LOOPIFIDEXMEWB
nextIFIDEXMEWB

The compiler's checklist (p. 46): the candidate must be independent of the branch decision — it must not produce a register the branch compares and must not be a branch itself — and safe to execute whether the branch is taken or not. It can come from three places.

FMUL F4, F4, F0 BNE R1, R2, LOOP delay slot SD F2, 0(R3) LD F0, 0(R1) at LOOP from before from the target from the fall-through
Fig. 5. Three sources for the slot: an instruction that had to execute anyway; the first instruction of the taken path, copied; the first instruction of the not-taken path, moved up.

From before: always a win

Take an instruction above the branch that the branch does not depend on and move it below. It had to execute on both paths, so the slot is useful whatever the outcome, with no safety condition beyond independence and no code growth. The notes' Example 2 (p. 48) in the papers' mnemonics: the ADD writes R1 and reads R2, R3; the branch reads R2 and R4; nothing links them.

ADD  R1, R2, R3
BEQ  R2, R4, L1
NOP              # delay slot
BEQ  R2, R4, L1
ADD  R1, R2, R3  # delay slot

From the target: a bet on "taken"

When nothing independent sits above the branch, copy the first instruction of the target into the slot and retarget the branch to the instruction after it. Taken, the slot did exactly what came next — for a loop, almost every time. Not taken, the slot instruction executes all the same, so it must be harmless on the fall-through path: typically it writes a register that path overwrites before reading. The notes' Example 3 (p. 49), with the loop made explicit:

L1:  SUB  R4, R5, R6
     ADD  R1, R2, R3
     BEQ  R1, R0, L1
     NOP              # delay slot
L1:  SUB  R4, R5, R6  # kept: first entry
L2:  ADD  R1, R2, R3  # new target
     BEQ  R1, R0, L2
     SUB  R4, R5, R6  # slot: the copy

The SUB now exists twice — the instruction duplication of p. 50. Only this branch is retargeted, to L2; the original stays at L1, because the first entry into the loop and any other branch that jumps to L1 still need it there. The price is code size and two copies to keep consistent; the gain is one cycle on every taken iteration — why the notes recommend it for loops, "particularly do-while constructs".

The example also hides the stall of section 1: ADD R1, R2, R3 produces R1 and BEQ R1, R0, L1 tests it in the very next cycle. Any instruction between the two would cure it; the quiz asks about this.

From the fall-through: a bet on "not taken"

The mirror bet moves the instruction that follows the branch on the sequential path into the slot. Not taken, the slot was needed anyway; taken, it still executes, so its result must be dead on the target path. The notes' Example 4 (p. 51): OR R7, R8, R9 is the fall-through instruction, SUB R4, R5, R6 at L1 the target.

     ADD  R1, R2, R3
     BEQ  R1, R0, L1
     NOP              # slot
     OR   R7, R8, R9  # not taken
L1:  SUB  R4, R5, R6  # taken
     ADD  R1, R2, R3
     BEQ  R1, R0, L1
     OR   R7, R8, R9  # slot
L1:  SUB  R4, R5, R6

The condition: R7 is not read at L1 or after it before being written again. Rarely-taken forward branches are the natural customers.

What the slot really costs, and why RISC-V has none

Two background facts complete the picture. A variant in which the slot is squashed exists — the cancelling branch (MIPS "branch likely"), whose slot executes only if the branch goes the predicted way: "from target" fills become safe, at the price of a flush when the prediction fails. And the slot writes the depth of one pipeline into the instruction set: a deeper pipeline has several bubbles and one slot hides only the first, a superscalar front end would need several slots, and an exception in the slot of a taken branch must restart at two non-adjacent addresses. Compilers fill a single slot usefully only about half of the time (Hennessy & Patterson: about 60 % filled, 80 % of those useful).

MIPS, SPARC and HP-PA each have one architecturally visible delay slot. RISC-V's designers rejected delay slots, with condition codes, as features that "complicate higher performance implementations"; its specification says control-transfer instructions in RV32I "do not have architecturally visible delay slots". McFarling had written in 1993 that delayed branches are "decreasingly effective as the number of delay slots to fill increases". The course uses RISC-V syntax with a MIPS-style slot as a convention; at the exam the exam text wins.

The exam's convention: "branch solved in ID stage, 1 cycle delay slot"

Now read the sentence every VLIW exercise of the 2024, 2025 and 2026 papers repeats: "The branch is completed with 1 cycle delay slot (branch solved in ID stage). No branch prediction." "Solved in ID" is early evaluation, hence the integer unit's "2 cycle latency to next Branch". "1 cycle delay slot" is the bubble of that pipeline, counted as a cycle of the iteration and written as "(delay slot)" in the row after the branch; nothing is scheduled in it, because the branch is the last instruction of the iteration and the exercise forbids the loop transformations that would move an instruction past it. "No branch prediction": the fetch after the slot waits for the ID-stage decision. The 2026 paper's LOOP1 (lesson 20 derives it):

EX2 of the 22 June 2026 paper, LOOP1, 3-issue VLIW (Memory 3, FP 3, Integer 1 — 2 to a branch), as the readers recomputed it: critical path 10 cycles with the slot, CPI = 10 / 5 = 2, code efficiency = 5 / (10 × 3) ≈ 0.17.
Memory UnitFloating Point UnitInteger Unit
C1LD F2, 0(R1)
C2
C3
C4FADD F6, F2, F2
C5
C6
C7SD F6, 0(R1)ADDUI R1, R1, 4
C8
C9BNE R1, R3, LOOP1
C10(delay slot)

In every exam table the delay slot is a cycle you count: the branch goes two cycles after the ADDUI that it tests, the slot is the next row, and the critical path ends there.

Fill the slot

The loop on the left sums and multiplies the elements of an array; its branch has an empty delay slot. Choose an instruction for the slot. The widget checks whether the move is legal and shows what executes on each path: the taken path continues at LOOP, the fall-through path continues with the SD after the loop. Green means the slot does work that path needed, orange means work that path did not need, a red cross means the program would be wrong. Every instruction here spends one cycle in EX.

  1. Before choosing: which of the seven candidates do you expect to be illegal, and why? Then try ADDUI R1, R1, 8 and the second BNE.
    What you should see

    Three are illegal. ADDUI R1, R1, 8 produces the R1 the branch compares, so the branch would test a stale value; the LD moved down from the top is read by the FADD and FMUL above the slot; and a branch cannot sit in a delay slot at all. Legality is about dependences, never about how useful the instruction is.

  2. Choose FMUL F4, F4, F0 and then the LD "from the target". Both give five cycles per iteration — so what is the difference between them?
    What you should see

    The FMUL is green on both paths: it is work every iteration needed, so nothing can go wrong. The LD is green when taken but orange on the exit path: the slot loads one element past the end of the array, which must be harmless (readable address, F0 dead after the loop), and the branch had to be retargeted with a copy of the LD placed before the loop. "From before" is a sure win; "from the target" is a bet that must be covered.

  3. Choose SD F2, 0(R3), the instruction after the loop. Is it legal? Is it a good idea for this branch?
    What you should see

    Legal, but a poor bet: the store is useful only once, when the loop exits, and runs needlessly on every taken iteration (and must be harmless then). The cycles per iteration stay at six. "From the fall-through" pays off on branches that are usually not taken; a loop branch is the opposite.

The puzzle, solved

The number in the hook is the branch offset. The June 2025 BNE points back to LOOP, its offset is negative, and a predictor that reads that bit says "taken": right on four iterations, wrong at the exit. The midterm BEQ points forward to DONE, its offset is positive, the same predictor says "not taken" and is right 100 times out of 101 — "always taken", blind to the sign, is the one wrong 100 times. The three lost cycles of the basic pipeline are the distance from IF to the end of ME; the course's pipeline moves the decision to ID, pays one, and the delayed branch gives even that cycle a job.

Exam-style questions

Explain the main concepts and benefits of each one of the following techniques for handling branches in a pipelined RISC-V processor: 1. Branch always not taken; 2. Branch always taken with a Branch Target Buffer; 3. Backward taken, forward not taken; 4. Delayed branch with delay-slot scheduling.

Show model answer

1. Branch always not taken. Fetch continues at PC + 4 as if the branch were an ordinary instruction; no hardware and no target address are needed. When the branch is not taken the cost is zero; when it is taken the one instruction fetched after the branch is flushed (turned into a NOP) and fetch restarts at the target: a 1-cycle penalty with the branch solved in ID. Good for if-then-else with a rare else path, bad for loop-closing branches, which are taken almost always.

2. Branch always taken with a BTB. Fetch continues at the target. The target is needed while the branch is still in IF, before decoding, so a Branch Target Buffer — a cache indexed by the address of the branch instruction whose entry holds the predicted target address — supplies it; on a hit the target is fetched in the next cycle. When the branch is not taken the instruction fetched from the target is flushed and fetch resumes at PC + 4 (1 cycle). On a BTB miss the processor falls back to PC + 4 or waits for the target. Good for backward loop branches; without a BTB the scheme has no benefit in a 5-stage pipeline because the target is not known before the outcome.

3. BTFNT. Use the direction of the branch, i.e. the sign of the offset: backward branches (loops) are predicted taken, forward branches (if-then-else, rarely entered paths) not taken. Needs only one bit of the instruction and no learning hardware; better accuracy than either uniform rule on mixed code. Not adaptive: a forward branch that is usually taken is mispredicted most of the time; and the direction is only known after decoding, so the guess is stored in the BTB entry to be usable in IF.

4. Delayed branch. The instruction in the slot after the branch is always executed, whatever the outcome; the branch takes effect after it. The compiler fills the slot with an instruction from before the branch (independent of it: always useful), from the target (useful when taken; must be harmless when not; usually duplicated at the label) or from the fall-through (useful when not taken; harmless when taken). A filled slot hides the 1-cycle bubble of the ID-resolved pipeline; a NOP in the slot is a stall. Benefit: no guessing and no flush logic; drawbacks: the slot ties the ISA to one pipeline depth, deeper or multiple-issue pipelines cannot be covered by one slot, and about half of the slots are not usefully filled — which is why RISC-V has no delay slot.

The 2026 paper gives five lines per sub-point: name the mechanism, say what it costs, what it buys, and when it fails. For point 2 the examiner looks for "indexed by the branch PC" and "target needed in IF"; for point 4 for "always executed" and the three sources with their conditions.

Consider the following loop executed on a 5-stage pipeline where the branch is solved in the ID stage (misprediction penalty 1 cycle):

INIT: ADDUI R1, R0, 0
      ADDUI R2, R0, 20
LOOP: LD    F0, 0(R1)
      FADD  F2, F0, F0
      SD    F2, 0(R1)
      ADDUI R1, R1, 4
      BNE   R1, R2, LOOP

For (a) branch always not taken, (b) branch always taken with a BTB that always hits, (c) BTFNT, complete the table (prediction, branch outcome, penalty for each iteration), then give the misprediction rate on the BNE and the total branch stall cycles.

Show model answer

The BNE executes with R1 = 4, 8, 12, 16, 20 against R2 = 20: five executions, outcomes T T T T NT.

Iteration1st2nd3rd4th5th
Branch OutcomeTTTTNT
(a) Prediction: always NTNTNTNTNTNT
(a) Penalty11110
(b) Prediction: always TTTTTT
(b) Penalty00001
(c) Prediction: BTFNT (backward → T)TTTTT
(c) Penalty00001

(a) 4 mispredictions of 5 = 80 %, 4 stall cycles. (b) 1 of 5 = 20 %, 1 stall cycle. (c) LOOP is above the BNE, the offset is negative, the branch is predicted taken: identical to (b), 20 %, 1 stall cycle.

The rate is only worth the point with the row of outcomes underneath it. The commonest slip is counting four executions instead of five: list the values of R1 at the branch before writing anything else.

In a program 20 % of the executed instructions are conditional branches and 60 % of the branches are taken. The pipeline solves branches in the ID stage and uses branch always not taken; branches are the only source of stalls and the pipeline fill is negligible. Compute the CPI.

Show model answer
\[ \text{CPI} = 1 + f_b \cdot \text{penalty} \cdot p_{\text{mis}} \] \[ = 1 + 0.2 \cdot 1 \cdot 0.6 = 1.12 \]

Always not taken mispredicts exactly the taken branches, so \(p_{\text{mis}} = 0.6\). For comparison: stalling gives 1.20, always taken with a perfect BTB 1.08; with the PC written in ME (penalty 3) the three become 1.60, 1.36, 1.24.

Write the formula first, then the numbers. The examiner checks that the penalty is 1 for an ID-resolved branch and that the misprediction probability of "not taken" is the taken fraction, not its complement.

The pipeline solves branches in ID and has one delay slot that is always executed. Fill the slot of the following loop with the "from target" strategy: write the scheduled code, give the cycles per iteration before and after, and state the conditions that make the transformation legal.

LOOP: LD    F0, 0(R1)
      ADDUI R1, R1, 8
      FADD  F2, F2, F0
      FMUL  F4, F4, F0
      BNE   R1, R2, LOOP
      NOP              # delay slot
Show model answer
      LD    F0, 0(R1)      # copy of the target, runs once
LOOP: ADDUI R1, R1, 8      # new target
      FADD  F2, F2, F0
      FMUL  F4, F4, F0
      BNE   R1, R2, LOOP
      LD    F0, 0(R1)      # delay slot: next iteration's load

Before: 5 instructions + 1 NOP = 6 cycles per iteration (m = 5, k = 1, CPI∞ = 1.2). After: 5 cycles per iteration (k = 0, CPI∞ = 1), because the slot now holds the next iteration's load. Legal because: the moved LD does not produce an operand of the branch (R1, R2); the slot LD writes F0, which the next FADD reads with the ADDUI in between, so no load-use stall appears; the ADDUI stays at least one instruction before the BNE that tests R1 (2-cycle ALU-to-branch latency); and on the exit path the extra load past the end of the array must be harmless — the address readable and F0 dead after the loop. The copy before the loop is the duplication cost of the strategy.

Four things earn the marks: the retargeted label, the copy before the loop, the cycle count before/after, and the exit-path condition. Forgetting the copy makes the first iteration read an F0 that was never loaded.

In a 5-stage pipeline where the branch outcome and the target address are computed in the ID stage, the penalty of a mispredicted branch under branch always not taken is:

  • 0 cycles Zero is the cost of a correct guess; the wrong one loses the fetch slot used while the branch was in ID.
  • 1 cycle The instruction fetched in the cycle the branch spends in ID is flushed; fetch restarts from the target in the next cycle.
  • 2 cycles Two would be the penalty of a branch resolved at the end of EX.
  • 3 cycles Three is the penalty of the basic pipeline, where the PC is written at the end of ME.
Show model answer

1 cycle: penalty = resolution stage − 1 = 2 − 1.

The quiz is matched against the course's pipeline, in which "branch solved in ID stage" is the standing assumption.

With the delayed branch technique, the instruction placed in the branch delay slot:

  • is executed only if the branch is taken That describes a cancelling ("branch likely") branch predicted taken, not the plain delayed branch of the course.
  • is executed only if the branch is not taken No: the slot does not depend on the outcome at all.
  • is always executed, whatever the outcome The branch takes effect after the slot; the compiler must choose an instruction that is correct on both paths.
  • is flushed whenever the prediction is wrong There is no prediction and no flush in a delayed branch — this is exactly the wording the notes get wrong on pp. 49 and 51.
Show model answer

It is always executed, regardless of the branch outcome.

Single answer. The distractor "flushed" is attractive because of the notes' wording; the rule of the ISA is the one on p. 46.

Which statements about the Branch Target Buffer used with branch always taken are true? (Multiple answers.)

  • It is indexed by the address of the branch instruction At fetch time the PC is the only thing the hardware has; the instruction bits are not yet available.
  • It stores the predicted target address Key: branch PC; value: predicted target (the notes' 0x100 → 0x80).
  • It decides whether the branch is taken The direction comes from the static rule (here) or from a history table (lesson 5); the BTB only says where.
  • On a hit the target is fetched in the cycle after the branch's IF That is the whole point: the target is available during IF, before decoding.
  • It is required by branch always not taken too Not taken needs no target: PC + 4 is always known.
Show model answer

True: indexed by the branch PC; stores the predicted target; on a hit the target is fetched in the next cycle. False: it does not decide the direction, and always not taken does not need it.

The 2026 paper has "Multiple Choice – Multiple answer" quizzes; tick every true statement and no false one.

Check yourself

Always taken needs a Branch Target Buffer and always not taken does not. Why?

  • Because PC + 4 is known in IF, while the target of a taken branch is not known before the instruction is decoded To fetch the target in the next cycle it must come from somewhere other than the instruction bits: a table indexed by the PC.
  • Because taken branches are rarer than not-taken ones Frequency has nothing to do with it; loop branches are taken almost always.
  • Because the BTB stores the outcome of the branch The BTB stores the target, not the direction.
  • Because without a BTB the branch instruction itself would be flushed The branch is never flushed; without a BTB the scheme simply cannot fetch anything useful before ID.

A loop runs 10 times; its branch sits at the bottom and jumps back. How many mispredictions do always not taken and always taken make, in that order?

  • 9 and 1 Not taken is wrong on the 9 taken iterations; taken is wrong only at the exit.
  • 10 and 0 The branch is not taken once, at the exit: that iteration is right for "not taken" and wrong for "taken".
  • 1 and 9 Reversed: a backward loop branch is taken on every iteration but the last.
  • 5 and 5 A static scheme has no coin to toss; it is right or wrong by the structure of the loop.

Which statements about the branch delay slot are true?

  • The instruction in the slot executes whatever the branch does The rule of the ISA: the branch takes effect after the slot.
  • A NOP in the slot costs exactly the cycle the slot was meant to save The slot is a cycle either way; a NOP makes it the stall of the stalled pipeline.
  • The hardware flushes the slot instruction when the branch is taken Nothing is flushed; that is why a "from target" instruction must be harmless on the other path.
  • The compiler, not the hardware, chooses what goes into the slot Only the compiler knows which instructions are independent and which registers are dead on each path.
  • An instruction the branch depends on may go into the slot if it is short Never: the branch would read a stale value. Length is irrelevant.

The slot is a contract, not a guess: always executed, filled by the compiler, and a NOP there is the bubble back again.

Early evaluation moved the branch comparison into ID. What is the hidden cost, in the words the exam papers use?

  • An ALU result reaches a following branch only after 2 cycles: an ADDUI immediately before the BNE that tests its result stalls one cycle "1 cycle latency to next Int/FP/L/S & 2 cycle latency to next Branch" — the branch asks for its operand in ID, one stage earlier than an ALU would.
  • The branch penalty grows to 2 cycles The opposite: the penalty shrinks from 3 to 1. The cost is on the operand side.
  • Loads can no longer be forwarded to ALU instructions Forwarding to the ALU is untouched; it is the branch that reads too early for forwarding to help.
  • Nothing: the comparator is free The hardware is cheap, but the data hazard is real and the exam's integer-unit sentence exists because of it.

The notes' Example 3 has ADD R1, R2, R3 immediately followed by BEQ R1, R0, L1. In the course's pipeline, with the branch solved in ID, what happens at the branch?

  • It waits one cycle in ID for R1, because the ADD is still in EX when the branch needs the value The 2-cycle ALU-to-branch latency: one independent instruction between them would remove the stall.
  • Nothing: forwarding delivers R1 in time Forwarding delivers to EX, not to ID; the branch asks one stage too early.
  • The ADD is moved into the delay slot automatically The hardware moves nothing; and the ADD could not go into the slot anyway, since the branch depends on it.
  • The branch is mispredicted No prediction is involved; this is a data hazard, not a control hazard.

A forward branch guards a rarely entered error handler and is taken 2 % of the time; a backward loop branch is taken 95 % of the time. Which schemes predict both branches well?

  • BTFNT Forward → not taken (right 98 %), backward → taken (right 95 %): the direction bit is exactly the information that separates the two.
  • Always not taken Right 98 % on the if, but wrong 95 % of the time on the loop.
  • Always taken Right 95 % on the loop, but wrong 98 % of the time on the error test.
  • Profile-driven prediction with a representative profile The hint bit says not taken for the first branch and taken for the second — as long as the profiled inputs behave like the real ones.

The two uniform rules are each an expert on one kind of branch; BTFNT and a good profile know which expert to ask.

Key take-aways

What to watch and read