算出正確答案、卻太晚的晶片
想像一名跑者衝向隊友要交接力棒。跑者很快、跑道淨空、交棒也演練過——但接棒的隊友是聽到固定的哨聲就起跑。如果接力棒在哨聲響起後晚了那麼一瞬間才到,跑者再完美都沒用:交接失敗,整隊取消資格。一顆數位晶片每秒都要上演這齣戲數十億次,而且是在它承載的每一個訊號上。
這裡有個讓每個初學者跌一跤的意外。我們通常認為:只要邏輯正確,電路就正確——給它對的輸入,邏輯閘就算出對的輸出。這是必要的,卻還不夠。一個由真實電晶體構成的真實邏輯閘,切換是需要時間的——電荷要在導線與通道中流動,會有一段極小但絕非零的延遲。把幾十個邏輯閘串起來,延遲就會累積。輸出是對的,但它太晚才出現。在一個有時脈的系統裡,「太晚」不過是「錯」的另一種說法。
時脈:讓十億個邏輯閘步調一致的節拍器
為什麼要設一個截止時限?因為沒有它,就是一片混亂。如果成千上萬個訊號都能在自己算完的那一刻隨意更新,較快的路徑就會超車較慢的路徑,晶片可能讀到一個半生不熟的答案。解法是整個工程界最強大的點子之一:一個共用的時脈訊號——一個以固定頻率上上下下的方波——像節拍器一樣讓整顆晶片跟著它行進。幾乎沒有任何事是「準備好了就動」;相反地,晶片只在時脈的上升邊緣才提交新的值。
服從時脈的元件,是正反器(flip-flop)(以及用它們組成的暫存器)。正反器是一個一位元的記憶單元,規則簡單而嚴格:絕大多數時間它完全無視資料輸入,然後在時脈的上升邊緣,把當下擺在輸入端的值「拍照」鎖住,並保持穩定直到下一個邊緣。正反器之間的一切,則是單純的組合邏輯——沒有記憶的邏輯閘——它可以在兩個時脈節拍之間的空檔裡盡情翻攪、穩定、產生結果。正反器加上時脈紀律,正是循序邏輯的核心。
clock: __|‾‾|__|‾‾|__|‾‾|__ a square wave, period = T
^ ^ ^
edge edge edge flip-flops only update HERE
|<--- T --->| one clock period = one 'turn'
If the clock runs at 1 GHz: T = 1 / 1e9 = 1 nanosecond.
Everything between two edges has just 1 ns to finish. Forever.發射與擷取的競賽:一條時序路徑
把畫面拉近到單一一格的推進,你就看見了後面一切的基本單位:一個發射(launch)正反器、一團邏輯、一個擷取(capture)正反器。在某個時脈邊緣,發射正反器把它儲存的位元打到一條導線上。那個位元接著穿過一串邏輯閘——每一個都是一顆標準單元(standard cell),一塊事先量測過、延遲已知的邏輯積木——再穿過連接它們的金屬導線(這也會增加延遲)。一個時脈週期之後,下一個邊緣抵達擷取正反器,而它必須發現資料早已穩定。這趟「發射→邏輯→擷取」的旅程,就是一條時序路徑(timing path)。
same clock edge launches and (one period later) captures
| |
clk --------+--------------------+--------------+----
| | |
+----v----+ logic +---v---+ arrives, must
D ---->| LAUNCH | --[ ]--[ ]--[ ]-->| CAPTURE| be stable
| FF | combinational | FF |
+---------+ (no memory) +--------+
t_clk-to-q + t_logic + t_wire must be < T
(launch FF (everything in (one clock
delay) between) period)這裡其實藏著兩個截止時限,學習軌道會各用整整一階去談。資料不能太晚到(否則擷取正反器取樣到的是一個還在變動中的值)——這種失敗叫做建立時間違規(setup violation)。但它也不能太早到、在邊緣之後、正反器還沒鎖完之前就又變了——這叫保持時間違規(hold violation)。現在先記住這個簡單的圖像:訊號必須在擷取邊緣周圍的一個時間窗內保持穩定。落在這個窗之外,你存下的位元就是未定義的。
為什麼你無法靠模擬跑出信心
檢查時序最直覺的方法,就跟你除錯軟體的方法一樣:把晶片跑起來。施加一組刺激——一組測試向量——觀察波形,確認每個訊號都及時穩定。這就是動態模擬,對一個極小的電路來說它很完美。問題出在涵蓋率。一個時序臭蟲只有在你的向量剛好觸發那條太慢的路徑、又剛好用到能讓它翻轉的資料時,才會現形。漏掉那一組刺激,那條慢路徑就會在你整場測試裡安靜地沉睡,然後在客戶的手機裡醒來。
而路徑的數量是天文數字。一個真實設計有數百萬到數十億條不同的「發射到擷取」路徑。要靠模擬確保安全,你需要一組能激出其中每一條最壞情況的向量——這在組合上根本不可能。模擬回答的是「這一次特定的執行有沒有準時?」我們真正需要的卻是「每一條路徑、在所有可能的資料下,有沒有準時?」這是兩個截然不同的問題。
靜態時序分析:一次檢查每一條路徑,且不需向量
突破在於:別再跑晶片,改成分析它的結構。靜態時序分析(STA)把整個設計變成一張時序圖(timing graph):每一個正反器、每一顆標準單元、每一條導線,都成為一個標上延遲的節點或邊,而這些延遲取自晶圓廠針對這些元件量測出來的特性庫。STA 接著走遍這張圖,針對最壞情況算出:一個訊號最晚可能在每個終點晚到多久——而且從頭到尾不施加任何一組輸入向量。「靜態」這個詞正是重點所在:它推理的是結構,而非任何一次特定的執行。
- 建立圖。在每個正反器處把設計切開,化成一組時序路徑,每條都從一個發射點、穿過組合邏輯與導線、到達一個擷取點。
- 把延遲加總。對每條路徑,把每顆單元與每段導線的最壞延遲加起來,找出訊號最晚抵達的時刻——它的抵達時間(arrival time)。
- 算出截止時限。根據時脈週期與擷取正反器的要求,推算出資料被允許抵達的最晚時刻——它的要求時間(required time)。
- 比較——這就是裕度。要求時間減去抵達時間就是餘量,稱為時序裕度(slack)。正裕度代表這條路徑趕上了截止時限;負裕度則標出一個必須在下線前修掉的違規。
因為 STA 自始至終只是沿著圖把延遲相加、相比較,它能在幾小時內擴展到整顆晶片,而不是向量模擬所需的地質年代。最關鍵的是它不需向量、且窮舉——它隱含地同時考慮每一條路徑的最壞情況,所以沒有任何慢路徑能因為缺了對的刺激而躲過。它用來比對的目標——時脈週期、建立與保持時間要求、輸入與輸出延遲——都被寫成時序約束(timing constraint),這份合約告訴 STA「準時」到底是什麼意思。