本文目標
RV32I 是 32-bit 的基本整數指令集,該指令集會使用到 32 個暫存器 (x0-x31),且一共有 47 道指令,RV32I 的指令一共可分成六大類:
為了方便, RISC-V 將一個 WORD 的大小設成 4 Bytes (32 bits) ,同時, RISC-V 的指令長度也是 32 bits,這些空間會被分割成好幾個 fields,不同類型的指令都會有不同的分配方式。
這種類型的指令代表暫存器與常數之間的運算:
| 31 20 | 19 15 | 14 12 | 11 07 | 06 00 |
+----------------------+---------+------------+--------+------------+
| immediate[11:0] | rs1 | funct3 | rd | opcode |
+----------------------+---------+------------+--------+------------+
addi rd, rs1, simm12
常數部分為 (simm12) 為 12 位元的有號數,運算時會將 12 位元擴展成 32 位元,與 rs1 暫存器做加法運算並將結果寫進 rd 暫存器中。
關於 simm12 擴展的邏輯,我們可以參考 rv32emu-next 的實作:
// FI_IMM_11_0 = 0b11111111111100000000000000000000 static inline int32_t dec_itype_imm(uint32_t inst){ return ((int32_t)(inst & FI_IMM_11_0)) >> 20; }
slti rd, rs1, simm12
運算時會將 12 位元擴展成 32 位元,再與 rs1 暫存器當做 signed number 做比較(如果指令為 SLTIU
則視為無號數),若 rs 暫存器 1 小於常數,則將數值 1 寫入 rd 暫存器,否則為 0 。
補充
本篇文章的 simm[number] 代表有號的 number 個位元,若是 uimm[number] 則代表無號的 number 個位元。
若出現在下方指令,就不再特別說明。
andi/ori/xori rd, rs1, simm12
將 simm12 擴展後與 rs1暫存器做 AND / OR / XOR 運算,再將結果寫入 rd 暫存器中。
slli/srli/srai rd, rs1, uimm5
常數部分為 unsigned 5 bits,將 rs1 暫存器做 shift 運算,偏移量為 uimm5 的值 (0 - 31),偏移後的結果會寫入 rd 暫存器。
lw/lh/lhu/lb/lbu rd, rs1, simm12
將 rs1 暫存器的內容加上 simm12 視為地址,並將該地址存放的值放到 rd 中。
包含 upper immediates 的指令
lui rd, uimm20
將 uimm20 存放到 rd 暫存器的高位 20 bits 中,剩餘的 12 bits 皆為 0 。
auipc rd, uimm20
unsigned 20-bit放到最高 20位元,剩餘 12位元補0,將此數值與 pc相加寫入 rd暫存器。
指令為暫存器與暫存器之間的運算
add/sub rd, rs1, rs2
將 rs1 與 rs2 做加法/減法運算後,再將結果寫入 rd暫存器。
slt/sltu rd, rs1, rs2
將 rs1 與 rs2 暫存器當做有號或是無號數做比較,若 rs1 小於 rs2 ,將數值 1 寫入 rd ,反之寫入 0 。
and/or/xor rd, rs1, rs2
將 rs1 與 rs2 做 AND/OR/XOR 運算,並將結果寫入 rd 中。
sll/srl/sra rd, rs1,, rs2
將 rs1 做 shift 運算,再將結果寫入 rd 暫存器,rs2 的最低 5 位為代表位移量。
NOP 指令即為不改變任何暫存器狀態,除了 pc 以外。
NOP 指令會被編碼成 addi x0, x0, 0
替代。
UJ-Format 包含了無條件跳躍 (Unconditional Jumps) 類型的指令。
jal rd, simm21
常數部分為 21 位元的有號數(此常數必須為 2 的倍數,代表 LSB 必為 0 ),因為此道指令編碼的常數位元數只有 20位元,所以只會將 simm21 的最高 20 位元放入指令編碼中,跳躍範圍為 -+1MiB ,同時也會將下一道指令的位址 pc+4 寫入 rd 暫存器中,在標準的 calling convention 中,rd 暫存器會使用 x1 。如果只是單純的 jump,並非是呼叫函示需要儲存其返回位址 pc+4,可用 jal x0, simm21 取代。
jalr rd, rs1, simm12
跳躍的位址為 rs暫存器加上 simm12 ,並把下一道指令的位址 pc+4 寫入 rd 暫存器中。
SB-Format 包含了條件跳躍 (Conditional Branches) 類型的指令。
beq/bne/blt/bltu/bge/bgeu rs1, rs2, simm13
常數部分必須為 2的倍數,即最低位元為 0 ,因為此道指令編碼的常數位元數只有 12 位元,所以只會將 simm13 的最高 12 位元放入指令編碼中,跳躍範圍為 -+4Kib 。
將內容存放到記憶體中的指令。
sw/sh/sb rs2, rs1, simm12
將 rs1 暫存器的內容加上 simm12 視為地址,並將 rs2 的資料寫到該地址中。
定義了一組 FENCE 指令,達到多個 thread 間的記憶體同步。
CSRRW / CSRRS / CSRRC / CSRRWI / CSRRSI / CSRRCI
定義了一組 CSR指令,可用來讀取寫入 CSR。
rdcycle 用來讀取最低 31-bit cycle CSR , rdcycleh 用來讀取最高 31-bit cycle 數。
用來讀取 time CSR 。
用來讀取 instret CSR 。
屬於 I-Type。
使用來呼叫 system call。
Debugger 用來切換進 Debugging 環境。