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 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.
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):
- \(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.
-
\[ S_0 = 2, \qquad W_0 = 2 + 2 = 4 . \]I0 LD F6, 34(R2)issues at 1 intoLoad1: 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_1 = \max(3,\ 5) = 5, \qquad W_1 = 7 . \]I1 LD F2, 45(R3)issues at 2 intoLoad2(F2.Qi = Load2). Its operand is ready, but the one load unit is busy withI0throughI0's write cycle 4, so it waits in its buffer:With a second load unit it would be \(S_1 = 3\), \(W_1 = 5\) — section 3.
-
\[ S_2 = \max(4,\ 7 + 1) = 8 \] \[ W_2 = 8 + 10 = 18 . \]I2 FMUL F0, F2, F4issues at 3 intoMUL1: 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_3 = \max(5,\ 4 + 1,\ 7 + 1) = 8 \] \[ W_3 = 8 + 2 = 10 . \]I3 FSUB F8, F6, F2issues at 4 intoADD1: 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:It starts in the same cycle as
I2: a different unit, so no conflict. -
\[ S_4 = \max(6,\ 18 + 1,\ 19) = 19 \] \[ W_4 = 19 + 40 = 59 . \]I4 FDIV F10, F0, F6issues at 5 intoMUL2: 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 withI2through that same cycle: -
\[ S_5 = \max(7,\ 11,\ 8,\ 11) = 11 \] \[ W_5 = 11 + 2 = 13 . \]I5 FADD F6, F8, F2issues at 6 intoADD2: F8 pending, Qj = ADD1; F2 pending, Qk = Load2; F6.Qi = ADD2 — the second rename of F6; the WAR withI3andI4and the WAW withI0cost nothing. It needs F8 (written at 10), F2 (at 7) and the adder, busy withI3through 10:Six instructions, last write at 59: \(\text{CPI} = 59 / 6 \approx 9.83\).
| INSTRUCTION | ISSUE | START EXEC | WRITE RESULT | Hazards Type | RSi | UNIT |
|---|---|---|---|---|---|---|
I0: LD F6, 34(R2) | 1 | 2 | 4 | None | Load1 | Load unit |
I1: LD F2, 45(R3) | 2 | 5 | 7 | Structural: the load unit is busy with I0 through its write at 4 | Load2 | Load unit |
I2: FMUL F0, F2, F4 | 3 | 8 | 18 | RAW with I1 for F2 | MUL1 | Mult / Div |
I3: FSUB F8, F6, F2 | 4 | 8 | 10 | RAW with I0 for F6; RAW with I1 for F2 | ADD1 | Adder |
I4: FDIV F10, F0, F6 | 5 | 19 | 59 | RAW with I2 for F0 (F6 already in the register file at issue) | MUL2 | Mult / Div |
I5: FADD F6, F8, F2 | 6 | 11 | 13 | RAW with I3 for F8; RAW with I1 for F2; WAR with I3, I4 and WAW with I0 for F6 solved by renaming | ADD2 | Adder |
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".
Before: end of cycle 6
After: cycle 7
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.
"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:
| INSTRUCTION | ISSUE | START EXEC | WRITE RESULT | one load unit (the notes) |
|---|---|---|---|---|
I0: LD F6, 34(R2) | 1 | 2 | 4 | 1 · 2 · 4 |
I1: LD F2, 45(R3) | 2 | 3 | 5 | 2 · 5 · 7 |
I2: FMUL F0, F2, F4 | 3 | 6 | 16 | 3 · 8 · 18 |
I3: FSUB F8, F6, F2 | 4 | 6 | 8 | 4 · 8 · 10 |
I4: FDIV F10, F0, F6 | 5 | 17 | 57 | 5 · 19 · 59 |
I5: FADD F6, F8, F2 | 6 | 9 | 11 | 6 · 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:
| INSTRUCTION | ISSUE | START EXEC | WRITE RESULT | Hazards Type | RSi | UNIT |
|---|---|---|---|---|---|---|
I0: LD F6, 34(R2) | 1 | 2 | 6 | None | RS1 | LDU1 |
I1: LD F2, 45(R3) | 2 | 3 | 7 | None | RS2 | LDU2 |
I2: FMUL F0, F2, F4 | 3 | 8 | 14 | RAW with I1 for F2 | RS5 | FPU2 |
I3: FSUB F8, F6, F2 | 4 | 8 | 11 | RAW with I0 for F6; RAW with I1 for F2 | RS3 | FPU1 |
I4: FDIV F10, F0, F6 | 5 | 15 | 27 | RAW with I2 for F0; RAW with I0 for F6 | RS6 | FPU2 |
I5: FADD F6, F8, F2 | 6 | 12 | 15 | RAW with I3 for F8; RAW with I1 for F2; WAR with I3, I4 and WAW with I0 for F6 solved by renaming | RS4 | FPU1 |
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.
| Row | Scoreboard | Tomasulo | Where you see it |
|---|---|---|---|
| Operand tracking | register availability (Rj, Rk flags) | tags: Qj, Qk in the station, Qi in the register file | MUL1 holds "Load2", not "F2 not ready" |
| Register renaming | none | implicit, through the station tags | F6.Qi = Load1 at cycle 1, ADD2 at cycle 6 |
| WAR / WAW hazards | must be stalled explicitly | eliminated by renaming | FADD writes at 22 on the scoreboard, at 13 here |
| Operand read timing | in 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 forwarding | none: wait for the register write, then read | yes, on the common data bus | FMUL executes from 10 there, from 8 here |
| Control model | one centralized scoreboard | decentralized: logic at each station and unit | a stalled load blocks issue there, waits in its buffer here |
| Functional units | ask the scoreboard whether they may start | the station checks its tags and starts | FMUL and FSUB both start at 8 on their own |
| Instruction readiness | RAW only, no renaming | RAW plus renamed operands: data-driven | FDIV fires the cycle after MUL1's broadcast |
| Common bus | no shared result forwarding | one broadcast serves every listener | cycle 7: three stations and a register |
| Complexity | simpler: one table, no tags, no bus (the notes write "simpler renaming logic") | more complex, higher performance | 62 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:
-
Every path through the program ends in
FDIV, which needs F0 fromFMUL, which needs F2 fromLD F2. The chainLD F2→FMUL→FDIVcarries \(2 + 10 + 40 = 52\) cycles of pure latency. No scheduler, static or dynamic, can shorten it: the values do not exist earlier. -
Tomasulo, one load unit:
\[ 59 = (5 + 2) + (1 + 10) + (1 + 40) . \]LD F2starts at 5 (the unit is busy withLD F6through cycle 4) and writes at 5 + 2 = 7. Then each link costs its latency plus one CDB hop: -
Scoreboard:
\[ \begin{aligned} 62 &= (5 + 1 + 1 + 1) \\ &+ (1 + 10 + 1) + (1 + 40 + 1) . \end{aligned} \]LD F2issues at 5 (the Integer unit is busy through cycle 4), reads at 6, executes at 7, writes at 8;FMULreads at 9, executes 10 to 19, writes at 20;FDIVreads at 21, executes 22 to 61, writes at 62. Each link costs a read cycle and a write cycle around its latency: -
\(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".
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.
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
| INSTRUCTION | ISSUE | START EXEC | WRITE RESULT | Hazards Type | RSi | UNIT |
|---|---|---|---|---|---|---|
| I0: LD F6, 34(R2) | 1 | 2 | 6 | None | RS1 | LDU1 |
| I1: LD F2, 45(R3) | 2 | 3 | 7 | None | RS2 | LDU2 |
| I2: FMUL F0, F2, F4 | 3 | 8 | 14 | RAW with I1 for F2 | RS5 | FPU2 |
| I3: FSUB F8, F6, F2 | 4 | 8 | 11 | RAW with I0 for F6, with I1 for F2 | RS3 | FPU1 |
| I4: FDIV F10, F0, F6 | 5 | 15 | 27 | RAW with I2 for F0, with I0 for F6 | RS6 | FPU2 |
| I5: FADD F6, F8, F2 | 6 | 12 | 15 | RAW with I3 for F8, with I1 for F2; WAR with I3, I4 and WAW with I0 for F6 solved by renaming | RS4 | FPU1 |
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:
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?
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)
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?
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?
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?
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?
Page 182 says "no hazards occurred". Which statement is right?
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?
This is the sentence of page 185 that the red pen corrected: the scoreboard is not more graceful here.
Key take-aways
- Three numbers per row, three rules: start = max(issue + 1, producer's write + 1, unit free); write = start + latency; one CDB write per cycle, oldest first. CPI = last write / N.
- The notes' run on one load unit: 1-2-4, 2-5-7, 3-8-18, 4-8-10, 5-19-59, 6-11-13 — issue in order, start and write out of order, the bus never used twice in a cycle.
- One broadcast serves every listener: at cycle 7 ⟨Load2, v(F2)⟩ fills MUL1, ADD1, ADD2 and the register F2 in the same cycle; MUL2 and F6, which are not listening, are untouched.
- Assumptions decide numbers: two independent load units move LD F2 from 5 to 3 and the finish from 59 to 57; the exam states its units, and you write down the one you use when a text does not.
- Against the scoreboard on the same code: 13 cycles to issue become 6 (stations instead of stalls), FADD's write 22 becomes 13 (renaming), each producer-consumer hop loses a read-operands cycle (the CDB) — and the finish moves only from 62 to 59, because the chain LD F2 → FMUL → FDIV is 52 cycles of latency that no scheduler can shorten.
- The price: tag comparators everywhere, one result per cycle on the bus, imprecise exceptions (the register file is written in place, out of order — true of the scoreboard too), poor scaling to several issues per cycle. The reorder buffer answers the exception problem; register alias tables and physical register files answer the scaling.
What to watch and read
-
Tomasulo Timing Quiz Part 2 — Georgia Tech HPCA
Milos Prvulovic, Udacity
A ready-made drill in the exact shape of Exercise 1: pause, fill issue / execute / write for each instruction with one CDB and fixed latencies, then watch the solution clip linked from it. Check Prvulovic's conventions against the four rules above before trusting a number.
-
Dynamic Scheduling with Tomasulo Algorithm — simulator
Virtual Labs, IIIT Hyderabad
Set the latencies to 2 / 2 / 10 / 40, enter the six instructions and step: the stations, the register alias table and the CDB activity in a second implementation. Then enter an exam loop body with a store and read the cycles off the timeline.
-
CS252 Lecture 7: Scoreboard, Tomasulo, Register Renaming
John Kubiatowicz, UC Berkeley25 pages
The same six instructions, the same three-table layout and the same mnemonics as the notes, with the two-load-unit timing (57): the right second source whenever a table of the notes looks wrong.
-
An Efficient Algorithm for Exploiting Multiple Arithmetic Units
R. M. Tomasulo, IBM Journal, 19679 pages
The abstract alone is the lesson: "a simple common data busing and register tagging scheme which permits simultaneous execution of independent instructions while preserving the essential precedences". Recognise Vj, Vk, Qj, Qk in his sink and source tags.
-
Digital Design and Computer Architecture, Lecture 15: Out-of-Order Execution
Onur Mutlu, ETH Zürich
Tomasulo as "restricted dataflow" — a tag is a promise of a value, the station a waiting room, the bus a loudspeaker — and why in-order issue and in-order retirement are kept around out-of-order execution: the bridge to the reorder buffer.