iT邦幫忙

2021 iThome 鐵人賽

DAY 2
0
Software Development

猴子都寫得出來的 RISC-V CPU Emulator系列 第 29

Hello World: 編譯環境建立

前兩天猴子先偷偷把之前編完的執行檔拿來驗證,
今天來把 Cross Compiler 的部分補完。

有些夥伴沒接觸過嵌入式系統,
可能不了解什麼是 Cross Compile,
初學者在寫程式的時候,
通常都是編譯環境和執行環境在同一個平台上。
例如:在自己電腦(x86)上寫 C 語言程式、編譯、執行,
就算從別台電腦把程式拿來執行,也同樣是在 x86 的電腦上面。

但是學過底層運作方式的夥伴就會知道,
實際上不同的電腦支援的指令是不一樣的,
就算同樣是 x86 ,也會因為型號的不同而不同(ex: sse 指令集),
導致 A 電腦編譯出來的執行檔在 B 電腦上面無法執行。

簡單來說就是當開發環境(寫程式、編譯的電腦)和執行環境(執行的電腦)不一樣的時候,
就需要輸出執行環境能運作的執行檔,才能放到要執行的平台上執行,
也就是剛剛提到的 Cross Compile。

最常見的就是在 x86 (桌上型電腦)上面開發 ARM (手機)平台上的軟體,
像 Linux 在桌上型電腦上可能 2 個小時就可以編譯完,
在 Raspberry Pi 上面大概按下編譯就可以睡到隔天,
光編譯的時間差就會導致開發效率的低落,
更別說 x86 平台上豐富的軟體支援了。

說了那麼多,
Cross Compile 的流程除了執行環境以外,其實跟一般開發流程一模一樣,
關鍵的地方在於如何取得 Cross Compiler,
下面就是怎麼取得在 x86 上面運作,輸出 RISC-V 執行檔的 Cross Compiler 流程。

編譯 RISC-V Toolchain 流程

  1. riscv-gnu-toolchain 下載原始碼到自己喜歡的位置
    記得準備足夠的硬碟空間,編譯後總大小約 12 GB
$git clone https://github.com/riscv/riscv-gnu-toolchain
$cd riscv-gnu-toolchain && git submodule update -i -r
  1. 安裝必要工具,猴子用的 Linux 環境是由 Jserv 和 PCMan 兩位前輩發起的 Lubuntu,
    按照 Ubuntu 的指示做就可以了
$sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev
  1. 設定 Toolchain,因為目前只有實作 I 指令集,
    這邊使用 rv32i 以及 ilp32 (32-bit soft-float) 的 abi,
    並指定安裝位置,這邊用 /home/user/Desktop/code/rv32i-ilp32-gnu-toolchain
//in riscv-gnu-toolchain folder
$./configure --prefix=/home/user/Desktop/code/rv32i-ilp32-gnu-toolchain --with-arch=rv32i --with-abi=ilp32
  1. 編譯並安裝到剛剛指定的資料夾,
    請注意記憶體大小和電腦速度一定要夠,
    不然編不過又找不到答案 + 等到天荒地老
$make
//wait for loooooooooooong time
  1. 執行,猴子習慣在 Makefile 中指定 Toolchain 路徑
//console
$make test
//Makefile
...
export PATH=$PATH:/home/user/Desktop/code/rv32i-ilp32-gnu-toolchain/bin
CC= riscv32-unknown-elf-gcc

test: test.c
    $(CC) $< -o test
...

實際程式

github 頁面 Tag: ITDay29

昨晚誤會 gcc 編譯出來的執行檔有 Memory Access Misalignment,
還以為自己對環境的運作了解的不夠深入,
講了大半天的 LOAD/STORE Exception 規範,該不會全錯?!
冷靜下來追查才發現是誤會一場,猴子嚇猴子果然是最可怕的!

Memory Access Misalignment 的問題是因為參考 RISC-V-TLM 的輸出,
回推 x2 初始值為 0x3FFFFdF + 32 = 0x3FFFFFF
把 stack 位置設定為 0x3FFFFFF 導致的問題,改成 0x40000 就OK了!

//RISC-V-TLM output
time 0 s: PC: 0x10054. time 0 s: ADDI: x2 + -32 -> x2(0x3ffffdf)
//cpu.cpp
register_file->set_value_integer(REGISTER_INTERFACE::x2, 0x40000); 

因為 gcc 編譯出來的是 ELF 格式,目前 Simulator 還不支援,
我們支援的 Intel HEX 格式需要用 objcopy 工具,
剛剛編 Toolchain 的時候也有一起做出來。

//Makefile
CROSS_COMPILE ?= riscv32-unknown-elf-
CC= $(CROSS_COMPILE)gcc
CFLAGS = -Wall -I. -O0 -nostdlib -march=rv32i -mabi=ilp32 --entry main

SRC= test
TARGET= binary

export PATH:=/home/user/Desktop/code/rv32i-ilp32-gnu-toolchain/bin:${PATH}

all: $(addsuffix .o, $(SRC))
	$(CROSS_COMPILE)objcopy -Oihex $< $(TARGET).hex
	readelf -a $<

%.o: %.c
	$(CC) $(CFLAGS) $< -o $@

clean:
	-rm *.o *.hex

執行結果

咦?!
這次可以從 readelf 印出來的資訊找到 main 起始位置為 0x10054
該不會我誤會 RISC-V-TLM 了吧!
明天再來研究看看 Intel HEX Format 的 segmentation 是不是真的要 * 16

//RISCV-SIM/test
$make
...
Symbol table '.symtab' contains 13 entries:
...
     9: 00010054    32 FUNC    GLOBAL DEFAULT    1 main
...

上面的疑問先放一邊,
今天和猴子一起來享受執行第一個程式的成就感吧,
一樣從 Cpu 設定 Simulator 起始執行位置為 0x1054
再把剛剛編出來的 binary.hex 放到 simulator 旁邊執行!

//cpu.cpp
...
       register_file->set_pc(0x1054);
       register_file->set_value_integer(REGISTER_INTERFACE::x2, 0x40000);
       while(true) {
                step();
                wait(delay);
        }
...
//RISCV-SIM
$make run
...
current_pc: 0x1054 target_pc: 0x1058 ADDI 2 16 2 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x3fff0 immValue: 0xfffffff0
current_pc: 0x1058 target_pc: 0x105c SW 2 8 12 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xc
current_pc: 0x105c target_pc: 0x1060 ADDI 2 16 8 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x40000 immValue: 0x10
current_pc: 0x1060 target_pc: 0x1064 ADDI 0 0 15 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x1064 target_pc: 0x1068 ADDI 15 0 10 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
current_pc: 0x1068 target_pc: 0x106c LW 2 12 8 rs1Value: 0x3fff0 rs2Value: 0x0 rdValue: 0x0 immValue: 0xc
current_pc: 0x106c target_pc: 0x1070 ADDI 2 16 2 rs1Value: 0x40000 rs2Value: 0x0 rdValue: 0x40000 immValue: 0x10
current_pc: 0x1070 target_pc: 0x0 JALR 1 0 0 rs1Value: 0x0 rs2Value: 0x0 rdValue: 0x0 immValue: 0x0
INVALID: Opcode :0
Illegal Instruction, end simulation!
exception!

成功!
自己編譯 Cross Compiler 就是這麼簡單~


上一篇
Run HEX File 之 Debug 總集篇
下一篇
Hello: 真的哈囉
系列文
猴子都寫得出來的 RISC-V CPU Emulator31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
juck30808
iT邦研究生 1 級 ‧ 2021-10-14 12:01:08

恭喜即將邁入完賽啦!

hsufit iT邦新手 5 級 ‧ 2021-10-15 03:39:31 檢舉

謝謝!
雖然這陣子真的快死掉了,
但真的要結束心情還是有點複雜XD

很高興看到你的留言,
這次完賽之後還會繼續完善這個專案,之後也歡迎多多交流~

我要留言

立即登入留言