Tomasulo cycle by cycle, and what it fixes compared with the scoreboard

After this lesson you will run the notes' six-instruction example through Tomasulo's stations to cycle 59 with four timing rules, fill the exam's table for it on whatever hardware the text gives, and say in numbers what the scoreboard lost on the same code.

Six instructions: two loads, a multiply, a subtract, an add, and a divide that takes 40 cycles. On the scoreboard of lesson 10 they took 62 cycles. The scoreboard needed 13 cycles just to issue the six, and it kept the result of the last one, FADD, locked in the adder until cycle 22 although the sum was ready at 16 — five idle cycles, because FDIV would read F6 from the register file only at 21 and the scoreboard has no other way to protect that read.

Tomasulo's machine issues the same six in six cycles and writes FADD's result at cycle 13. So how long does the whole program take now? Guess a number before you read on.

The answer is 59. Seven cycles of issue stalls gone, five cycles of a pointless wait gone — and the finish line moved by three. Where did the savings go? Keep the question: by the end of the lesson you will answer it with one sum.

The machine behind the tables, and the four rules

The example is the one Hennessy and Patterson use for Tomasulo's algorithm; the notes run it from page 163 to page 182, a fresh set of tables for every cycle in which something changes. It is also the program the scoreboard ran in lesson 10, with the same latencies, so the two runs compare like with like. Here it is in the papers' mnemonics (the notes write MULTD, SUBD, DIVD, ADDD and number the instructions 1 to 6; the papers count from I0, and so does this lesson):

I0: LD   F6, 34(R2)
I1: LD   F2, 45(R3)
I2: FMUL F0, F2, F4      ; RAW on F2 with I1
I3: FSUB F8, F6, F2      ; RAW on F6 with I0, on F2 with I1
I4: FDIV F10, F0, F6     ; RAW on F0 with I2, on F6 with I0
I5: FADD F6, F8, F2      ; RAW on F8 with I3, on F2 with I1
                         ; WAR on F6 with I3 and I4, WAW on F6 with I0

The notes never state the hardware; you have to read it off their tables, and you must, because one number of the run changes with it. Two load buffers, Load1 and Load2, feed one load unit with a latency of 2 cycles. Two stations ADD1 and ADD2 feed one adder, which does FADD and FSUB in 2 cycles. Two stations MUL1 and MUL2 feed one multiplier that also divides: 10 cycles for FMUL, 40 for FDIV. One common data bus. R2, R3 and F4 hold valid values from the start, so nothing in the window writes them. Fig. 1 is this machine; the fields of a station and the register-status row are the bookkeeping of lesson 11, the three stages are those of lesson 12.

register file reservation stations Qi F0 F2 F4 F6 F8 F10 Load1 Load2 ADD1 ADD2 MUL1 MUL2 Load unit Adder Mult / Div 2 cycles 2 cycles 10 / 40 CDB ⟨MUL1, v(F0)⟩ tags here one broadcast per cycle
Fig. 1. The machine the notes' tables describe: two stations per unit, one unit per class, one bus. The plain arrows carry operands down to the units and results to the bus; the blue arrows are the broadcast — every station and the register file hear the same ⟨tag, value⟩ in the same cycle.

If you want a picture to hang the rules on: issuing an instruction is handing in an order at a cloakroom with numbered tickets. For every input that is not ready you get a ticket number — the tag of the station that will produce it — instead of the value, and you hang your own number on the hook of your destination register: "F6 will be filled by ADD2". When a worker finishes, the number is called once over the loudspeaker, the common data bus: everybody holding that number copies the value, and the hook takes it only if it still shows that number. An older ticket called late is ignored; someone who already collected a value is not disturbed when the hook's label changes. Everything below is this picture with cycle numbers.

The rules that turn the picture into numbers are the stage conditions of lesson 12, read as arithmetic. They are also the conventions of the papers' solved exercises (the pre-filled row LD: 1 / 2 / 6 with latency 4 is these rules at work):

\[ S = \max(I + 1,\ W_{\text{prod}} + 1,\ U) \] \[ W = S + L \] \[ \text{CPI} = W_{\text{last}} / N \]
\(I\)
issue cycle: in program order, one per cycle, and only into a free station of the right class — a station is busy from its issue through its write cycle
\(S\)
start of execution
\(W_{\text{prod}}\)
write-result cycle of the producer of each operand that was still pending at issue: the value travels on the bus in that cycle and is used from the next one
\(U\)
first cycle in which a unit of the class is free; in these tables a unit is busy from the start through the write cycle of its instruction
\(L\)
latency of the unit; execution occupies the cycles \(S, \dots, S + L - 1\)
\(W\)
write-result cycle: one write per cycle on a single CDB — when two instructions are ready, the older goes first and the younger slips by one
\(N\)
number of instructions in the table (one iteration, in a loop)

Start = the cycle after the last thing you were waiting for; write = start + latency; the bus serves one result per cycle, oldest first.

Fifty-nine cycles, and the six moments that matter

Before touching the stepper, derive the six rows with the rules alone. This is what you do on the paper: row by row, each row's three numbers depending only on the rows above it and on the units.

  1. I0 LD F6, 34(R2) issues at 1 into Load1: R2 is ready, so Vj = v(R2) and A = 34; the register status says F6.Qi = Load1. Nothing to wait for, the load unit is free:

    \[ S_0 = 2, \qquad W_0 = 2 + 2 = 4 . \]
  2. I1 LD F2, 45(R3) issues at 2 into Load2 (F2.Qi = Load2). Its operand is ready, but the one load unit is busy with I0 through I0's write cycle 4, so it waits in its buffer:

    \[ S_1 = \max(3,\ 5) = 5, \qquad W_1 = 7 . \]

    With a second load unit it would be \(S_1 = 3\), \(W_1 = 5\) — section 3.

  3. I2 FMUL F0, F2, F4 issues at 3 into MUL1: F2 is pending, so Qj = Load2; F4 is ready, so Vk = v(F4); F0.Qi = MUL1. It waits for the tag Load2, which is called at 7:

    \[ S_2 = \max(4,\ 7 + 1) = 8 \] \[ W_2 = 8 + 10 = 18 . \]
  4. I3 FSUB F8, F6, F2 issues at 4 into ADD1: F6 is pending from Load1 — but Load1 broadcasts in this very cycle, so by the end of cycle 4 the station holds v(F6) (the notes strike the tag out, page 169); F2 is pending, Qk = Load2; F8.Qi = ADD1:

    \[ S_3 = \max(5,\ 4 + 1,\ 7 + 1) = 8 \] \[ W_3 = 8 + 2 = 10 . \]

    It starts in the same cycle as I2: a different unit, so no conflict.

  5. I4 FDIV F10, F0, F6 issues at 5 into MUL2: F0 is pending, Qj = MUL1; F6 has been in the register file since cycle 4, Vk = v(F6); F10.Qi = MUL2. It needs F0, written at 18, and the multiplier, busy with I2 through that same cycle:

    \[ S_4 = \max(6,\ 18 + 1,\ 19) = 19 \] \[ W_4 = 19 + 40 = 59 . \]
  6. I5 FADD F6, F8, F2 issues at 6 into ADD2: F8 pending, Qj = ADD1; F2 pending, Qk = Load2; F6.Qi = ADD2 — the second rename of F6; the WAR with I3 and I4 and the WAW with I0 cost nothing. It needs F8 (written at 10), F2 (at 7) and the adder, busy with I3 through 10:

    \[ S_5 = \max(7,\ 11,\ 8,\ 11) = 11 \] \[ W_5 = 11 + 2 = 13 . \]

    Six instructions, last write at 59: \(\text{CPI} = 59 / 6 \approx 9.83\).

The notes' run (page 182) in the paper's table. The hazards are written as the paper writes them; the station names are the notes' — an exam text names its own, RS1 to RS6.
INSTRUCTIONISSUESTART EXECWRITE RESULTHazards TypeRSiUNIT
I0: LD F6, 34(R2)124NoneLoad1Load unit
I1: LD F2, 45(R3)257Structural: the load unit is busy with I0 through its write at 4Load2Load unit
I2: FMUL F0, F2, F43818RAW with I1 for F2MUL1Mult / Div
I3: FSUB F8, F6, F24810RAW with I0 for F6; RAW with I1 for F2ADD1Adder
I4: FDIV F10, F0, F651959RAW with I2 for F0 (F6 already in the register file at issue)MUL2Mult / Div
I5: FADD F6, F8, F261113RAW with I3 for F8; RAW with I1 for F2; WAR with I3, I4 and WAW with I0 for F6 solved by renamingADD2Adder

Read the three number columns top to bottom. Issue: 1, 2, 3, 4, 5, 6 — in order, without a gap. Start: 2, 5, 8, 8, 19, 11 — out of order, I5 before I4. Write: 4, 7, 18, 10, 59, 13 — out of order again: the instruction issued last finishes fourth, ahead of FMUL, and the one issued fifth finishes last. The bus is used at 4, 7, 10, 13, 18 and 59, never twice in one cycle, so the single CDB cost nothing in this run. And the hazards column holds nothing but RAW and one structural wait: every WAR and WAW of the code is in the last row with the words "solved by renaming".

VjQjVkQk MUL1ADD1ADD2 —Load2v(F4)— v(F6)——Load2 —ADD1—Load2 F2oldQi: Load2 CDB waiting for LD F2

Before: end of cycle 6

⟨Load2, v(F2)⟩ VjQjVkQk MUL1ADD1ADD2 v(F2)—v(F4)— v(F6)—v(F2)— —ADD1v(F2)— F2v(F2)Qi: — CDB four listeners

After: cycle 7

Fig. 2. One broadcast, four deliveries. At the end of cycle 6 four cells hold the ticket number Load2. In cycle 7 the number is called once: the three stations copy the value into Vj or Vk and clear the tag, the register file writes F2 because its Qi still says Load2. MUL2 (Vk = v(F6)) and F6 (Qi = ADD2) are not listening and are untouched.

Cycle 7 is the one to remember. LD F2 finishes and ⟨Load2, v(F2)⟩ goes on the bus once. Four listeners take it in the same cycle: the register file (Qi[F2] = Load2 matches, the value lands, the tag is cleared), MUL1 (Qj = Load2 becomes Vj = v(F2)), ADD1 and ADD2 (Qk becomes Vk). One write, four deliveries — that is what the word common in common data bus buys. It is also why FMUL and FSUB both start in cycle 8: each was waiting for exactly this tag, and they use different units. The bus is a bottleneck for writes, never for starts.

The Tomasulo stepper: the notes' example, cycles 1 to 59

The notes' tables, as they stand at the end of each cycle: the instruction status in the paper's columns, the six stations with their fields, the units, and the register-status row Qi. Cells that changed in this cycle are highlighted; a row with a blue frame is broadcasting on the CDB; the log under the controls narrates the cycle.

  1. Before you step to cycle 5: LD F2 was issued at cycle 2 and needs nothing but R3. Predict its START EXEC with one load unit; then step and read the wait line of cycles 3 and 4.
    What you should see

    5. The unit is busy with LD F6 through its write cycle 4, so LD F2 waits in its buffer — a structural wait inside a station, not a stall at issue: FMUL, FSUB and the rest keep issuing behind it. Switch to two load units and it starts at 3.

  2. Stop at cycle 6 and count the cells that hold the tag Load2. Predict what changes at the end of cycle 7; then step once.
    What you should see

    Four cells: F2's Qi, MUL1.Qj, ADD1.Qk and ADD2.Qk. At cycle 7 all four are cleared and three values appear: one broadcast serves three stations and a register at once, and nothing else moves — MUL2 and F6 are not listening. The one insight this widget exists for: the bus carries one result, every listener takes it in the same cycle.

  3. At cycle 6 F6's Qi becomes ADD2, although Load1's value landed in F6 at cycle 4 and both FSUB (issued at 4) and FDIV (issued at 5) read F6. Predict whether FADD's write at 13 can harm either; then step to 13 and look at MUL2.Vk.
    What you should see

    No. Both copied v(F6) at issue — FSUB from the broadcast of cycle 4, FDIV from the register file in cycle 5 — and MUL2.Vk still says v(F6) at 13 and at 19, when FDIV starts. The write at 13 is accepted by the register file because Qi[F6] = ADD2, the youngest writer. That is renaming: a reader is tied to its producer's ticket, never to the name F6.

  4. Switch to two independent load units and step to the end. Which rows move, by how much, and why does FSUB's start move too?
    What you should see

    LD F2 starts at 3 and writes at 5; everything that waited for F2 moves up by two: FMUL 6 → 16, FSUB 6 → 8, FDIV 17 → 57, FADD 9 → 11. Same program, same latencies, one assumption changed: 57 instead of 59. An exam text states its load units for exactly this reason.

"WAW hazard on F6 is avoided: … DIVD, which uses F6, was issued in cycle 5: it read the value of F6 at that time" (page 179). That is the WAR story — a reader protected by its own copy. The WAW story is different: Qi[F6] always names the youngest writer, ADD2 from cycle 6 on, so a late broadcast from Load1 would bounce off the register file. In this run Load1 wrote at 4, before FADD even issued, so time alone kept the two writes to F6 in order and the mechanism was never tested. Keep the two answers apart: WAR — readers hold values or tags, never the name; WAW — the register file honours only the youngest tag.

In-order issue, out-of-order execution and completion: issue 1 to 6; start 2, 5, 8, 8, 19, 11; write 4, 7, 18, 10, 59, 13.

Change one assumption, and every number on its chain moves

The notes' number for LD F2 is 5. The classic Berkeley CS252 table of the same example — Hennessy and Patterson's model; the slides are in the resources — says 3. Both are right, for different machines. The notes keep the load unit busy through the write cycle of its instruction and have only one of them; the textbook model lets a second load buffer proceed on its own, as if loads were pipelined through memory. Then LD F2 starts at 3 and writes at 5, and the two cycles travel unchanged down everything that waited for F2:

The same program with two independent load units (the classic CS252 table, Hennessy and Patterson's model): every number on the chain of F2 is two cycles earlier.
INSTRUCTIONISSUESTART EXECWRITE RESULTone load unit (the notes)
I0: LD F6, 34(R2)1241 · 2 · 4
I1: LD F2, 45(R3)2352 · 5 · 7
I2: FMUL F0, F2, F436163 · 8 · 18
I3: FSUB F8, F6, F24684 · 8 · 10
I4: FDIV F10, F0, F6517575 · 19 · 59
I5: FADD F6, F8, F269116 · 11 · 13

The finish moves from 59 to 57, and the CPI from 9.83 to 9.5. Nothing else changed: same code, same latencies, one sentence about the hardware. That is the lesson for the paper. The hardware sentence of Exercise 1 — "2 RESERVATION STATIONS with 2 LOAD/STORE units", "1 FP unit" — is not decoration; it decides numbers, and the June 2025 text even pre-fills a second load at 2 / 3 / 7 to make the two-unit convention explicit. When a text leaves an assumption open, write down the one you use next to the table.

From the notes' tables to the paper's table

The papers' Exercise 1 differs from the notes' tables in three ways. The hardware: two stations per class, LOAD/STORE units of latency 4, FP units of 3, an INT_ALU/BR unit of 2, named RS1 to RS6 and LDU1, FPU1, ALU1. The columns: Hazards Type, RSi and UNIT after the three cycle numbers, and "Calculate the CPI" below. The code: a loop body with a store, an ADDUI and a BNE, none of which the notes' example has. (One more difference is spelling only: the 2024 and 2025 texts prefix every register with a dollar sign, $F2; the 2026 text drops it, and so does this notebook.) Three rules cover the newcomers. A store issues to a LOAD/STORE station, starts when its base register and its data are available, and its WRITE RESULT is the end of the memory write — it never uses the CDB, so it never collides with anybody on it. ADDUI and BNE use the integer station and unit. When no station of the class is free, issue waits — and everything behind it waits too — and the paper's word for the hazard is "Structural". The June 2026 exercise filled in is lesson 12. Here is the notes' program on the papers' kind of machine, with latencies chosen so that every rule fires once:

The six instructions on an exam-style machine: 2 RS (RS1, RS2) with 2 LOAD/STORE units (LDU1, LDU2), latency 4; 2 RS (RS3, RS4) with one FP add/sub unit (FPU1), latency 3; 2 RS (RS5, RS6) with one FP mul/div unit (FPU2), latency 6 for FMUL and 12 for FDIV; a single CDB; all cache hits.
INSTRUCTIONISSUESTART EXECWRITE RESULTHazards TypeRSiUNIT
I0: LD F6, 34(R2)126NoneRS1LDU1
I1: LD F2, 45(R3)237NoneRS2LDU2
I2: FMUL F0, F2, F43814RAW with I1 for F2RS5FPU2
I3: FSUB F8, F6, F24811RAW with I0 for F6; RAW with I1 for F2RS3FPU1
I4: FDIV F10, F0, F651527RAW with I2 for F0; RAW with I0 for F6RS6FPU2
I5: FADD F6, F8, F261215RAW with I3 for F8; RAW with I1 for F2; WAR with I3, I4 and WAW with I0 for F6 solved by renamingRS4FPU1

Check the rows the way an examiner does. I2 starts at 8 = I1's write + 1. I4 starts at 15 = I2's write + 1, and FPU2 is free: I2 executed in 8 to 13 and wrote at 14. I5 starts at 12 = I3's write + 1, with FPU1 free since I3's write at 11. Note that I4 now has a second RAW, with I0 for F6: with a load latency of 4, F6 is still pending when I4 issues at 5, so it captures the tag RS1 instead of the value — the hazards column depends on the latencies too. The CDB is used at 6, 7, 11, 14, 15 and 27, all distinct. CPI = 27 / 6 = 4.5. Had I2 and I3 wanted the bus in the same cycle, the older I2 would have written first and I3 would have slipped by one, together with everything waiting for F8 — lesson 14 is made of such collisions.

On the paper, fill each row with four questions in order. Is a station of my class free? — if not, ISSUE waits, write "Structural". Which of my sources is still pending, and when does its producer write? — START EXEC is the latest of those writes + 1, or ISSUE + 1 if none, or the first cycle my unit is free if that is later; write "RAW with I# for F#". Add the latency — WRITE RESULT. Does anybody older write in that same cycle? — if so, slip by one, "Structural on the CDB". Then CPI = last WRITE RESULT / N, and the WAR and WAW of the code go into the hazards column with the words "solved by renaming".

Before and after: the scoreboard on the same six instructions

Lesson 10 ran this program on the scoreboard — one Integer unit for the loads, one adder, two multipliers and a divider, the same latencies — and the notes print the final table on page 139: LD F6 1-2-3-4, LD F2 5-6-7-8, FMUL 6-9-19-20, FSUB 7-9-11-12, FDIV 8-21-61-62, FADD 13-14-16-22 (issue, read operands, execution complete, write result). Put the two runs on one time axis and every row of the notes' Table 19 becomes a visible difference.

Scoreboard against Tomasulo: the same six instructions on one time axis

Above, the scoreboard run of lesson 10; below, the Tomasulo run of this lesson. In each row: the issue cell (I), the scoreboard's read-operands cell (R), dashed bars for waiting with the hazard named when there is room, a solid bar for execution, and a tick for the write of the result — on the Tomasulo side, a CDB broadcast. Bars cut at the right edge continue beyond the window.

  1. Before you look: in which cycle does the scoreboard issue its last instruction, and in which does Tomasulo? Then find the two issue stalls of the scoreboard and name what caused each.
    What you should see

    13 against 6. The scoreboard holds LD F2 at issue in cycles 2 to 4 because its single Integer unit is busy through LD F6's write, and FADD in cycles 9 to 12 because the adder is busy with FSUB; every instruction behind a stalled one waits with it. Tomasulo has a second load buffer and a second add station: the wait happens inside the station, out of everybody's way.

  2. Look at FMUL in both runs. Count the cycles between the write of LD F2 and the first execution cycle of FMUL in each machine.
    What you should see

    Scoreboard: write at 8, read operands at 9, execution from 10 — two cycles. Tomasulo: write at 7, execution from 8 — one cycle, because the value came down the bus into MUL1 during the write. That one cycle is forwarding, and it is saved after every producer on the chain. The insight to take from this widget: the CDB removes the read-operands cycle after every producer, and renaming removes every wait at write result.

  3. FADD: predict the cycle in which its result is written in each machine, and say what the dashed bar after its execution in the scoreboard means.
    What you should see

    22 against 13. The scoreboard's FADD finished executing at 16 and waited five cycles for FDIV to read F6 at 21 — the WAR check at write result of lesson 9. In Tomasulo, FDIV copied v(F6) into MUL2 at cycle 5, so the write at 13 answers to nobody.

  4. Switch the Tomasulo machine to two load units. Which rows move, and why does the scoreboard row of LD F2 stay where it is?
    What you should see

    LD F2 and the four instructions downstream of F2 move up by two cycles; the last result lands at 57. The scoreboard side has one Integer unit and no second buffer, so its LD F2 still waits at issue until cycle 5. Different machines, different numbers — and the scoreboard's 62 is a fair comparison only with the one-load-unit 59.

The notes' Table 19 (page 184), row by row, with the place in the two runs where each row shows. One cell of the notes is corrected in the red-pen callout below this table; the sentence about exceptions on page 185 gets its own in the next section.
RowScoreboardTomasuloWhere you see it
Operand trackingregister availability (Rj, Rk flags)tags: Qj, Qk in the station, Qi in the register fileMUL1 holds "Load2", not "F2 not ready"
Register renamingnoneimplicit, through the station tagsF6.Qi = Load1 at cycle 1, ADD2 at cycle 6
WAR / WAW hazardsmust be stalled explicitlyeliminated by renamingFADD writes at 22 on the scoreboard, at 13 here
Operand read timingin the Read Operands stage, after issue (the notes write "at execution start")at issue (value) or when the tag is broadcast (bus)FMUL reads at 9 there; MUL1 fills at 3 and 7 here
Operand forwardingnone: wait for the register write, then readyes, on the common data busFMUL executes from 10 there, from 8 here
Control modelone centralized scoreboarddecentralized: logic at each station and unita stalled load blocks issue there, waits in its buffer here
Functional unitsask the scoreboard whether they may startthe station checks its tags and startsFMUL and FSUB both start at 8 on their own
Instruction readinessRAW only, no renamingRAW plus renamed operands: data-drivenFDIV fires the cycle after MUL1's broadcast
Common busno shared result forwardingone broadcast serves every listenercycle 7: three stations and a register
Complexitysimpler: one table, no tags, no bus (the notes write "simpler renaming logic")more complex, higher performance62 against 59 cycles, 13 against 6 to issue

The numbers behind the words, three of them. Forwarding. A scoreboard consumer waits for the register-file write and then spends a cycle of its own reading — results go only to the register file, and no bus carries a tag that a waiting unit could match: LD F2 writes at 8, FMUL reads at 9 and executes from 10. Tomasulo's FMUL executes from 8, the cycle after the write at 7, because the value came down the bus into MUL1. One cycle saved per producer-consumer hop. Renaming. The scoreboard's FADD completed execution at 16 and waited until 21 for FDIV to read F6 — the WAR test at write result. In Tomasulo FDIV copied v(F6) into MUL2 at cycle 5, so FADD's write at 13 answers to nobody: 22 becomes 13. The course schedule's name for this is implicit register renaming: no extra registers, the station tags are the new names. Distributed control. The scoreboard issues only when a unit is free, so LD F2 waits at issue for the Integer unit in cycles 2 to 4 and FADD for the adder in 9 to 12, with everything behind them. Tomasulo issues into a station and lets the wait happen there: 13 cycles to issue become 6.

Now the puzzle of the hook. All of that, and the run is only three cycles shorter. The sum that explains it:

  1. Every path through the program ends in FDIV, which needs F0 from FMUL, which needs F2 from LD F2. The chain LD F2 → FMUL → FDIV carries \(2 + 10 + 40 = 52\) cycles of pure latency. No scheduler, static or dynamic, can shorten it: the values do not exist earlier.

  2. Tomasulo, one load unit: LD F2 starts at 5 (the unit is busy with LD F6 through cycle 4) and writes at 5 + 2 = 7. Then each link costs its latency plus one CDB hop:

    \[ 59 = (5 + 2) + (1 + 10) + (1 + 40) . \]
  3. Scoreboard: LD F2 issues at 5 (the Integer unit is busy through cycle 4), reads at 6, executes at 7, writes at 8; FMUL reads at 9, executes 10 to 19, writes at 20; FDIV reads at 21, executes 22 to 61, writes at 62. Each link costs a read cycle and a write cycle around its latency:

    \[ \begin{aligned} 62 &= (5 + 1 + 1 + 1) \\ &+ (1 + 10 + 1) + (1 + 40 + 1) . \end{aligned} \]
  4. \(62 - 59 = 3\): one read-operands cycle saved on each of the three links of the chain, nothing more. The seven cycles of issue stalls and the five cycles of WAR waiting that Tomasulo removed all happened in the shadow of the divide and never touched the finish line. With two load units the chain starts two cycles earlier and the finish is 57 — again the chain, not the scheduler.

So the answer to the hook: the savings went to the instructions off the critical path — FSUB and FADD finish at 10 and 13 instead of 12 and 22 — and to the issue stage, which was free for new work from cycle 7. A program that was one long dependence chain would show the same three cycles and nothing else; a loop whose iterations are independent of each other shows all of it, which is why the next lessons run loops. Dynamic scheduling removes waiting caused by names and by structure; waiting caused by values stays.

What the scoreboard keeps, in the notes' words (page 183): it is simpler to implement, historically foundational, and more hardware-economical for small-scale or embedded designs. Fair: one table and no bus is cheap, and every out-of-order machine since inherits its idea of issuing into a controller and letting the controller decide. But the verdict the notes give and the exam expects is the same — "for high-performance out-of-order execution, Tomasulo's model is the clear winner".

Forwarding saves the read-operands cycle after every producer; renaming removes every wait at write result; the dependence chain stays exactly as long as its latencies.

What Tomasulo breaks, and what fixed it

The notes close with four drawbacks (pages 184 and 185). Each one is a sentence of the open question — "main concepts and benefits" has a cost side too — and each one points at a later lesson.

1. Complex hardware. Stations with operand-tracking logic, a tag field for every register, a bus that broadcasts to every station, and comparators in every station to match the tag: area, control complexity, power. "Scaling the CDB becomes harder with many Functional Units and many RSs" (page 185) — every new listener is one more comparator on the bus.

2. The CDB bottleneck. One result per cycle. In this run the six writes never collided, so the bus cost nothing; the two past papers of lesson 14 are built so that they do, and every collision slips the younger instruction and everything waiting for it. The mitigations the notes name — several CDBs, local bypass networks, a reorder buffer with a central commit stage — are what the next lessons and modern cores use.

3. No precise exceptions. The notes' footnote 6 gives the definition to memorise: in precise exception handling, all instructions before the faulting one must complete, and none after it should affect the architectural state. Tomasulo breaks it twice over, as Fig. 3 shows: completion is out of order, and the register file is written in place at write result. Suppose FDIV, executing from 19 to 58, faults at cycle 30. FADD, which comes after it in the program, wrote F6 at cycle 13; F10 holds nothing new. The state is one no sequential machine could ever have had, and there is nothing to undo it with — "once a value is written to the register file via CDB, it's hard to 'undo' it".

1 10 20 30 40 50 59 I4 FDIV I5 FADD RAW F0 fault here F6 already holds the younger result F10 still old: the older FDIV is not done
Fig. 3. Why the register file cannot be trusted at an exception. FADD, younger than FDIV, wrote F6 at 13; FDIV is still executing at 30. A fault there leaves a state no sequential run produces, and in-place writes cannot be taken back. The scoreboard, which also writes in place and out of order, has the same problem.

4. Limited scalability. The original design issues one instruction per cycle and has one bus; a superscalar must issue and retire several per cycle, so the CDB and the tag matching must widen with it, and the stations grow with every extra unit. Modern cores keep the idea and change the parts: a register alias table for the renaming, a physical register file for the values, and reorder-buffer commit for the order — "these ideas evolved from Tomasulo" (page 185). Lesson 17 adds the reorder buffer to a Tomasulo machine and shows how a result like FADD's write at 13 is made to wait its turn before it reaches the register file.

Robert Tomasulo's algorithm ran in the floating-point unit of the IBM System/360 Model 91, of which only about fifteen were built; the first went to NASA's Goddard Space Flight Center in October 1967 and was then the most powerful computer in user operation. His 1967 paper describes the hardware as "looking ahead" about eight instructions; the Model 91 had imprecise exceptions and out-of-order memory stores, the two problems the reorder buffer later fixed. Intel's Golden Cove core of 2021 keeps 512 instructions in flight in its reorder buffer — the same algorithm with a 64-times larger window. Tomasulo received the Eckert–Mauchly Award in 1997 "for the ingenious Tomasulo algorithm, which enabled out-of-order execution processors to be implemented".

Tomasulo's costs: comparators everywhere, one result per cycle on the bus, no precise exceptions, no easy way to go wide — and the reorder buffer is the answer to the third.

Exam-style questions

Explain the main concepts and benefits of each one of the following techniques used by modern superscalar processors (Tomasulo-style): 4. Reservation stations; 5. Dynamic register renaming; 6. Out-of-order execution.

Show model answer

Reservation stations. Buffers in front of each functional unit. An issued instruction waits there with its opcode, its operand values (Vj, Vk) if they were available at issue, otherwise the tags (Qj, Qk) of the stations that will produce them; the station is itself the tag under which the result will be broadcast. Benefits: the instruction leaves the in-order front end at once (no issue stall on a RAW hazard), waits locally and fires as soon as both operands arrive, results reach every waiting station and the register file in one cycle through the common data bus (forwarding), and hazard detection is distributed instead of centralized in a scoreboard.

Dynamic register renaming. At issue the destination register's status field Qi is set to the station's tag, so the register names its youngest producer; consumers take a value or a tag at issue and never consult the register name again. Benefits: WAR hazards disappear (a reader owns its copy or its producer's tag), WAW hazards disappear (only the broadcast whose tag matches Qi is written; an older writer's result is dropped), so the false dependences created by register reuse no longer serialize execution; more instructions in flight; loop iterations overlap without unrolling.

Out-of-order execution. An instruction executes as soon as its operands and a unit are available, whatever older instructions are doing: issue stays in order, execution and completion do not. Benefits: a long-latency instruction (a cache miss, a 40-cycle divide) no longer blocks independent work behind it — in the classic example FADD, issued after FDIV, writes its result at cycle 13 while FDIV finishes at 59 — giving higher unit utilization and IPC. Cost: results written out of order make exceptions imprecise and speculation hard to undo; the reorder buffer restores in-order commit.

Five lines per item on the paper. The examiner looks for the mechanism (fields, tags, the broadcast, Qi = youngest writer) and for the benefit stated as a hazard class removed or a stall avoided — not for the word "faster". A concrete number from the example ("FADD writes at 13, before the older FDIV") shows you have run it.

Let's consider the following assembly code to be executed on a CPU with dynamic scheduling based on the 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 ADD/SUB unit (FPU1) with latency 3; 2 RESERVATION STATIONS (RS5, RS6) with 1 FP MUL/DIV unit (FPU2) with latency 6 for FMUL and 12 for FDIV. Complete the table (row I0 is given: 1, 2, 6, None, RS1, LDU1) and calculate the CPI.

I0: LD   F6, 34(R2)
I1: LD   F2, 45(R3)
I2: FMUL F0, F2, F4
I3: FSUB F8, F6, F2
I4: FDIV F10, F0, F6
I5: FADD F6, F8, F2
Show model answer
INSTRUCTIONISSUESTART EXECWRITE RESULTHazards TypeRSiUNIT
I0: LD F6, 34(R2)126NoneRS1LDU1
I1: LD F2, 45(R3)237NoneRS2LDU2
I2: FMUL F0, F2, F43814RAW with I1 for F2RS5FPU2
I3: FSUB F8, F6, F24811RAW with I0 for F6, with I1 for F2RS3FPU1
I4: FDIV F10, F0, F651527RAW with I2 for F0, with I0 for F6RS6FPU2
I5: FADD F6, F8, F261215RAW with I3 for F8, with I1 for F2; WAR with I3, I4 and WAW with I0 for F6 solved by renamingRS4FPU1

Checks: I2 starts at 8 = write of I1 + 1; I3 at 8 = max(6 + 1, 7 + 1); I4 at 15 = write of I2 + 1, FPU2 free (I2 executed 8–13); I5 at 12 = write of I3 + 1, FPU1 free since 11. CDB at 6, 7, 11, 14, 15, 27: no collision. CPI = 27 / 6 = 4.5.

Marks go to the three numbers of every row, to the hazard written with its instruction and register, and to the CPI as "last WRITE RESULT / instructions". The row most often wrong is I4: its F6 is still pending at issue with a load latency of 4, so it has two RAW hazards, and it must wait for FPU2 as well as for F0 — here both conditions give 15.

The same six instructions run with latencies load 2, add/sub 2, mult 10, div 40, one adder, one multiplier-divider and two independent load units (the second load starts the cycle after its issue). In which cycle does FDIV write its result?

Show model answer

LD F2: issue 2, start 3, write 5. FMUL: start 6, write 16. FDIV: start 17 (F0 written at 16, the multiplier free), write 17 + 40 = 57. With one load unit held through its write cycle the chain starts two cycles later and the answer is 59.

The examiner checks that you propagated the two cycles along the chain and did not touch anything off it: FADD moves from 13 to 11 too, but it is not asked.

In Tomasulo's algorithm a result is broadcast on the CDB with the tag RS3. The register file writes it into F6 if and only if:

  • F6 is the destination of the broadcasting instruction Not enough: if a younger instruction has renamed F6 since, the write must be skipped.
  • Qi[F6] = RS3 The register file compares the tag with its Qi field: a match means RS3 is still the youngest writer of F6. This is how WAW is handled.
  • no reservation station is reading F6 Readers never block a write in Tomasulo: they hold a value or a tag of their own.
  • RS3 is the oldest busy station Age decides who gets the bus when two results are ready, not whether the register accepts the value.
Show model answer

Answer 2. If Qi[F6] = RS4, a younger writer owns the name and the broadcast is dropped by the register file (the stations waiting for RS3 still take it).

A quiz in the style of the 2026 block: single answer, one point.

Which hazards make a scoreboard stall but are eliminated by Tomasulo's algorithm?

  • RAW only RAW waits remain in both machines: a value that does not exist yet cannot be forwarded.
  • WAR and WAW Renaming through station tags: readers keep their copy or their producer's tag, the register file honours only the youngest tag.
  • structural hazards on the functional units Both machines wait for a busy unit; Tomasulo even adds a structural resource, the single CDB.
  • all data hazards Only the two name hazards; FMUL still waited four cycles in MUL1 for F2.
Show model answer

Answer 2. In the example the scoreboard held FADD's write from 16 to 22 for a WAR with FDIV; Tomasulo wrote it at 13.

The distractor "all data hazards" is the one students pick after reading "no hazards occurred" on page 182.

Which statements about the Common Data Bus are correct? (multiple answers)

  • It carries a value together with the tag of the producing reservation station The pair ⟨tag, value⟩ is what every listener matches against its own tags.
  • Every register of the register file is overwritten by each broadcast Only the register whose Qi equals the tag; the others ignore it.
  • At most one result can be broadcast per cycle on a single CDB The structural limit of the bus: the older instruction goes first, the younger slips.
  • A reservation station whose Qj equals the broadcast tag copies the value and clears Qj That is the capture: Vj takes the value, Qj becomes 0.
  • Stores write their data on the CDB A store writes memory; it never uses the bus, which is why it cannot collide with anybody on it.
Show model answer

Answers 1, 3 and 4.

The "multiple answer" form of the 2026 quizzes: tick every true statement, and nothing is said about penalties, so reason about each option on its own.

Why can the original Tomasulo algorithm not provide precise exceptions, and what is added to fix it?

Show model answer

Results are written into the register file at write result, in completion order, which is not program order: in the classic example FADD writes F6 at cycle 13 while the older FDIV finishes at 59. If FDIV raised an exception, F6 would already hold a value produced by a younger instruction and no earlier instruction could be guaranteed complete — the architectural state is inconsistent and in-place writes cannot be undone. The fix is a reorder buffer: results go to the ROB at write result and are committed to the register file in program order, so at an exception everything older has committed and everything younger is discarded. The scoreboard has the same problem, since it too writes registers in place and out of order.

Three things earn the marks: "out of order" plus "in place", the definition of precise (all before complete, none after visible), and the ROB as an in-order commit point.

Check yourself

End of cycle 6 of the notes' run. In cycle 7 the bus carries ⟨Load2, v(F2)⟩. Which cells change?

  • The Qi field of F2 in the register file Qi[F2] = Load2 matches: the value lands and the tag is cleared.
  • MUL1.Vj MUL1 held Qj = Load2 for the F2 of FMUL.
  • ADD1.Vk ADD1 held Qk = Load2 for the F2 of FSUB.
  • ADD2.Vk ADD2 held Qk = Load2 for the F2 of FADD — three stations from one broadcast.
  • MUL2.Vk MUL2 holds v(F6), copied from the register file at issue; it is not listening for anything but MUL1.
  • The Qi field of F6 Qi[F6] = ADD2 since cycle 6; the tag Load2 does not match, so the field is untouched.

One call, four listeners — and every other cell exactly as it was.

FSUB is issued at cycle 4 and has v(F6) in its station from that cycle. Why does it start executing at 8 and not at 5?

  • The adder is busy The adder is idle until cycle 8; nothing else uses it before.
  • It waits for F2, which Load2 broadcasts at cycle 7 Qk = Load2 until the broadcast; start = 7 + 1 = 8.
  • The CDB is busy in cycles 5 to 7 The bus is a limit on writes, not on starts; and it is idle in 5 and 6.
  • FMUL, which is older, must start first Tomasulo starts instructions out of order; the two happen to start together at 8 because they wait for the same tag.

Same machine as the notes (one load unit, held through its write cycle), but the load latency is 4 instead of 2. In which cycle does LD F2 start executing?

  • 3 That is the two-load-unit answer; with one unit it is busy.
  • 5 The notes' number with latency 2: LD F6 wrote at 4. With latency 4 it writes at 6.
  • 6 The unit is still busy in the write cycle 6.
  • 7 LD F6: issue 1, start 2, write 2 + 4 = 6; the unit is free from 7, so LD F2 starts at 7 and writes at 11.

FADD wrote F6 at cycle 13, yet FDIV — older, and a reader of F6 — only started at 19. Why is FDIV's result still correct?

  • FDIV reads F6 from the register file at 19 That would give it FADD's value — the WAR hazard exactly. A station never reads the register file after issue.
  • FDIV copied v(F6) into MUL2.Vk at issue, in cycle 5 The value was in the register file since Load1's broadcast at 4; the copy belongs to FDIV and nothing can change it.
  • The CDB delays FADD's write until FDIV has read F6 That is the scoreboard's answer (write held until 22). Tomasulo has no such check.
  • F6 was renamed to ADD2, so FDIV reads ADD2's value ADD2 produces the new F6, which FDIV must not see; the rename protects the old value, it does not redirect the reader.

Page 182 says "no hazards occurred". Which statement is right?

  • Tomasulo removed every RAW, WAR and WAW wait of the program FMUL waited from 3 to 7, FDIV from 5 to 18, FADD from 6 to 10 — all RAW.
  • RAW waits remained; what disappeared were the waits caused by names (WAR, WAW) and the stalls at issue Tomasulo turns a RAW into a wait inside a station and makes WAR and WAW non-hazards; it cannot create a value early.
  • Only structural hazards remained One structural wait (LD F2 for the load unit) and three RAW waits.
  • The hazards occurred but cost no cycles They cost exactly the latencies on the chain: 59 cycles instead of 6.

FDIV raises an exception at cycle 30. In which of the two machines does the register file hold the state a sequential machine would have?

  • Tomasulo F6 was overwritten by the younger FADD at 13.
  • The scoreboard It wrote F8 at 12 and F6 at 22 while FDIV was still running: also younger results in place.
  • Neither: both write registers in place and out of order; a reorder buffer is needed Precise exceptions need an in-order commit point, which neither algorithm has.
  • Both, because both issue in order In-order issue says nothing about the order of writes, and the writes are what the state is made of.

This is the sentence of page 185 that the red pen corrected: the scoreboard is not more graceful here.

Key take-aways

What to watch and read