
1993 年,英特爾釋出了高效能的 Pentium 處理器,也是歷史悠久的 Pentium 系列處理器的開端。Pentium 比之前的英特爾 486 處理器有很多改進,其中之一是更快的浮點除法演算法。一年後,數論教授 Nicely 教授在研究孿生素數的倒數時發現了一個問題:他的 Pentium 晶片在執行浮點除法時有時會產生錯誤的結果。英特爾本來認為這是“一個非常小的技術問題”,但令他們驚訝的是,這個漏洞成為了媒體的頭條新聞。受到數週的批評、嘲笑和負面宣傳後,英特爾同意更換所有有問題的 Pentium 晶片,這家公司因此損失了 4.75 億美元。
在本文中,我會討論 Pentium 的除法演算法,指出漏洞在 Pentium 晶片上的什麼位置,仔細研究晶片電路,並解釋問題成因。簡而言之,除法演算法使用了一個查詢表。1994 年,英特爾聲稱該漏洞的原因是指令碼錯誤導致表中遺漏了 5 個條目。但我的分析表明,由於查詢表定義中的數學錯誤,遺漏的條目實際上有 16 個。其中 5 個缺失條目觸發了該漏洞(也稱為 FDIV 漏洞,以浮點除法指令“FDIV”命名),而另外 11 個缺失條目則沒有影響。

這張 Pentium 晶片的照片顯示了 FDIV 漏洞的位置。
雖然 Nicely 教授引發了人們對 FDIV 漏洞的關注,但他並不是第一個發現它的。1994 年 5 月,英特爾對 Pentium 的內部測試顯示,浮點除法非常偶然時會出現輕微的誤差。由於只有 90 億分之一的值會導致此問題,因此英特爾認為問題微不足道:“這甚至不能算作錯誤。” 儘管如此,英特爾還是悄悄修改了 Pentium 電路以解決此問題。
幾個月後,也就是 10 月,Nicely 注意到他的素數計算結果有誤。他很快確定 1/824633702441 在三臺不同的 Pentium 計算機上都是錯誤的,但他的舊計算機給出了正確答案。他打電話給英特爾技術支援,但遭到了拒絕,因此 Nicely 向十幾家計算機雜誌和個人傳送了有關該錯誤的郵件。收件人之一是《沒有文件的 DOS》的作者 Andrew Schulman。他將電子郵件轉發給了 DOS 軟體工具公司的聯合創始人 Richard Smith。Smith 將電子郵件釋出在 Compuserve 論壇上,這是 20 世紀 90 年代的社交媒體。
《電子工程時報》的一名記者發現了 Compuserve 的帖子,並在 11 月 7 日的期刊上撰寫了有關 Pentium 漏洞的文章:英特爾修復了一個 Pentium FPU 故障。在文章中,英特爾解釋說,漏洞出在晶片的一個元件 PLA(可程式設計邏輯陣列)中,該元件充當除法運算的查詢表。英特爾已經修復了最新 Pentium 處理器中的漏洞,並將為相關客戶更換有故障的處理器。
問題可能已經悄然結束,但英特爾決定限制可以獲得替換品的客戶數量。如果客戶無法說服英特爾工程師他們需要精確度,他們就無法獲得修復後的 Pentium 處理器。使用者對只能繼續用有故障的晶片而感到憤怒,因此他們將投訴提交給了 comp.sys.intel 等線上群組。11 月 22 日,當 CNN 報道該漏洞時,爭議蔓延到了線下世界。隨著報紙開始報道該漏洞,以及英特爾成為脫口秀節目的笑柄,公眾對 Pentium 漏洞的認識加深了。
12 月 12 日,IBM 宣佈停止銷售 Pentium 計算機,英特爾陷入了困境。12 月 19 日,在 Nicely 首次報告該漏洞後不到兩個月,英特爾屈服了,並宣佈將為所有客戶更換有問題的晶片。此次召回使英特爾損失了 4.75 億美元(按當前幣值計算超過 10 億美元)。
與此同時,工程師和數學家正在分析該漏洞,其中包括設計浮點單元的工程師 Tim Coe。值得注意的是,透過研究 Pentium 的錯誤除法,Coe 對 Pentium 的除法演算法進行了逆向工程,並找出了它出錯的原因。Coe 和其他人撰寫了論文,描述了 Pentium 漏洞背後的數學原理。但到目前為止,還沒有人展示該漏洞是如何在物理晶片本身中實現的。
現在我將回顧一些有關浮點數的重要事項。二進位制數可以有小數部分,類似十進位制數。例如,二進位制數 11.1001 在二進位制小數點後有四位數字。二進位制小數點後的第一個數字表示 1/2,第二個數字表示 1/4,依此類推。因此,11.1001 對應於 3 + 1/2 + 1/16 = 3.5625。這樣的“定點”數可以表示小數值,但其範圍有限。
另一方面,浮點數包括了非常大的數字,例如 6.02×10^23,也包括非常小的數字,例如 1.055×10^−34。在十進位制中,6.02×10^23 的有效數字(或尾數)為 6.02,乘以 10 的冪,指數為 23。在二進位制中,浮點數的表示方式類似,也有有效數字和指數,只是有效數字乘以 2 的冪(而不是 10)。
計算機很早就開始就使用浮點數了,尤其是在科學計算場景中。多年來,不同的計算機對浮點數使用了不相容的格式。最終,當英特爾開發出 8087 浮點協處理器晶片(與 8086/8088 處理器搭配)時,一個標準出現了。該晶片的特性在 1985 年成為標準 (IEEE 754)。隨後,包括 Pentium 在內的大多數計算機都根據此標準實現浮點數。基本算術運算的結果應該精確到有效數字的最後一位。不幸的是,Pentium 處理器上的除法精度有時要差很多。
計算機如何執行除法?簡單的方法類似於小學長除法,只是用的是二進位制。這種方法用於英特爾 486 和更早的處理器,但過程很慢,商的每個位都需要一個時鐘週期。Pentium 處理器使用一種稱為 SRT 10 的新方法,以四進位制執行除法。因此,SRT 每一步生成兩位商,所以除法速度快一倍。我將用十進位制示例簡要解釋 SRT;嚴格的解釋在網上很容易找到。
下圖展示了十進位制長除法,並標明瞭重要部分。被除數除以除數,得到商。在長除法演算法的每一步中,你都會生成商的另一位數字。然後將除數(1535)乘以商數(2),並從被除數中減去該數,得到部分餘數。將部分餘數乘以 10,然後重複該過程,每一步生成一個商數和一個部分餘數。下圖在兩個商數之後停止,但你可以繼續計算以獲得所需的精度。

十進位制除法的重要部分。
請注意,除法比乘法更難,因為沒有簡單的方法可以確定每個商數。你必須估計一個商數,將其乘以除數,然後檢查商數是否正確。例如,你必須仔細檢查 1535 能除以 4578 兩次還是三次。
SRT 演算法透過一種不尋常的方法簡化了選擇商數的運算:它允許商中有負數。這種改變讓商數不需要那麼精確。如果你選擇的商數有點太大,則可以將負數用作下一位:這將抵消太大的數位,因為下一個除數將被新增而不是減去。
下面的示例顯示了其原理。假設你選擇 3 而不是 2 作為第一個商數。由於 3 太大,這一步的餘數為負數(-261)。在一般的除法運算中,你需要使用不同的商數重試。但使用 SRT 時,你可以繼續,在下一步中使用負數(-1)作為商數。最後,正數和負數的這些商可以轉換為標準形式:3×10-1 = 29,與之前的商相同。

以 10 為基數的除法,使用負商數。結果與上例相同。
SRT 演算法的一個優點是,由於商數只需接近,因此可以使用查詢表來選擇商數。具體來說,部分餘數和除數可以截斷為幾位,這樣查詢表就不會那麼大了。在此示例中,你可以將 1535 和 4578 截斷為 15 和 45,該表顯示 15 除 45 是 3,你可以使用 3 作為商數。
Pentium 不使用十進位制,而是使用四進位制的 SRT 演算法:兩位一組。因此,Pentium 上的除法比標準二進位制除法快一倍。使用四進位制 SRT 時,每個商數位可以是 -2、-1、0、1 或 2。在硬體中,乘以這些值都非常容易,因為乘以 2 可以透過位移位來完成。基數為 4 的 SRT 不需要 -3 或 3 的商數;這很方便,因為乘以 3 有點困難。總而言之,基數為 4 的 SRT 比常規二進位制除法快一倍,但它需要更多的硬體:查詢表、用於加或減 1 或 2 的倍數的電路以及將商轉換為標準形式的電路。
SRT 查詢表的目的是提供商數。也就是說,該表將部分餘數 p 和除數 d 作為輸入並提供適當的商數。正如 1994 年的人們解釋的那樣,Pentium 的查詢表是除法錯誤的源頭。該表缺少五個條目;如果 SRT 演算法訪問其中一個缺失的條目,它將生成錯誤的結果。在本節中,我將討論查詢表的結構並解釋出了什麼問題。
Pentium 的查詢表包含 2048 個條目,如下所示。該表有五個區域,分別對應商數字 +2、+1、0、-1 和 -2。此外,表的上部和下部區域未使用(由於 SRT 的數學原理)。未使用的條目填充了 0,這非常重要。特別要注意的是,五個紅色條目需要包含 +2,但卻錯誤地填充了 0。

Pentium 用於除法的 2048 條目查詢表。除數沿 X 軸,從 1 到 2。部分餘數沿 Y 軸,從 -8 到 8。
當 SRT 演算法使用上述表格時,部分餘數 p 和除數 d 是輸入。除數(縮放到 1 和 2 之間)為表格提供 X 座標,而部分餘數(在 -8 和 8 之間)提供 Y 座標。表格座標的細節很重要,所以我將詳細介紹。要選擇一個單元格,除數(X 軸)被截斷為 5 位二進位制值 1.dddd。(由於除數的第一位始終為 1,因此在表格查詢中將其忽略。)部分餘數(Y 軸)被截斷為 7 位有符號二進位制值 pppp.ppp。索引到表格中的 11 位意味著表格包含 2^11(2048)個條目。部分餘數以 2 的補碼錶示,因此值 0000.000 到 0111.111 是從 0 到(幾乎)8 的非負值,而值 1000.000 到 1111.111 是從 -8 到(幾乎)0 的負值。
在本節中,我將解釋如何在 Pentium 的硬體中實現查詢表。查詢表有 2048 個條目,因此可以將其儲存在具有 2048 個兩位輸出的 ROM 中。11(由於商數符號與部分餘數符號相同,因此符號未顯式儲存在表中。)但是,由於該表結構嚴謹(且大部分為空),因此可以更緊湊地儲存在一個稱為可程式設計邏輯陣列(PLA)的結構中。透過使用 PLA,Pentium 處理器將表儲存在 112 行而不是 2048 行中,從而節省了大量空間。即便如此,PLA 在晶片上的體積也足夠大,如果你眯起眼睛看,肉眼就可以看到它。

放大 Pentium 晶片上的 PLA 和相關電路。
PLA 的理念是提供一種密集且靈活的方式來實現任意邏輯函式。任何布林邏輯函式都可以表示為“乘積之和”,即一組透過“或”運算(求和)在一起的 AND 項(乘積)。PLA 有一個稱為 AND 平面的電路塊,用於生成所需的和項。AND 平面的輸出被饋送到第二個塊,即 OR 平面,該平面將這些項進行“或”運算。AND 平面和 OR 平面被組織為一個網格。每個網格點可以有或沒有電晶體,定義各種邏輯函式。關鍵在於,透過在網格中放置適當的電晶體模式,你可以建立任何函式。對於除法 PLA,有 22 個輸入(除數和部分餘數索引中的 11 位,以及它們的補碼)和兩個輸出,如下所示。

除法 PLA 的簡化圖。
如果函式結構允許用少量項來表達,則 PLA 比 ROM 更緊湊。PLA 的一個難點是弄清楚如何用最少的項來表達函式,以使 PLA 儘可能小。事實證明,這個問題通常是 NP 完備的。英特爾使用一個名為 Espresso 的程式透過啟發式方法生成緊湊的 PLA。
下圖顯示了 Pentium 中的除法 PLA。PLA 有 120 行,分成兩個 60 行的部分,中間有支援電路。11 個表輸入位進入中間的 AND 平面驅動器,產生 PLA 的 22 個輸入(每個表輸入及其補碼)。AND 平面電晶體的輸出經過輸出緩衝器並饋入 OR 平面。OR 平面的輸出經過中心的額外緩衝器和邏輯,產生兩個輸出位,表示 ±1 或 ±2 商數。下圖展示了修復該錯誤的更新後的 PLA;出故障的 PLA 長得差不多,只是電晶體模式不一樣。特別要注意的是,更新後的 PLA 在底部有 46 個未使用的行,而原始故障 PLA 有 8 個未使用的行。

這塊 PLA 已移除了金屬層來展示電路。此圖顯示了更新後的 Pentium 中的 PLA,因為這張照片效果更好。
下圖顯示了 PLA 的 AND 平面的一部分。在網格中的每個點,電晶體可以存在或不存在。一行中電晶體的模式決定了該行的邏輯項。垂直的 doped silicon(綠色)接地。垂直的 polysilicon(紅色)由輸入位模式驅動。如果 polysilicon 穿過 doped silicon,則會形成一個電晶體(橙色),當啟用時會將該行拉出來。有一條 metal line 連線一行中的所有電晶體行以產生輸出;圖中大部分 metal 已被去除,但在右側仍可見一些。

Pentium 中的 AND 平面的一部分。我分別將第一條 silicon 和 polysilicon 塗成綠色和紅色。
在顯微鏡下仔細檢查 PLA 後,我提取了 PLA 網格中的電晶體圖案。從電晶體影像中,我可以確定每個 PLA 行的方程式,然後生成查詢表的內容。請注意,PLA 中的電晶體不直接對映到表內容(與 ROM 不同)。因此,沒有與 5 個缺失表條目相對應的電晶體的具體位置。
PLA 的左側實現 OR 平面(下圖)。OR 平面確定行輸出產生的商是 1 還是 2。OR 平面相對於 AND 平面成 90° 方向:輸入是水平 polysilicon(紅色),而輸出線是垂直的。與之前一樣,電晶體(橙色)形成在 polysilicon 與 doped silicon 的交叉處。奇怪的是,每個 OR 平面都有四個輸出,而 PLA 本身有兩個輸出。

PLA 的 OR 平面的一部分。我移除了金屬層以顯示底層的 silicon 和 polysilicon。我畫了接地線和輸出線,顯示了金屬線的位置。
接下來,我將精確展示 AND 平面如何生成一個項。對於除法表,輸入是 7 個部分餘數位和 4 個除數位,如前所述。我將部分餘數位稱為 p6p5p4p3.p2p1p0,將除數位稱為 1.d3d2d1d0。這 11 位及其補碼會垂直輸入 PLA,如下圖頂部所示。這些線是 polysilicon,因此它們將形成電晶體柵極,在啟用時開啟相應的電晶體。底部的箭頭指向第一行中的九個電晶體。(很難分辨 polysilicon 是經過 doped silicon 還是經過 silicon 上方,因此電晶體並不都很明顯。)檢視電晶體及其輸入可發現 PLA 中的第一個項由 p0p1p2p3p4'p5p6d1d2 生成。

出錯 Pentium 晶片中的 PLA 的第一行。
下圖是一個查詢表的特寫,顯示了此 PLA 行如何將值 1 分配給四個表格單元格(深藍色)。你可以將 PLA 的每個項視為與二進位制模式的模式匹配,該模式可以包含“無關”值。第一個 PLA 項(上圖)與模式 P=110.1111, D=x11x 匹配,其中“無關”的 x 值可以是 0 或 1。由於一個 PLA 行可以實現多個單元格,因此 PLA 比 ROM 更高效;PLA 只需 112 行,而 ROM 需要 2048 行。

PLA 中的第一個條目將值 1 分配給四個深藍色單元格。
從幾何學上講,你可以將每個 PLA 項(行)視為覆蓋表格中的一個或多個矩形。但是,矩形不能是任意的,而必須在位邊界上對齊。請注意,表格邊界(洋紅色)中的每個“凸起”都需要一個單獨的矩形,因此需要一個單獨的 PLA 行。(這在後面很重要。)
如果區域恰好對齊,PLA 行可以生成一個大矩形,一次填充許多表格單元格。例如,PLA 中的第三項匹配 d=xxxx,p=11101xx。這個 PLA 行有效地填充了 64 個表格單元格(如下所示),取代了 ROM 中所需的 64 行。

PLA 中的第三項將值 1 分配給 64 個深藍色單元格。
總而言之,PLA 中的電晶體模式實現了一組方程,這些方程定義了表格的內容,並根據需要將商設定為 1 或 2。儘管表格有 2048 個條目,但 PLA 僅用 112 行表示內容。透過仔細檢查電晶體模式,我確定了出錯的 Pentium 晶片和修復後的 Pentium 晶片中的表格內容。
如前所述,查詢表具有與商數 +2、+1、0、-1 和 -2 相對應的區域。這些區域有著由數學邊界定義的不規則傾斜形狀。在本節中,我將解釋這些數學邊界,因為它們是理解 Pentium 漏洞機制的關鍵。
除法演算法的基本步驟是將部分餘數 p 除以除數 d 以獲得商數。下圖顯示了 p/d 如何確定商數。比率 p/d 將在頂部的線上定義一個點。(出於數學原因,該點將在 [-8/3, 8/3] 範圍內。)該點將落入下面五條線中的一條,定義商數 q。但五個商區域是重疊的;如果 p/d 位於綠色段之一,則有兩個可能的商數。圖表的下一部分說明了如何從部分餘數 p 中減去 q*d,從而將 p/d 移到中間,即 -2/3 和 2/3 之間。最後,將結果乘以 4(左移兩位),將間隔擴充套件 19 回 [-8/3, 8/3],與原始間隔大小相同。8/3 的界限可能看起來是隨機的,但其動機是確保新間隔與原始間隔大小相同,因此可以重複該過程。(出於代數原因,界限都是三分之一;值 3 來自底數 4 減 1.20)

處理除法步驟的輸入,產生下一步的輸入。
請注意,SRT 演算法有一些冗餘,但無法處理“太錯誤”的 q 值。具體來說,如果 p/d 位於綠色區域,則可以選擇兩個 q 值中的任意一個。但演算法通常無法從錯誤的 q 值中恢復。例如,如果 q 應該是 2,但選擇了 0,則下一個部分餘數將超出間隔,演算法無法恢復。這就是導致 FDIV 錯誤的原因所在。
下圖顯示了 SRT 查詢表的結構(也稱為 P-D 表,因為軸是 p 和 d)。上圖中的每個邊界都會變成表中的一條線。例如,上面的 p/d 在 4/3 和 5/3 之間的綠色部分會變成下表中的綠色區域,其中 4/3 d ≤ p ≤ 5/3 d。這些斜線顯示了可以使用特定商數 q 的區域。

P-D 表指定部分餘數(Y 軸)和除數(X 軸)的商數。
Pentium 中的查詢表基於上表,每個單元都量化為 q 值。但還有一個約束需要討論。
Pentium 的除法電路使用一種特殊電路來高效執行加法和減法:它叫做進位儲存加法器。此加法器帶來的一個後果是,每次訪問查詢表都可能轉到“正確”單元正下方的單元。這是預料之中的,應該沒什麼問題,但在非常罕見和複雜的情況下,這種行為會導致 Pentium 的五個缺失單元之一被訪問,從而觸發除法錯誤。在本節中,我將討論除法電路使用進位儲存加法器的原因、進位儲存加法器的工作原理以及進位儲存加法器如何觸發 FDIV 錯誤。
加法的問題是進位會使加法變慢。考慮手動計算 99999+1 這個例子。你將從 9+1=10 開始,然後進位 1,生成另一個進位,然後生成另一個進位,依此類推,直到計算完所有數字。計算機加法也存在同樣的問題。如果你要將兩個 64 位數相加,則低位可以生成進位,然後該進位會傳播到所有 64 位。進位訊號經過 64 層電路的時間很長,可能會限制 CPU 效能。因此,CPU 使用特殊電路來加快加法速度。
Pentium 的除法電路使用一種稱為進位儲存加法器的不尋常加法器電路來加(或減)除數和部分餘數。如果你要執行大量加法(如除法期間所發生的情況),進位儲存加法器會加快加法速度。其想法是,你不是在發生進位時向每個數字新增進位,而是將進位儲存在一個單獨的字元中。舉個十進位制的例子,499+222 等於 611,進位為 011;你不用將 1 進位到第二位,而是保留它。下次做加法時,新增之前儲存的進位,並再次儲存任何新的進位。進位儲存加法器的優點是可以平行計算每個數字位置的和與進位,速度很快。缺點是你需要在加法序列的末尾做一個緩慢的加法來新增剩餘的進位以得到最終答案。但是如果你執行多個加法(如除法),進位儲存加法器總體上會更快。
進位儲存加法器給查詢錶帶來了問題。我們需要使用部分餘數作為查詢表的索引。但進位儲存加法器將部分餘數分成了兩部分:和位和進位位。要獲得表索引,我們需要將和位和進位位相加。由於除法的每一步都需要進行這種加法,我們似乎又回到了使用慢速加法器的時代,而進位儲存加法器只會讓事情變得更糟。
訣竅在於,我們只需要部分餘數的 7 位作為表索引,因此我們可以使用另一種型別的加法器——超前進位加法器——使用強力邏輯平行計算每個進位。超前進位加法器中的邏輯對於每個位都變得越來越複雜,因此超前進位加法器對於大數字來說是不切實際的,但對於 7 位值來說是實用的。
下圖顯示了除法器使用的超前進位加法器。奇怪的是,加法器是一個 8 位加法器,但只使用了 7 位;也許 8 位加法器是英特爾的標準邏輯塊。我在這裡只對加法器做一個快速總結,細節留到另一篇文章中。在頂部,邏輯閘平行計算 8 對輸入中的每對的訊號:求和、生成進位和傳播進位。接下來,複雜的進位預測邏輯並行確定每個位置是否有進位。最後,XOR 門將進位應用於每個位。中間的電路用於測試;請參閱腳註。在底部,驅動器放大加法器各個部分的控制訊號,並將 PLA 輸出傳送到晶片的其他部分。透過計算重複電路塊的數量,你可以看到哪些塊是 8 位寬、11 位寬等等。每個位的進位預測邏輯都不同,因此沒有重複的結構。

為查詢表提供資訊的超前進位加法器。該電路塊位於晶片上的 PLA 正上方。我移除了金屬層,因此這張照片顯示了 doped silicon(深色)和 polysilicon(淡灰色)。
進位儲存和超前進位加法器可能看起來平平無奇,但它們是 FDIV 錯誤的關鍵部分,因為它們會更改表上的約束。原因在於部分餘數是 64 位,但計算表索引的加法器是 7 位。由於其餘位在求和之前被截斷,因此表索引的部分餘數和可能略低於實際的部分餘數。具體而言,表索引可以比正確單元低一個單元,偏移量為 1/8。回想一下之前的圖表,其中對角線將區域分開。必須將其中部分(但不是全部)行向下移動 1/8 以解決進位儲存效應,但英特爾做出了錯誤的調整,這就是 FDIV 錯誤的根本原因。(這種效應當時眾所周知,並在 SRT 除法論文中提到過,因此英特爾本不應出錯。)
FDIV 錯誤一個有趣的地方在於它極其罕見。如果 2048 個表中有 5 個錯誤的條目,那麼錯誤的除法應該很常見。但是,由於涉及進位儲存加法器的複雜數學原因,實踐中幾乎從未遇到過丟失的表條目:只有大約 90 億次隨機除法中的 1 次會遇到問題。要找到缺失的表項,你需要連續多次從進位儲存加法器中得到“不幸”的結果,這讓中獎的機率與中彩票的機率差不多。
我認為下圖是解釋 FDIV 錯誤發生原因的“確鑿證據”:頂部的洋紅色線應該位於傾斜的黑線上方,但它反覆穿過黑線。洋紅色線小心翼翼地保持在灰線上方,但那是錯誤的線。換句話說,英特爾在定義表的 +2 區域時選擇了錯誤的邊界線。在本節中,我將解釋為什麼這會導致錯誤。

查詢表的上半部分,解釋了 FDIV 錯誤的根源。
該圖根據 Pentium 查詢表中儲存的商值來著色:黃色代表 +2、藍色代表 +1、白色代表 0,洋紅色線條表示不同值之間的邊界。對角黑線是表格上的數學約束,定義必須為 +2 的區域、可以為 +1 或 +2 的區域、必須為 +1 的區域等等。為了讓表格正確,表格中的每個單元格值都必須滿足這些約束。中間的洋紅色線是正確的:它位於兩條黑線之間(冗餘的 +1 或 +2 區域),因此所有需要為 +1 的單元格都是 +1,所有需要為 +2 的單元格都是 +2,正如所要求的那樣。同樣,底部的洋紅色線位於黑線之間。但是,頂部的洋紅色線有故障:它必須位於頂部黑線上方,但它卻越過了黑線。結果是,一些需要 +2 的單元格最後變成了 0:這些是導致 FDIV 錯誤的缺失單元格。
請注意,頂部洋紅色線保持在對角灰線上方,同時儘可能緊密地跟隨它。如果灰線是正確的線,那麼表格將是完美的。不幸的是,英特爾在生成表格時為表格的上限選擇了錯誤的約束線。
但是為什麼有些對角線降低了 1/8,而其他線沒有降低呢?如上一節所述,由於進位儲存加法器截斷,查詢表的結果可能最終比實際 p 值指示的低一個單元格,即表索引的 p 值比實際值低 1/8。正確的單元格和下面的單元格都必須滿足 SRT 約束。因此,如果這會使約束更嚴格,則線會向下移動,但如果這會擴大冗餘區域,則線不會向下移動。特別是,頂線不能向下移動,但顯然英特爾將線向下移動並生成了錯誤的查詢表。
然而,英特爾對這個錯誤有不同的解釋。英特爾白皮書指出,問題出在將表格下載到 PLA 的指令碼中:一個錯誤導致指令碼省略了 PLA 中的幾個條目。我不相信這個解釋:缺失的條目對應的是數學錯誤,而不是複製錯誤。我懷疑英特爾的說法在技術上是正確的,但有誤導性:他們運行了一個 C 程式(他們稱之為指令碼)來生成表格,但該程式在邊界方面存在數學錯誤。
在《 Pentium 編年史》中,作者,Pentium Pro 的架構師 Robert Colwell 對 FDIV 錯誤提供了不同的解釋。他聲稱 Pentium 設計最初使用與 486 相同的查詢表,但在釋出前不久,管理層向工程師施加壓力,要求縮小電路以節省晶片空間。工程師優化了表格來縮小電路,並證明最佳化是可行的。不幸的是,他們的證明是有缺陷的,但測試人員信任工程師,沒有徹底測試這個改動,導致 Pentium 晶片帶著錯誤釋出了。這種解釋的問題在於,Pentium 從一開始就採用了與 486 完全不同的除法演算法:Pentium 使用基數為 4 的 SRT,而 486 使用標準二進位制除法。由於 486 沒有查詢表,這個故事就不攻自破了。此外,只要刪除 8 個未使用的行,PLA 可以很容易地變小,因此工程師們顯然沒有試圖縮小它。我懷疑,由於 Colwell 在俄勒岡州開發了 Pentium Pro,而最初的 Pentium 是在加利福尼亞州開發的,因此 Colwell 沒有獲得有關 Pentium 問題的第一手資料。
英特爾對該錯誤的修復很簡單,但也令人驚訝。你可能認為英特爾會將五個缺失的表值新增到 PLA 中,而這正是當時報道的內容。《紐約時報》寫道,英特爾透過在晶片上新增幾十個電晶體來修復了該缺陷。《EE Times》寫道:“這個修復需要在 PLA 中新增條目或額外的門序列。”
但是,更新後的 PLA(如下)顯示了完全不同的結果。更新後的 PLA 與原始 PLA 的大小完全相同。但大約 1/3 的條目從 PLA 中刪除,從而減少了數百個電晶體。PLA 的 120 行中只有 74 行被使用,其餘都留空。(原始 PLA 有 8 行空。)從 PLA 中刪除條目是怎麼解決問題的?

更新後的 PLA 有 46 行未使用。
解釋是,英特爾並沒有用正確的值 2 填充五個缺失的表條目。相反,英特爾用 2 填充了所有未使用的表條目,如下所示。這有兩個效果。首先,它消除了任何誤擊空條目的可能性。其次,它讓 PLA 方程式變得簡單得多。你可能認為表中的條目越多,PLA 就越大,但 PLA 項的數量取決於資料的結構。用 2 填充未使用的單元格後,未使用區域(白色)和“2”區域(黃色)之間的鋸齒狀邊框就會消失。如前所述,一個大矩形可以用一個 PLA 項覆蓋,但鋸齒狀邊框需要很多項。因此,更新後的 PLA 比原始的有缺陷的 PLA 小約 1/3。一個後果是,新 PLA 中的項與舊 PLA 中的項完全不同,因此無法找出修復該錯誤的具體電晶體。

錯誤的查詢表(左)和修正後的查詢表(右)的對比。
下圖顯示了出錯的 PLA 的前 14 行和修正後的 PLA 的前 14 行。如你所見,電晶體圖案(以及 PLA 項)完全不同。

錯誤的 PLA 的頂部(左)和修正的 PLA(右)。
Pentium 晶片的錯誤有多重要?這成為了一個極具爭議的話題。隨機除法運算失敗的情況非常罕見:大約 90 億個值中只有一個會觸發該錯誤。此外,錯誤的除法仍然基本準確:錯誤通常出現在小數點後第 9 位或第 10 位,最壞情況下,也就是錯誤出現在第 4 位有效數字中的情況非常罕見。英特爾的白皮書聲稱,典型使用者每 27,000 年會遇到一次問題,與 DRAM 位翻轉等其他錯誤源相比,這微不足道。英特爾表示:“我們的總體結論是,Pentium 處理器浮點單元的缺陷與絕大多數使用者無關。科學 / 工程和金融工程領域應用程式的少數使用者可能需要使用沒有缺陷的更新處理器或軟體解決方法。”
然而,IBM 做了自己的分析,表明該問題可能每隔幾天就會影響客戶,IBM 暫停了 Pentium 的銷售。(巧合的是,IBM 有一個處理器競品 PowerPC。)這場爭論登上了各大報紙;《洛杉磯時報》報道了雙方的發現,將爭論放在臺面上。英特爾很快作出讓步,同意更換所有 Pentium 處理器,解決了爭端。
我基本同意英特爾的分析。似乎只有一個人(Nicely 教授)在實際使用中注意到了這個錯誤。IBM 的分析似乎是故意找出觸發錯誤的數字。大多數人永遠不會遇到這個錯誤,即使他們遇到了,浮點精度的輕微下降對大多數人來說也沒什麼影響。從整個社會來看,更換 Pentium 處理器是一筆巨大的開支,而收益卻微乎其微。但另一方面,客戶期望處理器能提供準確的結果是合理的預期。
請注意,Pentium 錯誤是確定性的:如果你使用觸發問題的特定除數和被除數,你將 100% 得到錯誤的答案。Pentium 工程師 Ken Shoemaker 表示,人們對該錯誤的強烈抗議是因為客戶很容易重現它。當客戶可以在自己的計算機上輕鬆看到該錯誤時,即使這種情況是有意為之,英特爾也很難說客戶永遠不會遇到該錯誤。
FDIV 錯誤是最著名的處理器錯誤之一。透過檢查晶片,可以準確地看到它在晶片上的位置。但英特爾還有其他一些重要的錯誤。一些早期的 386 處理器存在 32 位乘法問題。與確定性的 FDIV 錯誤不同,386 會在特定溫度 / 電壓 / 頻率條件下不可預測地產生錯誤的結果。這裡的根本問題是佈局問題,廠商沒有提供足夠的電氣裕度來處理最壞的情況。英特爾還是出售了有故障的晶片,但將它們限制在 16 位市場;壞晶片被標記為“僅 16 位軟體”,而好處理器則標有 32 位適用。儘管英特爾不得不忍受諸如“英特爾稱某些 386 系統無法執行 32 位軟體”等令人尷尬的新聞頭條,但這個漏洞很快就被人們遺忘了。

386 的好壞版本對比。請注意底線上的標籤。
另一個令人難忘的 Pentium 問題是“F00F 錯誤”,即以 F0 0F 開頭的特定指令序列會導致處理器鎖定,直到重新啟動。該錯誤於 1997 年被發現,並透過作業系統更新得到解決。該錯誤可能存在於 Pentium 的大量微程式碼中。
你可能想知道為什麼英特爾需要釋出 Pentium 的新版本來修復 FDIV 錯誤,而不是更新微程式碼了事。問題是 Pentium(和更早的處理器)的微程式碼被硬編碼到 ROM 中,無法修改。英特爾在 Pentium Pro(1995 年)中添加了可修補的微程式碼。英特爾最初實現此特性是為了晶片除錯和測試。但在 FDIV 漏洞之後,英特爾意識到可修補的微程式碼對於修復漏洞也很有價值。Pentium Pro 將微程式碼儲存在 ROM 中,但它還有一個可容納多達 60 條微指令的靜態 RAM。在啟動期間,BIOS 可以將微程式碼補丁載入到此 RAM 中。在現代英特爾處理器中,微程式碼補丁已用於解決從 Spectre 漏洞到電壓問題等各種問題。

Pentium PLA 的頂部金屬層被移除,露出了 M2 和 M1 層。OR 和 AND 平面位於頂部和底部,中間是驅動程式和控制邏輯。
正如摩爾定律所述,隨著處理器中電晶體的數量呈指數級增長,處理器使用了更復雜的電路和演算法。除法就是一個例子。早期的微處理器,如英特爾 8080(1974 年,6000 個電晶體),沒有硬體支援除法或浮點運算。英特爾 8086(1978 年,29,000 個電晶體)在微程式碼中實現了整數除法,但需要 8087 協處理器晶片才能實現浮點運算。英特爾 486(1989 年,120 萬個電晶體)在晶片上增加了浮點支援。Pentium (1993 年,310 萬個電晶體)轉向了速度更快但更復雜的 SRT 除法演算法。僅 Pentium 的 PLA 部分就有大約 4900 個電晶體點,比 MOS Technology 6502 處理器還多——也就是說 Pentium 晶片一部分電路的一個元件使用的電晶體比 1975 年的整塊處理器還要多。
FDIV 漏洞對英特爾的長期影響是一個爭議話題。一方面,AMD 等競爭對手從英特爾的錯誤中獲益。AMD 的廣告列出了 AMD 晶片的優勢來嘲笑 Pentium 的問題,例如“你不必仔細檢查你的數學”和“實際上可以處理除法等複雜計算的嚴謹性。”另一方面,Pentium Pro 的架構師 Robert Colwell 表示,FDIV 漏洞可能對英特爾產生了淨收益,因為它為 Pentium 創造了巨大的知名度,同時也表明英特爾願意支援其品牌形象。無論如何,英特爾在 FDIV 漏洞中倖存了下來;時間將告訴我們英特爾如何渡過今天它遇到的新問題。
原文連結:
https://www.righto.com/2024/12/this-die-photo-of-pentium-shows.html
宣告:本文由 InfoQ 翻譯,未經許可禁止轉載。
智慧編碼工具層出不窮,究竟怎麼選、如何用?3 月 5 日 -14 日,InfoQ 極客傳媒將發起「智慧編碼系列」直播,邀請阿里、百度、騰訊、位元組、商湯、思碼逸等企業一起線上 Coding,與所有開發者直觀感受和評測數款國內外線上編碼工具在企業真實生產場景中的表現。歡迎掃碼或點選按鈕一鍵預約直播!

今日薦文
