riscvgcc - ccc-sp/riscv2os GitHub Wiki
RISC-V 組織釋出了很多開發工具與範例,我們可以從下列網址中看到這些工具。
RISC-V 的設計者也知道這樣的開放原始碼處理器,可能有很大的商業潛力,因此他們成立了一家稱為 SiFive 的公司,撰寫 RISC-V 的工具軟體,並且設計生產 RISC-V 晶片與開發板。
SiFive 所釋出的 FreedomStudio 是一整套 RISC-V 開發工具,包含 gcc, qemu 與整合開發環境等等。
在本書中,我們將使用 FreedomStudio 的 Windows 版做為主要示範,使用其中的 gcc 工具鏈與 qemu 虛擬機,但是不使用 FreedomStudio 的整合開發工具,而是改用 Visual Studio Code + git bash 作為開發環境。
所以要執行本書的範例前,請大家先從下列網址註冊下載 FreedomStudio for Windows (很大、1.7G),然後安裝好 Visual Studio Code 與 git for windows,記得安裝 git 時要勾選安裝 bash。
安裝好後請將系統路徑 PATH 設到其中的 gcc 與 qemu 所在資料夾中,在我電腦中的安裝路徑如下:
D:\install\FreedomStudio-2020-06-3-win64\SiFive\riscv64-unknown-elf-gcc-8.3.0-2020.04.1\bin;
D:\install\FreedomStudio-2020-06-3-win64\SiFive\riscv-qemu-4.2.0-2020.04.0\bin
想在 Visual Studio Code 中使用 git bash ,請先開一個 Terminal 之後,於 Terminal 右上角選 Select Default Shell 後,選擇 GitBash ,之後開的 Terminal 就都會是 git bash 了!
另外、我們會使用到 make 這個專案建置工具,您可以安裝 CodeBlocks ,然後將其中的 make 所在路徑加入 PATH 中。(我電腦中的 make 位於下列位置)
$ where make
C:\Program Files (x86)\CodeBlocks\MinGW\bin\make.exe
對於 Linux/Mac 的讀者,通常是很熟練的程式人員,請您自行安裝好 FreedomStudio for Linux/Mac,然後設定好路徑,至於使用何種編輯器與開發環境,那就任君選擇了!
以下是我的 Mac 之路徑設定檔,我將 FreedomStudio.app 放到了 /Applications/ 下
export DENO_INSTALL="/Users/mac020/.deno"
export PATH="/Users/mac020/Desktop/ccc/riscv64-unknown-elf-toolchain/bin:/Users/mac020/Library/Python/3.7/bin;/Users/mac020/Desktop/ccc/llvm/bin:$DENO_INSTALL/bin:$HOME/.cargo/bin:/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8/bin:/Applications/FreedomStudio.app/Contents/Eclipse/SiFive/riscv-qemu-5.1.0-2020.08.1/bin:$PATH"
RISC-V 的 C 語言編譯器是修改過的 gcc,64 位元版的 RISC-V gcc 稱為 riscv64-unknown-elf-gcc ,我們可以用 where 指令找出其位置:
$ where riscv64-unknown-elf-gcc
D:\install\FreedomStudio-2020-06-3-win64\SiFive\riscv64-unknown-elf-gcc-8.3.0-2020.04.1\bin\riscv64-unknown-elf-gcc.exe我們可以利用這個 gcc 來編譯 C 語言程式,或者當組譯器來組譯組合語言程式碼。
首先讓我們寫一個 c 語言程式如下:
File: add.c
int add(int a, int b) {
return a+b;
}接著使用 riscv64-unknown-elf-gcc 將其編譯為組合語言。
$ riscv64-unknown-elf-gcc -S add.c -o add.s
此時我們會看到 add.s 這個組合語言檔產生出來,內容如下:
.file "add.c"
.option nopic
.attribute arch, "rv64i2p0_m2p0_a2p0_f2p0_d2p0_c2p0"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 1
.globl add
.type add, @function
add:
addi sp,sp,-32
sd s0,24(sp)
addi s0,sp,32
mv a5,a0
mv a4,a1
sw a5,-20(s0)
mv a5,a4
sw a5,-24(s0)
lw a4,-20(s0)
lw a5,-24(s0)
addw a5,a4,a5
sext.w a5,a5
mv a0,a5
ld s0,24(sp)
addi sp,sp,32
jr ra
.size add, .-add
.ident "GCC: (SiFive GCC 8.3.0-2020.04.1) 8.3.0"這就是一個 RISC-V 的組合語言檔了!
接著我們用下列指令將其轉換成 ELF 格式的目的檔:
$ riscv64-unknown-elf-gcc -c add.s -o add.elf
然後用 objdump 程式將其反組譯回組合語言,此時可以看到其機器碼:
$ riscv64-unknown-elf-objdump -d add.elf
add.elf: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <add>:
0: 1101 addi sp,sp,-32
2: ec22 sd s0,24(sp)
4: 1000 addi s0,sp,32
6: 87aa mv a5,a0
8: 872e mv a4,a1
a: fef42623 sw a5,-20(s0)
e: 87ba mv a5,a4
10: fef42423 sw a5,-24(s0)
14: fec42703 lw a4,-20(s0)
18: fe842783 lw a5,-24(s0)
1c: 9fb9 addw a5,a5,a4
1e: 2781 sext.w a5,a5
20: 853e mv a0,a5
22: 6462 ld s0,24(sp)
24: 6105 addi sp,sp,32
26: 8082 ret
您會看到機器碼中有些指令是 4 碼 (像是 sd s0,24(sp) 這個指令的機器碼為 ec22),另一些指令是 8 碼 (像是 sw a5,-20(s0) 這個指令的機器碼為 fef42623),其中 4 碼的代表 16 bits 的壓縮模式指令 (一個十六進位對應到 4 個二進位,4*4=16bit),而8碼的代表標準的 32 bits 指令。
當我們使用下列指令,直接將 add.c 編譯為目的檔 add.elf 時,其產生的架構預設為《64位元且壓縮》的 rv64gc 格式,因此產生出來的指令長度會混雜 32 位元與 16 位元兩種。
必須注意的是,RISC-V 64 位元模式指的是具有 64 位元暫存器,但指令長度最大仍然是 32 位元的,例如以下組合語言中的 sd s0,24(sp) 是將 64 位元的 s0 暫存器存入到 24+sp 這個記憶體位址內,總共佔據 8 bytes (64 bits)。
$ riscv64-unknown-elf-gcc -c add.c -o add.elf
// 上一行相當於 riscv64-unknown-elf-gcc -march=rv64gc -c add.c -o add.elf
$ riscv64-unknown-elf-objdump -d add.elf
add.elf: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <add>:
0: 1101 addi sp,sp,-32
2: ec22 sd s0,24(sp)
4: 1000 addi s0,sp,32
6: 87aa mv a5,a0
8: 872e mv a4,a1
a: fef42623 sw a5,-20(s0)
e: 87ba mv a5,a4
10: fef42423 sw a5,-24(s0)
14: fec42703 lw a4,-20(s0)
18: fe842783 lw a5,-24(s0)
1c: 9fb9 addw a5,a5,a4
1e: 2781 sext.w a5,a5
20: 853e mv a0,a5
22: 6462 ld s0,24(sp)
24: 6105 addi sp,sp,32
26: 8082 ret
但是如果我們不使用壓縮格式節省空間,那麼就可以指定 -march=rv64g 產生完全不壓縮的 32 位元指令碼如下:
user@DESKTOP-96FRN6B MINGW64 /d/ccc109/co2os/code/02-sp/02-gcc (master)
$ riscv64-unknown-elf-gcc -march=rv64g -c add.c -o add64.elf
user@DESKTOP-96FRN6B MINGW64 /d/ccc109/co2os/code/02-sp/02-gcc (master)
$ riscv64-unknown-elf-objdump -d add64.elf
add64.elf: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <add>:
0: fe010113 addi sp,sp,-32
4: 00813c23 sd s0,24(sp)
8: 02010413 addi s0,sp,32
c: 00050793 mv a5,a0
10: 00058713 mv a4,a1
14: fef42623 sw a5,-20(s0)
18: 00070793 mv a5,a4
1c: fef42423 sw a5,-24(s0)
20: fec42703 lw a4,-20(s0)
24: fe842783 lw a5,-24(s0)
28: 00f707bb addw a5,a4,a5
2c: 0007879b sext.w a5,a5
30: 00078513 mv a0,a5
34: 01813403 ld s0,24(sp)
38: 02010113 addi sp,sp,32
3c: 00008067 ret
但是如果我們的 RISC-V 處理器是 32 位元的,那麼就應該用下列指令去產生 32 bit 暫存器的機器碼。
(這時候要指定機器的 abi 格式為 ilp32,意思是 int, long, pointer 都是 32 位元,以產生 32 位元格式的二進位檔)。
user@DESKTOP-96FRN6B MINGW64 /d/ccc109/co2os/code/02-sp/02-gcc (master)
$ riscv64-unknown-elf-gcc -march=rv32gc -c add.c -o add32.elf
cc1.exe: error: ABI requires -march=rv64
user@DESKTOP-96FRN6B MINGW64 /d/ccc109/co2os/code/02-sp/02-gcc (master)
$ riscv64-unknown-elf-gcc -march=rv32gc -mabi=ilp32 -c add.c -o add32.elf
user@DESKTOP-96FRN6B MINGW64 /d/ccc109/co2os/code/02-sp/02-gcc (master)
$ riscv64-unknown-elf-objdump -d add32.elf
add32.elf: file format elf32-littleriscv
Disassembly of section .text:
00000000 <add>:
0: 1101 addi sp,sp,-32
2: ce22 sw s0,28(sp)
4: 1000 addi s0,sp,32
6: fea42623 sw a0,-20(s0)
a: feb42423 sw a1,-24(s0)
e: fec42703 lw a4,-20(s0)
12: fe842783 lw a5,-24(s0)
16: 97ba add a5,a5,a4
18: 853e mv a0,a5
1a: 4472 lw s0,28(sp)
1c: 6105 addi sp,sp,32
1e: 8082 ret
現在,您應該已經會使用 RISC-V 的 gcc 與 objdump 等工具了,也就是 riscv64-unknown-elf-gcc 與 riscv64-unknown-elf-objdump。
但是到目前為止,雖然已經能編譯程式了,卻無法執行這些編譯出來的 ELF 目的檔 (就算有寫 main 函數也不行)。
因為我們電腦的 CPU 是 x86 處理器,而編譯出來的 ELF 檔卻是 RISC-V 的機器碼,所以我們無法執行這些碼。
我們必須使用 QEMU 虛擬機來執行 RISC-V 的機器碼!
(說明: RISC-V 組織原本在 linux 上有釋出一個稱為 spike 的虛擬機環境,但在 windows 沒有,而且 spike 似乎已經停止維護,改成以 QEMU 為主了)
接下來,我們將使用 gcc + qemu + make 來開發並執行 RISC-V 上的程式專案。