RISC-Vでベアメタルプログラミング(Lチカ編)
https://gyazo.com/fb339e727c72abc0e106dfc1a8df3e6a
事前準備
RISC-Vのコンパイラにパスを通しておく
code:~/.zshrc
export PATH=/Users/thata/src/learn-fpga/FemtoRV/FIRMWARE/TOOLCHAIN/xpack-riscv-none-embed-gcc-10.1.0-1.1-darwin-x64/bin:$PATH
サンプルプログラム(my_blink.S)とリンカスクリプト(my_baremetal.ld)を用意する。
code:my_blink.S
# my_blink.S
.equ IO_BASE, 0x400000
.equ IO_LEDS, 4
.text
.globl _start
.type _start, @function
_start:
li gp, IO_BASE # Base address of memory-mapped IO
li s0, 0x0 # s0 = 0
loop:
srai t0, s0, 21 # t0 = s0 >> 21
andi t0, t0, 0xf # t0 = t0 & 0xf
sw t0, IO_LEDS(gp) # t0の値をLEDレジスタにセット
addi s0, s0, 1 # s0 = s0 + 1
j loop
code:my_baremetal.ld
MEMORY
{
BRAM (RWX) : ORIGIN = 0x000000, LENGTH = 0x400000
}
SECTIONS
{
.text :
{
*(.text)
_end = .; /* 後日 malloc で使う予定 */
}
}
(TEXTセグメント以外があったらちゃんと動かなそうな気がする...)
以下の手順でビルドする。
code:sh
$ cd ~/src/learn-fpga/FemtoRV/FIRMWARE/MY_ASM_EXAMPLES
$ riscv-none-embed-as -march=rv32imaf -mabi=ilp32f my_blink.S -o my_blink.o
$ riscv-none-embed-ld -m elf32lriscv -b elf32-littleriscv --no-relax -Tmy_baremetal.ld my_blink.o -o my_blink.baremetal.elf
$ ../TOOLS/firmware_words my_blink.baremetal.elf -ram 262144 -max_addr 65535 -hex my_blink.hex
$ cp my_blink.hex ../firmware.hex
$ echo my_blink.hex > ../firmware.txt
ULX3Sへアップロード。
code:sh
$ cd ~/src/learn-fpga/FemtoRV/
$ make ULX3S.synth ULX3S.prog_fast
アップロードしたプログラムがいい感じに動けばOK
"明滅するLED"
おまけ
Makefileでビルドを自動化
code: Makefile
# 事前に learn-fpga/FemtoRV/FIRMWARE/TOOLCHAIN/xpack-riscv-none-embed-gcc-10.1.0-1.1-darwin-x64/bin へパスを通しておくこと
clean:
rm -f *.o *.elf *.hex
%.o: %.S
riscv-none-embed-as -march=rv32imaf -mabi=ilp32f $< -o $@
%.baremetal.elf: %.o
riscv-none-embed-ld -m elf32lriscv -b elf32-littleriscv --no-relax -Tmy_baremetal.ld $< -o $@
%.hex: %.baremetal.elf
../TOOLS/firmware_words $< -ram 262144 -max_addr 65535 -hex $@
cp $@ ../firmware.hex
echo $@ > ../firmware.txt
Makefileを使ったビルド&アップロード手順は以下の通り。
code:sh
$ cd ~/src/learn-fpga/FemtoRV/FIRMWARE/MY_ASM_EXAMPLES
$ make clean my_blink.hex
$ cd ../..
$ make ULX3S.synth ULX3S.prog_fast
参考
ファームウェアの書き換え方法(= ベアメタルなプログラムの書き込み方)を参考にベアメタルなプログラムを書き込む
Makefileの書き方
hexファイルの作り方
firmware_wordsにELFファイルを食わせてhexファイルを出力するっぽい。
参考になるかも
firmware_words.cpp
Software and compilers for RISC-V
ビルド手順の参考になりそう
「--no-relax」ってなんだろう?
code:FemtoRV/FIRMWARE/makefile.inc
...
RVCFLAGS=$(OPTIMIZE) $(RVINCS) $(DEVICES) -fno-pic -march=$(ARCH) -mabi=$(ABI) \
-fno-stack-protector -w -Wl,--no-relax
# Note: --no-relax because I'm using gp for fast access to mapped IO.
...
これかも
_start関数にある .option norelax の行は非常に重要です。 RISC-V用のリンカは最適化機能を備えており、シンボル値にアクセスする命令があるとGP相対アドレスでアクセスする命令に置き換え、命令数を減らそうとします。 この指定が無いと、リンカがGPが既に初期化されている前提で la gp, __global_pointer$ を mv gp, gp に置き換える最適化を行ないます。嵌りました。 .option norelax により、この最適化を抑制することができます。