D standard extension為雙精度浮點數(single-precision floating-point)指令擴充,將 32個浮點數暫存器(f0-f31)擴充成為 64-bit。

下面所提到的一般暫存器(general register)指的是在 RV32I或 RV64I的 x0-x31,浮點數暫存器則為 f0-f31。

雙精度載入與儲存指令 (Double-Precision Load and Store Instructions)

  • FLD
    fld rd, rs1, simm12
    立即值為 sign-extended 12-bit,載入位址則為 rs1一般暫存器加上 sign-extended 12-bit,將 double-precision浮點數從記憶體中載入 rd浮點數暫存器。

  • FSW
    fsd rs2, rs1, simm12
    立即值為 sign-extended 12-bit,儲存位址則為 rs1一般暫存器加上 sign-extended 12-bit,將 rs2浮點數暫存器的 double-precision浮點數值寫入記憶體。

雙精度浮點數運算指令 (Double-Precision Floating-Point Computational Instructions)

  • FADD.D/FSUB.D
    fadd.d/fsub.d rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做加法/減法運算,結果寫入 rd浮點數暫存器。

  • FMUL.D/FDIV.D
    fmul.d/fdiv.d rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做乘法/除法運算,結果寫入 rd浮點數暫存器。

  • FMIN.D/FMAX.D
    fmin.d/fmax.d rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做最大值/最小值比較,將最大值/最小值寫入 rd浮點數暫存器。

  • FSQRT.D
    fsqrt.d rd, rs1
    將 rs1浮點數暫存器做取平方根運算,結果寫入 rd浮點數暫存器。

  • F[N]MADD/F[N]MSUB
    f[n]madd/f[n]msub rd, rs1, rs2, rs3
    將 rs1浮點數暫存器與 rs2浮點數暫存器做乘法運算後,再與 rs3浮點數暫存器做加法/乘法運算,結果寫入 rd浮點數暫存器,n版本則為做完乘法運算後取 negate,為乘法與加法指令一次完成,中間並沒有經過 rounding,所以精準度上會比一般乘法指令加上加法指令還高。

雙精度浮點數轉換與移動指令 (Double-precision Floating-Point Conversion and Move Instructions)

將浮點數轉換為整數或整數轉換為浮點數指令,會根據指令中的 rm field來決定轉換的 rounding mode,以及單精度與雙精度之間的轉換。

  • FCVT.W.D/FCVT.L.D
    fcvt.w.d/fcvt.l.d rd, rs1
    將 rs1浮點數暫存器中的浮點數轉換為 signed 32-bit/64-bit,結果寫入 rd一般暫存器。

  • FCVT.D.W/FCVT.D.L
    fcvt.d.w/fcvt.d.l rd, rs1
    將 rs1一般暫存器中的signed 整數轉換為浮點數,結果寫入 rd一般暫存器。

  • FCVT.WU.D/FCVT.LU.D
    fcvt.wu.d/fcvt.lu.d rd, rs1
    將 rs1浮點數暫存器中的浮點數轉換為 unsigned 32-bit/64-bit,結果寫入 rd一般暫存器。

  • FCVT.D.WU/FCVT.D.LU
    fcvt.d.wu/fcvt.d.lu rd, rs1
    將 rs1一般暫存器中的unsigned 整數轉換為浮點數,結果寫入 rd一般暫存器。

  • FCVT.S.D/FCVT.D.S
    fcvt.s.d/fcvt.d.s rd, rs1
    為單精度與雙精度之間的轉換。

sign-injection Instructions

  • FSGNJ.D
    fsgnj.d rd, rs1, rs2
    rd浮點數暫存器中的數值來自 rs1浮點數暫存器,sign bit則為 rs2浮點數暫存器的 sign bit。

  • FSGNJN.D
    fsgnjn.d rd, rs1, rs2
    rd浮點數暫存器中的數值來自 rs1浮點數暫存器,sign bit則為 rs2浮點數暫存器的 sign bit的相反。

  • FSGNJX.D
    fsgnjx.d rd, rs1, rs2
    rd浮點數暫存器的數值來自 rs1浮點數暫存器,sign bit則為 rs1浮點數暫存器的 sign bit與 rs2浮點數暫存器的 sign bit做 xor運算。

只在 RV64有效,單純 floating point與 integer之間的移動指令,與上面不同的是,只做 bit的移動,並不做浮點數與整數之間的轉換。

  • FMV.X.D
    fmv.x.w rd, rs1
    將 rs1浮點數暫存器的 64-bit數值寫入 rd一般暫存器中。

  • FMV.D.X
    fmv.w.x rd, rs1
    將 rs1一般暫存器的 64-bit數值寫入 rd浮點數暫存器。

雙精度浮點數比較指令 (Double-Precision Floating-Point Compare Instructions)

  • FEQ.D/FLT.D/FLE.D
    feq.d/flt.d/fle.d rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做 eq/lt/le比較,結果寫入 rd一般暫存器。

雙精度浮點數分類指令 (Double-Precision Floating-Point Classify Instruction)

  • FCLASS.D
    fclass.d rd, rs1
    功能與 FCLASS.S類似。

Reference

[1] The RISC-V Instruction Set Manual

F standard extension為單精度浮點數(single-precision floating-point)指令擴充,增加了 32個浮點數暫存器(f0-f31),長度為 32-bit,以及一個浮點數控制與狀態暫存器(fcsr),與 IEEE 754-2008相容。

下面所提到的一般暫存器(general register)指的是在 RV32I或 RV64I的 x0-x31,浮點數暫存器則為 f0-f31。

浮點數控制與狀態暫存器 (Floating-Point Control and Status Register)

浮點數指令可設定成動態的去參照目前 fcsr中的 Rounding Mode bits來決定如何 rounding,fcsr的 Rounding Mode編碼則為下方表格。

若發生 floating point exception,則會去設定相對應的 fcsr exception flags,下方表格為各個 flag與其 floating point exception的對照。

單精度載入與儲存指令 (Single-Precision Load and Store Instructions)

  • FLW
    flw rd, rs1, simm12
    常數部分為 sign-extended 12-bit,載入位址則為 rs1一般暫存器加上 sign-extended 12-bit,將 single-precision浮點數值從記憶體中載入 rd浮點數暫存器。

  • FSW
    fsw rd, rs1, simm12
    常數部分為 sign-extended 12-bit,儲存位址則為 rs1一般暫存器加上 sign-extended 12-bit,將 rs1浮點數暫存器的 single-precision浮點數值寫入記憶體。

單精度浮點數運算指令 (Single-Presicion Floating-Point Computational Instructions)

  • FADD.S/FSUB.S
    fadd.s/fsub.s rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做加法/減法運算,結果寫入 rd浮點數暫存器。

  • FMUL.S/FDIV.S
    fmul.s/fdiv.s rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做乘法/除法運算,結果寫入 rd浮點數暫存器。

  • FMIN.S/FMAX.S
    fmin.s/fmax.s rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做最大值/最小值比較,將最大值/最小值寫入 rd浮點數暫存器。

  • FSQRT.S
    fsqrt.s rd, rs1
    將 rs1浮點數暫存器做取平方根運算,結果寫入 rd浮點數暫存器。

  • F[N]MADD/F[N]MSUB
    f[n]madd/f[n]msub rd, rs1, rs2, rs3
    將 rs1浮點數暫存器與 rs2浮點數暫存器做乘法運算後,再與 rs3浮點數暫存器做加法/乘法運算,結果寫入 rd浮點數暫存器,n版本則為做完乘法運算後取 negate,為乘法與加法指令一次完成,中間並沒有經過 rounding,所以精準度上會比一般乘法指令加上加法指令還高。

單精度浮點數轉換與移動指令 (Single-precision Floating-Point Conversion and Move Instructions)

將浮點數轉換為整數或整數轉換為浮點數指令,會根據指令中的 rm field來決定轉換的 rounding mode。

  • FCVT.W.S/FCVT.L.S
    fcvt.w.s/fcvt.l.s rd, rs1
    將 rs1浮點數暫存器中的浮點數轉換為 signed 32-bit/64-bit,結果寫入 rd一般暫存器。

  • FCVT.S.W/FCVT.S.L
    fcvt.s.w/fcvt.s.l rd, rs1
    將 rs1一般暫存器中的signed 整數轉換為浮點數,結果寫入 rd一般暫存器。

  • FCVT.WU.S/FCVT.LU.S
    fcvt.wu.s/fcvt.lu.s rd, rs1
    將 rs1浮點數暫存器中的浮點數轉換為 unsigned 32-bit/64-bit,結果寫入 rd一般暫存器。

  • FCVT.S.WU/FCVT.S.LU
    fcvt.s.wu/fcvt.s.lu rd, rs1
    將 rs1一般暫存器中的unsigned 整數轉換為浮點數,結果寫入 rd一般暫存器。

sign-injection instructions

  • FSGNJ.S
    fsgnj.s rd, rs1, rs2
    rd浮點數暫存器中的數值來自 rs1浮點數暫存器,sign bit則為 rs2浮點數暫存器的 sign bit。

  • FSGNJN.S
    fsgnjn.s rd, rs1, rs2
    rd浮點數暫存器中的數值來自 rs1浮點數暫存器,sign bit則為 rs2浮點數暫存器的 sign bit的相反。

  • FSGNJX.S
    fsgnjx.s rd, rs1, rs2
    rd浮點數暫存器的數值來自 rs1浮點數暫存器,sign bit則為 rs1浮點數暫存器的 sign bit與 rs2浮點數暫存器的 sign bit做 xor運算。

單純 floating point與 integer之間的 move指令,與上面不同的是,只做 bit的移動,並不做浮點數與整數之間的轉換。

  • FMV.X.W
    fmv.x.w rd, rs1
    將 rs1浮點數暫存器的 32-bit數值寫入 rd一般暫存器中,若在 RV64,則用 sign bit填滿最高 32-bit。

  • FMV.W.X
    fmv.w.x rd, rs1
    將 rs1一般暫存器的最低 32-bit(已用IEEE 754-2008編碼好)數值寫入 rd浮點數暫存器。

單精度浮點數比較指令 (Single-Precision Floating-Point Compare Instructions)

  • FEQ.S/FLT.S/FLE.S
    feq.s/flt.s/fle.s rd, rs1, rs2
    將 rs1浮點數暫存器與 rs2浮點數暫存器做 eq/lt/le比較,結果寫入 rd一般暫存器。

單精度浮點數分類指令 (Single-Precision Floating-Point Classify Instruction)

  • FCLASS.S
    fclass.s rd, rs1
    根據下表分類 rs1浮點數暫存器的浮點數值,結果寫入 rd一般暫存器。

Reference

[1] The RISC-V Instruction Set Manual

這邊紀錄一下如何一口氣更改所有目錄檔案的方法。

習慣上,希望目錄權限為 755 (drwxr-xr-x),檔案權限則為 644(-rw-r–r–)。

更改目錄權限:

find . -type d -exec chmod 755 {} \;

更改檔案權限:

find . -type f -exec chmod 644 {} \;

使用 find指令去過濾為目錄或檔案,接著更改權限。

這邊紀錄一下如何 ssh連線其它機器可以免輸入密碼登入。

假設我們目前在 A機器,想要連線 B機器。

先在 A機器產生 id_rsa與 id_rsa.pub,若已產生可免去此步驟, key預設放在 ~/.ssh,按下 enter繼續。

jim@A:~ $ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/users/jim/.ssh/id_rsa):

請勿輸入密碼,直接按下 enter繼續。

Enter passphrase (empty for no passphrase):

一樣不用輸入密碼,直接按下 enter繼續。

Enter same passphrase again:

建立好 key了。

Your public key has been saved in /home/users/jim/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:Q14hkz7AJG0rcEv1kmffq6gE5C6GkqTb9J/CYWj8UHw jim@A

解說一下產生的一對 key的作用。

  • id_rsa: private key
  • id_rsa.pub: public key

將 id_rsa.pub的內容複製到遠端機器的 ~/.ssh/authorized_keys中,之後在登入時,便會用本地的 id_rsa與遠端機器的 id_rsa.pub做認證,便可免輸入密碼登入遠端機器。

接下來在 B機器建立目錄 ~/.ssh,如果已有可跳過此步驟。

jim@A:~ $ ssh b@B mkdir -p .ssh
jim@B's password:

把 A機器的 ~/.ssh/id_rsa.pub也就是 public key複製到 B機器的 .ssh/authorized_keys。

方法 1

jim@A:~ $ cat .ssh/id_rsa.pub | ssh jim@B 'cat >> .ssh/authorized_keys'
jim@B's password:

方法 2

jim@A:~ $ ssh-copy-id -i .ssh/id_rsa.pub jim@B
jim@B's password:

即可成功登入 B機器而不需要輸入密碼了。

jim@A:~ $ ssh jim@B

本文介紹一下資料模型 (Data Model),下表格為 C語言型別對應不同資料模型的長度。

Type LP32 ILP32 LP64 ILP64 LLP64
CHAR 8 8 8 8 8
SHORT 16 16 16 16 16
INT 16 32 32 64 32
LONG 32 32 64 64 32
LONG LONG 64 64 64 64 64
POINTER 32 32 64 64 64

LP32 與 ILP32是在 32-bit平台,LP64、ILP64 與 LLP64則是在 64-bit平台。

LP32 指的是 LONG與 POINTER為 32-bit,ILP32 指的是 INT、LONG與 POINTER為 32-bit,LP64 指的是 LONG與 POINTER為 64-bit,ILP64 指的是 INT、LONG與POINTER為 64-bit,LLP64 指的是 LONG LONG與 POINTER為 64-bit,不管是在哪個資料模型,LONG LONG皆為是 64-bit。

資料模型規定了每個 C語言型別的長度,讓編譯器編譯出來的程式能使用正確的資料長度與平台溝通。

M standard extension為整數乘法與除法指令擴充,RV32IM 即為RV32I 指令集加上 M standard extension,RV64IM 即為RV64I 指令集加上 M standard extension,以下所提到的 XLEN-bit在 RV32或 RV64分別是 32-bit與 64-bit,介紹各個指令的用途與格式。

乘法運算 (Multiplication Operations)

  • MUL
    MUL rd, rs1, rs2
    將 rs1暫存器與 rs2暫存器做乘法運算,會產生 2 x XLEN-bit 的運算結果,將此結果的最低 XLEN-bit寫入 rd暫存器。

  • MULH/MULHU/MULHSU
    mulh/mulhu/mulhsu rd, rs1, rs2
    將 rs1暫存器與 rs2暫存器做乘法運算,會產生 2 x XLEN-bit 的運算結果,將此結果的最高 XLEN-bit寫入 rd暫存器。MULH 為 signed x signed,MULHU 為 unsigned x unsigned,MULHSU 為 signed x unsigned。

  • MULW
    mulw rd, rs1, rs2
    RV64 才會有此道指令,將 rs1暫存器的最低 32-bit與 rs2暫存器的最低 32-bit做乘法運算,並將此結果的最低 32-bit做 sign-extension成 64-bit寫入 rd暫存器。

除法運算 (Division Operations)

  • DIV/DIVU
    div/divu rd, rs1, rs2
    將 rs1暫存器與 rs2暫存器做 signed/unsigned 除法運算,結果寫入 rd暫存器。

  • REM/REMU
    rem/remu rd, rs1, rs2
    將 rs1暫存器與 rs2暫存器做 signed/unsigned 餘數運算,結果寫入 rd暫存器。

  • DIVW/DIVUW
    divw/divuw rd, rs1, rs2
    RV64 才會有此道指令,將 rs1暫存器的最低 32-bit與 rs2暫存器的最低 32-bit做 signed/unsigned 除法運算,並將此結果做 sign-extension成 64-bit寫入 rd暫存器。

  • REMW/REMUW
    remw/remuw rd, rs1, rs2
    RV64 才會有此道指令,將 rs1暫存器的最低 32-bit與 rs2暫存器的最低 32-bit做 signed/unsigned 餘數運算,並將此結果做 sign-extension成 64-bit寫入 rd暫存器。

Reference

[1] The RISC-V Instruction Set Manual

RV64I是基於 RV32I的指令集架構,本文只會說明與 RV32I不同之處,RV64I將在 RV32I的 32個 32-bit暫存器給擴大成 64-bit,所有的指令也轉換成是操作在 64-bit暫存器上,也額外增加一些指令能夠操作 64-bit暫存器中的最低 32-bit,這些指令會以 W 為結尾,以下介紹各個指令的用途與格式。

整數運算指令 (Integer Computational Instructions)

整數暫存器與常數指令 (Integer Register-Immediate Instructions)

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

  • ADDIW
    addiw rd, rs1, simm12
    常數部分為 sign-extended 12-bit,會將 12-bit做 sign-extension成 32-bit後,再與 rs1暫存器的最低 32-bit做加法運算,並將此結果 sign-extension成 64-bit寫入 rd暫存器。addiw rd, rs1, 0 是將 rs1暫存器的最低 32-bit做 sign-extension成 64-bit寫入 rd暫存器,可寫成 sext.w rd, rs1

  • SLLI/SRLI/SRAI
    slli/srli/srai rd, rs1, uimm6
    常數部分為 unsigned 6-bit,範圍為 0~63,為 shift amount,將 rs1暫存器做 shift運算,結果寫入 rd暫存器,SLLI為 logical左移,會補 0到最低位元,SRLI為 logical右移,會補 0到最高位元,SRAI為 arithmetic右移,會將原本的 sign bit複製到最高位元。

  • SLLIW/SRLIW/SRAIW
    slliw/srliw/sraiw rd, rs1, uimm5
    常數部分為 unsigned 5-bit,範圍為 0~31,為 shift amount,將 rs1暫存器的最低 32-bit做 shift運算,並將此結果 sign-extension成 64-bit寫入 rd暫存器,SLLIW為 logical左移,會補 0到最低位元,SRLIW為 logical右移,會補 0到最高位元,SRAIW為 arithmetic右移,會將原本的 sign bit複製到最高位元。

  • LUI (Load upper immediate)
    lui rd, uimm20
    將 unsigned 20-bit放到 rd暫存器的 31-12 bits,並將最低的 12-bit補 0。

  • AUIPC(add upper immediate to pc)
    auipc rd, uimm20
    unsigned 20-bit放到最高 20位元,剩餘 12位元補0,將此 32-bit數值 sign-extension成 64-bit,與 pc相加寫入 rd暫存器。

整數暫存器與暫存器指令 (Integer Register-Register Insructions)

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

  • ADDW/SUBW
    addw/subw rd, rs1, rs2
    將 rs1暫存器的最低 32-bit與 rs2暫存器的最低 32-bit做加法/減法運算,並將此結果 sign-extension成 64-bit寫入 rd暫存器。

  • SLL/SRL/SRA
    sll/srl/sra rd, rs1,, rs2
    將 rs1暫存器做 shift運算,結果寫入 rd暫存器,rs2暫存器的最低 6-bit為 shift amount。

  • SLLW/SRLW/SRAW
    sllw/srlw/sraw rd, rs1,, rs2
    將 rs1暫存器的最低 32-bit做 shift運算,並將此結果 sign-extension成 64-bit寫入 rd暫存器,rs2暫存器的最低 5-bit為 shift amount。

載入與儲存指令 (Load and Store Instructions)

  • LD/LW/LWU
    ld/lw/lwu rd, rs1, simm12
    常數部分為 sign-extended 12-bit,載入位址則為 rs1暫存器加上 sign-extended 12-bit,LD為載入 64-bit資料寫入 rd暫存器,LW/LWU為載入 32-bit資料分別做 unsigned/signed extension成 64-bit後寫入 rd暫存器。

  • SD
    sd rs2, rs1, simm12
    常數部分為 sign-extended 12-bit,儲存位址則為 rs1暫存器加上 sign-extended 12-bit,SD為將 rs2暫存器完整 64-bit資料寫入記憶體。

RV64I指令集也包含本文沒有提到的 RV32I指令,功能一樣,只是將操作的暫存器寬度從 32-bit擴大為 64-bit。

Reference

[1] The RISC-V Instruction Set Manual

在 Shell programming中如何使用 case。

case 語法

case value in
    pattern1)
        command
        ...
        command
        ;;
    pattern2)
        command
        ...
        command
        ;;
    pattern3)
        command
        ...
        command
        ;;
    ...
esac

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if [ "$#" -ne 1 ]
then
echo "Usage: $0 [0-9]"
exit 1
fi

case "$1" in
0) echo zero;;
1) echo one;;
2) echo two;;
3) echo three;;
4) echo four;;
5) echo five;;
6) echo six;;
7) echo seven;;
8) echo eight;;
9) echo nine;;
*) echo "The input is not between 0 and 9."
esac

進階用法

  1. 多個情況匹配的時候,類似 or的功能
    pattern1 | pattern2

  2. 一個範圍匹配
    [0-9]

Integer Calling convention

有 32個 32-bit(RV32)或 64-bit(RV64)的暫存器(register),為 x0-x31,下表為各個暫存器其 ABI Name與其用途:

Register ABI Name Description Saver
x0 zero Hard-wired zero -
x1 ra Return address Caller
x2 sp Stack pointer Callee
x3 gp Global pointer -
x4 tp Thread pointer -
x5 t0 Temporary/alternate link register Caller
x6-7 t1-2 Temporaries Caller
x8 s0/fp Saved regsiter/frame pointer Callee
x9 s1 Saved register Callee
x10-11 a0-1 Function arguments/return values Caller
x12-17 a2-7 Function arguments Caller
x18-27 s2-11 Saved registers Callee
x28-31 t3-6 Temporaries Caller

x0 (zero)

x0內容值恆為 0,在許多的指令運算中,使用到 0值的機會很多,因此將其中一個暫存器的值直接固定為 0可帶來許多方便。

x1 (ra)

x1(ra) 使用來放置 return address,當函數(function)要結束返回時,便會返回到此暫存器中所儲存的位址,此暫存器為 Caller save,意謂者在一函數(Caller)中要呼叫另一函數(Callee)之前必須先將放置在 x1中的 Caller返回位址儲存到 stack中,因為當一呼叫其他函式,此被呼叫的函式的返回位址會同樣放到 x1(ra)中,所以此暫存器的內容值保存是由 Caller負責。

x2 (sp)

x2(sp) 用來放置 stack指標的值,可使用此暫存器對 stack做儲存載入的操作,此暫存器為 Callee save,當一被呼叫的函式想修改此暫存器的內容值前,必須先將此內容給保存下來,並在函式返回前,恢復其原本的值,換句話說,此暫存器的內容值在函式被呼叫前與此函式結束後的值應該保持一致。

x3 (gp)

x3(gp) 用來放置 global指標的值,此指標通常指向整個執行檔中的某個位址,可利用此指標來讀取資料。

x4 (tp)

x4(tp) 用來放置 thread指標的值,指向 Thread local storage。

x5 (t0)

x5(t0) 可用來當做一般的暫存器使用或者是第二個用來放置 return address的暫存器,此暫存器為 Caller save,即 Caller若在呼叫完某一函式後對此暫存器的內容有需要,必須在呼叫此函數之前將其內容值給儲存起來,避免 Callee修改到內容值。

x6-7 (t1-2) & x28-31 (t3-6)

一般的暫存器使用,為 Caller save,呼叫函式前後並不保證其暫存器內容值一致,若 Caller對其暫存器內容值有所需要,必須在呼叫函式前,先儲存起來,待呼叫函式結束後,在恢復其暫存器原本的內容值。

x8 (s0)

x8 可被當做是 Saved register或 Frame pointer使用,此暫存器為 Callee save,若在某函式內,要使用到此暫存器時,需先將其內容值給儲存起來,才可以使用,並在函式返回之前恢復原先的內容值,因爲在此函數被呼叫前後必須維持此暫存器的內容一致,在程式碼的產生上,可設定是否需要 Frame pointer去指向當前函式所使用到 stack的底部,此非為必要,所以在優化上,以 GCC為例,下 -fomit-frame-pointer選項,可將此暫存器當作 Saved register使用,增加一個可用的暫存器,提升效能。

x9 (s1) & x18-27 (s2-11)

Saved register,為 Callee save,需由 Callee負責其內容值的保存。

x10-x11 (a0-1)

x10(a0) 與 x11(a1) 使用來傳遞參數或函式返回時會將其返回值放入此暫存器中,為 Caller save。

x12-17 (a2-7)

x12-17(a2-7) 使用來傳遞參數,為 Caller save。

RV32E Calling Convention

RV32E 只有 16個 32-bit的暫存器,為 x0-x15,有 6個傳遞參數用暫存器 a0-a5,2個 Callee save暫存器 s0-s1,以及 3個 temporaries暫存器 t0-t2。

總結

每個暫存器皆可當做一般的暫存器使用,Calling convention 則是規定了一系列的函式與函式之間互動的慣例,例如:如何傳遞參數給函式,如何取得函式結束後回傳的返回值,哪些暫存器的內容值需在函式呼叫前後保持一致,哪些暫存器其值若在函式呼叫結束後仍有需要,則需在函式呼叫前先行儲存起來,若一函式編譯成 object file後,提供外部連結,則須嚴格遵守 Calling convention,才能與其他函式溝通。

RV32I為 32-bit基本整數指令集,有 32個 32-bit暫存器(x0-x31),總共有 47道指令,以下介紹各個指令的用途與格式。

整數運算指令 (Integer Computational Instructions)

整數暫存器與常數指令 (Integer Register-Immediate Instructions)

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

  • ADDI
    addi rd, rs1, simm12
    常數部分為 sign-extended 12-bit,會將 12-bit做 sign-extension成 32-bit後,再與 rs1暫存器做加法運算,將結果寫入 rd暫存器,addi rd, rs1, 0 可被使用來當做 mov指令。

  • SLTI
    slti rd, rs1, simm12
    常數部分為 sign-extended 12-bit,會將 12-bit做 sign-extension成 32-bit後,再與 rs1暫存器當做 signed number做比較,若 rs暫存器1小於常數,則將數值 1寫入 rd暫存器,反之則寫入數值 0。

  • SLTIU
    sltiu rd, rs1, simm12
    常數部分為 sign-extended 12-bit,會將 12-bit做 sign-extension成 32-bit後,再與 rs1暫存器當作 unsigned number做比較·若 rs1暫存器小於常數,則將數值 1寫入 rd暫存器,反之則寫入數值 0。

  • ANDI/ORI/XORI
    andi/ori/xori rd, rs1, simm12
    常數部分為 sign-extended 12-bit,會將 12-bit做 sign-extension成 32-bit後,再與 rs1暫存器做 AND/OR/XOR運算,將結果寫入 rd暫存器。

  • SLLI/SRLI/SRAI
    slli/srli/srai rd, rs1, uimm5
    常數部分為 unsigned 5-bit,範圍為 0~31,為 shift amount,將 rs1暫存器做 shift運算,結果寫入 rd暫存器,SLLI為 logical左移,會補 0到最低位元,SRLI為 logical右移,會補 0到最高位元,SRAI為 arithmetic右移,會將原本的 sign bit複製到最高位元。

  • LUI (Load upper immediate)
    lui rd, uimm20
    將 unsigned 20-bit放到 rd暫存器的最高 20-bit,並將剩餘的 12-bit補 0,此指令可與 ADDI搭配,一起組合出完整 32-bit的數值。

  • AUIPC(add upper immediate to pc)
    auipc rd, uimm20
    unsigned 20-bit放到最高 20位元,剩餘 12位元補0,將此數值與 pc相加寫入 rd暫存器。

整數暫存器與暫存器指令 (Integer Register-Register Insructions)

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

  • ADD/SUB
    add/sub rd, rs1, rs2
    將 rs1暫存器與 rs2暫存器做加法/減法運算,將結果寫入 rd暫存器。

  • SLT/SLTU
    slt/sltu rd, rs1, rs2
    將 rs1暫存器與 rs2暫存器當做 singed/unsigned number做比較,若 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-bit為 shift amount。

NOP 指令

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

控制轉移指令 (Control Transfer Instructions)

分別有兩種控制轉移指令,無條件跳躍(Unconditional jumps)與條件分支(Conditional branches)

無條件跳躍 (Unconditional Jumps)

  • JAL (jump and link)
    jal rd, simm21
    常數部分為 sign-extended 21-bit,要注意的是此常數必須為 2的倍數,即最低位元為 0,因為此道指令編碼的常數位元數只有 20位元,所以只會將 signed 21-bit的最高 20位元放入指令編碼中,跳躍範圍為 -+1MiB,同時也會將下一道指令的位址 pc+4寫入 rd暫存器中,在標準的 calling convention中,rd暫存器會使用 x1。如果只是單純的 jump,並非是呼叫函示需要儲存其返回位址 pc+4,可用 jal x0, simm21 取代。

  • JALR (jump and link register)
    jalr rd, rs1, simm12
    常數部分為 sign-extended 12-bit,跳躍的位址為 rs暫存器加上 sign-extended 12-bit,並把下一道指令的位址 pc+4寫入 rd暫存器中。

條件跳躍 (Conditional Branches)

  • BEQ/BNE/BLT/BLTU/BGE/BGEU
    beq/bne/blt/bltu/bge/bgeu rs1, rs2, simm13
    常數部分為 sign-extended 13-bit,要注意的是此常數必須為 2的倍數,即最低位元為 0,因為此道指令編碼的常數位元數只有 12位元,所以只會將 signed 13-bit的最高 12位元放入指令編碼中,跳躍範圍為 -+4Kib,BEQ/BNE將 rs1暫存器與 rs2暫存器做相同與不同的比較,若成立則跳躍,BLT/BLTU將 rs1暫存器與 rs2暫存器分別做 signed/unsigned小於比較,若成立則跳躍,BGE/BGEU將 rs1暫存器與 rs2暫存器分別做 signed/unsigned大於等於比較,若成立則跳躍,跳躍的位址則為 pc加上 sign-extended 13-bit。

載入與儲存指令 (Load and Store Instructions)

RV32I 必須使用載入與儲存指令去存取記憶體,前面的運算指令只能夠對暫存器做操作。

  • LW/LH/LHU/LB/LBU
    lw/lh/lhu/lb/lbu rd, rs1, simm12
    常數部分為 sign-extended 12-bit,載入位址則為 rs1暫存器加上 sign-extended 12-bit,LW為載入 32-bit資料寫入 rd暫存器,LH/LHU為載入 16-bit資料分別做 unsigned/signed extension成 32-bit後寫入 rd暫存器,LB/LBU為載入 8-bit資料分別做 unsigned/signed extension成 32-bit後寫入 rd暫存器。

  • SW/SH/SB
    sw/sh/sb rs2, rs1, simm12
    常數部分為 sign-extended 12-bit,儲存位址則為 rs1暫存器加上 sign-extended 12-bit,SW為將 rs2暫存器完整 32-bit資料寫入記憶體,SH為將 rs2暫存器最低 16-bit資料寫入記憶體,SB為將 rs2暫存器最低 8-bit資料寫入記憶體。

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
    使用來呼叫 system call。

  • EBREAK
    Debugger 用來切換進 Debugging 環境。

Reference

[1] The RISC-V Instruction Set Manual

0%