iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 11
0

前情提要


昨日總算是克服了我們的簡單目標,最後一塊拼圖就是在前幾天規劃的 rvgc 函式庫。今天就讓我們開始探索 RISC-V 的指令集架構吧!主要的參考來源是 RISC-V 基金會的規格文件。歷史、沿革這些東西對於理解技術在當代科技的地位是非常重要沒有錯,但是這些面向不是本系列的重點,因此予以略過。

給新加入的讀者:筆者是多年來享受電腦系統的方便性的後生晚輩,由於某一天發現自己對於 ELF 的格式如此漠不關心感到汗顏,因此決定以最黑手的方式理解這個已經奮鬥了數十年的規格。

同樣息息相關但我們應該沒有時間觸及的面向是除錯器。如果各位讀者對 debug 相關議題有興趣,這裡是公司夥伴的鐵人賽系列文,歡迎大家去參考參考。

預期目標


RISC-V 的設計哲學是追求硬體簡化,因此指令集非常精簡。同時,又為了顧及軟體的需求,RISC-V 基金會將指令集拆分為模組化的設計,而由每一個英文字母代表模組的意義。比方說,最基本的模組叫做 I 指令集,因為整數處理的指令都在這裡;又,最近剛結束的 RISC-V 第七次 workshop 裡面有研究團隊報告了 H 指令集代表虛擬化平台需使用的 Hypervisor 等指令。

我們這個函式庫名稱取作 rvgc,是為了代表 RISC-V General-Comppression 的縮寫,G 代表通用的部份,涵蓋 IMAFDC 代表壓縮指令。根據目前社群的共識,雖然標準上說的基本支援指令集是 I,但是事實上如果沒有支援 IMA,幾乎不可能運行作業系統。對於這個系列文要支援到哪裡,筆者打算至多完成 IMA,也就是整數操作、乘法除法、以及原子操作指令集,原因無他,要迴避浮點數很簡單,但是在通用的整數領域裡面,不曉得什麼時候可能會碰到非得要乘除法和原子操作的情況;至於壓縮指令,會讓程式設計的內容複雜化,就暫且不做。

今日會開始介紹 rvgc 的實作,主要參考自最新的 2.2 版規格中的 RV32I、RV64I、M 與 A 等四個章節;至於像是編碼的細節之類的部份,筆者參考第 19 章與第 20 章。等到完成的差不多之後,我們會再回頭用其他稍微複雜的程式來檢驗 as 的實作,必要時也重構一些既有的 as 內容。以下筆者會先介紹 RISC-V 64-bit GC 使用的暫存器,然後會介紹指令型態。預計花三天的時間來講解指令集與函式庫,同時為了提昇 go 語言能力,筆者也考慮引進測試框架來強化這個部份的設計。

暫存器


整數暫存器共有 32 個,寫組語程式時可以用 x0~x31 來代表這些暫存器;因此,在指令的編碼中,一個暫存器會佔去 5 個位元。其中,讀取 x0 總是回傳 0 且寫入無效,算是 RISC (減量指令集 CPU 設計)很通用的一個設定。除了這些編號之外,它們還有一些綽號,是為了彰顯它們的使用方式而命名的別名。以表格列舉如下

真實名稱 綽號 呼叫後保留 說明
x0 zero 總是為零
x1 ra 回傳時的目標位址
x2 sp stack 指標
x3 gp 不應修改 global 指標
x4 tp 不應修改 thread 指標,與 gp 同屬其他系統機制可能用到的暫存器
x5~x7,x28~x31 t0~t2,t3~t6 t 字頭代表暫存用
x8~x9,x18~x27 s0~s1,s2~s11 s 字頭暫存器由被呼叫函數儲存
x10~x17 a0~a7 a 字頭暫存器代表參數,a0 也作回傳值使用

指令型態


RV64I 的指令集一律是 32-bit 的長度,但由於編碼上的先進設計,讓 C 指令集可以方便地加入 16-bit 指令,能夠大幅壓縮編譯出來的執行檔大小。設計指令型態時,原始團隊的優先考量是讓硬體在解碼指令的時候能夠越方便越好,因此刻意讓功能相近的區間能夠盡量在同一個區段;比方說所有種類的指令裡面,暫存器最多會用到三個,最少也會用到一個,但它們所佔的空間都相同。這個原則能夠精簡化硬體在處理暫存器編碼時的努力,進而提昇效能。

根據規格書,總共有 4 種指令集型態。為求排版美觀,筆者這裡用純文字格式編排,再以無 markdown 格式輸出。ASCII 表格上方數字是該欄位的上界與下界位元,數字越大表示作為 32-bit 整數越高位(significant)。

R-type

| 31      25 | 24   20 | 19   15 | 14      12 | 11  07 | 06      00 |
+------------+---------+---------+------------+--------+------------+
|   funct7   |   rs2   |   rs1   |   funct3   |   rd   |   opcode   |
+------------+---------+---------+------------+--------+------------+

R 型態指令包含三個暫存器,完全在處理暫存器。暫存器的邏輯運算指令是這一類的大宗。

I-type

| 31                20 | 19   15 | 14      12 | 11  07 | 06      00 |
+----------------------+---------+------------+--------+------------+
|   immediate[11:0]    |   rs1   |   funct3   |   rd   |   opcode   |
+----------------------+---------+------------+--------+------------+

牽涉到一個 12-bit 整數的使用的指令。從記憶體取值即是一例:以 rs1 為基準,12-bit 有號整數 imm 為調整的記憶體位址的內容,會被存入 rd 暫存器中。

S-type

| 31      25 | 24   20 | 19   15 | 14      12 | 11  07 | 06      00 |
+------------+---------+---------+------------+--------+------------+
|  imm[11:5] |   rs2   |   rs1   |   funct3   | i[4:0] |   opcode   |
+------------+---------+---------+------------+--------+------------+

例子:存入值到記憶體:以 rs1 為基準,12-bit 有號整數 imm 為調整的記憶體位址的內容,會被寫入 rs2 的內容。

U-type

| 31                                       12 | 11  07 | 06      00 |
+---------------------------------------------+--------+------------+
|             immediate[31:12]                |   rd   |   opcode   |
+---------------------------------------------+--------+------------+

這個指令需要動到 rd 暫存器中高達 20-bit 的內容。有兩個主要的指令使用這個格式,它們是 lui 指令,代表將 rd 暫存器的 [31:12] 取代為指令中的整數;另一個是 auipc,將 rd 取代為 pc 暫存器加上這個整數部份,常用於函數呼叫。

小結


今日我們開始介紹 RISC-V 指令集了,相較於標題中的酷炫關鍵詞 go、binutils,它是最慢登場的。本日的介紹應該讓各位讀者基本地認識了 RISC-V 指令集架構才是,甚至也可能讓各位想起了計算機結構裡面的 MIPS,他們的確有相當的淵源。RISC-V 正在緩慢崛起的此時,各位除了觀望之外,希望筆者以及夥伴的的相關系列文能夠讓各位有種見證歷史的感覺,如果真的能學到東西那就更棒了。無論如何,我們明日再會!


上一篇
第十日:as 初傳捷報
下一篇
第十二日:RISC-V 整數基本指令集的五十道陰影
系列文
與妖精共舞:在 RISC-V 架構上使用 GO 語言實作 binutils 工具包30

尚未有邦友留言

立即登入留言