本頁面的文字和圖像允許在CC-BY-SA 3.0協議四和GNU自由文檔許可證下修改和再使用。
20年前,準確地說是2001年7月和2002年9月,嵌入式CPU“方舟一號”和通用CPU“龍芯一號”相繼發布,分別在嵌入式和通用CPU領域結束了我國無芯歷史。方舟一號設計用途是網絡通信處理器,因為無需軟件生態,也不追求通用性能,所以自己設計了指令集。龍芯一號的目標是桌面計算機,軟件生態很重要,各種各樣的應用軟件更需要CPU具有均衡的通用性能,于是采用了當時在高性能服務器中使用較多的MIPS III指令集。
這兩款CPU在不同的領域代表著我國自主CPU的第一步,承載著一段歷史,承載著發展自主CPU技術的希望。但我更感興趣的是它們的技術特點和差異,想要弄明白為什么方舟一號分明誕生得更早,但結束“只能使用進口CPU制造計算的歷史”的卻是龍芯一號。人云亦云不是我的風格,我的疑惑只能自己解決。這兩款CPU太老了,資料很難查找,但我還是成功收集到了足夠的信息,終于弄明白了這個問題。
下面我先把它們的一些參數列一個表格,然后再細細說明它們的特點和差異。
兩者封裝大小差不多,但方舟一號的管腳數量更多,這是因為它是嵌入式設計,許多的設備管理功能都集成在芯片內部,更多的引腳可以直聯更多類型的的外部設備和低速總線。龍芯一號因為制程相對先進一些,并且作為通用CPU不需在芯片內部集成各種附加功能,頻率更高反而功耗更低。緩存大小兩者一樣,功耗也差不多,整數性能看起來也差不多,那為什么仍然說方舟是嵌入式CPU,而龍芯是通用CPU呢?
兩者的整數性能雖然看起來接近,但實際的評價標準卻不相同。方舟一號是設計為處理網絡數據的嵌入式CPU,要求低成本低功耗,因此設計十分簡化,比如指令集中沒有包含除法指令,只支持加、減、乘的整數計算,除法和余數計算都只能用軟件方式處理。所以說方舟一號的200MIPS指標是不包含除法的,這在當時也是許多嵌入式CPU都有的特征。方舟一號與當時絕大多數的嵌入式CPU共有特征還包括不支持硬件浮點運算,以及不像通用CPU那樣通過動態調度、亂序執行、分支預測等方式來提高程序的運行效率。
后來的方舟二號和一號相比,在設計上區別不大,主要還是通過使用更先進的工藝制程,改進物理設計來提高工作頻率。二號的主頻達到了400MHz,但指令集仍然只有78條指令。而龍芯一號則完整地實現了MIPS III的全部指令,在設計上也是以通用CPU為目標。MIPS架構(指令集)的資料比較多,不需要詳細說明,方舟架構的資料很少,我也是偶然從一個網友那里得到了它的指令集參考手冊。通過對方舟指令集手冊的閱讀,并與龍芯一號進行比較,我總算明白了方舟不適合作為通用CPU的原因。
首先是特權等級,方舟架構并沒有像通用CPU那樣進行特權指令和用戶指令的區分,也就是說應用軟件可以為所欲為,只有在軟件完全可控的嵌入式環境中,才可以保證整個系統的安全性。方舟架構雖然分為用戶模式和監督模式,但監督模式只是用于處理異常和中斷,與安全無關。龍芯一號除了實現了MIPS-III中定義的特權等級之外,還額外地實現了抵御緩沖區溢出類攻擊的硬件安全設計,整個計算機系統的安全具有較高的保障。
然后是指令集的設計,為了降低CPU的設計復雜度,減少晶體管數量,方舟架構總共只有78條指令。雖然遠不如通用CPU指令集豐富,但與其它的嵌入式CPU相比則是同一水平。方舟架構也是繼承了RISC思想的典型設計,指令字長32位,有32個通用寄存器,內存訪問是Load/Store的模式,其它指令的操作數都來自寄存器和立即數,而不直接訪問內存。下圖是方舟指令集參考手冊中的指令列表,從圖中可以看到許多適用于嵌入式而不適用于通用CPU的指令設計。
立即數(直接寫在代碼中的數字)加載只支持21bit的無符號數,而沒有加載有符號立即數據的指令。有符號數只能先放在內存中,再用Load類指令加載到寄存器,對于有大量立即數參與的計算,運行效率偏低。直接跳轉指令支持兩種尋址方式,J指令跳轉目標地址由21bit有符號立即數直接指定,這種在代碼中固化絕對地址的跳轉方式,只在嵌入式應用中才會出現。JA指令的寄存器+偏移量尋址倒是通用處理器中常見的方式,但偏移量只支持10bit無符號數,偏移范圍很小,只有4K(1024<<2)字節。10bit的范圍是0~1023,僅1K字節大小,但由于指令字長32bit(4字節),指令會隱含地把立即數左移2bit,因此實際范圍擴大了4倍。4K長度可以適用于簡單的嵌入式程序,但桌面程序需要的跳轉范圍一般要大得多,如果要適應復雜的桌面應用程序,就需要接力跳轉。Branch類是分支跳轉指令,也就是根據大于、小于等各種條件跳轉到不同的代碼分支。但跳轉的偏移范圍是用10bit的有符號立即數表示的,有效范圍是±2K字節(512<<2),對于桌面應用程序來說這個跳轉偏移范圍太小了。MIPS III的分支跳轉范圍是16bit有符號數,范圍是±128K字節,對于20年前的應用程序還夠用,到了現在就成了影響程序性能的因素之一。方舟架構的整數運算指令只有4個,其中加法分為寄存器加寄存器和寄存器加立即數兩條指令,乘法和減法各一條,沒有支持立即數的格式。實際程序中有大量的使用固定數字參與計算的表達式,在方舟架構以及其它類似架構的CPU上,就需要從內存中把數字讀入寄存器再進行計算,比常見的通用CPU要多花費一條指令。而且沒有除法指令,所有的除法和余數計算都要使用軟件方式來處理,比如最簡單的軟件除法就是用被除數減去除數,把結果再賦值給被除數,在循環中統計減了多少次之后結果為負數,次數減一就是商。雖然算法很簡單,但完成這樣一次循環至少需要十幾條指令,循環多少次需要執行的指令數就翻多少倍,而硬件除法只需要一條指令。雖然可以優化軟件除法的代碼,但仍然會遠遠低于硬件指令的效率。在簡單的嵌入式程序中這種效率是可以接受的,不過如果與通用CPU相比,即使80286這樣古老的CPU,也是有硬件除法指令的。在當時,不只是方舟,還有許多同類的嵌入式CPU也是這樣的設計。缺少浮點指令,這也是當時各種嵌入式CPU的共同點。因為不支持基本的浮點加減乘除,那么更高級的三角函數、對數、指數等各種與浮點計算相關的指令也是不存在的。缺少硬件浮點計算的能力對于嵌入式CPU不是問題,無論是路由器、交換機這類網絡通信設備,還是電子詞典、學習機這樣的消費產品,都不需要進行大量浮點計算,使用軟件浮點就能滿足要求。但在2000年之后,沒有硬件浮點的CPU已經不可能用于通用計算設備,即使386都可以額外配置387浮點協處理器,486的滿血版本已內置了浮點單元,何況當時已經進入了Pentium 4的時代,桌面環境中大量的圖形圖像計算都非常考驗CPU浮點計算的能力。在Linux命令行下工作的專業軟件雖然不需要軟件界面,但在科研領域對浮點計算的性能要求卻遠高于普通的桌面軟件,作為通用處理器的龍芯一號雖然有不弱的硬件浮點性能,但事實上也滿足不了當時對CPU性能的要求,畢竟龍芯一號只有相當于Pentium 2的性能,與當時Intel最高性能CPU差距極大。方舟架構的其它指令在當時也同樣都只能適用于嵌入式應用場景,主要是指令的數量太少,有許多較復雜的計算必須分拆成多條指令來執行,無法高效地運行通用應用程序。只是單從指令集分析,我無法確切知道方舟一號與龍芯一號的性能差距。其實把它們倆放在一起對比性能是不公平的,因為方舟一號本身就是為嵌入式應用而設計,并不擅長通用計算,假如測試程序中有大量除法和浮點計算,或者邏輯比較復雜有大量的條件判斷和分支跳轉,方舟的弱勢就會非常明顯。方舟一號除了在指令集方面不能承載通用應用,在CPU的設計上也盡量簡化,比如龍芯一號中那些現代通用CPU的特征,方舟一號就沒有。但方舟一號屬于嵌入式CPU的大量特征也是龍芯一號沒有的,比如SoC擴展能力、功耗管理單元、定時器單元、片上系統總線和設備總線等等,都表現出方舟一號是一款設計成熟的嵌入式CPU,能夠滿足當時國內對嵌入式CPU的大部分要求。
一年半之后的方舟二號也使用了0.18um的制程,通過更好的工藝和改進的設計把功耗降低了四分之三,也就是300mW的樣子,頻率則達到了400MHz,加上把指令緩存和數據緩存增加一倍,CPU整體性能估計應是方舟一號的2.5倍左右。這與方舟架構參考手冊中提到的目標相符:“The instruction set was designed to allow a very small with very low power consumption, yet highperformance implementation. ”——該指令集的設計允許一個非常小、功耗非常低但性能很高的實現。只可惜方舟3號沒有繼續完成,方舟的領頭人自己放棄了,沒有堅持到底!!!
從資料中我明白了方舟系列CPU確實是專為嵌入式應用而設計,無法作為通用CPU使用。而龍芯一號無論是指令集還是結構設計,都是以通用CPU為目標,雖然性能只相當于Pentium 2,但確實能夠適用于各種應用場景,運行任何應用程序都沒有明顯的短板。能夠用來制造包括PC在內的通用計算設備,大概就是“通用”的本意。后來的龍芯二號性能達到了龍芯一號的3~5倍,雖然仍然與當時的主流CPU有很大差距,但在一些對性能要求不高的場合也有了成批量的使用。當前龍芯最新型號3A5000的性能已經踏進了主流CPU的門檻,并使用了自主設計的LoongArch架構(指令集),龍芯CPU的軟件生態也在有序發展,終于堅持到了朝陽升起。