2017年6月16日 星期五

淺談古代CPU:副處理器的故事(2)


前情提要:淺談古代CPU:副處理器的故事(1)

8087並不是第一顆浮點運算器,甚至也不是第一顆浮點運算「微處理器」;早在1977年,AMD(別驚訝,AMD 1969年成立,只比Intel晚一年)就做了Am9511和Am9512算數運算器,Intel還跟AMD拿了這兩顆的授權,做了8231和8232,當作8080的副處理器,不過這幾顆運算器跟8087不太一樣,對於主CPU來說,它們只是一個單純的輸出入裝置,CPU只是把資料單純的送進去或讀出來,裡面做甚麼對CPU來說是完全無關的。



但是8087就不同了,作為8086的副處理器,8087基本上被看成是CPU的延伸,原則上,8087等於是「擴大」了8086的指令集,我們先花一點篇幅,提一下這個算是基本計算機概論的小常識:指令集。

之前在介紹32bit與4G的時候,我們曾經用黑盒子來概略描述CPU的行為,一個CPU如果當作一個黑盒子,它的行為基本上就是:向外界(基本上就是記憶體)輸出位址,然後存取資料。
現在這個黑盒子要再複雜一點:雖然還是一樣輸出位址給記憶體,從記憶體存取資料,但是對黑盒子內部來說,記憶體存取的東西分成兩種,分別是「指令」和「資料」。

電腦的另外一個名字叫「電子計算機」,顧名思義,電腦是用來計算的,要計算,需要告訴電腦兩件事:第一,做甚麼計算;第二,要被計算的數字;前者稱為指令,後者稱為資料。
很多人都用過算盤或是那種只有數字和加減乘除的掌上型計算機;當你要計算很多數字,例如把幾十個數字各自照公式計算然後平均等等,會怎麼做呢?首先,你要有張紙,上面記錄要計算的數字,以及計算的公式,你看一眼公式,把數字打進計算機或算盤,根據公式計算,然後把計算結果寫回那張紙,完成工作;恭喜你,你做的事情就是現代電腦所做的事情;那張紙就是記憶體,你和算盤或計算機做的事,就是CPU做的事。

電腦與過去那些簡單的計算機最大的不同,在於電腦可以把「如何計算」這個資訊變成一種可以存放在記憶體裡面的資料,這種資料有個特別的名稱叫「指令」,CPU就是從記憶體讀進指令,根據指令再從記憶體裡讀取資料,進行運算,或是把運算結果寫進記憶體,一個CPU可以接受的指令格式,稱為這個CPU的「指令集」。

為什麼要談這麼多基本常識?因為8086「認得」8087的指令;在8086的指令集中,8087的浮點運算指令開頭的第一個byte是11011xxx(xxx表示不固定),這個11011被稱為是ESC code(其實二進位11011就是ASCII code裡面的ESC代碼),8086讀取指令時,看到這個ESC就會知道「不是自己可以處理的指令」(x86的指令格式其實很複雜,如果想要了解全貌,可以參考http://ref.x86asm.net/ 這個網站)。

可是問題來了,8086是讀取了8087的指令後,才會知道「這不是我可以處理的」,問題是8087在8086「外面」,這時候該怎麼辦?再送出去給8087?
8086和8087的工程師想出了一個非常巧妙的方法,簡單的說,8086的接腳除了位址和資料線以外,還有一些額外的訊號線,8086用了一部份訊號線來表示「現在8086的行為」,例如「正在讀取指令」、「正在讀取資料」或是「寫資料到記憶體」等等。

如果各位去看8087的資料,會發現8087的腳位跟8086很像,實際上。8087跟8086大部分訊號線都接在一起,換句話說。8087可以「看」到8086大部分訊號,當8087看到8086正在讀取指令,而且指令的最前面是11011,它就知道這個指令是給它的,不需要另外再送訊號給8087,更妙的是,8086接到8087的指令後,如果這個指令的後段是要8087存取記憶體,8086收到後會把腳位訊號切換為記憶體存取(就是告訴記憶體「我要存取記憶體了」,然後停止動作(這有個專有名詞叫Dummy Read),讓8087接管這些訊號完成存取動作。

不過後來的80287和387就沒有像8087這樣的設計,而是完全等主CPU(286/386)讀進指令,解碼完畢後,再從資料線傳到副處理器,主CPU解碼完畢會再啟動一個I/O動作,286會對I/O位址0xF8、0xFA、以及0xFC輸出資料給287,而386則是0x800000f8 和0x800000fc,手冊裡還會特別聲明,希望程式不要自己去亂動這幾個I/O port位址,不過事實上287和387也不會去真的去看記憶體或是I/O的位址線(事實上它們的接腳裡面沒有位址線)就是了;為什麼不像8087那樣做呢?有個原因是8087的運作方式必須跟8086完全同步,287/387是可以跟主CPU頻率不同,另外一個原因是8087這種設計如果遇到浮點指令本身格式有錯時,8087解碼不出來,8086會一直等待下去,也就是會當掉,一般來說遇到這種情況,應該是CPU解碼後會丟出一個「例外狀況」,附帶一提,如果一個系統沒有裝浮點運算器,卻讀進浮點指令,也是要丟出一個「例外狀況」。

不管怎麼設計,在486之前,因為浮點運算器都是外接的,所以總是會有一些專用的訊號線讓這兩顆IC互相通訊,但是到了486,因為一開始就把浮點運算器和CPU整合成一顆,所以根本就沒有這樣的預留線路,可是後來(1991年中)Intel出了486SX(沒有浮點運算器的便宜版486)後,有些買了486SX的用戶發現沒有浮點運算很不方便要求升級方案時,這個問題就顯得很棘手,怎麼辦呢?Intel的辦法是:出一顆「完整的」486,但是腳位稍微改一下(其實只是多加一根腳),改個名字叫「487 SX」,當這顆晶片裝到主機板預留的腳位時,有一根線會把旁邊那顆486SX「整顆關掉」。

但是這還不是最機車的設計,大概在同一段時間前後(1990-1991),Intel為了應付某些386的用戶需要升級卻不想換主機板的需求,推出了一種稱為RapidCAD的處理器升級方案。

簡單的說, RapidCAD是一種「裝在386腳座上面的486」,晶片本身其實是個拿掉內建cache的486DX,但是對外的腳位完全是386,問題來了,386的浮點運算器80387是外接的,可是486DX的浮點運算器是內建的,怎麼辦呢?

Intel想出來的方法是:多做一顆「假的」387,事實上賣出來的RapidCAD一套有兩顆晶片,一顆稱為RAPIDCAD-1,腳位和386一樣,另外一顆稱為RAPIDCAD-2,腳位跟387一樣;那顆RAPIDCAD-2唯一的用途,是針對某些用來偵測「387存不存在」的腳位(精確一點的說,是FERR這隻腳),送出正確的訊號,讓主機板和處理器都「以為」387在上面,實際上卻是在CPU那邊。

 RapidCAD的整數效能其實並不好,受限於386匯流排的限制以及拿掉了那8K cache,整數效能大概只比同頻率的386高個10%而已,可是浮點運算就很威了,因為跟CPU在同一顆晶片,RapidCAD的浮點運算可以比387高70%,所以這顆其實是為急需浮點運算又不想花錢買整台486系統的386客戶量身打造的,這也是它名字的由來:Rapid"CAD",那個年代,最需要浮點運算的,除了科學研究以外,大概就是電腦輔助設計(CAD)了。


下次我們會來談談「傳說中的」Weitek 3167/4167。

淺談古代CPU:副處理器的故事(3)

1 則留言:

Peter 提到...

謝謝您提供了這麼精采的故事,很喜歡!