Worked examples — Tomasulo's algorithm
5.3.3 · D3· Hardware › Advanced Microarchitecture › Tomasulo's algorithm
Yeh page Tomasulo's algorithm ka "sab kuch daal do" companion hai. Hum teen stages (Issue, Execute, Write Result) aur teen structures (reservation stations, register tags, Common Data Bus) already jaante hain. Yahan hum har tarah ki situation ke through grind karte hain jo algorithm face kar sakta hai — ek worked example per case, taaki koi bhi scenario tumhare liye naya na ho.
Symbols jo hum har jagah use karte hain (poori tarah se restated)
Yeh page self-contained hai: neeche har symbol use se pehle define kiya gaya hai, taaki tumhe kabhi parent note pe waapis jaana na pade.
Timing model jo hum har jagah use karte hain
Cycle counts meaningless hain jab tak sab log agree na karein ki "kab ek stage hota hai." Isliye hum ek simple model fix karte hain aur ise har example ke liye use karte hain neeche. Jab bhi tum ek cycle number dekhte ho, woh in rules ka paalan karta hai.
Scenario matrix
Kuch naya define karne se pehle, teen plain-word labels yaad karte hain un ways ke liye jismein ek instruction doosre se clash kar sakta hai. Ek hazard bas "ek wajah hai jis se instruction B galat answer de sakta hai agar hum ise bahut jaldi run hone dein."
Yeh un cases ka full grid hai jo hum cover karenge. Har cell "space ka corner" name karta hai, aur woh example number jo usme fit hota hai.
| # | Case class | Kya stress ho raha hai |
|---|---|---|
| E1 | Pure RAW chain | Ek hazard jo tum remove nahi kar sakte |
| E2 | WAW clash | Ek register pe do writes; late write jeet ta hai |
| E3 | WAR "false" clash | Earlier read ko later write se bachna hai |
| E4 | Zero dependency | Instruction jiske dono operands ready hain — issue aur run instantly |
| E5 | Degenerate broadcast | Ek result compute hua lekin har listener ne ignore kiya |
| E6 | Limiting behaviour | Long chain vs. wide independent code — ILP kahan saturate hoti hai |
| E7 | Real-world word problem | Load/store + memory-disambiguation |
| E8 | Exam twist | CDB structural conflict: do units same cycle mein finish |
| E9 | Control hazard + commit | Branch misprediction aur reorder-buffer commit |
Cell coverage check: RAW → E1, E6, E8. WAR → E3. WAW → E2, E5. No-dependency/zero → E4. Degenerate/ignored value → E5. Limiting → E6. Word problem/memory → E7. Structural/exam → E8. Control + commit → E9. Har cell hit ho gaya.
E1 — Pure RAW chain (woh hazard jo tum remove nahi kar sakte)
Forecast: Abhi andaza lagao — kya ADD issue ke right baad start hota hai, ya use wait karna padta hai? Kitne cycles ke liye?
- Cycle 1 — MUL Mult1 ko issue hota hai. Dono operands ready (, NULL).
R1.tag = Mult1set karo. Yeh step kyun? Issue par hum register file mein jo bhi hai snapshot lete hain; R1 ka tag ab kehta hai "Mult1 ka wait karo." - Cycle 2 — ADD Add1 ko issue hota hai. R5 ready hai (), lekin R1 ka tag
Mult1hai, toh ADD store karta hai, empty. Yeh step kyun? ADD tag copy karta hai, ek IOU — yeh block nahi karta, bas yaad rakhta hai ki kise uska number dena hai. - Cycles 2–5 — MUL execute karta hai (issued @1, toh execute hai cycles se , latency 4). ADD run nahi kar sakta: NULL. Yeh step kyun? Yeh woh ek wait hai jo hum kabhi skip nahi kar sakte — data genuinely abhi exist nahi karta.
- Cycle 6 (write) — MUL broadcast karta hai (execute @5 khatam hua, write @6, yani ). Add1
Mult1dekhta hai, set karta hai, clear karta hai. Yeh step kyun? CDB number deliver karta hai jaise hi woh written hota hai; ADD isi cycle mein capture karta hai. - Cycle 7 — ADD execute karta hai (capture ke baad wala cycle) ke saath; cycles 7–8 execute karta hai; result cycle 9 par broadcast ( start, phir ).
Verify: MUL: , cycle 6 mein written. ADD: , cycle 9 mein written. RAW ne ek real wait force kiya — ADD MUL ke result broadcast hone se pehle shuru nahi ho sakta tha. Yeh correct hai; data physically arrive karna tha.
E2 — WAW clash (do writes ek register ko)
Forecast: Dono R1 write karte hain. Kaun si value survive karti hai — 42 ya 12? Kya MUL ka broadcast SUB ke answer ko overwrite karta hai?
- Cycle 1 — MUL issue hota hai Mult1 ko.
R1.tag = Mult1. Execute cycles 2–5, write @6. Kyun? R1 ab Mult1 ko promise ki gayi hai. - Cycle 2 — SUB Add1 ko issue hota hai, operands ready.
R1.tag = Add1overwritesMult1. Execute cycles 3–4, write @5. Yeh step kyun? Register file sirf apna latest producer yaad rakhta hai. Yeh overwrite woh renaming hai jo WAW ko dissolve karti hai — R1 ab newer writer ko point karta hai. - Cycle 5 — SUB writes, broadcast karta hai. R1.tag
Add1hai, toh R1 latch karta hai, tag → NULL. Yeh step kyun? Ek broadcast ek register tab latch karta hai jab us register ka tag broadcasting RS ka naam rakhe. R1 ka tagAdd1hai aur yeh Add1 ka broadcast hai, toh match hold hoti hai aur 12, R1 mein land karta hai — yeh woh write hai jo jeetnee chahiye. - Cycle 6 — MUL writes, broadcast karta hai. R1.tag ab NULL hai (≠ Mult1), toh R1 ise ignore karta hai. Yeh step kyun? Same match rule, ab fail ho rahi: tag overwrite ho gayi, toh stale earlier write harmlessly vanish ho jaati hai — exactly WAW guard.
Verify: Final (program order mein last writer). compute hua lekin discard hua — exactly wahi jo ek correct in-order machine produce karta. Koi stall zarurat nahi thi.

Add1 hai, tags match karte hain, toh 12 latch ho jaata hai. Dashed coral arrow MUL ka baad wala cycle 6 mein broadcast hai: R1 ka tag already NULL hai, tags mismatch hote hain, toh 42 drop ho jaata hai. Do "tag matches / tag ≠" labels padho — yeh ek tasveer mein poora WAW mechanism hai.
E3 — WAR clash (earlier read ko later write se bachna hai)
Forecast: DIV R2=100 read karta hai lekin abhi bhi crunching kar raha hai jab ADD R2=10 write karna chahta hai. Kya DIV aakhir mein 10 divide karta hai ya 100?
- Cycle 1 — DIV issue hota hai. R2 aur R3 abhi ready hain, toh DIV values copy karta hai Mult1 mein.
R1.tag=Mult1. Yeh step kyun? Yeh crucial line hai. Tomasulo operand values Issue par read karta hai, baad mein nahi. DIV ke paas 100 ki private copy hai. - Cycle 2 — ADD Add1 ko issue hota hai, ready.
R2.tag=Add1. ADD cycles 3–4 execute karta hai, @5 write karta hai. Kyun? ADD DIV ki copy ko touch nahi karta; yeh sirf register file ka R2 change karta hai. - Cycle 5 — ADD writes, broadcast karta hai; R2 register file mein 10 ban jaata hai. Yeh step kyun? DIV ka ek alag buffer hai — register-file change use reach nahi kar sakta.
- Cycle 12 — DIV writes. DIV @1 issued hua, toh cycles 2–11 execute karta hai (latency 10), aur uska Write Result agle cycle mein hai: . Usne apna snapshot use kiya. Yeh step kyun? Model exactly apply karna: last execute cycle 11 hai, aur Write Result uske baad apna cycle hai, toh cycle 12.
Verify: (correct — purana R2 use kiya), cycle 12 mein written (). Register file ka R2 ab hai. WAR "false" hazard ne kuch nahi rokaa kyunki Issue par values padhna hi rename hai. Yeh scoreboarding pe Tomasulo ka edge hai, jo ADD ko stall kar deta.
E4 — Zero dependency (dono operands ready = instant run)
Forecast: Zero dependencies ke saath, kya Issue aur Execute ke beech koi wait hai? CDB par result kab appear hota hai?
- Cycle 1 — Add1 ko issue hota hai, , dono =NULL. Kyun? Copy karne ke liye koi tag nahi — register file real values rakhta hai, IOUs nahi.
- Cycles 2–3 — execute karta hai (issue par execute condition already true hai, toh execute bilkul agle cycle mein start hota hai). Yeh step kyun? Yeh "zero input" case hai: koi producer ka wait nahi, toh sirf fixed stages ka cost hai.
- Cycle 4 — writes. Last execute cycle 3 hai, toh Write Result uske baad apna cycle hai: cycle . broadcast karta hai; R4 28 latch karta hai, tag → NULL. Yeh step kyun? Chahe koi dependents sun nahi rahe, Write stage phir bhi chalti hai — har op ko Issue, Execute, aur Write se guzarna padta hai. Yeh woh stage hai jo doosre examples mein bhi dikhti hai aur E4 mein bhi dikhaana zaroori hai.
Verify: , cycle 4 mein written. Latency = issue (1) + execute (2) + write (1) = total 4 cycles, koi dependency stall nahi. Yeh best case hai aur woh baseline hai jiske against baaki measure hote hain.
E5 — Degenerate broadcast (ek value compute hui phir ignore hui)
Forecast: ADD R1 read karta hai. Lekin R1 rewrite ho gaya. Kiska 42/12 ADD mein flow karta hai?
- Cycle 1 — MUL issues Mult1 ko,
R1.tag=Mult1. Execute 2–5, write @6. Yeh step kyun? MUL R1 ka pehla writer hai, toh R1 ka tag Issue par Mult1 ko point karne ke liye set hota hai. - Cycle 2 — SUB Add1 ko issues hota hai, operands ready,
R1.tag=Add1overwrites Mult1. Execute 3–4, write @5. Yeh step kyun? SUB R1 ka baad wala writer hai; register file hamesha newest producer ka tag rakhta hai, toh koi bhi baad wala reader Add1 se bind karega, Mult1 se nahi. - Cycle 3 — ADD Add2 ko issues hota hai. Woh R1 ka current tag
Add1read karta hai, toh ; R5 se. Yeh step kyun? ADD R1 ke newest producer se bind karta hai — renamed wale (Add1) se — stale MUL se kabhi nahi. - Cycle 5 — SUB writes, broadcast karta hai: ADD ka clear hota hai, . ADD ab cycles 6–7 execute karta hai, @8 writes.
Yeh step kyun?
Add1par CDB match correct, renamed value seedha ADD ke reservation station mein deliver karta hai. - Cycle 6 — MUL writes, broadcast karta hai: koi bhi RS aur koi register Mult1 ke liye sun nahi raha. 42 unused mar jaata hai. Yeh step kyun? Yeh degenerate corner hai — ek perfectly valid result jiske zero consumers hain. Broadcast-to-all "0 listeners" ko "5 listeners" ki tarah handle karta hai, toh kuch special nahi chahiye.
Verify: ADD compute karta hai. MUL result 42 compute hua lekin koi nahi consume kiya — sequential semantics match karta hai, jahan SUB ka 12, MUL ke 42 ko shadow karta hai ADD ke R1 read karne se pehle.
E6 — Limiting behaviour (jahan parallelism saturate hoti hai)
Forecast: Out-of-order chain (A) ki kitni help kar sakta hai? Kya (B) quarter time mein finish karta hai?
- Chain (A): I1 @1 issue hoti hai, 2–3 execute, @4 writes (Add1 free). I2 ko I1 ki value chahiye, @4 capture karta hai, execute 5–6, write @7. I3 @7 capture, execute 8–9, write @10. I4 @10 capture, execute 11–12, write @13. Yeh step kyun? Har write exactly pichli write + 3 hai (op latency 2 + 1 write cycle). RAW irreducible hai — out-of-order-execution ek true data chain ko beat nahi kar sakta. Yeh limit hai: latency chain length ke saath linearly badhti hai. Yahan ek saath zyada se zyada ek ADD busy hai, toh do slots kaafi hain.
- Independent (B): I1 @1 issue hoti hai (Add1), I2 @2 (Add2), I3 @3 (kya Add1 already free hai? nahi — Add1 @4 write karta hai). Slots track karo: I1 Add1 use karta hai (busy c1–4), I2 Add2 use karta hai (busy c2–5), I3 ko ek free slot chahiye — Add1 c4 par free hota hai, toh I3 Add1 leta hai, c4 issue karta hai (ek-cycle issue slip), exec 5–6, write @7; I4 Add2 leta hai (c5 free hota hai), c5 issue karta hai, exec 6–7, write @8. Last write @8. Yeh step kyun? Yahan limit structural hai: sirf do RS slots, toh teesra aur chautha ADD slot free hone ka wait karte hain. Fixed 2-slot hardware ke saath, independent code RS count se cap hoti hai, data se nahi.
Verify: Chain cycle 13 par finish hoti hai; independent cycle 8 par. Independent faster hai lekin 4× faster nahi — fixed 2 RS slots (aur single CDB) pipeline ko serialise karte hain. Lesson: ILP isse bounded hai jo scarcer ho, true dependencies ya hardware resources (RS slots, CDB).

E7 — Real-world word problem (loads, stores, memory ordering)
Forecast: Store aur load same memory address touch karte hain. Kya yeh ek "hazard" hai jo register tags dekh sakte hain?
- Load issue hota hai Load1 ko, address
[R1]compute karta hai, execute karta hai; result F2 CDB par tagLoad1ke saath broadcast hota hai. Kyun? Loads kisi bhi producer ki tarah behave karte hain: unka tagLoad1F2 ko rename karta hai. - MUL issues, , wait karta hai, phir execute karta hai. Yeh step kyun? Normal RAW through F2 — tags se handle hota hai, koi memory involved nahi.
- Store issue hota hai: uska data operand F4 tag
Mult1paata hai; uska address R1 use karta hai. Store ko load ke address pata hone se pehle commit nahi karna chahiye, kyunki woh alias ho sakte hain. Yeh step kyun? Register tags register dependencies dekhte hain, memory wale nahi. Same-address load/store ek memory dependency hai jo se invisible hai. Hume memory-disambiguation chahiye: store ko tab tak hold karo jab tak earlier load/store addresses compute aur compare na ho jaayein.
Verify: Order Load → (address-compare) → Store hona chahiye. R1=pixel ka address, value 200 (F0=0.5): F2=200, F4=100, memory finally [R1] par 100 rakhta hai. Agar store load ko bypass kar deta, toh woh purana pixel read karta ya garbage write karta — ek correctness bug jo akele tags catch nahi kar sakte.
E8 — Exam twist (CDB structural conflict)
Forecast: Do winners, ek bus. Kya ek result kho jaata hai? Delay hota hai? Tie kaise toot ta hai?
- Dono cycle 4 par execute finish karte hain, toh dono Write-Result cycle request karte hain (hamaare model mein cycle 5, ). Lekin sirf ek CDB slot exist karta hai. Yeh step kyun? Execution units alag hain, lekin broadcast ek shared, 1-per-cycle resource hai — CDB par ek structural hazard.
- Cycle 5 — arbiter CDB grant karta hai older instruction ko (program order: ADD, inst 1). ADD broadcast karta hai. Yeh step kyun? Ek fixed priority (program order) ek deterministic, deadlock-free choice guarantee karta hai — koi result kabhi drop nahi hota, ek sirf defer hota hai.
- Cycle 6 — MUL broadcasts , ek cycle late. Uska result multiply unit ke output latch mein cycle 5 mein bus free hone ka wait karte hua baitha raha. Yeh step kyun? Unit apni finished value ek output latch mein tab tak rakhti hai jab tak bus free na ho; kuch recompute ya lost nahi hota, sirf postponed hota hai.
- Downstream effect:
Mult1ka wait kar rahi koi bhi instruction cycle 6 mein 30 capture karta hai cycle 5 ki jagah, toh uska execute start cycle 7 par slip karta hai — ek-cycle delay exactly ek step dependency chain mein neeche ripple karta hai, aage nahi. Yeh step kyun? Ek single-cycle CDB stall zyada se zyada ek cycle propagate karta hai har direct consumer ko; yeh compound nahi karta, isliye ek extra CDB usually kaafi hota hai.
Verify: ADD , cycle 5 mein written. MUL , cycle 6 mein written (arbiter ne ise ek cycle defer kiya). Correctness intact; sirf timing ek cycle slip karta hai. Real machines precisely is bottleneck ko reduce karne ke liye multiple CDBs add karte hain — E6 limit ko echo karte hue.

E9 — Control hazard aur commit (branch + reorder buffer)
Plain-Tomasulo jaisa abhi tak describe hua hai results broadcast karta hai aur unhe registers immediately change karne deta hai. Yeh problem hai jaise hi ek branch appear hota hai: hum galat path se instructions execute kar chuke ho sakte hain. Real machines ise reorder buffer (ROB) add karke fix karti hain taaki results program order mein commit karein aur speculative kaam pheka ja sake.
Forecast: ADD pehle se ek galat guess par execute ho gayi. Kya R4 corrupt hoga?
- SUB execute karta hai, R1 ROB ke through rename hota hai; uski value 0 order mein commit karegi. Kyun? Har result ab pehle ROB mein land karta hai, register file mein nahi — taaki ise undo kiya ja sake.
- BEQZ not-taken predict kiya; ADD issue hoti hai aur speculatively execute karti hai, uska result ek ROB entry mein parked hai (abhi R4 mein nahi likha). Yeh step kyun? ROB ADD ko compute karne deta hai commit kiye bina — speculation with a safety net.
- Branch resolve hota hai: R1 = 0, toh IS taken hai → misprediction. Branch ke baad har ROB entry (ADD) squash hoti hai; ADD ka reservation station aur jo bhi tag usne R4 ke liye set kiya woh flush ho jaata hai. Yeh step kyun? Commit se pehle squash karna matlab hai ki wrong-path ADD ne architectural state kabhi touch nahi kiya — control hazard erase ho jaata hai.
- Fetch
skippar redirect hoti hai; MUL run karta hai aur order mein commit karta hai. Yeh step kyun? Sirf correct-path instructions kabhi commit tak pahunchti hain, toh program order aur correctness preserve hoti hai.
Verify: ⇒ branch taken (galat predict kiya). ADD ka speculative result () discard ho jaata hai, toh R4 unchanged rehta hai. MUL , R9 mein commit karta hai. Final architectural state exactly correct (taken) path se match karta hai — ROB commit stage woh hai jo Tomasulo ko branches ke under safe banata hai.
Recall Self-test
Kaun sa hazard genuine wait force karta hai? ::: RAW (Read-After-Write) — true data dependency. Hamaare timing model ke under, latency wala op jo cycle mein issued hua apna result kab write karta hai? ::: Cycle mein (Write-Result last execute cycle ke baad apna cycle hai). E2 mein MUL ka result kyun ignore hota hai? ::: R1 ka tag Add1 pe overwrite ho gaya tha; "sirf tab latch karo jab tag match hو" rule stale Mult1 broadcast ko drop karta hai. E3 mein DIV, R2 par baad wali write se kyun corrupt nahi hota? ::: DIV ne R2 ki value Issue par mein copy ki thi; register-file changes us private copy tak nahi pahunch sakte. Uska write cycle 12 par land karta hai (). E6 ki independent code mein speed kya limit karta hai? ::: Fixed 2 RS slots (aur single CDB) — ek structural, data nahi, limit. Kaun sa hazard register tags ko invisible hai (E7)? ::: Ek memory (same-address load/store) dependency — memory-disambiguation chahiye. Reorder buffer ek mispredicted branch ko kaise undo karta hai (E9)? ::: Woh branch ke baad har entry ko commit se pehle squash karta hai, toh speculative results kabhi architectural registers tak nahi pahunchte.
Yeh bhi dekho: Tomasulo's algorithm · reorder-buffer · common-data-bus · scoreboarding · instruction-level-parallelism.