Risc-V¶
介紹 Risc-V 基礎內容。
0x01 架構特點¶
RISC-V 是一種新興的開放指令集架構(ISA),具有高度的靈活性與可擴展性。它以模組化設計為核心理念,允許開發者根據實際需求選擇不同的擴充模組來構建處理器。例如,最基本的指令集包括 RV32I 和 RV64I,而可選的擴充則涵蓋了乘除法(M)、原子操作(A)、浮點運算(F、D)、壓縮指令(C)以及適用於高效能運算的向量指令集(V)等。
該架構支援多種位元寬度,包括 32 位元(RV32)、64 位元(RV64)以及未來預留的 128 位元(RV128)模式,從而滿足從嵌入式系統到高效能伺服器等多樣化應用場景。RISC-V 也明確區分了處理器的特權等級,定義了使用者態(U-mode)、監督態(S-mode)和機器態(M-mode),為作業系統的運作和安全機制提供良好支援。
下表列出了一些常見的指令集擴充及其簡要說明:
名稱 | 描述 |
---|---|
RV32I / RV64I | 基本整數指令集,構建最小系統的核心 |
M 擴充 | 支援整數乘除法運算 |
A 擴充 | 提供原子操作能力,適用於多核心系統 |
F / D 擴充 | 支援浮點數運算:F 為單精度,D 為雙精度 |
C 擴充 | 16 位元壓縮指令,提升程式碼密度 |
V 擴充 | 向量處理能力,用於資料並行運算 |
Zicsr / Zifencei | 控制狀態暫存器操作與指令同步控制 |
0x02 暫存器¶
RISC-V 處理器架構定義了一組通用暫存器與控制狀態暫存器(CSR),用於處理資料、控制流程及儲存狀態資訊。以下是這兩類暫存器的簡要介紹。
(1)通用暫存器¶
RISC-V 一共有 32 個通用暫存器,編號為 x0
到 x31
,每個暫存器為 32 位元(RV32)或 64 位元(RV64)。這些暫存器也有對應的 ABI 名稱,常用於組合語言程式設計。
暫存器編號 | ABI 名稱 | 用途說明 |
---|---|---|
x0 | zero | 恆為 0 的唯讀暫存器 |
x1 | ra | 返回位址暫存器 |
x2 | sp | 堆疊指標 |
x3 | gp | 全域指標 |
x4 | tp | 執行緒指標 |
x5-x7 | t0-t2 | 臨時暫存器 |
x8 | s0/fp | 保存暫存器/框架指標 |
x9 | s1 | 保存暫存器 |
x10-x17 | a0-a7 | 函式參數/返回值 |
x18-x27 | s2-s11 | 保存暫存器 |
x28-x31 | t3-t6 | 臨時暫存器 |
注意事項: - x0
是特殊的唯讀暫存器,總是返回 0
,寫入無效。 - 臨時暫存器(t0
~t6
)在函式呼叫過程中不保證保留。 - 保存暫存器(s0
~s11
)在函式呼叫間必須被保存與恢復。
(2)CSR 暫存器¶
CSR(Control and Status Register)是用於控制、狀態監控與設定處理器運作的重要暫存器。RISC-V 中 CSR 的位址空間為 12 位元,共支援 4096 個 CSR 暫存器。
常見的 CSR 暫存器包括:
名稱 | 位址 | 功能說明 |
---|---|---|
mstatus | 0x300 | 機器級狀態暫存器,儲存全域中斷啟用、特權等資訊 |
misa | 0x301 | 指示目前支援的指令集架構 |
mie | 0x304 | 中斷啟用暫存器 |
mtvec | 0x305 | 中斷向量表位址 |
mepc | 0x341 | 發生例外時的程式計數器保存暫存器 |
mcause | 0x342 | 例外/中斷原因暫存器 |
mtval | 0x343 | 錯誤位址資訊 |
mcycle | 0xB00 | 機器模式下的週期計數器 |
注意事項: - CSR 可透過特殊屬於 zicsr
擴充指令集的指令 csrrw
、csrrs
、csrrc
等進行讀寫。 - 依據特權等級分類,CSR 分為使用者態(u)、監督態(s)、機器態(m*)等。 - 在簡化實作中,僅支援機器態(M-mode)的 CSR 是常見情況。
(3)浮點暫存器¶
RISC-V 架構在支援浮點擴充(如 F
和 D
擴充)時,提供了一組專用的浮點暫存器,用於浮點數的運算與資料儲存。
RISC-V 定義了 32 個浮點暫存器,編號為 f0
到 f31
,每個暫存器通常為 32 位元(單精度,RV32F)或 64 位元(雙精度,RV64D),具體取決於所實作的浮點擴充。
暫存器編號 | ABI 名稱 | 用途說明 |
---|---|---|
f0-f7 | ft0-ft7 | 臨時暫存器 |
f8-f9 | fs0-fs1 | 保存暫存器 |
f10-f17 | fa0-fa7 | 函式參數/返回值 |
f18-f27 | fs2-fs11 | 保存暫存器 |
f28-f31 | ft8-ft11 | 臨時暫存器 |
注意事項: - 浮點暫存器的 ABI 命名與整數暫存器類似,但前綴為 f
。 - 根據 ABI 約定,函式參數透過 fa0
~fa7
傳遞,返回值通常使用 fa0
。 - ft*
為呼叫端保存(Caller-saved),fs*
為被呼叫端保存(Callee-saved)。 - 使用浮點暫存器需處理器支援相應的浮點擴充指令集,如 F
(單精度)或 D
(雙精度)。
0x03 常見指令¶
(1)常見整數指令(R 型)¶
R 型指令用於暫存器間的算術或邏輯運算,格式:rd = rs1 op rs2
。
指令 | 含義 | 範例 | 說明 |
---|---|---|---|
add | 加法 | add x5, x1, x2 | x5 = x1 + x2 |
sub | 減法 | sub x5, x1, x2 | x5 = x1 - x2 |
sll | 邏輯左移 | sll x5, x1, x2 | 按 x2 位左移 x1 |
srl | 邏輯右移 | srl x5, x1, x2 | 按 x2 位右移 x1 |
sra | 算術右移 | sra x5, x1, x2 | 保留符號位的右移 |
and | 位元與 | and x5, x1, x2 | 位元 AND 運算 |
or | 位元或 | or x5, x1, x2 | 位元 OR 運算 |
xor | 位元異或 | xor x5, x1, x2 | 位元 XOR 運算 |
slt | 小於置位(有號) | slt x5, x1, x2 | 若 x1 < x2 ,x5=1 否則為 0 |
sltu | 小於置位(無號) | sltu x5, x1, x2 | 同上但比較無號值 |
(2)常見立即數指令(I 型)¶
I 型指令對暫存器和立即數執行運算,適用於快速計算和常數賦值。
指令 | 含義 | 範例 | 說明 |
---|---|---|---|
addi | 加立即數 | addi x5, x1, 10 | x5 = x1 + 10 |
andi | 與立即數 | andi x5, x1, 0xF | 位元 AND ,遮罩操作常用 |
ori | 或立即數 | ori x5, x1, 1 | 設定某一位常用 |
xori | 異或立即數 | xori x5, x1, 0x1 | 反轉特定位 |
slti | 小於立即數 | slti x5, x1, 5 | 有號立即數比較 |
sltiu | 無號小於立即數 | sltiu x5, x1, 5 | 無號立即數比較 |
slli | 左移立即數 | slli x5, x1, 3 | 最多移位 31(RV32) |
srli | 右移立即數 | srli x5, x1, 2 | 無號邏輯右移 |
srai | 算術右移立即數 | srai x5, x1, 2 | 有號右移保留符號位 |
lui | 載入高位立即數 | lui x5, 0x12345 | x5 = 0x12345000 |
auipc | 加偏移到 PC | auipc x5, 0x10 | x5 = PC + 0x10000 |
(3)載入指令(I 型)¶
用於從記憶體位址讀取資料到暫存器。位址由 rs1 + offset
計算得出。
指令 | 含義 | 範例 | 說明 |
---|---|---|---|
lb | 載入位元組(有號) | lb x5, 0(x1) | 從 x1 指向的位址讀 1 位元組,符號擴展到 x5 |
lbu | 載入位元組(無號) | lbu x5, 0(x1) | 零擴展 |
lh | 載入半字(16 位元) | lh x5, 2(x1) | 符號擴展 16 位元整數 |
lhu | 載入半字(無號) | lhu x5, 2(x1) | 零擴展 |
lw | 載入字(32 位元) | lw x5, 4(x1) | 常用載入指令 |
lwu | 載入字(無號,RV64) | lwu x5, 4(x1) | 32 位元零擴展用於 RV64 |
ld | 載入雙字(64 位元) | ld x5, 8(x1) | RV64 架構下的主力指令 |
(4)儲存指令(S 型)¶
用於將暫存器中的值寫入記憶體。位址仍由 rs1 + offset
計算。
指令 | 含義 | 範例 | 說明 |
---|---|---|---|
sb | 儲存位元組 | sb x5, 0(x1) | 僅存 1 位元組 |
sh | 儲存半字(16 位元) | sh x5, 2(x1) | |
sw | 儲存字(32 位元) | sw x5, 4(x1) | RV32 和 RV64 共用 |
sd | 儲存雙字(64 位元) | sd x5, 8(x1) | 僅 RV64 可用 |
(5)控制流指令(B 型)¶
用於實現條件跳轉,配合標籤使用,跳轉偏移為 12 位元立即數(±4KB)。
指令 | 含義 | 範例 | 說明 |
---|---|---|---|
beq | 相等跳轉 | beq x1, x2, label | 若 x1 == x2 ,跳轉到 label |
bne | 不等跳轉 | bne x1, x2, label | 若 x1 != x2 ,跳轉 |
blt | 小於跳轉(有號) | blt x1, x2, label | 若 x1 < x2 ,跳轉 |
bge | 大於等於跳轉(有號) | bge x1, x2, label | 若 x1 >= x2 ,跳轉 |
bltu | 小於跳轉(無號) | bltu x1, x2, label | 無號小於比較跳轉 |
bgeu | 大於等於跳轉(無號) | bgeu x1, x2, label | 無號大於等於比較跳轉 |
(6)跳轉與連結指令(J 型、I 型)¶
用於過程呼叫與函式跳轉,保留返回位址到 ra
。
指令 | 含義 | 範例 | 說明 |
---|---|---|---|
jal | 跳轉並連結(J 型) | jal x1, label | x1 = PC+4; PC = label (用於呼叫) |
jalr | 暫存器間接跳轉(I 型) | jalr x1, x2, 0 | x1 = PC+4; PC = x2 + 0 (常用於返回或呼叫函式指標) |
- 若目標暫存器為
x0
,表示不保存返回位址(純跳轉)。 jal
通常用於呼叫函式,jalr
用於從函式返回。
(7)特殊系統呼叫指令(環境互動)¶
指令 | 含義 | 範例 | 說明 |
---|---|---|---|
ecall | 環境呼叫(系統呼叫) | ecall | 觸發環境呼叫例外 |
ebreak | 除錯中斷點 | ebreak | 觸發除錯例外 |
0x04 呼叫規約¶
堆疊框架結構圖(RISC-V ABI 堆疊框架)
高位址
│
│ +-----------------------------+
│ | 需要保存的暫存器 |
│ +-----------------------------+
│ | 超過暫存器傳參的參數 > a7/fa7|
│ +-----------------------------+
│ | 返回位址 (ra) │
│ +-----------------------------+
│ | 區域變數 |
│ +-----------------------------+
│ | (16 位元對齊空間填充) |
└──► 堆疊指標 (sp)
1. 堆疊成長¶
- 向低位址方向成長
sp
(x2)總是保持 16 位元對齊- 常用
sp
作為指標存取堆疊內容(例如:ld t0, 16(sp)
)
2. 保存暫存器內容¶
由 callee 保存: - s0–s11
(x8–x9, x18–x27) → 用於長期保存的資料 - fs0–fs11
(f8–f9, f18–f27) → 浮點保存暫存器 - ra
(x1) → 返回位址(call 偽指令修改 ra 後由被呼叫函式保存)
由 caller 保存: - t0–t6
(x5–x7, x28–x31) - ft0–ft11
(f0–f7, f28–f31)
參數大小限制¶
- 小於指標大小(如
char
、short
):放在暫存器低位 - 超過一個指標大小(如
long long
):需要偶數對齊暫存器 -
例如 RV32 中:
void foo(int, long long)
int
→a0
long long
→a2
和a3
(跳過a1
,偶數對齊)
-
超過兩個指標大小(如
struct { long x[3]; }
) → 以引用傳遞
返回值傳遞規則¶
- 返回值使用:
- 整數:
a0
,a1
- 浮點數:
fa0
,fa1
- 結構體返回:
- ≤2 pointer-word:用
a0/a1
-
2 pointer-word:呼叫端分配記憶體,並透過隱藏指標作為第一個參數傳遞
暫存器使用一覽¶
類型 | 名稱 | 用途 | 由誰保存 |
---|---|---|---|
x1 | ra | 返回位址 | 呼叫端(caller) |
x2 | sp | 堆疊指標 | 被呼叫端(callee) |
x8 | s0 / fp | 框架指標 / 保存暫存器 | 被呼叫端 |
x10–x17 | a0–a7 | 參數 / 返回值暫存器 | 呼叫端 |
x5–x7, x28–x31 | t0–t6 | 臨時變數 | 呼叫端 |
x9, x18–x27 | s1, s2–11 | 保存變數 | 被呼叫端 |
f10–f17 | fa0–fa7 | 浮點參數 / 返回值 | 呼叫端 |
f0–f7, f28–f31 | ft0–11 | 浮點臨時變數 | 呼叫端 |
f8–f9, f18–f27 | fs0–fs11 | 浮點保存變數 | 被呼叫端 |
注意,嚴格來說,
sp
、ra
暫存器並不屬於暫存/保留暫存器的分類,這兩個暫存器的處理多以由被呼叫端(callee)保存為主。同時,像是gp
等暫存器也不屬於這個範疇。