2 2 RISC V::RV32I 指令介紹 - ianchen0119/AwesomeCS GitHub Wiki

本文目標

  • 學習 RV32I

進入正題

RV32I 是 32-bit 的基本整數指令集,該指令集會使用到 32 個暫存器 (x0-x31),且一共有 47 道指令,RV32I 的指令一共可分成六大類:

  • I-Type 包含了暫存器與立即數的運算。
  • R-Type 此類型的指令會有 3 個暫存器作為 Input ,分別是: rd, rs1, rs2 。
  • S-Type 包含存取記憶體的指令。
  • SB-Type 分支指令 (條件跳轉)。
  • U-Type 將立即數放到高位,這些指令被設計來實現完整的 32 bits 運算。
  • UJ-Type 跳轉指令。

為了方便, RISC-V 將一個 WORD 的大小設成 4 Bytes (32 bits) ,同時, RISC-V 的指令長度也是 32 bits,這些空間會被分割成好幾個 fields,不同類型的指令都會有不同的分配方式。

I-Format

這種類型的指令代表暫存器與常數之間的運算:

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

ADDI

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[U]

slti rd, rs1, simm12

運算時會將 12 位元擴展成 32 位元,再與 rs1 暫存器當做 signed number 做比較(如果指令為 SLTIU 則視為無號數),若 rs 暫存器 1 小於常數,則將數值 1 寫入 rd 暫存器,否則為 0 。

補充 本篇文章的 simm[number] 代表有號的 number 個位元,若是 uimm[number] 則代表無號的 number 個位元。 若出現在下方指令,就不再特別說明。

ANDI/ORI/XORI

andi/ori/xori rd, rs1, simm12

將 simm12 擴展後與 rs1暫存器做 AND / OR / XOR 運算,再將結果寫入 rd 暫存器中。

SLLI/SRLI/SRAI

slli/srli/srai rd, rs1, uimm5

常數部分為 unsigned 5 bits,將 rs1 暫存器做 shift 運算,偏移量為 uimm5 的值 (0 - 31),偏移後的結果會寫入 rd 暫存器。

  • SLLI 代表邏輯左移
  • SRLI 為邏輯右移
  • SRAI 為算數右移

LW/LH/LHU/LB/LBU

lw/lh/lhu/lb/lbu rd, rs1, simm12

將 rs1 暫存器的內容加上 simm12 視為地址,並將該地址存放的值放到 rd 中。

  • LW 為載入 32 bits (WORD) 資料寫入 rd 中。
  • LH/LHU 為載入 16 bits (Half of WORD) 資料分別做有號與無號擴展成 32 bits 後寫入 rd 中。
  • LB/LBU 為載入 8 bits (BYTE) 資料分別做有號與無號擴展成 32 bits 後寫入 rd 中。

U-Format

包含 upper immediates 的指令

LUI (Load upper immediate)

lui rd, uimm20

將 uimm20 存放到 rd 暫存器的高位 20 bits 中,剩餘的 12 bits 皆為 0 。

  • AUIPC (add upper immediate to pc)
auipc rd, uimm20

unsigned 20-bit放到最高 20位元,剩餘 12位元補0,將此數值與 pc相加寫入 rd暫存器。

R-Format

指令為暫存器與暫存器之間的運算

ADD/SUB

add/sub rd, rs1, rs2

將 rs1 與 rs2 做加法/減法運算後,再將結果寫入 rd暫存器。

SLT/SLTU

slt/sltu rd, rs1, rs2

將 rs1 與 rs2 暫存器當做有號或是無號數做比較,若 rs1 小於 rs2 ,將數值 1 寫入 rd ,反之寫入 0 。

AND/OR/XOR

and/or/xor rd, rs1, rs2

將 rs1 與 rs2 做 AND/OR/XOR 運算,並將結果寫入 rd 中。

SLL/SRL/SRA

sll/srl/sra rd, rs1,, rs2

將 rs1 做 shift 運算,再將結果寫入 rd 暫存器,rs2 的最低 5 位為代表位移量。

NOP 指令

NOP 指令即為不改變任何暫存器狀態,除了 pc 以外。 NOP 指令會被編碼成 addi x0, x0, 0 替代。

UJ-Format

UJ-Format 包含了無條件跳躍 (Unconditional Jumps) 類型的指令。

JAL (jump and link)

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 (jump and link register)

jalr rd, rs1, simm12

跳躍的位址為 rs暫存器加上 simm12 ,並把下一道指令的位址 pc+4 寫入 rd 暫存器中。

SB-Format

SB-Format 包含了條件跳躍 (Conditional Branches) 類型的指令。

BEQ/BNE/BLT/BLTU/BGE/BGEU

beq/bne/blt/bltu/bge/bgeu rs1, rs2, simm13

常數部分必須為 2的倍數,即最低位元為 0 ,因為此道指令編碼的常數位元數只有 12 位元,所以只會將 simm13 的最高 12 位元放入指令編碼中,跳躍範圍為 -+4Kib 。

  • BEQ/BNE 將 rs1 與 rs2 做相同與不同的比較,若成立則跳躍。
  • BLT/BLTU 將 rs1與 rs2 分別做有號/無號的小於比較,若成立則跳躍。
  • BGE/BGEU 將 rs1 與 rs2 分別做有號/無號的大於等於比較,若成立則跳躍,跳躍的位址則為 pc 加上 simm13 。

S-Format

將內容存放到記憶體中的指令。

SW/SH/SB

sw/sh/sb rs2, rs1, simm12

將 rs1 暫存器的內容加上 simm12 視為地址,並將 rs2 的資料寫到該地址中。

  • SW 將 rs2 暫存器完整 32 bits 資料寫入地址。
  • SH 將 rs2 暫存器最低 16 bits 資料寫入地址。
  • SB 將 rs2 暫存器最低 8 bits 資料寫入地址。

Memory model

定義了一組 FENCE 指令,達到多個 thread 間的記憶體同步。

控制與狀態暫存器指令 (Control and Status Register Instructions)

CSR Instructions

CSRRW / CSRRS / CSRRC / CSRRWI / CSRRSI / CSRRCI

定義了一組 CSR指令,可用來讀取寫入 CSR。

Timers and Counters

RDCYCLE[H]

rdcycle 用來讀取最低 31-bit cycle CSR , rdcycleh 用來讀取最高 31-bit cycle 數。

RDTIME[H]

用來讀取 time CSR 。

RDINSTRET

用來讀取 instret CSR 。

Environment Call and Breakpoints

ECALL

屬於 I-Type。

使用來呼叫 system call。

EBREAK

Debugger 用來切換進 Debugging 環境。

Reference