Advertisement
Guest User

Ubuntu 20.04で32bitRISC-V RV32IMAC ilp32、Newlibのツールチェインを利用してQEMUでgdbを使う方法

a guest
Dec 19th, 2020
157
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.42 KB | None | 0 0
  1. 自分でビルドしたRISC-V RV32IMAC ilp32、Newlibのツールチェインを利用して
  2. Ubuntu 20.04のQEMUでgdbが使えました
  3. Ubuntu Server 20.04を入れたRaspberry Pi 4でも可能です
  4. RISC-Vの命令セットの学習などに使えます
  5.  
  6.  
  7. 1.QEMUのuser modeをインストールする
  8. sudo apt install qemu-user
  9.  
  10.  
  11. 2.プログラムのコンパイル方法
  12.  
  13. C言語
  14. riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -o test01 test01.c
  15.  
  16. C言語でmath.hの数学関数を使ってる場合
  17. riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -o test01 test01.c -lm
  18.  
  19. C++
  20. riscv32-unknown-elf-g++ -march=rv32imac -mabi=ilp32 -O2 -o test01 test01.c
  21.  
  22. アセンブラのソースが含まれてる場合(test01.cがC言語ソース、test01-asm.sがアセンブラソースの場合)
  23. riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O2 -c -o test01.o test01.c
  24. riscv32-unknown-elf-as -march=rv32imac -mabi=ilp32 -a=test01-asm.lst -o test01-asm.o test01-asm.s
  25. riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -o test01 test01.o test01-asm.o
  26.  
  27.  
  28. 4.実行方法
  29. -LオプションでRISC-V RV32IMAC ilp32 Newlibのツールチェインがインストールされたディレクトリを指定します
  30.  
  31. qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ test01
  32.  
  33.  
  34. 5.QEMUでGDBでリモートデバッグする
  35.  
  36. (1)gdbのインストール
  37. sudo apt install gdb-multiarch
  38.  
  39.  
  40. (2)gdbの設定(.gdbinitを作成)
  41. 例ではディレクトリ~/risc-v/rv32imacでデバッグ作業を行う場合の設定です
  42. set auto-load safe-pathで~/risc-v/rv32imacを指定してます
  43. この場合、b mainでmainにブレークポイントを設定し、
  44. target remote :1234で1234ポートに接続して、cで実行継続する設定です
  45. mainにブレークポイントが設定されているのでmainで止まります
  46.  
  47.  
  48. 作業用ディレクトリを作成
  49. mkdir -p ~/risc-v/rv32imac
  50.  
  51.  
  52.  
  53. ホームディレクトリに.gdbinitを作成する
  54. set auto-load safe-pathで設定を自動的に読み込むディレクトリを指定します
  55. vi ~/.gdbinit
  56.  
  57.  
  58. 以下~/.gdbinit
  59. set auto-load safe-path ~/risc-v/rv32imac
  60. set pagination 0
  61.  
  62.  
  63.  
  64.  
  65. ディレクトリ~/risc-v/rv32imacに.gdbinitを作成する
  66. set sysrootではRISC-V RV32IMAC ilp32 Newlibのツールチェインがインストールされたディレクトリを指定します
  67.  
  68. cd ~/risc-v/rv32imac
  69. vi .gdbinit
  70.  
  71.  
  72. 以下.gdbinit
  73. set architecture riscv:rv32
  74. set sysroot /opt/riscv/riscv-rv32imac-ilp32-newlib
  75. b main
  76. target remote :1234
  77. c
  78.  
  79.  
  80.  
  81. (3)デバッグ用にコンパイル(-gオプションをつける、最適化を抑制するために-O0にしてます)
  82.  
  83. riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -g -O0 -o test01 test01.c
  84.  
  85.  
  86. (4)qemuを-gオプションで起動してgdb接続待ちにする
  87. 1234は接続待ちするポート番号(.gdbinitで指定したポート番号を使う)
  88.  
  89. qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ -g 1234 test01 &
  90.  
  91.  
  92. (5)gdbを起動
  93.  
  94. gdb-multiarch test01
  95.  
  96.  
  97. (6)main関数で停止するのでgdbのコマンドを入力してデバッグする
  98. sでステップ実行、cで続行、info registerでレジスタ一覧、
  99. p /x $a0でa0レジスタを16進表示、x $sp+16でsp+16のメモリの内容を表示、
  100. p fooでfooという変数の内容を表示などなど
  101. gdbのコマンドなどは調べてください
  102.  
  103.  
  104. rv32imac、ilp32でビルドしたコンパイラでもABIはilp32になりますが
  105. gccのオプションの-march=rv32gc -mabi=ilp32、-march=rv32imafc -mabi=ilp32と指定すれば
  106. RV32GCやRV32IMAFCなどでコンパイルすることもできます。
  107. (当然ですが、この場合あらかじめビルドされている標準のライブラリの中ではFPU命令は使われません)
  108.  
  109. またRV32GCやRV32IMAFCでコンパイルしたプログラムをマイコンで動作させるには
  110. マイコンが浮動小数点演算に対応してる必要があります
  111. ここではあくまでQEMUで実行する場合の話になります
  112.  
  113. ABIがilp32の場合は浮動小数点数のパラメータは整数レジスタ渡しになります
  114. 単精度浮動小数点数ならa0からa7に倍精度浮動小数点数ならa0からa7でa0、a1のように連続した2つのレジスタに格納されます
  115. 例えば、func1(int i, double d);ならiがa0に、dがa1に下位32bit、a2に上位32bitが格納されます。)
  116. 戻り値は倍精度浮動小数点数の場合はa0に下位32bitが、a1に下位32bitが格納されます
  117. RV32Iでは倍精度浮動小数点レジスタから整数レジスタ、整数レジスタから倍精度浮動小数点レジスタへ転送する場合は
  118. 一度メモリに保存してからメモリから読み込むとよいようです
  119. 単精度浮動小数点数に関してはfmv.x.w a0, fa0やfmv.w.x fa0, a0のように
  120. 浮動小数点レジスタから整数レジスタ、整数レジスタから浮動小数点レジスタへ直接転送ができます。
  121.  
  122.  
  123. テストプログラム1
  124.  
  125. コンパイル方法
  126.  
  127. riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -O0 -c -g -o test01.o test01.c
  128. riscv32-unknown-elf-as -march=rv32imac -mabi=ilp32 -a=test01-asm.lst -g -o test01-asm.o test01-asm.s
  129. riscv32-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -g -o test01 test01.o test01-asm.o
  130.  
  131.  
  132. 実行
  133. qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ test01
  134.  
  135.  
  136. gdbで実行
  137. qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ -g 1234 test01 &
  138. gdb-multiarch test01
  139.  
  140.  
  141. 以下test01.c
  142.  
  143. #include <stdio.h>
  144.  
  145. extern int test_asm_func01(int, int);
  146. int main(int argc, char *argv[]) {
  147.  
  148. for (int j = 1; j <= 5; j++) {
  149. for (int i = 1; i <= 5; i++) {
  150. printf("%d * %d + %d = %d", i, j, i, test_asm_func01(i, j));
  151. printf("\n");
  152. }
  153. }
  154.  
  155. return 0;
  156. }
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163. 以下test01-asm.s
  164.  
  165. # int test_asm_func01(int a, int b);
  166. .text
  167. .align 1
  168. .globl test_asm_func01
  169. test_asm_func01:
  170. mv a2, a0
  171. mul a0, a0, a1
  172. add a0, a0, a2
  173. jr ra
  174.  
  175. .end
  176.  
  177.  
  178.  
  179.  
  180.  
  181.  
  182.  
  183.  
  184. テストプログラム2
  185.  
  186. コンパイル方法
  187. 単精度浮動小数点命令を使うので-march=rv32imafcでコンパイルします
  188.  
  189. riscv32-unknown-elf-gcc -march=rv32imafc -mabi=ilp32 -O0 -c -g -o test02.o test02.c
  190. riscv32-unknown-elf-as -march=rv32imafc -mabi=ilp32 -a=test02-asm.lst -g -o test02-asm.o test02-asm.s
  191. riscv32-unknown-elf-gcc -march=rv32imafc -mabi=ilp32 -g -o test02 test02.o test02-asm.o -lm
  192.  
  193.  
  194. 実行
  195. qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ test02
  196.  
  197.  
  198. gdbで実行
  199. qemu-riscv32 -L /opt/riscv/riscv-rv32imac-ilp32-newlib/ -g 1234 test02 &
  200. gdb-multiarch test02
  201.  
  202.  
  203.  
  204. 以下test02.c
  205.  
  206. #include <stdio.h>
  207.  
  208. extern int test_asm_sin01(int start, int end, int step);
  209. void print01(int i, float a);
  210.  
  211. int main(int argc, char *argv[]) {
  212. test_asm_sin01(0, 360, 10);
  213.  
  214. return 0;
  215. }
  216.  
  217. void print01(int i, float a) {
  218. printf("sin(%3d) = %.6f", i, a);
  219. printf("\n");
  220. }
  221.  
  222.  
  223.  
  224.  
  225.  
  226.  
  227.  
  228. 以下test02-asm.s
  229.  
  230. # test_asm_sin01(int start, int end, int step);
  231. .text
  232. .align 1
  233. .globl test_asm_sin01
  234. test_asm_sin01:
  235. # 保存領域確保
  236. # spの値は16バイト境界に揃える
  237. addi sp, sp, -(20 + 12)
  238. # 戻りアドレスを退避
  239. sw ra, (sp)
  240. # 保存レジスタの値を退避
  241. sw s1, 4(sp)
  242. sw s2, 8(sp)
  243. sw s3, 12(sp)
  244. fsw fs0, 16(sp)
  245.  
  246. # 関数のパラメーターはa0からa7に保存される
  247. mv s1, a0
  248. mv s2, a1
  249. mv s3, a2
  250. la a0, pi
  251. flw fa0, (a0)
  252. la a0, num01
  253. flw fa1, (a0)
  254. fdiv.s fs0, fa0, fa1
  255. loop01:
  256. # s1 > s2の比較なのでs2に1を足したものとbgeで比較する
  257. add a0, s2, 1
  258. bge s1, a0, jump01
  259. fcvt.s.w fa0, s1
  260. fmul.s fa0, fs0, fa0
  261.  
  262. # abiがilp32なのでパラメーターを整数レジスタで渡す
  263. fmv.x.w a0, fa0
  264.  
  265. # sin関数の呼び出し
  266. call sinf
  267. # abiがilp32なのでパラメーターが整数レジスタa0に戻ってくる
  268. # print01に渡すのにa0に戻ってきた値をa1に入れる
  269. mv a1, a0
  270. mv a0, s1
  271. call print01
  272. add s1, s1, s3
  273. j loop01
  274. jump01:
  275. # 戻り値を設定
  276. mv a0, zero
  277.  
  278. # 保存レジスタの値を復帰
  279. lw s1, 4(sp)
  280. lw s2, 8(sp)
  281. lw s3, 12(sp)
  282. flw fs0, 16(sp)
  283.  
  284. # 戻りアドレスを復帰
  285. lw ra, (sp)
  286.  
  287. # 保存領域開放
  288. addi sp, sp, 20 + 12
  289. jr (ra)
  290.  
  291. .data
  292. .align 2
  293. pi: .float 3.141592653589793
  294. num01: .float 180.0
  295.  
  296. .end
  297.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement