JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

電源域:閘控、電壓島與其間的管路

你手機裡的晶片,此刻多半在睡覺。相機區塊、數據機、神經網路引擎——全都被斷電到硬生生的零,完全不耗電流,同時只有一座極小的「常開島」維持著時脈滴答作響。本篇要談的就是這個把戲:晶片如何被切割成各自獨立控制的**電源域**,你又如何一把關掉某個區塊的供電軌、把它的[[ic-leakage-power|漏電功耗]]徹底斃命,以及那一小群特殊單元——隔離、位準轉換、保留——如何在某個區域熄燈時,不讓整顆晶片化為一團垃圾。

一顆晶片,多座城市:電源域

在第 4 階你學到一個殘酷的事實:就算一顆電晶體完全關閉,它仍會滲漏電流。漏電從不休息。你用不同臨界電壓的電晶體去對抗它——關鍵路徑外用又慢又緊的高 Vt 單元,路徑上用又快又漏的低 Vt 單元。這有幫助,但有天花板。高 Vt 電晶體漏得比較少,卻不是完全不漏。把一點點小漏乘上幾十億顆電晶體,一顆什麼事都沒做的晶片,照樣能在一夜之間把電池榨乾。要讓某個區塊的漏電真正歸,你只有一個選項:把它的電,整個拿走。

這一個念頭,逼著我們改變看待晶片的方式。到目前為止,你心裡的晶片大概是「一整片電路,到處都吃同一個供電電壓——就叫它 VDD」。但現代的系統單晶片(SoC)比較像一個國家,而不是一座城市:它被劃分成多個區域,每個區域有自己的供電政策。一個[[ic-power-domain|電源域]],就是晶片上一塊「供電與電源狀態被當成一個整體一起控制」的區域——同一個電壓、同一組開關控制、同一份低功耗意圖。CPU 可能是一個域,GPU 是另一個,常開的即時時脈又是第三個。當 GPU 閒置時,它的域可以被完全關掉,而 CPU 的域照常活著——就像你把沒人用的房間燈關掉,卻不切斷整棟房子的電一樣。

電源閘控:牆上的總開關

那你究竟要怎麼把電從一塊區域拿走?你在它的供電線上插一個開關。這就是[[power-gating|電源閘控]],而執行它的單元叫做[[ic-power-switch-cell|電源開關單元]]——它在實體上不過是一顆又大又寬的電晶體,被插在常開供電軌與該區塊的本地供電軌之間。把那顆電晶體打開,區塊就看到滿滿的 VDD;把它關掉,區塊就被切斷、懸空,幾乎不再有任何漏電。把它想成是供給某個房間的牆上總開關:一推,整個房間就死了,不管裡頭插了多少盞燈。

它有兩種口味,依照它坐在哪一條軌上來命名。頭開關(header)是一顆 PMOS 電晶體,位在全域 VDD 與區塊的「虛擬 VDD」之間;關掉它,區塊的頂端就被斷糧。腳開關(footer)是一顆 NMOS 電晶體,位在區塊的「虛擬接地」與真正的接地之間;關掉它,區塊的底端就被切斷。腳開關(NMOS)較小、單位面積導通更好,所以很常見;但頭開關直接閘控供電本身,在某些 I/O 情況下能簡化設計。無論哪一種,這顆開關電晶體本身都是用高 Vt 矽做的——它一生大半時間都處於關閉,要去擋住它背後所有電路的漏電,所以你留在路徑上的這一顆電晶體,自己也必須盡可能少漏。

        global VDD  (always-on rail)
            |
          --+--   <- HEADER switch (PMOS, high-Vt)
           /        gated by SLEEP
            |
        virtual VDD  o-------------------+
            |                            |
        [  gated logic block:  ]   <- this whole domain
        [  the CPU, GPU, etc.  ]      goes to ~0 leakage
            |                            |
        virtual GND  o-------------------+
            |
          --+--   <- FOOTER switch (NMOS, high-Vt)
           /        gated by SLEEP
            |
        global GND  (always-on rail)

SLEEP = 0 -> switches ON  -> block powered, virtual rails = real rails
SLEEP = 1 -> switches OFF -> block cut off, leakage collapses to ~0
一顆頭開關(PMOS)與/或腳開關(NMOS)被插在常開軌與區塊的**虛擬**軌之間。關掉開關,區塊的漏電就崩潰歸零——代價是開關導通時,跨在它上面會有一點點 IR 壓降。

隔離單元:別讓一具屍體投票

當你把一個區塊關掉的那一刻,第一件會嚴重出錯的事情是這個:GPU 域熄燈了——但它的輸出線在實體上仍然延伸進隔壁那個常開的 CPU 域,而 CPU 域醒得很,正在讀這些線。當一個斷電區塊的輸出被放著懸空,它的電壓會漂移到某個不確定的值,既不是乾淨的 1,也不是乾淨的 0。把這個懸空訊號餵進一個活著的閘,會發生兩件壞事:活閘可能把它解讀成一個隨機值(汙染清醒的邏輯),更糟的是,一個卡在軌道中間的輸入,會讓一個 CMOS 閘的兩顆電晶體同時都半開,於是從 VDD 到接地打開了一條直通的路徑——橫桿電流(crowbar current)。你關掉一個區塊本想省電,結果卻把它的鄰居給燒了。

解法是一個[[ic-isolation-cell|隔離單元]]:在每一條從「可切換域」跨、進入「常開域」的訊號上,都放一個小閘。當域有電時,隔離單元是透明的——真正的訊號直接穿過。就在域關閉之前的那一瞬間,一個隔離致能(isolation enable)訊號觸發,這個單元就把它的輸出鉗位(clamp)到一個已知、安全的固定值——通常是硬性的 0(AND/NOR 式鉗位)或硬性的 1(OR 式鉗位),看哪一個能讓下游邏輯開心。接收域現在看到的是一個乾淨穩定的值,不管那個死掉的域輸出漂得多離譜。屍體再也不准投票。

位準轉換與保留:橋接電壓、記住狀態

隔離處理的是關掉的問題。現在來認識電壓不同的問題。回想一下電壓島:一顆 0.9 V 的 CPU 與一塊 0.6 V 的感測器區塊對話。從 0.9 V 區塊送出的邏輯「1」,是一個 0.9 V 的訊號——但對 0.6 V 區塊來說,輸入端來個 0.9 V,可能會過度驅動它薄氧化層的電晶體,或單純地搞亂它預期的位準。反方向更糟:一個 0.6 V 的「1」抵達一個 0.9 V 的閘時,可能落在那個閘的切換臨界以下,於是沒辦法把閘的電晶體完全關掉——閘就半開著,漏出橫桿電流,還把輸入讀錯。兩塊電壓不同的區域,不能就這樣直接接在一起。

橋樑是一個[[ic-level-shifter|位準轉換器]]:放在每一條跨越電壓邊界的訊號上,把一個邏輯值從來源電壓翻譯成在目標電壓下乾淨的版本。降壓轉換器(0.9 V → 0.6 V)大致上就是一個用對了供電的緩衝器。升壓轉換器(0.6 V → 0.9 V)則高明些——它通常兩個供電都需要,外加一對小小的交叉耦合電晶體,把一個微弱的低電位輸入「啪」地拉成飽滿的高電位輸出。在電源閘控的設計裡,你常會看到一種合而為一的致能位準轉換器(enable level shifter),一個單元同時做隔離與位準轉換,因為這兩種邊界經常重合。

還剩一個問題,而且是最殘忍的一個。當你切斷一個域的電,裡面每一個正反器都會遺忘。CPU 算到一半;現在它的暫存器全是垃圾。每次喚醒都從頭開機,既慢又會燒掉自己的能量。答案是[[ic-retention-register|保留暫存器]]:一種特殊的正反器,內含一個極小的次要鎖存器——一顆「氣球」或影子單元——由常開電源透過一條纖細、超低漏電的路徑供電。在關機前的那一刻,一個儲存(save)訊號把暫存器的值複製進氣球;接著主正反器斷電、不再漏電;喚醒時,一個還原(restore)訊號把值複製回來。這個域醒來時,剛好停在它離開的地方,彷彿只是眨了一下眼。

編舞:一次喚醒/睡眠的時序

現在來看這四種單元一起共舞,因為順序就是一切。如果你在隔離之前就把一個區塊斷電,它的輸出會懸空地穿過常開邏輯,你就會得到前面說的橫桿大火。如果你在供電軌穩定之前就還原保留值,你複製回去的是垃圾。儲存、隔離、閘控、還原這幾個步驟,必須以嚴格的順序觸發,通常由一個小小的常開電源管理單元(PMU)來驅動。以下是一個可切換域,標準的「入睡再喚醒」週期。

  1. 停止時脈。讓這個域靜默——把未完成的工作收尾或暫停,並閘掉它的時脈,讓任何東西都不處於切換到一半的狀態。在你拔掉它的電之前,這個區塊必須在邏輯上是閒置的。
  2. 儲存狀態(拉起 save)。保留暫存器把它們當前的值複製進常開的影子鎖存器。狀態現在被安全地停泊在「即將死去的邏輯」之外。
  3. 拉起隔離(isolation)。每一條跨入常開域的輸出,都被它的隔離單元鉗位到安全的固定值。邊界現在封住了。
  4. 關閉電源(拉起 sleep)。電源開關單元斷開,讓虛擬軌崩潰。此時——也唯有此時——這個域的漏電才被壓到幾乎為零。
  5. ……睡眠……這個域熄燈靜置,不耗任何電。常開邏輯照常運作,在那個死掉的域原本說話的地方,只看到乾淨的鉗位值。
  6. 重新供電(放掉 sleep),然後等。電源開關重新接通——逐步爬升以避免衝擊電流湧入——而你必須等待虛擬軌爬回穩定的 VDD,才能信任裡面的任何東西。
  7. 還原狀態(拉起 restore)。供電軌穩定後,保留暫存器把儲存的值從影子鎖存器複製回來。這個域現在持有的,正是它入睡前的狀態。
  8. 放掉隔離,然後啟動時脈。放開隔離鉗位,讓真正的訊號再次跨越邊界流動,最後解開時脈閘,這個域就彷彿什麼都沒發生過似地恢復運作。
signal        going to SLEEP            staying asleep    WAKING UP
            ------------------------|                |----------------------
clock     ___|‾stop‾|________________________________________|‾run‾|_______
save      ________|‾|___________________________________________________   (pulse)
isolation _________|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|________
sleep     ____________|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|___________________
restore   ___________________________________________________|‾|________   (pulse)
rails     ‾‾‾‾‾‾‾‾‾‾‾‾\__________ 0 V (no leakage) ________/‾‾‾‾‾‾‾‾‾‾‾‾‾

KEY ORDERING RULES:
  isolate  BEFORE  sleep        (seal outputs before they float)
  sleep    AFTER   save         (don't kill power before state is parked)
  restore  AFTER   rails stable  (don't reload state into a half-dead rail)
  isolation released LAST        (only un-clamp once the domain is fully alive)
斷電/上電的交握。這種嚴格的巢狀順序——隔離包在睡眠之內,儲存在前、還原在後——正是讓一個關掉的域不去汙染一個活著的域的關鍵。順序搞錯,你不是丟掉狀態,就是把 VDD 短路到接地。

接下來往哪走:讓它動態化、形式化

退一步看看你建造出來的東西:一顆被切割成電源域電壓島的晶片,有開關去斃掉漏電,有隔離去封住死者,有位準轉換器去橋接電壓,有保留去記憶。這是真實而深刻的省電——你的手機能撐一天而非一小時,原因就在這裡。但請注意,還有兩樣東西不見了,而它們正好就是接下來的兩階。

第一,到目前為止,一個域不是全開就是全關,一座島固定在一個電壓。但 CPU 並不需要全速才能讀電子郵件。如果你能在工作負載輕的時候把時脈放慢、把電壓降低,只在需要時才把兩者一起拉回去呢?因為動態功耗隨頻率成正比、又隨電壓的平方成正比,就算只降一點點電壓,也能省下巨量的能量。這種連續的、即時的微調,就是動態電壓頻率調節(DVFS)——把本階靜態的島,變成一個你可以即時轉動的旋鈕。