跳转至

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 個通用暫存器,編號為 x0x31,每個暫存器為 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 擴充指令集的指令 csrrwcsrrscsrrc 等進行讀寫。 - 依據特權等級分類,CSR 分為使用者態(u)、監督態(s)、機器態(m*)等。 - 在簡化實作中,僅支援機器態(M-mode)的 CSR 是常見情況。

(3)浮點暫存器

RISC-V 架構在支援浮點擴充(如 FD 擴充)時,提供了一組專用的浮點暫存器,用於浮點數的運算與資料儲存。

RISC-V 定義了 32 個浮點暫存器,編號為 f0f31,每個暫存器通常為 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 < x2x5=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)

參數大小限制

  • 小於指標大小(如 charshort):放在暫存器低位
  • 超過一個指標大小(如 long long):需要偶數對齊暫存器
  • 例如 RV32 中:

    void foo(int, long long)
    

    • inta0
    • long longa2a3(跳過 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 浮點保存變數 被呼叫端

注意,嚴格來說,spra 暫存器並不屬於暫存/保留暫存器的分類,這兩個暫存器的處理多以由被呼叫端(callee)保存為主。同時,像是 gp 等暫存器也不屬於這個範疇。

參閱