Visual walkthrough — RISC-V extensions (M, A, F, D, V, C)
5.1.8 · D2· Hardware › Instruction Set Architecture (ISA) › RISC-V extensions (M, A, F, D, V, C)
Parent note ne bataya tha ki A extension ek "compare-and-swap" ko ek hi hardware instruction mein karta hai, lr.w aur sc.w use karke. Lekin kyun yeh do instructions ka pair guarantee karta hai ki koi bhi update kabhi lost nahi hoga? Yeh page poori story bilkul zero se build karta hai — do log, ek shared number, aur ek rule jo unhe ek doosre ke upar step karne se rokta hai.
Yahan sab kuch base ISA (plain loads aur stores) par tika hua hai aur quietly cache coherence ki tracking machinery borrow karta hai. Hum dono ko saath-saath build karenge.
Step 1 — Shared number aur do workers
KYA HAI. Hamare paas ek memory location hai aur do cores hain. Ek core bas ek independent chhoti machine hai jo instructions chalati hai. Memory numbered boxes ki ek row hai; counter ek box mein rehta hai.
KYUN. Jab tak hum atomic operations ki baat nahi karte, hume wo cheez dikhni chahiye jo unhe protect karti hai: ek value jo ek se zyada worker touch karte hain. Agar sirf ek hi worker hoti, toh plain load/store bilkul safe hota aur yeh saara extension pointless hota.
PICTURE. Teal box memory mein shared counter hai. Do burnt-orange figures Core A aur Core B hain. Har ek ke paas ek private notepad hai (ek register — core ke andar ek tiny scratch box). Abhi number 7 hai.
Step 2 — Race kaise cheez tod deti hai dekho (plain load/store tarika)
KYA HAI. Dono cores ordinary base-ISA instructions se naive sequence chalate hain:
Yahan lw = load word (memory box ko register mein copy karo), addi = add immediate (register + ek fixed number), sw = store word (register ko wapas box mein copy karo). a0 mein counter ka address hai; parentheses (a0) ka matlab hai "woh box jis address ki taraf point karta hai."
KYUN. Hume bimari dekhni chahiye pehle ilaaj karne se. Khatara timing mein hai: agar do workers interleave ho jaayein?
PICTURE. Time neeche ki taraf chal raha hai. Dono cores 7 padhte hain koi bhi likhne se pehle. Dono 8 compute karte hain. Dono 8 store karte hain. Do increments hue — lekin counter sirf ek se badha. Ek update silently lost ho gaya. Yeh ek race condition hai.
Step 3 — Fix: lr.w se address reserve karo
KYA HAI. Hum plain load ko ek special load se replace karte hain:
lr.w woh sab kuch karta hai jo lw karta hai — counter ko t0 mein copy karna — plus yeh hardware se dhire se kehta hai: "Main is address ko dekh raha hoon." Core woh address ek hidden per-core slot mein likhta hai jise reservation register kehte hain.
KYUN. Step 2 mein ek update lose hona isliye hua kyunki ek write ko koi yaad nahi tha ki value badli ya nahi. Ek reservation wahi yaad hai: ek chhota sa flag jo kehta hai "jab maine dekha tab yeh location clean thi; mujhe batao agar koi ise disturb kare."
PICTURE. Core A lr.w execute karta hai. Value 7 uske register mein aata hai (orange arrow), aur ek doosra plum arrow address ko reservation slot mein dalta hai, jo VALID light up ho jaata hai.
Step 4 — Reservation trouble kaise notice karta hai (coherence watching karta hai)
KYA HAI. Jab Core A counter ke address par reservation hold kiye hua hai, maan lo Core B usi cache line mein likhta hai. Hardware invalidate kar deta hai Core A ki reservation — uska flag VALID se INVALID flip ho jaata hai.
KYUN. Reservation toot jaani chahiye jis pal koi aur value disturb kare; warna yeh ek aisa wada hoga jo koi nibhata nahi. RISC-V iske liye nayi snooping hardware nahi add karta — yeh cache-coherence protocol reuse karta hai jo pehle se har write broadcast karta hai. Jab coherence Core A ko batata hai "is line ki tumhari copy ab stale hai," wahi signal reservation clear kar deta hai.
PICTURE. Core B ka write ek coherence "invalidate" signal (teal arrow) Core A ki cache line ko bhejta hai. Reservation slot INVALID flip ho jaata hai (plum, crossed out). Core A ne abhi tak apna store nahi chalaya — ise pehle hi warn kar diya gaya hai.
Step 5 — sc.w: sirf tab store karo agar kisine disturb nahi kiya
KYA HAI. Conditional store:
sc.w reservation check karta hai:
- Reservation abhi bhi VALID hai → store perform karo, aur
t2 = 0set karo (success). - Reservation INVALID hai → memory mein kuch nahi karo,
t2 = 1set karo (failure).
KYUN. Yahi poora trick hai. Ek store jo chalane se inkaar kare jab duniya uske neeche badal gayi ho, woh kabhi kisi aur ka update overwrite nahi kar sakta. Return code t2 humara program batata hai kya hua, taaki hum react kar sakein.
PICTURE. Do outcomes side by side. Left: reservation VALID, t1 = 8 memory mein jaata hai, t2 = 0. Right: reservation INVALID, memory untouched, t2 = 1.
Step 6 — Retry loop jo sab kuch jodta hai
KYA HAI. Complete lock-free increment:
retry:
lr.w t0, (a0) # read counter, reserve address
addi t1, t0, 1 # t1 = counter + 1
sc.w t2, t1, (a0) # try to store t1
bnez t2, retry # if t2 != 0 (failed), loop
bnez t2, retry = branch if not equal zero: agar t2 1 hai (failure), toh retry par jump karo.
KYUN. Ek single attempt fail ho sakti hai — yeh guaranteed correct hai lekin guaranteed done nahi. Ise ek loop mein wrap karna "correct" ko "correct aur eventually complete" mein badal deta hai: har baar jab hum retry karte hain, hum sabse nayi value se fresh start karte hain, toh hamaara +1 hamesha current data par apply hota hai, kabhi stale data par nahi.
PICTURE. Ek flow: lr.w → addi → sc.w → branch. Success (t2 = 0) bahar nikalta hai; failure (t2 = 1) lr.w par wapas curl karta hai.
Step 7 — Do cores ko loop ke through trace karo (koi update lost nahi)
KYA HAI. Step 2 ki race dobara chalao, lekin ab lr/sc ke saath. Counter 7 se shuru hota hai.
| Time | Core A | Core B | Memory |
|---|---|---|---|
| 1 | lr.w reads 7, reserve |
— | 7 |
| 2 | — | lr.w reads 7, reserve |
7 |
| 3 | — | sc.w 8 → success, t2=0 |
8 |
| 4 | sc.w 8 → fail (reservation B ne tod di), t2=1 |
— | 8 |
| 5 | loop: lr.w 8 padhta hai, reserve |
— | 8 |
| 6 | sc.w 9 → success, t2=0 |
— | 9 |
KYUN. Step 2 se compare karo, jo 8 par khatam hua tha (ek lost). Yahan counter 9 tak pahunchta hai — dono increments land hue. Core A ka pehla sc purposely fail hua jis pal Core B ne likha, toh A ne fresh 8 re-read kiya aur correctly 9 produce kiya.
PICTURE. Step 2 jaisa hi downward-time diagram, lekin ab A ka store BLOCKED stamp kiya gaya hai aur A 8 re-read karne ke liye loop karta hai. Dono +1 survive karte hain.
Step 8 — Edge cases jinpar kabhi mat giro
KYA HAI & KYUN. Ek correct picture ke liye corners cover karne chahiye:
PICTURE. Ek 2×2 grid, har corner case ke liye ek tile, har ek reservation flag ki fate aur resulting t2 dikhata hai.
Ek-picture summary
Sab ek saath: reservation flag hero hai. lr ise VALID set karta hai, koi bhi foreign write ise clear karta hai, aur sc iska palan karta hai — sirf VALID par store karta hai, INVALID par fail karta hai (aur retry force karta hai). Woh ek flag hi hai jo ek increment ko indivisible banata hai, bhalee chahe woh alag read aur write steps se bani ho.
Recall Feynman retelling — ek 12 saal ke bachche ko batao
Do bacche ek whiteboard par ek number share karte hain aur dono 1 add karna chahte hain. Agar woh bas padhein, add karein, aur rewrite karein, toh dono 7 padh sakte hain, dono 8 likh sakte hain, aur ek bachche ki baari gayab ho jaati hai. Toh hum har bachche ko ek sticky note dete hain: jab tum padho, number ka address apne note par likho aur use chipka do — yahi hai lr, "load and reserve." Ek watcher (woh cache-coherence system jo pehle se exist karta hai taaki sab ke copies match karein) teri note us pal hi fad deta hai jab koi aur woh number change kare. Jab tum likho, tum sirf tab likhte ho agar tumhari note abhi bhi chipki hui ho — yahi hai sc, "store-conditional." Agar teri note gayi, tera write refuse ho jaata hai aur tumhe "1" milta hai matlab nahi, dobara koshish karo; agar abhi bhi hai toh tum likhte ho aur "0" milta hai matlab ho gaya. Ise ek loop mein wrap karo taaki ek refusal tumhe wapas newest number re-read karne bhej de. Ab koi bhi update kabhi lost nahi hota, aur hume koi bhaari lock ki zarurat nahi padi — humne bas woh whisper network reuse kiya jo caches already chala rahi thi.
Recall Quick self-test
lr.w woh kya karta hai jo lw nahi karta? ::: Yeh loaded address ko per-core reservation register mein bhi record karta hai aur use VALID mark karta hai.
Kya cheez ek reservation ko INVALID banaa deti hai? ::: Koi bhi doosra core us cache line mein likhta hai (ya ek interrupt/context switch), jo cache-coherence protocol ke through signal hota hai.
sc.w ke result register ke do possible values kya hain aur unka kya matlab hai? ::: 0 = store succeed hua (reservation valid thi); 1 = store fail hua (reservation invalid thi, retry karna padega).
Do-core trace mein, counter sahi tarike se 9 tak kyun pahunchta hai, 8 ki jagah? ::: Core A ka sc fail ho jaata hai jab Core B likhta hai, toh A fresh 8 re-read karta hai aur 9 store karta hai — dono increments survive karte hain.
Real code mein ek single sc attempt kyun kaafi nahi hai? ::: sc fail ho sakta hai (contention ya spurious causes se), toh hum loop karte hain aur retry karte hain jab tak succeed na ho, forward progress guarantee karte hue.