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

走進一顆 DRAM 晶片:列、行、更新

上一篇我們學到了 DRAM 為何便宜、密集卻又慢——每一位元只用一個電晶體和一個會漏電的電容。現在我們要打開晶片本身:一張由列與行構成的方格、一個一次讀出一整列的巧妙列緩衝、挑出某一位元的 RAS/CAS 之舞,以及讓那些漏電位元保命的更新動作。讀完之後,你會清楚看見 DRAM 那古怪的時序究竟從何而來。

一個位元:一個電晶體和一只會漏的水桶

從上一篇我們已經知道交易內容:一個 DRAM 位元,不過是一個電晶體看守著一只極小的電容。電容裡有電荷代表 1,空了代表 0。這正是 DRAM 為何如此美妙地密集又便宜的原因——每一位元幾乎不花什麼——也正是它為何又慢又彆扭的原因,因為電容是一只會漏的水桶。電荷在幾毫秒內就會漏光,而且讀取是破壞性的:要查出桶裡有沒有水,你得把它倒出來。

兩個事實直接從這裡掉出來,而晶片其餘的部分就是為了應付它們而造的。第一,因為電荷極小、讀取又會把它毀掉,晶片不能只是瞥一眼某個位元——它必須放大並重寫。第二,因為水桶會漏,每個位元都得週期性地補滿,否則就會忘記自己的值。我們即將認識的整套設計——列、列緩衝、感測放大器、更新排程——存在的理由,就是要把一面牆的漏水桶,變成處理器能可靠讀寫的東西。

一顆晶片是一張方格:列與行

位元不是排成一條線;它們被鋪成一張巨大的二維方格。要點名一個位元,你得給出一個(row)和一個(column),就像點戲院裡的座位(J 排、14 號)。一顆現代晶片把這張方格塞進好幾個彼此獨立的記憶庫(bank)——把一個 bank 想成一塊有自己機械裝置的子方格,於是晶片可以同時對不只一個下手。在一個 bank 裡,挑一個列會一次啟動一整列;接著挑一個行,才選出那一列裡你真正要的那一片。

為什麼是方格,而不是一條平坦的線?成本。一個由 N 個位元組成的平坦陣列,所需的佈線會隨 N 成長;而一張大約 sqrt(N) 列乘 sqrt(N) 行的方形方格,所需的位址線少得多,因為同一套選列與選行的電路被整張方格共用。對一個 2^30 個位元的 bank 來說,平坦方案毫無希望,但一張 2^15 乘 2^15 的方格只需要 15 個列位址位元和 15 個行位址位元。正是這張方格,讓每顆晶片塞進十億個位元變得負擔得起。

列緩衝與 RAS/CAS 之舞

這裡就是它的核心。要讀一個位元,晶片先送出一個列位址閃控(RAS):它啟動一整列,把那一列裡的每個位元都倒進感測放大器,由它們偵測那些微弱的電荷並鎖存進一個類似 SRAM 的快速暫存區,叫作列緩衝然後,一個行位址閃控(CAS)才從那個緩衝裡挑出你要的那幾個位元組。緩衝此刻握著一整列,熱騰騰、隨時可取。這兩段時序——RAS 開一列要多久、CAS 從緩衝讀出要多久——就是記憶體模組規格表上那組著名的 RAS/CAS 延遲數字。

  1. ACTIVATE(RAS):控制器送出一個列位址;被選中那一列的位元流向感測放大器,鎖存進列緩衝。這是那個又慢又貴的步驟。
  2. READ(CAS):控制器送出一個行位址;要求的位元組直接從列緩衝裡讀出——很快,因為沒碰到任何電容,碰的只是那份已鎖存的副本。
  3. 對同一列再下更多 CAS:只要那一列保持開啟,後續的讀或寫就直接命中緩衝——這是一次列緩衝命中,DRAM 所能提供的最便宜存取。
  4. PRECHARGE:在同一個 bank 裡另一列能開啟之前,目前這一列必須先被寫回(記得,讀取是破壞性的),位元線也要重置。要到那時,一個新的 ACTIVATE 才能開始。

這解釋了 DRAM 的雙重人格。如果你下一次存取正好落在那條已經開啟的列裡,它就完全跳過 ACTIVATE 和 PRECHARGE——一次列緩衝命中,飛快。如果它落在同一個 bank 的另一列,你就得付 PRECHARGE 加上一次全新的 ACTIVATE 再加上 CAS——一次列緩衝未命中,慢上好幾倍。所以 DRAM 在晶片這一層也獎賞空間區域性:停留在一條開啟列裡的存取很便宜,到處亂跳的存取則猛敲那套慢吞吞的開關循環。列緩衝實際上就是一個內建在每個 bank 裡、只有一列大小的快取。

更新:把會漏的水桶補滿

現在來談那個漏。每個電容都會漏電,所以放著不管,一個 DRAM 位元會在幾十毫秒內忘掉自己的值。解藥是更新(refresh):晶片週期性地把每一列重新讀進感測放大器,再原樣寫回,電荷補滿。因為一次普通的讀取本來就會放大並重寫一整列,更新不過就是把那套機制按排程跑一遍。一個控制器必須在一個更新視窗內——常見約 64 毫秒——把每個 bank 的每一列都走一遍,一遍又一遍,永遠如此,只為了不讓記憶體悄悄爛掉。這個更新,正是讓「Dynamic RAM」(動態隨機存取記憶體)有了那個「D」的東西。

更新不是免費的,而這份誠實很要緊。當一條列正在被更新時,那個 bank 沒法服務真正的請求,所以更新偷走了一片頻寬,也偶爾添上延遲的小打嗝——一筆小但真實的稅,幾個百分點,而且隨著晶片越來越密、要走的列越來越多而增長。相對地,SRAM 完全不需要更新:它用一個鎖存電路握住自己的位元,只要電源還在就一直待著不動。這又是一個 SRAM 每位元更快也更簡單、而 DRAM 更便宜卻更難伺候的理由——正是上一篇那個取捨,如今在時序裡現了形。

記憶體控制器指揮這支樂團

沒有人會親手把生的 RAS/CAS/PRECHARGE/更新指令遞給晶片。那是記憶體控制器的活,今天它是一塊就坐在處理器晶粒上的電路。它接過快取未命中遞來的那些簡單位址,把每一個拆成 bank、列、行,然後在遵守數十條時序規則的同時排程那些底層指令——ACTIVATE 與 READ 之間、PRECHARGE 與下一個 ACTIVATE 之間的最小間隔,以及那永不止息的更新死線。關鍵在於,它會試著重排請求,讓命中某條已開啟列的那些先走,盡可能把昂貴的列緩衝未命中變成便宜的命中。

Bank 是控制器的另一根桿子。因為每個 bank 都有自己的列緩衝和機械裝置,控制器可以在 bank 0 還忙著 precharge 的時候,去開 bank 1 的一條列——把它們的慢步驟重疊起來,而不是串成一列。這種記憶庫層級平行性,正是 DRAM 儘管每一次存取都慢、卻仍能交出高頻寬的辦法:許多慢動作平行地跑,於是位元組源源不絕地湧出,儘管任何單一個位元組,都是走了一趟漫長又孤獨的路才抵達。這跟管線對指令玩的把戲一模一樣,只是套在記憶庫上。

退一步,那個誠實的大重點就現身了。這一整套機械裝置提高的是吞吐量,但對單獨一次冷存取的延遲幾乎無能為力——開一條全新的列照樣得花它那幾十奈秒,跟十年前同一套物理。這正是為什麼 DRAM 頻寬攀升得遠比 DRAM 延遲下降得快,也正是為什麼依記憶體之牆,記憶體如此頻繁地設下了真正的效能天花板。下一篇會直接接著講:DDR 世代、通道與位寬如何把頻寬翻倍再翻倍,以及延遲為何頑固地拒絕跟上。