從一個正反器到一個能存住字組的暫存器
在上一篇裡,我們純粹用閘做出了記憶:一個能存住單一位元的閂鎖,接著是只在時脈滴答那一瞬間才改變的邊緣觸發正反器。一個正反器記住一個位元——有用,但電腦要擺弄的是整個數字。解法直白得可愛:把 32 個正反器並排排好,把同一個時脈接到它們全部,你就有了一個 32 位元的暫存器,能一次擷取並存住一整個字組。這裡沒有什麼聰明的新點子,只是重複而已——一旦一位元的單元做好了,加寬幾乎是免費的。
光禿禿的暫存器每一拍都會把自己覆寫掉,這通常不是你要的。所以真實的暫存器會多一個小小的控制閘:一條寫入致能(write-enable)訊號。致能打開時,暫存器在下一個時脈邊緣取樣它的資料輸入;致能關閉時,它無視輸入,安靜地保住原本存著的值。多了這一條線,暫存器就成了一本一字組大的筆記本,機器的其餘部分可以命令它寫入、隨時讀取——程式計數器、狀態旗標,以及計算到一半的工作值,全都住在像這樣的暫存器裡。
把許多這樣的暫存器集合起來,再加上一個依編號挑選要讀或寫哪一個的辦法,你就有了一個暫存器檔——CPU 那一小組超快的便箋格,也就是 RISC-V 機器的 x0..x31。後面的階梯我們會大量倚賴它;此刻只要記住這個畫面:暫存器檔不過是這些受時脈控制的暫存器排成的一個整齊陣列,再栓上幾條位址線而已。
同步設計:整顆晶片共用一面鼓
現在想像晶片上散布著數以百萬計這樣的暫存器,它們之間接著一團團組合邏輯——這架階梯前面講過的加法器、多工器、解碼器。你要怎麼防止亂局——某個區塊讀一個值的同時,鄰居卻還改到一半?答案就是同步設計,它是所有硬體裡最重要的紀律之一。晶片上每個暫存器都聽同一個時脈。除了在時脈邊緣,暫存器之間什麼都不動。兩次邊緣之間,組合邏輯愛怎麼翻攪、抖動都可以;反正還沒有人在看。
每個週期的節奏都一樣:滴答一響,所有暫存器同時對它們的輸入拍下快照、鎖進新值。這些新鮮的值接著經由暫存器之間的組合邏輯往外擴散——加法器算出一個和、多工器挑一條路——結果朝著下一排暫存器奔去。只要每個結果都在下一次滴答之前抵達並穩定下來,下一張快照就會擷取到正確的值,機器便乾淨俐落地往前跨一步。整台電腦就成了一場巨大的「一二三木頭人」:大家在兩拍之間拼命跑,一拍響就定格。
建立與保持:每條訊號都必須守住的兩個承諾
在「下一拍之前穩定下來」這句話裡藏著一個陷阱。正反器是個實體的東西;它沒辦法可靠地擷取一個在時脈邊緣那一刻還在抖的值。所以每個正反器都附帶兩個你必須守住的時序承諾。建立時間(setup time)是輸入在時脈邊緣之前必須已經穩定多久;保持時間(hold time)是它在邊緣之後必須繼續保持穩定多久。任何一個沒守住,正反器就可能落進一個模糊的中間狀態——有一陣子既不是 0 也不是 1——這是一種真正危險、叫做亞穩態(metastability)的情況,會污染所有去讀它的東西。
建立關乎慢路徑,保持關乎快路徑。把時脈邊緣想成相機快門。建立時間是說,被拍的人在快門按下之前必須先靜止一會兒,照片才不會糊掉——輸入必須夠早抵達。保持時間是說,被拍的人不可以在快門一開就立刻奪門而出,否則畫面會拖影——輸入在邊緣之後不可以太快就改變。好的設計會在每個正反器、每個週期都同時滿足這兩者,而且還留有餘裕,去對抗溫度、電壓與製造上的浮動。
關鍵路徑:那條替整體定速限的慢路
同步設計逼我們面對的關鍵問題是:一個時脈週期最短能多短?它必須長到讓每一條在某拍從暫存器出發的訊號,都能在下一拍之前抵達下一個暫存器、並滿足它的建立時間。穿過邏輯的某些路徑很短,做完還綽綽有餘。但其中總有一條閘鏈是最慢的——而那條單一的最壞情況路徑,叫做關鍵路徑,它決定了一切。不論其他每條路徑多快,週期都不可能短於訊號走完那一條路所需的時間。
想像一支行進樂隊必須全員同時抵達下一條線。樂隊只能走得跟它最慢的成員一樣快:就算其他人都衝刺,整排也得等那個落後的人到了才准跨步。把最壞那條鏈加總——發射端正反器的時脈到輸出延遲、加上穿過最深那團邏輯雲的每一個閘延遲、再加上接收端正反器的建立時間——這個總和就是最短週期時間。它的倒數就是最高時脈速率。所以當規格表吹噓自己幾 GHz 時,它真正報的,是晶片上最壞那條路徑長度的倒數。
[REG]--->( gate )--->( gate )--->( gate )--->[REG]
A B C D E
clk-to-Q + gate B + gate C + gate D + setup
(launch) (logic delay) (capture)
-----------------------------------------------------
= critical-path delay = T_min
max clock rate = 1 / T_min這正是架構師為何對關鍵路徑念念不忘。想跑更快,你就找出那條最慢的鏈把它縮短——用更快的進位先行(carry-lookahead)設計換掉慢的漣波進位加法器,或者在一團長邏輯雲中間插進一個額外的暫存器、把它切成兩段。後面那個把一條長路切成幾個較短管線階段的把戲,正是你在下一個階梯會遇到的整套管線觀念的種子。不過要誠實地點出陷阱:縮短關鍵路徑讓你滴答得更快,但它本身完全不會減少一個程式需要多少週期——快慢永遠是指令數、每指令週期數與週期時間三者一起,從來不是時脈單獨決定。
為什麼這是整台機器的樞紐
退一步看看你現在握有什麼。組合邏輯會計算但會遺忘;暫存器會記住。把組合雲接在受時脈控制的暫存器之間,再用一面共用的時脈統管全局,你就有了每一台時序機器的基本形狀——包括處理器本身,它其實不過就是一些暫存器(程式計數器、暫存器檔、管線狀態)被一些邏輯(ALU、解碼器、多工器)隔開,全都跟著同一面鼓前進。基礎那一階的提取—解碼—執行循環,正是這同一個模式從遠處看的樣子。
而關鍵路徑,正是這幅整潔的數位圖像與真實晶片殘酷物理之間的橋梁。它就是時脈速率為何在 2000 年代中期停止攀升的原因:把週期推得越來越短,意味著要燒越來越多的功率與熱去把訊號更快地趕過那條最壞路徑,直到這面牆貴得付不起。這個領域的回答不是更快的時脈,而是每一拍做更多工、用更多核心——正是這架階梯上方等著你的平行與加速器。所以「一個週期最短能多短?」這個樸實的問題,竟是形塑現代計算機結構最深的力量之一。