Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 自分でビルドしたRISC-V RV32IMAC ilp32、Newlibのツールチェインを利用して
- Ubuntu 20.04のQEMUでgdbが使えました
- Ubuntu Server 20.04を入れたRaspberry Pi 4でも可能です
- RISC-Vの命令セットの学習などに使えます
- 1.QEMUのuser modeをインストールする
- sudo apt install qemu-user
- 2.プログラムのコンパイル方法
- C言語
- riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -o test01 test01.c
- C言語でmath.hの数学関数を使ってる場合
- riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -o test01 test01.c -lm
- C++
- riscv32-unknown-elf-g++ -march=rv32imac -mabi=ilp32 -O2 -o test01 test01.c
- アセンブラのソースが含まれてる場合(test01.cがC言語ソース、test01-asm.sがアセンブラソースの場合)
- riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -c -o test01.o test01.c
- riscv32-unknown-elf-as -march=rv32imac -mabi=ilp32 -a=test01-asm.lst -o test01-asm.o test01-asm.s
- riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -o test01 test01.o test01-asm.o
- 4.実行方法
- -LオプションでRISC-V RV32IMAC ilp32 Newlibのツールチェインがインストールされたディレクトリを指定します
- qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ test01
- 5.QEMUでGDBでリモートデバッグする
- (1)gdbのインストール
- sudo apt install gdb-multiarch
- (2)gdbの設定(.gdbinitを作成)
- 例ではディレクトリ~/risc-v/rv32imacでデバッグ作業を行う場合の設定です
- set auto-load safe-pathで~/risc-v/rv32imacを指定してます
- この場合、b mainでmainにブレークポイントを設定し、
- target remote :1234で1234ポートに接続して、cで実行継続する設定です
- mainにブレークポイントが設定されているのでmainで止まります
- 作業用ディレクトリを作成
- mkdir -p ~/risc-v/rv32imac
- ホームディレクトリに.gdbinitを作成する
- set auto-load safe-pathで設定を自動的に読み込むディレクトリを指定します
- vi ~/.gdbinit
- 以下~/.gdbinit
- set auto-load safe-path ~/risc-v/rv32imac
- set pagination 0
- ディレクトリ~/risc-v/rv32imacに.gdbinitを作成する
- set sysrootではRISC-V RV32IMAC ilp32 Newlibのツールチェインがインストールされたディレクトリを指定します
- cd ~/risc-v/rv32imac
- vi .gdbinit
- 以下.gdbinit
- set architecture riscv:rv32
- set sysroot /opt/riscv/riscv-rv32imac-ilp32-newlib
- b main
- target remote :1234
- c
- (3)デバッグ用にコンパイル(-gオプションをつける、最適化を抑制するために-O0にしてます)
- riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -g -O0 -o test01 test01.c
- (4)qemuを-gオプションで起動してgdb接続待ちにする
- 1234は接続待ちするポート番号(.gdbinitで指定したポート番号を使う)
- qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ -g 1234 test01 &
- (5)gdbを起動
- gdb-multiarch test01
- (6)main関数で停止するのでgdbのコマンドを入力してデバッグする
- sでステップ実行、cで続行、info registerでレジスタ一覧、
- p /x $a0でa0レジスタを16進表示、x $sp+16でsp+16のメモリの内容を表示、
- p fooでfooという変数の内容を表示などなど
- gdbのコマンドなどは調べてください
- rv32imac、ilp32でビルドしたコンパイラでもABIはilp32になりますが
- gccのオプションの-march=rv32gc -mabi=ilp32、-march=rv32imafc -mabi=ilp32と指定すれば
- RV32GCやRV32IMAFCなどでコンパイルすることもできます。
- (当然ですが、この場合あらかじめビルドされている標準のライブラリの中ではFPU命令は使われません)
- またRV32GCやRV32IMAFCでコンパイルしたプログラムをマイコンで動作させるには
- マイコンが浮動小数点演算に対応してる必要があります
- ここではあくまでQEMUで実行する場合の話になります
- ABIがilp32の場合は浮動小数点数のパラメータは整数レジスタ渡しになります
- 単精度浮動小数点数ならa0からa7に倍精度浮動小数点数ならa0からa7でa0、a1のように連続した2つのレジスタに格納されます
- 例えば、func1(int i, double d);ならiがa0に、dがa1に下位32bit、a2に上位32bitが格納されます。)
- 戻り値は倍精度浮動小数点数の場合はa0に下位32bitが、a1に下位32bitが格納されます
- RV32Iでは倍精度浮動小数点レジスタから整数レジスタ、整数レジスタから倍精度浮動小数点レジスタへ転送する場合は
- 一度メモリに保存してからメモリから読み込むとよいようです
- 単精度浮動小数点数に関してはfmv.x.w a0, fa0やfmv.w.x fa0, a0のように
- 浮動小数点レジスタから整数レジスタ、整数レジスタから浮動小数点レジスタへ直接転送ができます。
- テストプログラム1
- コンパイル方法
- riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O0 -c -g -o test01.o test01.c
- riscv32-unknown-elf-as -march=rv32imac -mabi=ilp32 -a=test01-asm.lst -g -o test01-asm.o test01-asm.s
- riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -g -o test01 test01.o test01-asm.o
- 実行
- qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ test01
- gdbで実行
- qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ -g 1234 test01 &
- gdb-multiarch test01
- 以下test01.c
- #include <stdio.h>
- extern int test_asm_func01(int, int);
- int main(int argc, char *argv[]) {
- for (int j = 1; j <= 5; j++) {
- for (int i = 1; i <= 5; i++) {
- printf("%d * %d + %d = %d", i, j, i, test_asm_func01(i, j));
- printf("\n");
- }
- }
- return 0;
- }
- 以下test01-asm.s
- # int test_asm_func01(int a, int b);
- .text
- .align 1
- .globl test_asm_func01
- test_asm_func01:
- mv a2, a0
- mul a0, a0, a1
- add a0, a0, a2
- jr ra
- .end
- テストプログラム2
- コンパイル方法
- 単精度浮動小数点命令を使うので-march=rv32imafcでコンパイルします
- riscv32-unknown-elf-gcc -march=rv32imafc -mabi=ilp32 -O0 -c -g -o test02.o test02.c
- riscv32-unknown-elf-as -march=rv32imafc -mabi=ilp32 -a=test02-asm.lst -g -o test02-asm.o test02-asm.s
- riscv32-unknown-elf-gcc -march=rv32imafc -mabi=ilp32 -g -o test02 test02.o test02-asm.o -lm
- 実行
- qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ test02
- gdbで実行
- qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ -g 1234 test02 &
- gdb-multiarch test02
- 以下test02.c
- #include <stdio.h>
- extern int test_asm_sin01(int start, int end, int step);
- void print01(int i, float a);
- int main(int argc, char *argv[]) {
- test_asm_sin01(0, 360, 10);
- return 0;
- }
- void print01(int i, float a) {
- printf("sin(%3d) = %.6f", i, a);
- printf("\n");
- }
- 以下test02-asm.s
- # test_asm_sin01(int start, int end, int step);
- .text
- .align 1
- .globl test_asm_sin01
- test_asm_sin01:
- # 保存領域確保
- # spの値は16バイト境界に揃える
- addi sp, sp, -(20 + 12)
- # 戻りアドレスを退避
- sw ra, (sp)
- # 保存レジスタの値を退避
- sw s1, 4(sp)
- sw s2, 8(sp)
- sw s3, 12(sp)
- fsw fs0, 16(sp)
- # 関数のパラメーターはa0からa7に保存される
- mv s1, a0
- mv s2, a1
- mv s3, a2
- la a0, pi
- flw fa0, (a0)
- la a0, num01
- flw fa1, (a0)
- fdiv.s fs0, fa0, fa1
- loop01:
- # s1 > s2の比較なのでs2に1を足したものとbgeで比較する
- add a0, s2, 1
- bge s1, a0, jump01
- fcvt.s.w fa0, s1
- fmul.s fa0, fs0, fa0
- # abiがilp32なのでパラメーターを整数レジスタで渡す
- fmv.x.w a0, fa0
- # sin関数の呼び出し
- call sinf
- # abiがilp32なのでパラメーターが整数レジスタa0に戻ってくる
- # print01に渡すのにa0に戻ってきた値をa1に入れる
- mv a1, a0
- mv a0, s1
- call print01
- add s1, s1, s3
- j loop01
- jump01:
- # 戻り値を設定
- mv a0, zero
- # 保存レジスタの値を復帰
- lw s1, 4(sp)
- lw s2, 8(sp)
- lw s3, 12(sp)
- flw fs0, 16(sp)
- # 戻りアドレスを復帰
- lw ra, (sp)
- # 保存領域開放
- addi sp, sp, 20 + 12
- jr (ra)
- .data
- .align 2
- pi: .float 3.141592653589793
- num01: .float 180.0
- .end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement