MinCamlのテストプログラム
必要そうな命令は実装し終えたので、自作CPUの上でMinCaml のテストプログラムを動かして動作確認をしていく。
→ ひととおり動いた!
動いた
test/4649.ml
test/sum.ml
test/print.ml
test/ack.ml
test/adder.ml
test/cls-bug.ml
test/cls-bug2.ml
test/cls-rec.ml
test/cls-reg-bug.ml
test/even-odd.ml
test/fib.ml
test/float_if.ml
test/funcomp.ml
test/gcd.ml
sum-tail.ml
shuffle.ml
join-reg.ml
join-reg2.ml
join-stack.ml
join-stack2.ml
join-stack3.ml
non-tail-if.ml
spill.ml
spill2.ml
spill3.ml
mandelbrot.ml
matmul.ml
float_if.ml
inprod-loop.ml
inprod-rec.ml
inprod.ml
動かなかった
test/float.ml
ライブラリ関数などが面倒そうなのでスキップ
4649
code:test/4649.ml
print_int 4649; print_newline ()
https://gyazo.com/cad432b1c5f07ed17a574c2bbaa854a3
sum
code:test/sum.ml
let rec sum x =
if x <= 0 then 0 else
sum (x - 1) + x in
print_int (sum 100)
https://gyazo.com/9f7c8b4e339db3b4000f7e0c15d007a9
5050 のはずが、550 になってしまう。
以下のコードだと
code:test/sum2.ml
let rec sum x =
if x <= 0 then 0
else
let s = sum (x - 1) + x in
let _ = print_int x; print_newline () in
let _ = print_int s; print_newline () in
s in
print_int (sum 101)
以下のように出力される。計算結果は大丈夫そうだけど、100 → 10に、5050 が 550 になっているみたい。
→ print_int のバグでした
https://gyazo.com/674bec690273d83d37967a52e94e1f72
print_int のバグを修正
print_int のバグと、スタックのアドレスのバグを両方修正。
code:diff
diff --git a/Makefile b/Makefile
index 4307822..ea6280b 100644
--- a/Makefile
+++ b/Makefile
@@ -52,7 +52,7 @@ firmware/firmware.hex:
# riscv64-unknown-elf-objcopy -O verilog --verilog-data-width 4 firmware/firmware.elf firmware/firmware.hex
# MinCaml のプログラムをビルド
-FIRMWARE_TARGET = test/pi
+FIRMWARE_TARGET = test/sum
firmware/firmware.hex: firmware/$(FIRMWARE_TARGET).ml firmware/libmincaml.S firmware/stub.S
firmware/bin/min-caml firmware/${FIRMWARE_TARGET}
riscv64-unknown-elf-gcc -nostdlib -march=rv32if -mabi=ilp32f -Wl,-Tfirmware/custom.ld firmware/stub.S firmware/${FIRMWARE_TARGET}.s firmware/libmincaml.S -o firmware/firmware.elf
diff --git a/firmware/libmincaml.S b/firmware/libmincaml.S
index a3fde6d..d8616c9 100644
--- a/firmware/libmincaml.S
+++ b/firmware/libmincaml.S
@@ -11,13 +11,14 @@ min_caml_print_int:
// 使用するレジスタ
// s1: 出力する整数(作業用)
// s2: 商
+ // s3: 出力する整数
// レジスタをスタックへ退避
- addi s10, s10, 12
+ addi s10, s10, 16
sw ra, -12(s10)
sw s1, -8(s10)
sw s2, -4(s10)
-
+ sw s3, 0(s10)
bgt a0, zero, min_caml_print_int_positive
@@ -28,8 +29,9 @@ min_caml_print_int_negative:
li t1, 0xffffffff
xor s1, s1, t1
- // 出力する整数を、作業用レジスタ s1 にコピー
+ // 出力する整数を、作業用レジスタ s1 と s3 にコピー
addi s1, s1, 1
+ mv s3, s1
// "-" を出力
li a0, '-'
@@ -38,75 +40,94 @@ min_caml_print_int_negative:
j min_caml_print_int_main
min_caml_print_int_positive:
- // 出力する整数を、作業用レジスタ s1 にコピー
+ // 出力する整数を、作業用レジスタ s1 と s3 にコピー
mv s1, a0
+ mv s3, s1
min_caml_print_int_main:
// 10ケタ目を出力
mv a0, s1 // 割られる数
li a1, 1000000000 // 割る数
+ blt s3, a1, min_caml_print_int_9 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 9ケタ目を出力
+min_caml_print_int_9:
mv a0, s1 // 割られる数
li a1, 100000000 // 割る数
+ blt s3, a1, min_caml_print_int_8 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 8ケタ目を出力
+min_caml_print_int_8:
mv a0, s1 // 割られる数
li a1, 10000000 // 割る数
+ blt s3, a1, min_caml_print_int_7 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 7ケタ目を出力
+min_caml_print_int_7:
mv a0, s1 // 割られる数
li a1, 1000000 // 割る数
+ blt s3, a1, min_caml_print_int_6 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 6ケタ目を出力
+min_caml_print_int_6:
mv a0, s1 // 割られる数
li a1, 100000 // 割る数
+ blt s3, a1, min_caml_print_int_5 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 5ケタ目を出力
+min_caml_print_int_5:
mv a0, s1 // 割られる数
li a1, 10000 // 割る数
+ blt s3, a1, min_caml_print_int_4 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 4ケタ目を出力
+min_caml_print_int_4:
mv a0, s1 // 割られる数
li a1, 1000 // 割る数
+ blt s3, a1, min_caml_print_int_3 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 3ケタ目を出力
+min_caml_print_int_3:
mv a0, s1 // 割られる数
li a1, 100 // 割る数
+ blt s3, a1, min_caml_print_int_2 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 2ケタ目を出力
+min_caml_print_int_2:
mv a0, s1 // 割られる数
li a1, 10 // 割る数
+ blt s3, a1, min_caml_print_int_1 // 「出力する整数 < 割る数」の場合、次の桁へ進む
call div
mv s1, a1 // 新しい割られる数
call print_num
// 1ケタ目を出力
+min_caml_print_int_1:
li t1, '0'
add t2, t1, s1 // 出力する文字
mv a0, t2
@@ -116,7 +137,9 @@ min_caml_print_int_main:
lw ra, -12(s10)
lw s1, -8(s10)
lw s2, -4(s10)
- addi s10, s10, -12
+ lw s3, 0(s10)
+ addi s10, s10, -16
+
ret
// 1桁の数字を出力。ただし、0の場合は何も出力しない
@@ -124,18 +147,16 @@ min_caml_print_int_main:
print_num:
// レジスタをスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// メイン処理
- beq a0, zero, print_num_break
li t1, '0'
add t2, t1, a0 // 出力する文字
mv a0, t2
call putc
-print_num_break:
// レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
@@ -147,7 +168,7 @@ print_num_break:
div:
// レジスタをスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// メイン処理
li t1, 0 // 商
@@ -173,7 +194,7 @@ div_break:
mv a1, t2
// レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
@@ -186,7 +207,7 @@ div_break:
getc:
// ra をスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// 受信データが来るまで待機
lw t0, UART_CTRL_OFFSET(gp)
@@ -197,7 +218,7 @@ getc_break:
lw a0, UART_DATA_OFFSET(gp)
// ra レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
@@ -210,7 +231,7 @@ getc_break:
putc:
// ra をスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// メイン処理
sw a0, UART_DATA_OFFSET(gp)
@@ -224,7 +245,7 @@ putc_wait:
putc_break:
// ra レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
@@ -236,7 +257,7 @@ putc_break:
put_newline:
// ra をスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// 改行コードを出力
li a0, '\n'
@@ -245,7 +266,7 @@ put_newline:
call putc
// ra レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
@@ -256,7 +277,7 @@ put_newline:
min_caml_clear_screen:
// ra をスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// 画面クリアのためのエスケープシーケンスを送信
li a0, 27
@@ -277,7 +298,7 @@ min_caml_clear_screen:
call putc
// ra レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
@@ -289,7 +310,7 @@ min_caml_clear_screen:
sleep:
// ra をスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// メイン処理
li t0, 0
@@ -301,7 +322,7 @@ sleep_loop:
sleep_break:
// ra レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
@@ -339,7 +360,7 @@ min_caml_int_of_float:
min_caml_print_newline:
// ra をスタックへ退避
addi s10, s10, 4
- sw ra, -4(s10)
+ sw ra, 0(s10)
// 改行コードを出力
li a0, '\n'
@@ -348,6 +369,6 @@ min_caml_print_newline:
call putc
// ra レジスタの値を復元
- lw ra, -4(s10)
+ lw ra, 0(s10)
addi s10, s10, -4
ret
diff --git a/firmware/stub.S b/firmware/stub.S
index 03d6914..1adad42 100644
--- a/firmware/stub.S
+++ b/firmware/stub.S
@@ -13,7 +13,7 @@ _start:
// ヒープポインタの開始アドレス (0x4E000000 ~ 0x4EFFFFFF)
li s11, 0x4E000000
- call min_caml_clear_screen
+ // call min_caml_clear_screen
// スタックポインタのアドレスとヒープポインタのアドレスを引数に min_caml_start を呼び出す
li a0, 0x4F000000
diff --git a/firmware/test/print.ml b/firmware/test/print.ml
index 9797f7b..b8b18b8 100644
--- a/firmware/test/print.ml
+++ b/firmware/test/print.ml
@@ -1,3 +1,6 @@
-print_int 123;
-print_int (-456);
-print_int (789+0)
+print_int 123000;
+print_newline ();
+print_int (-456000);
+print_newline ();
+print_int (789000+0);
+print_newline ()
mandelbrot.ml
動いた〜
https://gyazo.com/c77ebe2aea73decea7b8d9604ccb6fac