Tomasulo meets a loop: eighteen cycles of renaming in action

After this lesson you will run two iterations of the notes' loop through Tomasulo's four tables cycle by cycle, explain why a register ignores the very value it was waiting for, say where the eighteen cycles go, and fill the exam's one-iteration table for the same loop.

Five instructions, the same five every time round the loop: load an element into F0, multiply it by F2 into F4, store F4 back, step R1 down by 8, branch back. The first load misses in the cache and spends eight cycles in memory. At cycle 10 it finally puts its value, M[80], on the common data bus — and F0, the very register the load names as its destination, ignores it.

At cycles 15 and 16 the two multiplies broadcast their products, and F4 ignores both. Yet at cycles 17 and 18 both products land in memory, at addresses 80 and 72, exactly where they belong. Who took the values, if not the registers? And why is ignoring them the right thing to do?

The notes spend pages 194–221 on these eighteen cycles. The answer is a single word, and it comes at the end of section 5.

The loop, the machine and the four tables

The loop is the one lesson 15 unrolled by hand: it multiplies every element of an array by the scalar in F2, walking down from the address in R1. This time nobody touches the code. In the papers' mnemonics:

I0  LOOP: LD    F0, 0(R1)      ; F0 ← M[R1]
I1        FMUL  F4, F0, F2     ; F4 ← F0 × F2
I2        SD    F4, 0(R1)      ; M[R1] ← F4
I3        SUBI  R1, R1, 8      ; next element, 8 bytes down
I4        BNE   R1, R2, LOOP   ; predicted taken

The assumptions are on page 194: the first LD misses in the cache and takes 8 clock cycles, the second hits and takes 1 (so does the third, in the tables), FMUL takes 4, and the branch is predicted taken, so issue carries on with the next iteration. SUBI and BNE run on the integer side and are not tabulated. R1 starts at 80, so the iterations touch the addresses 80, 72 and 64. The question behind the example: memory is slow and floating point moderately slow — can the machine keep working while the first load is stuck in memory?

The notes never draw the machine; you read it off their tables. Three load buffers, Load1 to Load3; three store buffers, Store1 to Store3; two multiply stations, Mult1 and Mult2 (the add stations stay empty). One common data bus, the CDB. One memory unit that serves one load at a time, so a hit cannot overtake a miss. And a multiplier that works on two FMULs at once: in cycles 12 to 14 both stations count down together. Fields and stages are those of lesson 11 and lesson 12.

The four tables of the notes (pages 194–195) and what a cell holds.
TableColumnsWhat a cell holds
Instruction statusIssue Exec Comp Write Respencil bookkeeping, not hardware: the cycle of issue, the last cycle of execution, the cycle of the broadcast (for a store: of the memory write)
Load and store buffersBusy Addr FUAddr: the effective address, computed at issue from the R1 of that moment; FU, for a store: the tag of the producer of its data, replaced by the value when it arrives
Reservation stationsTime Name Busy Op Vj Vk Qj QkVj, Vk: operand values already in the station; Qj, Qk: tags of the producers of the missing ones (empty = present); Time: execution cycles still to run after this one
Register result statusR1 F0 F2 F4 …for each FP register the tag of the youngest instruction that will write it, empty when the register holds its value; R1 is shown as a value, because the integer side is not tabulated

Two kinds of field do the renaming. Addr, Vj and Vk are copies: what exists at issue is copied into the entry, so a later write to R1 or F2 cannot disturb it — a WAR hazard dies there. Qj, Qk and a store's FU hold a tag, the name of the buffer or station that will produce the missing value: a reader is tied to its producer, not to a register name. And the register status names only the youngest writer of each register, so an older result can never land on top of a younger one — a WAW hazard dies there.

Load1 Busy Addr Yes 80 LD F0, 0(R1) Mult1 Vj Vk Qj — R(F2) Load1 FMUL F4, F0, F2 Store1 Addr FU 80 Mult1 SD F4, 0(R1) F0 F4 nothing computed yet register status R1 F0 F4 80 Load1 Mult1
Fig. 1. The end of cycle 3 (pages 196–199): iteration 1 exists as a chain of tags. Mult1 waits for Load1 (its Qj), Store1 waits for Mult1 (its FU), and the register status points F0 and F4 at their future producers. Nothing has been computed: Load1's miss keeps the memory busy until cycle 9.

The timing rules are lesson 12's stages turned into arithmetic, as in lesson 13. Issue goes in program order, one instruction per cycle, into a free entry of the right class; a missing operand never delays issue — it becomes a tag. Execution starts the cycle after the last operand arrives. The CDB carries one result per cycle, oldest first: every entry holding the tag copies it, the register file only if the register's status still names that tag. An entry stays busy until its result has been broadcast; a store executes the cycle after its data arrives and writes memory in the next.

\[ S = \max(I + 1,\ W_{\text{prod}} + 1,\ U) \] \[ \text{Exec Comp} = S + L - 1 \] \[ W = \text{Exec Comp} + 1 = S + L \]
\(I\)
issue cycle
\(S\)
first execution cycle — the START EXEC column of the exam's table
\(W_{\text{prod}}\)
write cycle of the producer of the last operand to arrive; the value is usable from the next cycle
\(U\)
first cycle in which the unit is free (for a load: the memory unit)
\(L\)
latency: 8 for the missing load, 1 for a hit, 4 for FMUL
\(W\)
write-result cycle: the notes' Write Res, the exam's WRITE RESULT — one cycle later for every older result that takes the CDB first

"WAW hazards mainly on R1" (page 194). Inside an iteration the hazard on R1 is a WAR — LD and SD read R1, SUBI writes it after them — written "anti-dependence with I0 and I2 for R1" in the row of I3 of the exam's table. It dies when Load1 and Store1 copy the address 80 at issue. Across iterations R1 carries a true dependence; the WAW from one SUBI to the next never binds. Full inventory: lesson 15's dependence tables.

Issue copies what exists — a value, an address — and writes a tag for what does not; after that, no entry cares what its registers are called.

Cycles 1–8: issue runs ahead of the data

Fig. 1 is the end of cycle 3: LD₁ entered Load1 at cycle 1, FMUL₁ entered Mult1 at cycle 2 — copying the value of F2 (Vk = R(F2)) and recording the tag of F0 (Qj = Load1) — and SD₁ entered Store1 at cycle 3 with FU = Mult1. Nothing has been computed, yet the iteration's data-flow graph already exists in hardware, written in tags.

Cycles 4 and 5 belong to the integer side: SUBI₁ and BNE₁ issue, R1 reads 72 from cycle 5, the branch is predicted taken. Cycles 6 to 8 are the reason the example exists: iteration 2 issues while iteration 1 still waits for memory — LD₂ into Load2 with Addr = 72, FMUL₂ into Mult2 with Qj = Load2, SD₂ into Store2 with FU = Mult2. The register status is simply overwritten, F0 from Load1 to Load2 and F4 from Mult1 to Mult2, while Mult1 keeps Qj = Load1 and Store1 keeps FU = Mult1.

At the end of cycle 8, six instructions from two iterations are in flight, all spelled with F0 and F4, and nothing conflicts: every waiting field holds a tag, and the tags all differ. The notes call it the dynamic loop unrolling effect (page 193): two copies of the body side by side, although nobody copied the code.

The loop on Tomasulo's machine, cycle by cycle

The notes' four tables at the end of each cycle, from 0 to 18, under a picture of the same moment. A blue entry is busy; an orange arrow runs from a producer to an entry that waits for its tag; a blue arrow is a value travelling on the CDB in this cycle; a grey crossed arrow is a broadcast the register file ignores; the yellow stroke marks the entry that broadcasts or writes memory. The small circle on a multiply station is its Time field. Highlighted cells changed in this cycle; MULTD is written FMUL, as in the papers.

  1. Step to cycle 5 and predict: at the end of cycle 8, which entries will hold the tag Load1, and what will F0's status say? Then step to 8.
    What you should see

    Only Mult1 holds Load1, in its Qj. F0's status says Load2 (since cycle 6) and F4's says Mult2 (since 7), while Store1 still holds Mult1 and Store2 holds Mult2. The register status always names the youngest writer; every waiting entry keeps the tag it recorded at its own issue. Two iterations sit side by side with the same register names and different tags.

  2. Stop at cycle 9. Load1 is about to broadcast M[80]. Who will take it — Mult1, F0, both or neither? Commit to an answer, then press Next.
    What you should see

    Mult1 only. Its Qj holds Load1, so the tags match; F0's status holds Load2, so the register file ignores the broadcast — the grey crossed arrow. This is the one insight the widget exists for: a value goes to whoever holds its tag, never to a register name.

  3. At cycle 12 the operand of FMUL₃ will be ready one cycle later. Predict the cycle in which FMUL₃ issues and the one in which SD₃ issues; then step to 16.
    What you should see

    15 and 16. Both multiply stations stay busy until their results are written, at 15 and 16, and the notes refill Mult1 in the very cycle it broadcasts. SD₃ has a free buffer all along but cannot overtake FMUL₃: issue is in order. The stations held until write result and the order of issue decide where the time goes — not the register names.

  4. At cycle 18, read the ISSUE column from top to bottom, then the WRITE RESULT column. Which one is in program order?
    What you should see

    ISSUE: 1, 2, 3, 6, 7, 8, 11, 15, 16 — always increasing: issue is in order. WRITE RESULT: 10, 15, 17, 11, 16, 18, 13 — LD₂ and LD₃ finish before FMUL₁ has written: execution and completion are out of order.

One thing looks odd: LD₂, a hit issued at cycle 6, executes only at cycle 10. The notes' reason (page 202) is wrong.

In order in, out of order through: issue goes 1, 2, 3, 6, 7, 8, while LD₂ finishes executing at cycle 10 — before FMUL₁ has even started.

Cycle 10: one broadcast, two answers

At cycle 9 Load1 completes its eighth memory cycle and SUBI₂ issues. Cycle 10 is the moment of the puzzle. Load1 puts ⟨Load1, M[80]⟩ on the CDB, and every entry compares the tag with its fields. Mult1 holds Qj = Load1: it copies the value into Vj, and its Time field starts at 4, for the execution cycles 11 to 14. The register file compares the tag with F0's status, which since cycle 6 says Load2: no match, F0 does not change. Load1 is freed, and Load2 finally gets the memory unit.

Load1 Mult1 M[80] Qj = Load1 Vj ← M[80] M[80] F0 Load1 F0 ← M[80] no younger load

If F0 still named Load1

Load1 Mult1 M[80] Qj = Load1 Vj ← M[80] M[80] F0 Load2 Load2 ignored owns F0 now

Cycle 10 as it happens

Fig. 2. One broadcast, two register-status rows. Left: had the second load not been issued, F0 would still name Load1 and would take M[80] along with Mult1. Right: what the notes show at cycle 10 — F0 names Load2 since cycle 6, so only Mult1, whose Qj holds Load1, takes the value. The register keeps its claim for the younger load.

Ignoring the value loses nothing: the only instruction that needed M[80] was FMUL₁, and it took it from the bus. F0 must end up holding what the youngest load put in it, so it listens for that load's tag only; taking M[80] would overwrite a claim that belongs to a younger instruction. This is WAW protection, and it costs no stall at all.

Dynamic register renaming — at issue, the reservation station or buffer that will produce a result becomes the name of that result, its tag. Readers record the tag instead of the register, and the register file accepts a broadcast only from the youngest writer of each register. WAR and WAW hazards disappear; RAW dependences stay.

What did the tags buy? Draw both iterations as a dependence graph, as lesson 15 did: a chain of true dependences inside each iteration, LD → FMUL → SD, and four name dependences between them — two WAW, because F0 and F4 are written twice, and two WAR, because FMUL₁ reads F0 before LD₂ writes it and SD₁ reads F4 before FMUL₂ writes it. A machine that tracks registers by name must respect all of them; one that renames keeps only the true ones.

Names or tags: the same two iterations

Two iterations on the notes' machine. Above, the dependence graph: orange arrows are true dependences, black arrows the name dependences between the iterations (WAR dashed, WAW dotted). Below, the cycles of the six floating-point instructions. With renaming off, the machine falls back on the safe rule for names: an instruction may not issue while an older one still has to write its destination register or still has to read it; it issues in the cycle after that write or read. (The scoreboard of lesson 9 checks the WAR at write instead; here that gives the same cycles, because each WAR ends in the same cycle as its WAW.) An empty dashed box is the time an instruction cannot issue, on its own arcs or queued behind an older one. The R1 arcs from SUBI₁ to LD₂ and SD₂ are true dependences too; the integer side settles them by cycle 5, so they are left out of the picture.

  1. With the toggle off, count the arcs that end at LD₂ and predict the first cycle in which it may issue. Then read its row.
    What you should see

    Two arcs: WAW with LD₁, which writes F0 at cycle 10, and WAR with FMUL₁, which reads F0 at the same moment, when the broadcast arrives. LD₂ issues at 11 — five cycles stuck at issue, with FMUL₂ and SD₂ queued behind it. FMUL₂ then hits the same wall on F4 until Mult1 writes at 15.

  2. Predict which arcs survive when you switch renaming on. Then switch.
    What you should see

    The four name arcs are crossed out; the four true arcs stay, relabelled with tags — Load1 and Mult1 in the first iteration, Load2 and Mult2 in the second. Renaming removes exactly the dashed and dotted arcs and leaves the RAW chains: the data still flows LD → FMUL → SD, and the second iteration slides left until it overlaps the first.

  3. With renaming on, LD₂ issues at 6 but still starts executing only at 10. Which arc explains the wait?
    What you should see

    None — no arc ends at LD₂ any more. It waits for the memory unit, busy with the miss until cycle 9: a structural limit that renaming cannot touch. The second iteration still finishes five cycles earlier, at 18 instead of 23.

A value goes to whoever holds its tag; the register file is one more listener, and it listens only for its youngest writer.

Cycles 11–18: countdowns, a full house and two stores

Cycle 11 repeats cycle 10 for the second iteration: Load2 broadcasts M[72] and Mult2 takes it. In the same cycle LD₃ is issued into Load3 (Addr = 64) and F0's status becomes Load3. Issued, not started — the notes say otherwise.

From cycle 11 the two multiplies count down side by side, Mult1 from 3 to 0 and Mult2 from 4 to 1. At cycle 12 the machine hits its first wall. FMUL₃ is next in program order and its operand will be ready at cycle 13, but both multiply stations are busy — and a station stays busy until its result has been broadcast, at 15 and 16. No free station, no issue: a structural hazard on the reservation stations, not a data hazard. Because issue is in order, SD₃ waits behind it although Store3 is free. At cycle 13 Load3 broadcasts M[64]; no younger load has claimed F0, so the register file takes it — the only loaded value F0 keeps in the whole run.

At cycle 15 Mult1 broadcasts M[80]×F2: Store1, which has held FU = Mult1 since cycle 3, takes it, and F4, whose status names Mult2, ignores it. Mult1 is freed and, in the notes, refilled in the same cycle with FMUL₃, which reads F0 = M[64] from the register file. At cycle 16 Mult2's product goes to Store2, and F4 ignores this one too: its status now names Mult1. Cycles 17 and 18 belong to the stores, and the notes narrate them with two slips.

The result of the exercise is the notes' last instruction-status table (page 221), here in the paper's headings plus the notes' Exec Comp:

Pages 195–221 in one table: the instruction status at the end of cycle 18. For a store, WRITE RESULT is the memory write.
IterINSTRUCTIONISSUESTART EXECEXEC COMPWRITE RESULTHazards TypeRSi
1LD F0, 0(R1)12910None (a cache miss: 8 cycles)Load1
1FMUL F4, F0, F22111415RAW with LD₁ for F0Mult1
1SD F4, 0(R1)3161617RAW with FMUL₁ for F4Store1
2LD F0, 0(R1)6101011Structural: memory unit busy until 9; WAW with LD₁, WAR with FMUL₁ for F0 (solved by renaming)Load2
2FMUL F4, F0, F27121516RAW with LD₂ for F0; WAW with FMUL₁, WAR with SD₁ for F4 (solved by renaming)Mult2
2SD F4, 0(R1)8171718RAW with FMUL₂ for F4Store2
1 3 5 7 9 11 13 15 17 19 21 LD₁ FMUL₁ SD₁ LD₂ FMUL₂ SD₂ LD₃ FMUL₃ SD₃ SUBI, BNE cache miss no free station the notes stop at 18
Fig. 3. Where the eighteen cycles go (cycles 1–22; the dashed line is the end of the notes' tables, iteration 3 is finished with the same rules). Dot: issue. Dashed wash: waiting in a station or buffer. Solid bar: executing. Tick: write result — for a store, the memory write. Empty dashed box: cannot issue. Issue is in order, execution is not: LD₂ finishes at 10 before FMUL₁ starts at 11, and the two iterations overlap from cycle 6 to cycle 18.

Four causes account for every cycle of the chart. The miss: nothing floating-point executes from cycle 2 to 9, yet issue goes on, and by cycle 8 six instructions wait in buffers and stations. Stations held until write result: Mult1 is occupied from cycle 2 to 15 — fourteen cycles for four of multiplying — and that keeps FMUL₃ out for three cycles. In-order issue: SD₃ queues behind FMUL₃. The memory unit: one access at a time, so LD₂ waits behind the miss. What costs nothing: the CDB (results at 10, 11, 13, 15 and 16, never two in a cycle; on the papers' machine they collide, lesson 14) and the register names.

Change one assumption at a time and the chain moves (same rules as the notes' run):

One assumption changed at a time. Each cell is ISSUE / START EXEC / WRITE RESULT; the fifth column is the cycle in which the store of iteration 3 writes memory.
MachineLD₂FMUL₂FMUL₃SD₃ writesWhat it shows
the notes' machine6/10/117/12/1615/16/2022the run of Fig. 3
a third multiply station6/10/117/12/1612/14/1820FMUL₃ issues right behind LD₃; SD₃ follows at 13
a non-blocking cache6/7/87/9/1313/15/1921a hit may pass the miss: the second product is written at 13, before the first at 15
the first load hits too6/7/97/10/1412/14/1820one iteration every 5 cycles; LD₂ slips one cycle on the CDB
the papers' release rule6/10/117/12/1616/17/2123a station is free only the cycle after its write: one cycle later from FMUL₃ on

Two rows deserve a sentence. With a non-blocking cache, LD₂ writes F0 at cycle 8 and LD₁'s older value arrives at 10 — the classic WAW hazard, a stale write after a fresh one — and the register file ignores it, because F0's status no longer names Load1. With every load a hit, the loop settles into one iteration every 5 cycles, CPI = 1, the single-issue limit: each FMUL holds its station 7 or 8 cycles while a new one arrives every 5, so on average 1.5 stations are busy (a count not in the notes). The miss alone stretched Mult1's stay to fourteen cycles.

The time goes to the miss, to stations held until write result, to in-order issue and to the single memory unit — never to the register names.

The paper's version: one iteration, one table

The exam never asks for the four tables: Exercise 1 of every paper from 2024 to 2026 gives one iteration on a fixed machine, a dependence table and then one timing table, filled with the eight conventions of lesson 12. The formats differ in more than layout:

Two iterations and a bit, on a machine read off the tables: the first load misses (8 cycles), later loads hit (1), FMUL takes 4. Issue / Exec Comp / Write Res; SUBI and BNE not tabulated. A station freed by its broadcast is refilled in the same cycle; Time counts down.

One iteration, all hits; LDU 4, FPU 3, ALU 2; stations RS1 to RS6. ISSUE / START EXEC / WRITE RESULT / Hazards Type / RSi / UNIT for every instruction, the branch included. In the model answers a station is free only the cycle after its WRITE RESULT, and a store writes memory at START EXEC + 4 without using the CDB. CPI = last WRITE RESULT / instructions.

Here is the loop on the machine of July 2024 and June 2026: a single CDB; RS1 and RS2 with load/store units LDU1 and LDU2, latency 4; RS3 and RS4 with FPU1, latency 3; RS5 and RS6 with ALU1, latency 2. The first row is the one every paper pre-fills.

This lesson's loop in the format of Exercise 1 (machine of July 2024 and June 2026). CPI = 15 / 5 = 3.0.
INSTRUCTIONISSUESTART EXECWRITE RESULTHazards TypeRSiUNIT
I0 LOOP: LD F0, 0(R1)126NoneRS1LDU1
I1 FMUL F4, F0, F22710RAW with I0 for F0RS3FPU1
I2 SD F4, 0(R1)31115RAW with I1 for F4RS2LDU2
I3 SUBI R1, R1, 8457WAR with I0, I2 for R1 (solved by renaming)RS5ALU1
I4 BNE R1, R2, LOOP5811RAW with I3 for R1; Structural: CDB taken at 10 by I1RS6ALU1
  1. I0, given. Issue 1 into RS1; R1 is valid, so it starts at 2, occupies LDU1 for 4 cycles (2 to 5) and writes F0 on the CDB at 6. From cycle 1, F0's tag is RS1.

  2. I1 FMUL. Issue 2 into RS3: F2 is copied, F0 is recorded as the tag RS1 — a RAW with I0. The value arrives on the CDB at 6, so START EXEC = 6 + 1 = 7 and WRITE RESULT = 7 + 3 = 10.

  3. I2 SD. Issue 3 into RS2, since RS1 is busy until 6. The address uses R1, valid now; the data is the tag RS3, a RAW with I1 for F4. START EXEC = 10 + 1 = 11; WRITE RESULT = 11 + 4 = 15, the end of the memory write — a store takes no CDB slot.

  4. I3 SUBI. Issue 4 into RS5; R1 is valid, so it starts at 5 and writes at 5 + 2 = 7, the bus being free. It overwrites R1 while I2 still has to use its address (from cycle 11): a WAR with I0 and I2 for R1, harmless because both copied R1 at issue.

  5. I4 BNE. Issue 5 into RS6. R1 comes from I3 at 7 and ALU1 is free after I3's write, so START EXEC = 8 and the result would be ready for 8 + 2 = 10. At 10 the bus belongs to I1, the older instruction: BNE writes at 11 — Structural on the CDB. The papers never say whether a branch uses the CDB; if you assume it does not, write 10 and say so — the CPI does not change.

  6. CPI. The last WRITE RESULT of the iteration is the store's: CPI = 15 / 5 = 3.0.

Two cells deserve a second look. FMUL waits in RS3 from cycle 3 to 6 while FPU1 is idle — a RAW wait inside a station, not a stall at issue. SUBI writes R1 at 7 while SD still needs its address at 11 to 15: harmless, because the address was copied at issue ("solved by renaming"). A second FP unit or ALU, as on the June 2025 machine, changes nothing here: every wait is a RAW or the bus.

The theory part asks for the same mechanism in words; the first exam-style question below answers four items of the June 2026 question on this loop.

One assumption the walkthrough never questions: the branch. BNE₁ and BNE₂ are predicted taken, and the next iteration issues before either is resolved. The rule of lesson 4 forbids updating registers or memory before the branch outcome is known, and plain Tomasulo cannot take a write back: a broadcast goes straight into the register file, a store straight into memory. So, as lesson 15 said when it struck out the notes' word "speculatively" (page 193), it may issue past a predicted branch, but nothing behind the branch may execute until the branch is resolved. Here that costs nothing: a BNE needs only R1, which the integer side has ready, so BNE₂ (issued at 10) is resolved by cycle 11, before LD₃ executes at 12. Make the branch wait for a slow result, a load that misses, and everything behind it sits in its station: the overlap is gone. Executing past the branch and undoing a wrong guess is the reorder buffer's job.

Back to the puzzle. F0 and F4 ignored those values because each register listens only for the tag of its youngest writer; the values went where their tags pointed — M[80] to Mult1, the two products to Store1 and Store2 and on to memory at 80 and 72. The one word is tag: in Tomasulo's machine a value is addressed to the instructions that wait for it, not to a register.

On the paper: one iteration, START EXEC = the producer's WRITE RESULT + 1, a station free the cycle after its write, CPI = 15 / 5 = 3.0 for this loop.

Exam-style questions

Explain the main concepts and benefits of each one of the following techniques used by modern superscalar processors (Tomasulo-style), in about five lines each: (1) dynamic instruction scheduling; (4) reservation stations; (5) dynamic register renaming; (6) out-of-order execution. Use as example the loop LD F0, 0(R1); FMUL F4, F0, F2; SD F4, 0(R1); SUBI R1, R1, 8; BNE R1, R2, LOOP, not unrolled, whose first LD misses in the cache.

Show model answer

Dynamic instruction scheduling. The hardware decides at run time when each instruction executes, preserving the data flow: instructions issue in program order and wait in reservation stations until their operands are available, instead of stalling the pipeline behind the first one that must wait. Benefit: it adapts to latencies known only at run time, such as a cache miss. In the loop the second iteration's LD, FMUL and SD issue at cycles 6, 7 and 8 while the first LD is still missing; the same code runs well on different implementations. Cost: stations, tag-compare logic and a common data bus.

Reservation stations. Buffers in front of the functional units (Load1–3, Mult1–2, Store1–3 in the example) holding the operation, the operand values (Vj, Vk) or the tags of the stations that will produce them (Qj, Qk). An instruction enters a free station even when its operands are missing (the first FMUL at cycle 2 with Qj = Load1) and fires when they arrive on the CDB. Benefits: distributed hazard detection, operands read once, and the station names serve as tags for renaming. Limit: a station is held from issue to write result, so their number bounds the overlap; with two multiply stations the third FMUL cannot issue until cycle 15.

Dynamic register renaming. At issue, the station or buffer that will produce a result becomes the name of that result; the register result status maps each register to its youngest producer (F0: Load1, then Load2 from cycle 6; F4: Mult1, then Mult2). Readers record the tag, not the register, and the register file accepts a broadcast only from the youngest producer: at cycle 10 M[80] goes to Mult1 but not to F0. Benefit: WAR and WAW hazards disappear without stalls, so two iterations using the same F0 and F4 are in flight together (dynamic loop unrolling); only RAW dependences remain.

Out-of-order execution. Instructions execute and complete when their operands are ready, not in program order; issue stays in order. The second LD completes at cycle 10, before the first FMUL starts at 11. Benefit: long latencies (misses, FP operations) are overlapped with independent work, functional units stay busy and CPI drops. Requirements and limits: renaming against WAR and WAW; without a reorder buffer results reach the registers out of order, so exceptions are imprecise and nothing may execute past an unresolved branch.

Four of the eight sub-points of the June 2026 open question, one point each. Give a mechanism and a benefit in every item, with its cost or limit; the numbers of the loop are the evidence. Do not write "Tomasulo is speculative": speculation (item 2) and the reorder buffer (item 7) are the answers of lesson 17.

Let's consider the following assembly code containing multiple types of intra-loop dependences. Complete the following table by inserting all types of true-data-dependences, anti-dependences and output dependences for each instruction (I0: None).

I0  LOOP: LD    F0, 0(R1)
I1        FMUL  F0, F0, F2
I2        SD    F0, 0(R1)
I3        SUBI  R1, R1, 8
I4        BNE   R1, R2, LOOP

Let's consider the previous assembly code to be executed on a CPU with dynamic scheduling based on TOMASULO algorithm with all cache HITS, a single Common Data Bus and: 2 RESERVATION STATIONS (RS1, RS2) with 2 LOAD/STORE units (LDU1, LDU2) with latency 4; 2 RESERVATION STATIONS (RS3, RS4) with 1 FP unit (FPU1) with latency 3; 2 RESERVATION STATIONS (RS5, RS6) with 1 INT_ALU/BR unit (ALU1) with latency 2. Please complete the table INSTRUCTION / ISSUE / START EXEC / WRITE RESULT / Hazards Type / RSi / UNIT (row I0 given: 1, 2, 6, None, RS1, LDU1). Calculate the CPI.

Show model answer

Dependences: I0 — None. I1 — true data dependence with I0 for F0; output dependence with I0 for F0. I2 — true data dependence with I1 for F0. I3 — anti-dependence with I0 and I2 for R1. I4 — true data dependence with I3 for R1.

INSTRUCTIONISSUESTART EXECWRITE RESULTHazards TypeRSiUNIT
I0 LD F0, 0(R1)126NoneRS1LDU1
I1 FMUL F0, F0, F22710RAW with I0 for F0; WAW with I0 for F0 (solved by renaming)RS3FPU1
I2 SD F0, 0(R1)31115RAW with I1 for F0RS2LDU2
I3 SUBI R1, R1, 8457WAR with I0, I2 for R1 (solved by renaming)RS5ALU1
I4 BNE R1, R2, LOOP5811RAW with I3 for R1; Structural: CDB taken at 10 by I1RS6ALU1

CPI = 15 / 5 = 3.0.

The timing is cell for cell the table of section 5: reusing F0 as the destination of FMUL costs nothing, because the SD records the tag RS3, not the name F0. Check the dependence table first — the output dependence of I1 with I0 for F0 is the easiest line to forget — and then the CDB conflict at cycle 10. If you assume that a branch does not use the CDB, write 10 for I4 and say so; the CPI does not change.

The notes' loop example (first LD misses: 8 cycles; later LDs hit: 1 cycle; FMUL: 4 cycles; 3 load buffers, 3 store buffers, 2 multiply reservation stations; one memory unit serving one access at a time; a store executes in the cycle after its data arrives and writes memory in the next cycle) is redone with the convention of the exam papers: a reservation station becomes free only in the cycle after its WRITE RESULT. In which clock cycle does the store of the third iteration write to memory?

Show model answer

Iterations 1 and 2 do not change: no station is reused before cycle 15. Mult1 writes at 15 and is free at 16, so the third FMUL issues at 16 with both operands ready (F0 = M[64] since cycle 13), executes from 17 to 20 and writes at 21. The third SD issues at 17 into Store3, receives the product at 21, executes at 22 and writes memory at 23.

One convention moves two numbers: the notes refill Mult1 in the cycle it broadcasts (third FMUL at 15, third SD writing at 22). On the paper, use the papers' rule and state it in one line.

In the loop example (the first LD misses in the cache, 8 cycles), at cycle 10 the LD of the first iteration writes M[80] on the Common Data Bus. At that moment the register result status of F0 holds Load2. Which units store the value? (single answer)

  • Register F0 only F0's status names Load2, not Load1: the register file ignores a broadcast whose tag is not its youngest writer.
  • Reservation station Mult1 only Mult1's Qj holds Load1, so it copies the value into Vj; F0 waits for Load2.
  • Both F0 and Mult1 That would let an older value overwrite F0 after a younger load has claimed it — exactly the WAW hazard the tag compare prevents.
  • Store buffer Store1 Store1 waits for the tag Mult1, the producer of its data, not for Load1.
Show model answer

Answer 2. Only Mult1, whose Qj = Load1, captures M[80]; the register file writes F0 only if F0's status equals the broadcast tag, and since cycle 6 it equals Load2.

The distractors test the two halves of the rule: stations match on Q fields, the register file on its own status field.

In the same example, why can the FMUL of the third iteration not be issued at cycle 12? (single answer; motivate your answer)

  • RAW hazard on F0 A missing operand never stops issue: the FMUL would simply record the tag Load3 — and F0 is ready at 13 anyway.
  • No free multiply reservation station Mult1 and Mult2 are busy until their results are written at 15 and 16; without a station the instruction cannot issue.
  • The Common Data Bus is busy Nothing is written on the bus at cycle 12, and the bus never delays issue.
  • The branch has not been resolved yet The branch is predicted taken and issue proceeds; the LD of the same iteration issued at 11.
Show model answer

Answer 2: a structural hazard on the reservation stations. A station stays busy from issue to write result; Mult1 holds the first FMUL from cycle 2 to 15 because its load missed, Mult2 holds the second until 16. The third FMUL issues at 15, when Mult1 is freed, and in-order issue keeps the third SD behind it until 16.

The motivation point is for naming the station's lifetime — from ISSUE to WRITE RESULT, not to the end of execution — and the knock-on effect on the SD behind it.

True or false: in the loop example, the LD of the second iteration — a cache hit issued at cycle 6 — starts executing only at cycle 10 because two loads to the same address must be executed in program order.

  • True The two loads read different addresses (80 and 72), and two loads never need to be ordered: neither writes memory.
  • False It waits for the single memory unit, busy with the first load's miss until cycle 9 (a blocking cache); with a non-blocking cache it would execute at 7.
Show model answer

False. The addresses differ and loads do not conflict with loads; the delay is structural — one memory unit, occupied by the miss from cycle 2 to 9. Memory disambiguation concerns a load and an earlier store to the same address.

The notes give the wrong reason on pages 202 and 208; the statement is built from it.

Check yourself

At cycle 8, F4's register status says Mult2. Store1, issued at cycle 3, has to store the first product. Where will it get it?

  • From the CDB at cycle 15, because its FU field holds the tag Mult1 Store1 recorded the producer's tag at issue; when ⟨Mult1, M[80]×F2⟩ is broadcast, the tags match.
  • From F4, after Mult2 has written it Mult2's product belongs to iteration 2: Store1 would store the wrong element. After issue, Store1 never looks at F4 again.
  • From F4 at cycle 15, when Mult1 writes it there At cycle 15 F4's status names Mult2, so the register file ignores Mult1's broadcast: F4 never holds that product.
  • It reads Mult1's Vj and Vk and multiplies them itself Entries never read each other; a result reaches its consumers only through the CDB.

At cycle 10 Load2's buffer still shows Addr = 72, although R1 already reads 64. Why is that right?

  • The address was computed from R1 at issue, in cycle 6, and copied into the buffer The copy is what makes the WAR on R1 harmless: SUBI may change R1 afterwards.
  • R1 is renamed like F0, and Load2 waits for the SUBI's tag Load2 holds a value, 72, not a tag; it waits for nobody's result.
  • The address is recomputed only when the load executes If it were, LD₂ would read address 64 and load the wrong element.
  • The notes forgot to update the buffer There is nothing to update: the address of iteration 2 is 72 for good.

Which changes would let FMUL₃ issue at cycle 12, right behind LD₃? Select all that apply.

  • A third multiply station Mult3 is free at 12: FMUL₃ issues with Qj = Load3, and SD₃ follows at 13 (the what-if table).
  • A first load that hits instead of missing Then Mult1 writes at 8 and is free long before 12: FMUL₃ issues at 12 into Mult1.
  • A second CDB No two results compete for the bus in cycles 11 to 14; the bus is not the obstacle.
  • Issuing two instructions per cycle FMUL₃ would reach the issue stage earlier and wait for a station all the same: Mult1 and Mult2 stay busy until 15 and 16 whatever the issue width, because their loads arrive at 10 and 11.
  • A non-blocking cache It moves the second iteration earlier: Mult2 writes at 13 and FMUL₃ issues at 13 — close, but not 12.

The obstacle at cycle 12 is a full house of stations: anything that frees a station earlier, or adds one, helps; anything else does not.

At cycle 13 Load3 broadcasts M[64] and F0 takes it. Who reads that value from the register file?

  • FMUL₃, at its issue in cycle 15: Vj = M[64] No station held the tag Load3 at 13, because FMUL₃ had no station yet; when it finally issues, F0 holds the value and it is copied.
  • Mult1, through the CDB at cycle 13 Mult1 still holds FMUL₁ with Vj = M[80]; nobody holds the tag Load3 at 13.
  • Nobody: the value is overwritten before anyone reads it The next load, LD₄, issues only at cycle 19, so F0 = M[64] is still there at 15.
  • SD₃ SD₃ needs F4, not F0, and gets it as the tag Mult1.

At cycle 12 Mult2's Time field reads 3. What does it tell you?

  • Three execution cycles are still to run after this one: Mult2 finishes executing at 15 Mult2 got M[72] at 11 and executes from 12 to 15; Time counts down 4, 3, 2, 1, 0.
  • Mult2 has executed for 3 cycles At cycle 12 it has executed one cycle, cycle 12 itself.
  • Mult2 writes its result in 3 cycles It writes at 16, four cycles later: execution ends at 15 and the broadcast is the cycle after.
  • FMUL has a latency of 3 cycles In the notes FMUL takes 4 cycles; 3 is the latency of the papers' FP unit, a different machine.

BNE₂ issues at cycle 10, predicted taken, and LD₃ issues at 11. On plain Tomasulo, without a reorder buffer, which statement is true?

  • LD₃ may enter Load3 before BNE₂ is resolved, but it may not execute until BNE₂ is resolved Issue only fills a buffer. Execution would end in a broadcast that writes F0 for good, and nothing in this machine can take it back.
  • LD₃ may execute and broadcast before BNE₂ is resolved; on a misprediction its value is discarded Discarded by what? The broadcast writes the register file at once, and no copy of the old F0 is kept. Undoing is the reorder buffer's job (lesson 17).
  • LD₃ may not even issue until BNE₂ is resolved Issuing past the predicted branch is what lets the next iteration into the stations, as LD₂, FMUL₂ and SD₂ did at cycles 6 to 8.
  • In the notes' run this rule delays LD₃ by several cycles BNE₂ needs only R1, ready since cycle 10, so it is resolved by 11; LD₃ executes at 12 anyway. The rule costs nothing in this loop.

Key take-aways

What to watch and read