半導体が自炊できない国になったけどコンピュータの中身に興味ある高校生へ
音声合成マクロ付きpptm(合成mp4はyoutubeチャンネル)の元ノートを見たい人は以下
RISC-V32IシミュレータにCGRAをくっつける方法
RISCVが流行るかもと思う人が増えています.他のCPUとの違いを一言で説明するには,シミュレータを書くのが一番です.シミュレータ自体は1日で書けます.あと,コンパイラとかライブラリとか探してビルドして,なんやかんや環境を作ると,C言語で書いたプログラムがシミュレータの上で走るようになります.これで,実行形式ファイルの構造や,教科書ではよくわからないCPUの動きが観察できます.画像処理プログラムが動くまでの全3日程度の作業の様子をまとめました.最後は,昔流行ったベクトル命令ではなく,CGRAをくっつけて(単にHOSTにするのではなく)新計算原理コンピュータの研究に使います.(なかしま@ならせんたんだい)
RISCVの一番小さい構成は,"gcc -march=rv32i -mabi=ilp32"が出すコードが動く構成です.ああ,いきなり何だかわからないですね.gccは,GNU C-Compilerのことで,いろんなCPUに対応できるCコンパイラです.なかしまは,gcc-1.40(1991/6/1)を使って,VPPスパコンのVLIW用初期Cコンパイラを作りました.元のダウンロードリンクはなくなっていますが,ここに一式入っています.当時のgccは,ファイル数で200程度しかありませんでした.今では10万以上と500倍に増え,VPPの流れを汲むFRV用のコードも入っています.シミュレータを書く前にコンパイル環境を作る必要があって,gccは欠かせません.そして,gccでクロスコンパイラを作るために,アセンブラやリンカが入っているbinutilsが必要です.
まず,FreeBSDかLinuxが動く環境を用意します.ログインしたら,ここから一式ダウンロードします.次に,"cd; zcat proj-riscv32.tgz|tar xpf -"とすると,$HOMEに以下ができます.
proj-riscv32/src/README proj-riscv32/src/binutils-2.35.2.tgz … アセンブラやリンカなど proj-riscv32/src/conv-c2d/ … 工事中:CGRAコンパイラ proj-riscv32/src/conv-mark/ … 工事中:CGRAコンパイラ proj-riscv32/src/dsim/ … シミュレータ proj-riscv32/src/gcc-10.3.0.tgz … コンパイラ proj-riscv32/src/gmp-5.1.3.tgz … コンパイラのビルドに必要なもの proj-riscv32/src/isl-0.18.tgz … コンパイラのビルドに必要なもの proj-riscv32/src/mpc-1.0.1.tgz … コンパイラのビルドに必要なもの proj-riscv32/src/mpfr-3.1.2.tgz … コンパイラのビルドに必要なもの proj-riscv32/src/newlib-3.3.0.tgz … ライブラリ
では,binutilsに取り掛かります."cd proj-riscv32/src; zcat binutils-2.35.2.tgz|tar xpf -; cd binutils-2.35.2"とします.中にMAKE_INSTALLというファイルがあり,以下のように書いてあります.
CC=gcc;export CC ./configure -target=riscv32-elf --prefix=/usr/home/nakashim/proj-riscv32 gmake CC=gcc > Make.log 2>&1 #gmake install CC=gcc
"-target=riscv32-elf"は,32bit-ELF形式を使う指示です."--prefix=/usr/home/nakashim/proj-riscv32"は,これから作るツールの置き場所です.置き場所は適当に変えてOKです.ただし,.cshrcに,"set path = ($HOME/proj-riscv32/bin $path)"と追加するなど,コマンドサーチパスに入れておく必要があります.これを使って,"./MAKE_INSTALL"とします.ログはMake.logに書き込まれるので,別のウィンドウから,"tail -f Make.log"として,進行状況を確認できます.MAKE_INSTALLが終って,Make.logの最後にエラーが出ていなければ,"make install"とします."ls -rtl ~/proj-riscv32/bin"とすると,以下のように表示されるはずです.元々あったFreeBSD7.2R用のファイルに上書きされて,各ファイルの日付が新しくなっていることを確認しましょう.
-rwxr-xr-x 1 nakashim usr1000 3848329 4 5 19:01 riscv32-elf-strings -rwxr-xr-x 1 nakashim usr1000 3850539 4 5 19:01 riscv32-elf-size -rwxr-xr-x 2 nakashim usr1000 4050560 4 5 19:01 riscv32-elf-ranlib -rwxr-xr-x 2 nakashim usr1000 5878883 4 5 19:01 riscv32-elf-objdump … 逆アセンブラができた -rwxr-xr-x 2 nakashim usr1000 4474035 4 5 19:01 riscv32-elf-objcopy -rwxr-xr-x 2 nakashim usr1000 4050529 4 5 19:01 riscv32-elf-ar -rwxr-xr-x 2 nakashim usr1000 4474062 4 5 19:01 riscv32-elf-strip -rwxr-xr-x 2 nakashim usr1000 2324702 4 5 19:01 riscv32-elf-readelf -rwxr-xr-x 2 nakashim usr1000 3888561 4 5 19:01 riscv32-elf-nm -rwxr-xr-x 1 nakashim usr1000 286691 4 5 19:01 riscv32-elf-elfedit -rwxr-xr-x 1 nakashim usr1000 3806974 4 5 19:01 riscv32-elf-c++filt -rwxr-xr-x 1 nakashim usr1000 3861838 4 5 19:01 riscv32-elf-addr2line -rwxr-xr-x 2 nakashim usr1000 5595075 4 5 19:01 riscv32-elf-as … アセンブラができた -rwxr-xr-x 1 nakashim usr1000 4430647 4 5 19:01 riscv32-elf-gprof -rwxr-xr-x 4 nakashim usr1000 5875146 4 5 19:02 riscv32-elf-ld.bfd -rwxr-xr-x 4 nakashim usr1000 5875146 4 5 19:02 riscv32-elf-ld … リンカができた
次に,gccを作るのに必要なものを入れていきます.binutilsと同じように,以下を実行します.自信のない人は,";"毎に区切って実行します.
cd ..; zcat gmp-5.1.3.tgz |tar xpf -; cd gmp-5.1.3; vi MAKE_INSTALL; ./MAKE_INSTALL; make install cd ..; zcat mpfr-3.1.2.tgz|tar xpf -; cd mpfr-3.1.2; vi MAKE_INSTALL; ./MAKE_INSTALL; make install cd ..; zcat mpc-1.0.1.tgz |tar xpf -; cd mpc-1.0.1; vi MAKE_INSTALL; ./MAKE_INSTALL; make install cd ..; zcat isl-0.18.tgz |tar xpf -; cd isl-0.18; vi MAKE_INSTALL; ./MAKE_INSTALL; make install
いよいよ,gcc本体です.Make.logは,gcc-10.3.0の隣のgcc-objdirにできます.MAKE_INSTALLの中にある,"-target=riscv32-elf"が重要です.
"cd ..; zcat gcc-10.3.0.tgz |tar xpf -; cd gcc-10.3.0; vi MAKE_INSTALL; ./MAKE_INSTALL"
Make.logの最後に,"[configure-target-libbacktrace]"というエラーが出ている場合は,気にする必要はありません.他のエラーは対処する必要があります.うまくできたら,"cd ../gcc-objdir; make install"を実行します.
"ls -rtl ~/proj-riscv32/bin"とすると,以下のように追加されているはずです.gccも上書きされて,各ファイルの日付が新しくなっていることを確認しましょう.
-rwxr-xr-x 2 nakashim usr1000 3492386 4 5 20:17 riscv32-elf-g++ -rwxr-xr-x 2 nakashim usr1000 3492386 4 5 20:17 riscv32-elf-c++ -rwxr-xr-x 1 nakashim usr1000 3493114 4 5 20:17 riscv32-elf-gfortran -rwxr-xr-x 1 nakashim usr1000 117629379 4 5 20:17 riscv32-elf-lto-dump -rwxr-xr-x 1 nakashim usr1000 1937887 4 5 20:17 riscv32-elf-gcov-tool -rwxr-xr-x 1 nakashim usr1000 1828991 4 5 20:17 riscv32-elf-gcov-dump -rwxr-xr-x 1 nakashim usr1000 2722939 4 5 20:17 riscv32-elf-gcov -rwxr-xr-x 1 nakashim usr1000 3490095 4 5 20:17 riscv32-elf-cpp -rwxr-xr-x 1 nakashim usr1000 91604 4 5 20:17 riscv32-elf-gcc-ranlib -rwxr-xr-x 1 nakashim usr1000 91592 4 5 20:17 riscv32-elf-gcc-nm -rwxr-xr-x 1 nakashim usr1000 91640 4 5 20:17 riscv32-elf-gcc-ar -rwxr-xr-x 2 nakashim usr1000 3476640 4 5 20:17 riscv32-elf-gcc-10.3.0 -rwxr-xr-x 2 nakashim usr1000 3476640 4 5 20:17 riscv32-elf-gcc
最後に,newlibを入れます.newlibは,C言語でよく使う関数がたくさん入ったライブラリです.馴染みのprintf()も入っています.binutilsと同じように,以下を実行します.
cd ..; zcat newlib-3.3.0.tgz |tar xpf -; cd newlib-3.3.0; vi MAKE_INSTALL; ./MAKE_INSTALL; make install
コンパイラ,アセンブラ,リンカ,ライブラリが揃いました.でも,あと1つ,シミュレータで動く実行形式ファイルを作るために大事なものが欠けています.C言語で書いたプログラムは,どこから実行されるでしょう.main()という関数から実行されることは知っていますね.でも,実行形式ファイルの先頭は,そうではありません._startというラベルが付いた命令が,最初に実行される機械語命令です._startは,いくつかのレジスタに初期値をセットして,main()を呼び出し,main()から戻ってきたら,最後にexit()に繋ぐ役割りを果たします._startは,アセンブリ言語で書いて,アセンブルしておく必要があります.具体的には以下にあります._start.sがアセンブリ言語で書いたソースプログラムです.Makefileがあるので,この場所で,"make"とします.さっき作ったアセンブラを使って,オブジェクトである_start.oが生成されます.
proj-riscv32/lib/dsim32-lib/Makefile proj-riscv32/lib/dsim32-lib/_start.s proj-riscv32/lib/dsim32-lib/_start.o
また,同じ場所に,マップファイルがあります.中身は以下です.これを使うと,_startが0x00010000番地になるようにコンパイルできます.使い方はあとで説明します.
proj-riscv32/lib/dsim32-lib/_map SECTIONS { .text 0x00010000 : { *(.text) } }
では,_start.sの中身を見てみましょう.★は,シミュレータとの連携が必要な部分です.0x1008はシミュレータがスタックポインタの初期値を書き込む場所で,_start.sが拾ってスタックポインタに対応するレジスタspにセットします.同様に,0x100cはシミュレーション対象プログラムの引数の数で,レジスタa0にセットします.0x1010は個々の引数に対応する文字列先頭アドレス群の先頭番地で,レジスタa1にセットします.そして,レジスタa2には0をセットします.main()を呼び出して戻ってきたら,レジスタa7にexit()に対応する0x0011をセットして,システムコール(ecall)命令を実行します.シミュレータは,この命令を実行したらシミュレーションを終了します.以上が実行形式ファイルの扱い方です.なお,0x0011というのは,ベースにした,リオーダバッファ/コヒーレントキャッシュ/マルチスレッディングを備えるARMv8シミュレータ(これも自作)が使っていた番号なので,ここだけの方言です.
.section .text.startup .align 2 .globl _start .type _start, @function _start: .option push .option norelax 1: auipc gp, %pcrel_hi(__global_pointer$) addi gp, gp, %pcrel_lo(1b) .option pop ★ la a0, 0x1008 ★ lw sp, 0(a0) # init SP la a0, __libc_fini_array # Register global termination functions call atexit # to be called upon exit call __libc_init_array # Run global initialization functions ★ la a2, 0x100c ★ lw a0, 0(a2) # a0 = argc ★ la a1, 0x1010 # a1 = argv ★ li a2, 0 # a2 = envp = NULL call main .globl exit exit: .globl _exit _exit: .globl _kill _kill: .globl _halt _halt: ★ li a7,0x0011 ★ ecall
コンパイル環境ができあがったので,以下のプログラムをコンパイルしてみましょう.
proj-riscv32/sample/test/Makefile-bsd proj-riscv32/sample/test/Makefile-dsim proj-riscv32/sample/test/test000.c
プログラムの本体はtest000.cです.中身は以下です.
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <math.h> main(argc,argv) int argc; char **argv; { typedef long double Dll; printf("sizeof DLL=%d\n", sizeof(Dll)); puts("Hello"); }
まず,FreeBSDかLinuxが動いている環境で直接動く実行形式ファイルを作ってみましょう. Makefile-bsdを使って,この場所で,"make -f Makefile-bsd"とします.test000-bsdが生成されます."objdump -D test000-bsd | less"とすると,逆アセンブル結果が表示されます. ほとんどの人はIntel命令を観察できます.CPUにARMを使っている人はARM命令を観察できます.この実行形式ファイルは,"./test000-bsd"により,すぐに動かすことができ,
Intelでは sizeof DLL=12 Hello ARMv8では sizeof DLL=16 Hello
と表示されます.sizeof DLL=の後ろに表示される数字は,long double型データのバイト長です.IEEE754-2008の単精度浮動小数点数(float)は4, 倍精度浮動小数点数(double)は8ですが,long doubleがCPUによって違うことを知るために表示しています.この値は,プログラムをコンパイルして動かしてみなくても,"echo | gcc -dM -E - | egrep '__SIZEOF_LONG_DOUBLE__'" として,コンパイラに教えてもらえる場合もあります.教えてくれるコンパイラであれば,プログラムの中で__SIZEOF_LONG_DOUBLE__を参照することもできます.
次に,さっき作ったRISCV32Iのコンパイラを使ってみましょう.今度は"make -f Makefile-dsim"です.Makefile-dsimの中身には,以下が書いてあります.
PROJTOP = ../../ CC = riscv32-elf-gcc CFLAGS = -I. -O3 $(OPTION) -mstrict-align -march=rv32i -mabi=ilp32 AS = riscv32-elf-as ASFLAGS = LD = riscv32-elf-ld LDFLAGS = -static -M $(PROJTOP)/lib/dsim32-lib/_map LIBS = -lgcc -lm -lc LIBFLAGS = -L$(PROJTOP)/lib/gcc/riscv32-elf/10.3.0/rv32i/ilp32 -L$(PROJTOP)/riscv32-elf/lib/ test000-dsim: test000.o $(LD) $(LDFLAGS) -o $@ $(PROJTOP)/lib/dsim32-lib/_start.o $< $(LIBFLAGS) --start-group $(LIBS) --end-group
コンパイラは"riscv32-elf-gcc -march=rv32i -mabi=ilp32",アセンブラは"riscv32-elf-as"を使います.そして,リンカは,
"riscv32-elf-ld -static -M ../..//lib/dsim32-lib/_map -o test000-dsim ../..//lib/dsim32-lib/_start.o test000.o -L../..//lib/gcc/riscv32-elf/10.3.0/rv32i/ilp32 -L../..//riscv32-elf/lib/ --start-group -lgcc -lm -lc --end-group"と使います.前に用意した_mapと_start.o,オブジェクトであるtest.o,newlibで作ったライブラリを使って,実行形式ファイルtest000-dsimができ上がります.中身がどうなっているか確認しましょう.
"riscv32-elf-objdump -D test000-dsim | less"とすると,以下のように表示されるはずです.先頭に_startがあって,指定通り0x10000から始まっています.そして,jal <main>の後ろに_exitがあって,ecallで終っています.うまくできているようです.
10000 <_start>: 10000: 00012197 auipc gp,0x12 10004: 80018193 addi gp,gp,-2048 # 21800 <__global_pointer$> 10008: 00001537 lui a0,0x1 1000c: 00850513 addi a0,a0,8 # 1008 <_start-0xeff8> 10010: 00052103 lw sp,0(a0) 10014: 00000517 auipc a0,0x0 10018: 22c50513 addi a0,a0,556 # 10240 <__libc_fini_array> 1001c: 1e4000ef jal ra,10200 <atexit> 10020: 2a4000ef jal ra,102c4 <__libc_init_array> 10024: 00001637 lui a2,0x1 10028: 00c60613 addi a2,a2,12 # 100c <_start-0xeff4> 1002c: 00062503 lw a0,0(a2) 10030: 000015b7 lui a1,0x1 10034: 01058593 addi a1,a1,16 # 1010 <_start-0xeff0> 10038: 00000613 li a2,0 1003c: 190000ef jal ra,101cc <main> 10040 <_exit>: 10040: 01100893 li a7,17 10044: 00000073 ecall
さっきは,IntelやARMのコンパイラを使って実行形式ファイルを作ったので,IntelやARMの本物CPU上でプログラムが動きました.でも,これから動かす実行形式ファイルはRISCV32I用です.本物CPUがなければ,動きもどうもしません.そこで役に立つのがシミュレータです.シミュレータには,機能の違いによって大きく2種類あります.1つは,OSも動かせる,本物CPUと全く同じ機能があるシミュレータ,もう1つは,OSは動かないけど,さっき作ったtest000-dsimのようなアプリケーションなら動かせるシミュレータです.OSの開発やデバッグには前者が必要ですが,この記事ではそこまで必要ないので,後者を作ります.
さらに,シミュレータには,必要な精度の違いによって大きく4種類の作り方があります.(1)トレースベースシミュレータは,実機の機能を使って,実行した命令列から,条件分岐命令に関するアドレス情報や,メモリ参照に関するアドレス情報を取り出し,キャッシュメモリのヒット率などの性能を見積もるシミュレータです.命令デコード機能を作る必要がないので,シミュレータの構造も簡単で高速ですが,実機がなけれは情報を取り出せないので,この記事の目的には合いません.(2)アーキテクチャシミュレータ(命令アキュレート)は,機械語命令を1つずつ逐次実行します.自身で命令デコーダを持っているので,実機がなくてもプログラムを実行できます.アーキテクチャの機能検証や,コンパイラのデバッグなどに使うことができます.ただし,実行した命令数を数えることができますが,実行に要したクロックサイクル数,つまり,性能については見積ることが困難です.(3)パイプラインシミュレータ(サイクルアキュレート)は,ハードウェアのパイプライン構造をある程度模倣して,キャッシュミスやCPU間通信遅延も含めたクロックサイクル数の見積りが可能なシミュレータです.本物に近いので,シミュレーション速度は遅くなりますが,アーキテクチャ研究によく使われるタイプです.(4)RTLシミュレータ(クロックアキュレート)は,ハードウェアが有するレジスタを模倣し,レジスタからレジスタまでの処理をハードウェアと同じようにソフトウェアで記述することで,マイクロアーキテクチャをほぼ再現するシミュレータです.シミュレーション速度はさらに遅くなります.代わりに,設計したハードウェアの各部分の状態も再現できるので,ハードウェアのデバッグのために大変役に立つシミュレータです.もちろん,ハードウェア記述言語(HDL)で書いて,HDLシミュレータで動かすこともできます.しかし,HDLシミュレータの動作速度は極めて遅いので,大きなプログラムを走らせることは事実上不可能です.この記事で作るシミュレータは(3)と(4)の混成です.
前に,ARMv8シミュレータ(これも自作)をベースにしたと言いました.元のARMv8シミュレータは,ここに入っています."proj-arm64/src/csim"です.これを全部説明したら,ものすごい時間がかかりますので,ここからは,RISCV32Iに変えるために必要だった改造について説明します.ARMv8との違いもわかります.改造後のシミュレータは,最初に展開した,"proj-riscv32/src/dsim"にあります.主に違うのは,以下のファイルです.
まずARMv8です.ファイル名はリンクになっています. proj-arm64/src/csim/decode.c … ARMv8命令デコーダ(4009行) proj-arm64/src/csim/exec.c … 実行制御(162行) proj-arm64/src/csim/alu.c … ALUの動作(444行) 次にRISCV32Iです.ファイル名はリンクになっています. proj-riscv32/src/dsim/decode.c … RISCV32I命令デコーダ(459行) proj-riscv32/src/dsim/exec.c … 実行制御(105行) proj-riscv32/src/dsim/alu.c … ALUの動作(55行)
ARMv8には浮動小数点命令やSIMD命令がたくさん入っているので,単純比較はできませんが,それでも,RISCV32Iはずいぶん少ない行数で書けています. RISCV32Iのシミュレータを書いて気づいた点は以下です.(1)とにかく機能が最小限(SPARCと同レベル)で条件付き実行もない.(2)条件分岐命令自身にレジスタsrc1とsrc2を比較する機能が入っているので,条件フラグ(NZCV)がいらない.いろいろいらないので,シミュレータが早く作れました.
では,シミュレータをコンパイルしましょう.各種Makefileがあります.
proj-riscv32/src/dsim/Makefile-bsd … FreeBSD用 proj-riscv32/src/dsim/Makefile-cent … CentOS用 proj-riscv32/src/dsim/Makefile-fugaku … Fugaku用 proj-riscv32/src/dsim/Makefile-zynq … ZYNQ用
使っている環境がCentOSの場合,proj-riscv32/src/dsim/の中で,"make -f Makefile-cent all clean"とすると,dsim-centができます.他も同様です.FugakuとかZYNQがない人は,うちの研究室に遊びに来れば使えます.
最初に作ったtest000-dsimをシミュレータで動かしてみましょう."cd ~/proj-riscv32/sample/test ; ../../src/dsim/dsim test000-dsim"とすると,以下のように表示されるはずです.★部分が,test000.cの出力と実行完了報告です.全部で16スレッドを動かす能力がありますが,test000.cは1スレッドしか使わないので,スレッド0だけが動きます.ちなみに,このシミュレータは,dsim.hを書き換えることで,最大64コア,2048スレッドまで増やすことができます.
RISCV+EMAX6 Simulator Version 1.3 2022/04/02 08:04:35 nakashim Exp nakashim $ MAXTHRD = 16 ※スレッド数は16 MAXCORE = 4 ※コア数は4 THR/CORE = 4.000000 (should be integer) ※コア当たりのスレッド数は4 ROBSIZE = 16 (actives are CORE_ROBSIZE-1) ※リオーダバッファのエントリ数 LINESIZE = 64B ※キャッシュラインのサイズ I1SIZE = 16384B (4way delay=16) ※命令L1キャッシュサイズ D1SIZE = 16384B (4way delay=16) ※データL1キャッシュサイズ L2SIZE = 131072B (4way dirdl=50, cc=100, mm=150) ※データL1キャッシュサイズ MAXL1BK = 8 ※L1キャッシュバンク数 MAXL2BK = 8 ※L2キャッシュバンク数 MAXMMBK = 4 ※主記憶バンク数 memspace = 00000000-4fffffff ※主記憶空間 riscv_hdr = 00001000- ※ELFヘッダ情報,スタックポインタ初期値,引数情報 riscv_param = 00001080- ※コマンド引数文字列 aloclimit = -4dffff00 ※malloc()上限 stack/thr = 00010000 ※スレッドあたりスタックサイズ上限 stackinit = -4fffff00 ※スタックポインタ初期値 ELF:text=0001014c rodt=000008e4 data=0000099c sdat=00000010 sbss=00000014 bss=00000034 symt=000013c0 strt=00000bbf BSD:text=00011a38 data=000009f8 bss=-------- symt=-------- strt=00000bbf init text=00010000-00021a37 ※text領域 init data=00021a38-0002242f ※data領域start_address=0x00010000 ※実行開始アドレス malloc_topadr=0x00022440 ※malloc()アドレス初期値 malloc_latest=0x00022440 ※malloc()アドレス最終値 stack_pointer=0x4fffff00 ※スタックポインタ値 00001000: 00022440 00022440 4fffff00 00000001 00001080 00000000 00000000 00000000 ※ELFヘッダ情報,スタックポインタ初期値,引数情報 00001020: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001040: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001060: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001080: 74736574 2d303030 6d697364 58455400 00000000 00000000 00000000 00000000 ※コマンド引数文字列 000010a0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000010c0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000010e0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Monitor-000:00000000_00000000 00010000 IPC=0.000 ROB= 004:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#04待機通知 008:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#08待機通知 012:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#12待機通知 001:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#01待機通知 005:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#05待機通知 009:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#09待機通知 013:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#13待機通知 002:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#02待機通知 006:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#06待機通知 010:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#10待機通知 014:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#14待機通知 003:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#03待機通知 007:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#07待機通知 011:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#11待機通知 015:STOP/COMPLETE steps/cycle=00000000_00000000/00000000_00000001 ※仕事がないのでThread#15待機通知 sizeof DLL=16 ★★★test000.cの出力 Hello ★★★test000.cの出力 000:EXCSVC RISCV normal end ★★★test000.c正常終了 000:STOP/COMPLETE steps/cycle=00000000_00000d52/00000000_0000c0eb ★★★test000.c実行命令数とサイクル数 riscv_malloc_top = 0x3a457000 ※malloc()アドレス最終値 ==== Program normal end. ==== Hit any key in X. ※全スレッド停止 ====debug information (all bitmaps should be 0)==== l2rq.v_stat:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ※L2ディレクトリの正常動作確認用 d[00].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 d[01].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 d[02].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 d[03].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 d[04].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 d[05].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 d[06].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 d[07].bm/lk:0000/00000000_00000000 ※L2ディレクトリの正常動作確認用 ====execution time==== exec_ptime=5/128(_SC_CLK_TCK) ※以下は,各スレッドの実行命令数,サイクル数,各キャッシュ利用情報 malloc_top=0070453a ====PE steps, cycles, cache statistics (l1:I$ d1:D$ l2:incore-L2$ g2:other-L2$)==== 000:step=00000000_00000d52 cycle=00000000_0000c0eb i1( 94.7%)wait=00000000_00000789 d1( 96.7% hit=00000000_00000663 mis=00000000_00000038)wait=00000000_000015c4 l2( 0.0% hit=00000000_00000000 mis=00000000_000000f2) g2( 0.0% hit=00000000_00000000 mis=00000000_000000e6) flush(L1->00000000_00000ab5cycle, L2->00000000_00000000cycle) 001:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 002:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 003:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 004:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000ab5cycle, L2->00000000_00000000cycle) 005:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 006:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 007:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 008:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000ab5cycle, L2->00000000_00000000cycle) 009:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 010:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 011:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 012:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000ab5cycle, L2->00000000_00000000cycle) 013:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 014:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) 015:step=00000000_00000000 cycle=00000000_0000c0eb i1( nan%)wait=00000000_00000000 d1( nan% hit=00000000_00000000 mis=00000000_00000000)wait=00000000_00000000 l2( nan% hit=00000000_00000000 mis=00000000_00000000) g2( nan% hit=00000000_00000000 mis=00000000_00000000) flush(L1->00000000_00000000cycle, L2->00000000_00000000cycle) ====SELF=== ru_utime = 0.043054sec ru_stime = 0.001344sec ru_maxrss = 2004KB ru_ixrss = 216KB ru_idrss = 1840648KB ru_isrss = 128KB ru_minflt = 252 ru_majflt = 0 ru_nswap = 0 ru_inblock = 0 ru_oublock = 0 ru_msgsnd = 0 ru_msgrcv = 0 ru_nsignals= 0 ru_nvcsww = 3 ru_nivcsw = 0 ====CHILD=== ru_utime = 0.000000sec ru_stime = 0.000000sec ru_maxrss = 0KB ru_ixrss = 0KB ru_idrss = 0KB ru_isrss = 0KB ru_minflt = 0 ru_majflt = 0 ru_nswap = 0 ru_inblock = 0 ru_oublock = 0 ru_msgsnd = 0 ru_msgrcv = 0 ru_nsignals= 0 ru_nvcsww = 0 ru_nivcsw = 0
ところで,この実行方法では,多少の付加情報が出力されますが,命令がどう動いているかはわかりません.シミュレータのいいところは,本物CPUでは覗くことができない内部状態を表示できる点です.今度は,"../../src/dsim/dsim -b0 test000-dsim | less"としてみましょう.全部の内部情報が表示されます.これを見慣れると,立派にコンピュータの専門家です.でも最初は目が回りますね.
" ../../src/dsim/dsim -b0 test000-dsim | egrep '^000:RT' | egrep -v PENDING | uniq | less"としてみましょう.スレッド0の命令実行完了(RETIRE:RT)部分のみが表示されます.これなら,命令が順に実行されている様子を観察できます.初心者向きです.
000:RT 00000000_00000000 00010000 * [cond=e:updt=0] add/s:R03<-00000000_00022000 ★先頭アドレスから実行開始 000:RT 00000000_00000001 00010004 * [cond=e:updt=0] add/s:R03<-00000000_00021800 000:RT 00000000_00000002 00010008 * [cond=e:updt=0] add/s:R10<-00000000_00001000 000:RT 00000000_00000003 0001000c * [cond=e:updt=0] add/s:R10<-00000000_00001008 000:RT 00000000_00000004 00010010 * [cond=e:updt=0] lw :R02<-00000000_4fffff00 000:RT 00000000_00000005 00010014 * [cond=e:updt=0] add/s:R10<-00000000_00010014 000:RT 00000000_00000006 00010018 * [cond=e:updt=0] add/s:R10<-00000000_00010240 000:RT 00000000_00000007 0001001c * [cond=e:updt=0] bl :R01<-00000000_00010020 000:RT 00000000_00000008 00010200 * [cond=e:updt=0] add/s:R02<-00000000_4ffffee0 000:RT 00000000_00000009 00010204 * [cond=e:updt=0] sw 000:RT 00000000_00000009 00010204 * [cond=e:updt=0] sw :A=4ffffefc,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00010020:D1W miss (l1bank=3 A=4ffffefc rq_push=0 pushA=00000000) D1W ENQUEUED stat=2 A=4ffffefc STBF=00000000_00000000_00000000_00010020 000:RT 00000000_0000000a 00010208 * [cond=e:updt=0] sw 000:RT 00000000_0000000a 00010208 * [cond=e:updt=0] sw :A=4ffffef8,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000:D1W miss (l1bank=3 A=4ffffef8 rq_push=0 pushA=00000000) D1W D1RQWAIT stat=2 A=4ffffef8 STBF=00000000_00000000_00000000_00000000 000:RT 00000000_0000000a 00010208 * [cond=e:updt=0] sw :A=4ffffef8,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000:l1bank=3,index=7,way=0,A=4ffffef8,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000,d=1,s=0 D1W COMPLETED:d1w L2 includes l2bank=3 index=63 w=0 A=4ffffef8 000:RT 00000000_0000000b 0001020c * [cond=e:updt=0] add/s:R08<-00000000_4fffff00 000:RT 00000000_0000000c 00010210 * [cond=e:updt=0] sw 000:RT 00000000_0000000c 00010210 * [cond=e:updt=0] sw :A=4ffffeec,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00010240:l1bank=3,index=7,way=0,A=4ffffeec,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00010240,d=1,s=0 D1W COMPLETED:d1w L2 includes l2bank=3 index=63 w=0 A=4ffffeec 000:RT 00000000_0000000d 00010214 * [cond=e:updt=0] add/s:R13<-00000000_00000000 000:RT 00000000_0000000e 00010218 * [cond=e:updt=0] add/s:R12<-00000000_00000000 000:RT 00000000_0000000f 0001021c * [cond=e:updt=0] lw :R11<-00000000_00010240 000:RT 00000000_00000010 00010220 * [cond=e:updt=0] add/s:R10<-00000000_00000000 000:RT 00000000_00000011 00010224 * [cond=e:updt=0] bl :R01<-00000000_00010228 000:RT 00000000_00000012 00012e18 * [cond=e:updt=0] add/s:R02<-00000000_4ffffeb0 000:RT 00000000_00000013 00012e1c * [cond=e:updt=0] sw 000:RT 00000000_00000013 00012e1c * [cond=e:updt=0] sw :A=4ffffedc,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_4fffff00:l1bank=3,index=7,way=0,A=4ffffedc,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_4fffff00,d=1,s=0 D1W COMPLETED:d1w L2 includes l2bank=3 index=63 w=0 A=4ffffedc 000:RT 00000000_00000014 00012e20 * [cond=e:updt=0] sw 000:RT 00000000_00000014 00012e20 * [cond=e:updt=0] sw :A=4ffffed8,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000:l1bank=3,index=7,way=0,A=4ffffed8,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000,d=1,s=0 D1W COMPLETED:d1w L2 includes l2bank=3 index=63 w=0 A=4ffffed8 000:RT 00000000_00000015 00012e24 * [cond=e:updt=0] add/s:R08<-00000000_4ffffee0 000:RT 00000000_00000016 00012e28 * [cond=e:updt=0] sw 000:RT 00000000_00000016 00012e28 * [cond=e:updt=0] sw :A=4ffffebc,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000:D1W miss (l1bank=2 A=4ffffebc rq_push=0 pushA=00000000) D1W ENQUEUED stat=2 A=4ffffebc STBF=00000000_00000000_00000000_00000000 【途中省略】 000:RT 00000000_00000cfe 00016554 * [cond=e:updt=0] add/s:R15<-00000000_00000000 000:RT 00000000_00000cff 00016558 * [cond=e:updt=0] bne 000:RT 00000000_00000cff 00016558 RESTARTING 000:RT 00000000_00000d00 0001655c * [cond=e:updt=0] sw 000:RT 00000000_00000d00 0001655c * [cond=e:updt=0] sw :A=4ffffe5c,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000:l1bank=1,index=7,way=0,A=4ffffe5c,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000,d=1,s=0 D1W COMPLETED:d1w L2 includes l2bank=1 index=63 w=0 A=4ffffe5c 000:RT 00000000_00000d01 00016560 * [cond=e:updt=0] add/s:R15<-00000000_00000001 000:RT 00000000_00000d02 00016564 * [cond=e:updt=0] add/s:R20<-00000000_00020169 000:RT 00000000_00000d03 00016568 * [cond=e:updt=0] add/s:R15<-00000000_00000001 000:RT 00000000_00000d04 0001656c * [cond=e:updt=0] add/s:R19<-00000000_00000000 000:RT 00000000_00000d05 00016570 * [cond=e:updt=0] lw :R15<-00000000_00000001 000:RT 00000000_00000d06 00016574 * [cond=e:updt=0] add/s:R14<-00000000_00000001 000:RT 00000000_00000d07 00016578 * [cond=e:updt=0] add/s:R15<-00000000_00000000 000:RT 00000000_00000d08 0001657c * [cond=e:updt=0] sw 000:RT 00000000_00000d08 0001657c * [cond=e:updt=0] sw :A=4ffffeac,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000:l1bank=2,index=7,way=0,A=4ffffeac,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_00000000,d=1,s=0 D1W COMPLETED:d1w L2 includes l2bank=2 index=63 w=1 A=4ffffeac 000:RT 00000000_00000d09 00016580 * [cond=e:updt=0] lw :R15<-00000000_00000000 000:RT 00000000_00000d0a 00016584 * [cond=e:updt=0] bne 000:RT 00000000_00000d0a 00016584 RESTARTING 000:RT 00000000_00000d0b 00016588 * [cond=e:updt=0] add/s:R15<-00000000_00000000 000:RT 00000000_00000d0c 0001658c * [cond=e:updt=0] b 000:RT 00000000_00000d0d 000165bc * [cond=e:updt=0] add/s:R10<-00000000_00000000 000:RT 00000000_00000d0e 000165c0 * [cond=e:updt=0] lw :R01<-00000000_000105a8 000:RT 00000000_00000d0f 000165c4 * [cond=e:updt=0] lw :R08<-00000000_4ffffed0 000:RT 00000000_00000d10 000165c8 * [cond=e:updt=0] lw :R09<-00000000_00000000 000:RT 00000000_00000d11 000165cc * [cond=e:updt=0] lw :R18<-00000000_00000000 000:RT 00000000_00000d12 000165d0 * [cond=e:updt=0] lw :R19<-00000000_00000000 000:RT 00000000_00000d13 000165d4 * [cond=e:updt=0] lw :R20<-00000000_00000000 000:RT 00000000_00000d14 000165d8 * [cond=e:updt=0] lw :R21<-00000000_00000000 000:RT 00000000_00000d15 000165dc * [cond=e:updt=0] lw :R22<-00000000_00000000 000:RT 00000000_00000d16 000165e0 * [cond=e:updt=0] add/s:R02<-00000000_4ffffe80 000:RT 00000000_00000d17 000165e4 * [cond=e:updt=0] br 000:RT 00000000_00000d17 000165e4 RESTARTING 000:RT 00000000_00000d18 000105a8 * [cond=e:updt=0] add/s:R15<-00000000_00000000 000:RT 00000000_00000d19 000105ac * [cond=e:updt=0] beq 000:RT 00000000_00000d1a 000105b8 * [cond=e:updt=0] add/s:R15<-00000000_0000000a 000:RT 00000000_00000d1b 000105bc * [cond=e:updt=0] sw 000:RT 00000000_00000d1b 000105bc * [cond=e:updt=0] sw :A=4ffffeb0,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_0000000a:l1bank=2,index=7,way=0,A=4ffffeb0,M=00000000_00000000_00000000_ffffffff<-00000000_00000000_00000000_0000000a,d=1,s=0 D1W COMPLETED:d1w L2 includes l2bank=2 index=63 w=1 A=4ffffeb0 000:RT 00000000_00000d1c 000105c0 * [cond=e:updt=0] lw :R15<-00000000_0000000a 000:RT 00000000_00000d1d 000105c4 * [cond=e:updt=0] add/s:R10<-00000000_0000000a 000:RT 00000000_00000d1e 000105c8 * [cond=e:updt=0] lw :R01<-00000000_000105fc 000:RT 00000000_00000d1f 000105cc * [cond=e:updt=0] lw :R08<-00000000_4ffffef0 000:RT 00000000_00000d20 000105d0 * [cond=e:updt=0] add/s:R02<-00000000_4ffffed0 000:RT 00000000_00000d21 000105d4 * [cond=e:updt=0] br 000:RT 00000000_00000d21 000105d4 RESTARTING 000:RT 00000000_00000d22 000105fc * [cond=e:updt=0] add/s:R15<-00000000_0000000a 000:RT 00000000_00000d23 00010600 * [cond=e:updt=0] add/s:R10<-00000000_0000000a 000:RT 00000000_00000d24 00010604 * [cond=e:updt=0] lw :R01<-00000000_000101f0 000:RT 00000000_00000d25 00010608 * [cond=e:updt=0] lw :R08<-00000000_00000000 000:RT 00000000_00000d26 0001060c * [cond=e:updt=0] add/s:R02<-00000000_4ffffef0 000:RT 00000000_00000d27 00010610 * [cond=e:updt=0] br 000:RT 00000000_00000d27 00010610 RESTARTING 000:RT 00000000_00000d28 000101f0 * [cond=e:updt=0] lw :R01<-00000000_00010040 000:RT 00000000_00000d29 000101f4 * [cond=e:updt=0] add/s:R10<-00000000_00000000 000:RT 00000000_00000d2a 000101f8 * [cond=e:updt=0] add/s:R02<-00000000_4fffff00 000:RT 00000000_00000d2b 000101fc * [cond=e:updt=0] br 000:RT 00000000_00000d2b 000101fc RESTARTING 000:RT 00000000_00000d2c 00010040 * [cond=e:updt=0] add/s:R17<-00000000_00000011 ★_start.sに戻ってきました 000:RT 00000000_00000d2d 00010044 * [cond=e:updt=0] ecal ★exit()システムコールに到達して実行完了
最後に,画像処理プログラムを試しましょう."cd ~/proj-riscv32/sample/filter; ../../src/dsim/dsim -x filter-dsim -f 81.ppm 82.ppm"です.
© Copyrights Computing Architecture Lab. All Rights Reserved