SimuSimple: SimpleScalar Simulator Version 0.3 Copyringt (c) 2001, Kenji KISE 1. Requirements Most of the development of SimuSimple has been done on the Intel Linux Machines. The recommended system for running SimuSimple: OS : Red Hat Linux 6.2 Processor : Intel Celeron C compiler: gcc version egcs-2.91.66 (egcs-1.1.2 release) OS : TurboLinux 6.0 Processor : AMD K6-2 C compiler: gcc version 2.95.2 19991024 (release) 2. Install and run You can build SimuSimple from the sources in C language. After you download a package of simusimple-0.3.tar.gz, simply type the following commands: $gunzip simusimple-0.3.tar.gz $tar xvf simusimple-0.3.tar $make If you execute SimuSimple with no argument, you will find a usage. $SimuSimple SimpleScalar Simulator Ver 0.3 by Kenji KISE, Jan 2001 Usage: SimuSimple [-option] executable [arguments] -d: debug mode(set ms.debug_flag). -s: print systemcall trace(set ms.syscall_flag). Execute a sample program. $SimuSimple ./Sample/print 3. License Commercial use prohibited. Welcome to use, distribute and improve this program. 4. No warranty There is no warranty for the program. 5. SimuSimple simulates: (1)SPECint95 099.go 124.m88ksim 126.gcc 129.compress 130.li 132.ijpeg 134.perl 147.vortex 6. To understand this simulator (in Japanese) SimuSimple は SimpleScalarアーキテクチャ(参考文献[1]) の実行コードを実行する機能レベルのシミュレータである。 メインのループを1回まわるたびに1命令を実行する。 より機能の高いものに、スーパースカラプロセッサやアウ トオブオーダ実行のプロセッサの状態をクロックを単位と して変更するシミュレータがあるが、SimuSimple にはその ような機能はない。 SimuSimpleはプロセッサの機能ではなく、できる限りシンプ ルなシミュレータの実現を目指して記述した。プロセッサの 学習や研究のスタートとしての利用に適している。 プログラムは次の7つのファイルからなる。これらの 中の getasm.c debug.c という2つのファイルはシミ ュレータの動作には関係がなく, -d オプションを追加 してシミュレータを起動した場合にのみ利用される。 define.h : 構造体や定数の定義 simu.c : メイン関数や、初期化のためのコード memory.c : メモリからのロード、ストアと命令の初期化 execute.c: それぞれの命令の動作記述 syscall.c: システムコールの実装 debug.c : デバッグの機能を利用するためのコード getasm.c : 命令からアセンブラ表現を得るためのコード バージョン0.3のコードにおける上の7つのファイルの 合計の行数は(たったの)1867行である。 また、シミュレーションの実行速度ではなく、記述理解の 容易さに重点をおいて記述した。C言語の知識があれば 数日で全体を把握できる(はずである)。 以下は、プログラムを理解するための補足である。 6.1 define.h define.hに多くを詰め込みすぎた感じがするが、プログラ ムで利用する定数、マクロ、構造体(共有体)の全てを define.h の中に詰め込んだ。プログラムに目を通す前に define.h を眺めるべきである。 define.h の最初(1節)は型の定義である。プロセッサ内 でデータが流れる所は typedef int DATA_TYPE という符号 付きの整数を利用する。 アドレスが流れる所は typedef int ADDR_TYPE という符号 なしの整数を利用する。 SimpleScalarアーキテクチャの命令は64ビット長なので、 命令のために INST_TYPE を定義している。 2節は定数の定義である。これはプログラム中で必要と なる定数の定義であり、目を通す必要はない。 3節はマクロの定義である。ここで定義される Imm , Ofs といった単語は参考文献[1]で登場する命令定義で利用 されているものである。 それ以外のマクロと区別するために参考文献[1]で現れる マクロは、最初の1文字だけを大文字とした。 4節は構造体と共用体の定義である。シミュレータで利用 するグローバル変数は次の3つしか存在しない。 struct architecture_state as; struct machine_setting ms; struct evaluate e; 特に、これら3つの構造体には注意を払う必要がある。 構造体architecturestate はプロセッサのアーキテクチャ ステートである。この中には、お決まりのプログラムカウ ンタや整数レジスタ、浮動小数点レジスタ等が含まれる。 メモリを除けば、構造体architecturestateの内容がプロセッ サの状態である。このため、as.r[7] = 1; といった記述が アーキテクチャステートへの変更であることが明確になる。 構造体machine_setting はシミュレータのプロセッサ構成を 指定するものである。バージョン0.3のコードでは追加され ていないが、スーパースカラプロセッサのシミュレータにな った場合には、サイクル当たりにフェッチする命令数といっ たパラメタが構造体machine_setting の中に記述されるで あろう。 構造体evaluate は上の2つの構造体に入らないグローバル な変数の集合である。これらには、シミュレータがコードを 走らせた後に表示する情報のための変数などが定義される。 5節は関数のプロトタイプ宣言である。これは、コンパイラ の文句を最小限にするための努力であり、これに目を通す 必要はない。 6.2 simu.c プログラムの始まりは simu.c の最後にある main() という 5行の関数である。 int main(int argc, char **argv, char **argp){ init(argc, argv, argp); main_loop(argc, argv); return 0; } 関数init はシミュレーション開始に必要な初期化のための 関数で、シミュレータの実行オプションの解析、メモリや レジスタ等の初期化、実行プログラムの読み込みなどをおこ なう。 関数main_loop には1つのwhileループがあり、このループ を回るたびに例外なく1命令が実行(シミュレート)される。 main_loop は C-d による強制終了や、終了を示す特別な命令 により終了される。 このため、main関数の最後の return 0; が処理されることはない点に注意してほしい。 return 0; の上にシミュレーション終了時のコードを書いて も無駄に終る。 (関数main の return 0; はコンパイラの文句を無くすために 入れているに過ぎない。) 関数init の中では、最初に3つのグローバル変数を0で初期 化する。次に、シミュレータへのオプションを解析する。 最後に、実行コードを実行プログラムの仮想アドレスにロード しメモリの内容を設定するために関数mem_init を呼ぶ。 関数mem_init は memory.c で定義されている。 関数init が終了すると、関数main_loop に進み1命令ごと 実行コードを処理していく。 6.3 memory.c 実行コードは31ビットの仮想アドレス空間を持つ。この仮想 アドレス空間を取り扱う関数と実行コードを仮想アドレス空間 にロードする関数が memory.c の中で定義されている。 32ビット(0x00000000から0xFFFFFFFFまで)の仮想アドレスを メモリに割り当てることは大変なので、仮想アドレスの中で 実行コードが利用する部分のみの必要に応じて割り当てる。 この割当の単位をブロックと呼ぶこととする。 シミュレータ上のブロックのサイズは64KBである。 全ての仮想アドレスを64KB単位でブロックに分割すると、合計 のブロックの数は 65536個になる。 最大で65536個のブロック において、実行コードが利用するアドレスのブロックに対応す るメモリが割り当てられることになる。 char *block_table[65536]; 利用中のブロックを表現するために、上の block_table を利用 する。利用中のブロック状態はアーキテクチャステートの一部 として構造体architecturestate で定義される。 block_table は65536個の配列で、関数mem_init の中で全て NULL に初期化される。 ある仮想アドレス addr が実行コードにより参照されると、 index = (addr >> 16) & 0x7fff; により block_table へのインデックスを計算する。 block_table[index] の内容が NULL の場合には、該当ブロック のメモリが割り当てていないとして、ブロックサイズのメモリを 割り当てる。 block_table[index] = (char *)callock(BLOCK_SIZE, 1); 仮想アドレスに値を書いたり、読んだりするために次の関数が 利用できる。 void load_memory(ADDR_TYPE, char *, int); void store_memory(ADDR_TYPE, char *, int); void load_byte(ADDR_TYPE, char*); void load_2byte(ADDR_TYPE, short *); void load_4byte(ADDR_TYPE, DATA_TYPE*); void load_8byte(ADDR_TYPE, ULL*); void store_byte(ADDR_TYPE, char); void store_2byte(ADDR_TYPE, short); void store_4byte(ADDR_TYPE, DATA_TYPE); void fetch_instruction(ADDR_TYPE, INST_TYPE *); load_memory は、指令した仮想アドレスから、指定したバイト数 を読み込みバッファに格納する。 store_memory は、指定したバイト数をバッファから指令した 仮想アドレスに書き込む。 fetch_instruction は指定した仮想アドレスから8バイト読み込 み INST_TYPE の変数に格納する。 それ以外は関数名になっているバイト数を読んだり、書いたりす る。 memory.c にあるもうひとつの関数が mem_init である。この関数 は block_table と幾つかの変数の初期化と、実行コードの仮想ア ドレスへの読み込みをおこなう。 実行コードは ecoff形式 となっているので、この実行コードを 解析して、実行コード中の命令やデータを仮想アドレスに格納す る。 この関数はシミュレータにいて最も複雑な所なので、仮想アドレ スを適切に設定すると理解して、進んだほうがいいかもしれない。 6.4 execute.c execute.c では、それぞれの命令の動作にしたがってアーキテク チャステートを更新する関数execute_instruction のみを定義 する。 処理する命令を解析して、命令の種類を判断して switch文で それぞれの命令の動作を記述している。 もし、命令セットが add命令 だけだった場合には 関数execute_instruction は下の様になる。 void execute_instruction(INST_TYPE *ir){ switch(ir->op & 0xff){ case __ADD: Rd = Rs + Rt; break; } } なお、 Rd, Rs, Rtなどは define.h においてマクロ定義されて いる。__ADD も define.h において定義されている。 #define __ADD 0x40 #define Rd (as.r[ir->low>>8 & 0xFF]) 参考までに、上のコードを cpp により処理すると下のコードと なる。 void execute_instruction(INST_TYPE *ir){ switch(ir->op & 0xff){ case 0x40 : (as.r[ir->low>>8 & 0xFF])=(as.r[ir->low>>24 & 0xFF]) + (as.r[ir->low>>16 & 0xFF]); break; } } 6.5 syscall.c SimpleScalarアーキテクチャには、システムコールを呼び出すた めの命令としてSYSCALL命令がある。この命令が処理された場合 には関数execute_syscall を呼びだして適切な処理をおこなう。 syscall.c には、この関数execute_syscall が記述されている。 6.6 debug.c SimuSimple を実行する際に -d オプションを付けることにより、 デバッグモードでシミュレータを起動できる。 このモードでは、1命令処理されるたびに関数debugmode が呼 ばれ、インタラクティブな実行コードの処理が可能となる。 例えばリターンを押すことで1命令処理を進めプロセッサ内の レジスタを表示する。 debug.c には関数debugmode と、それこで使われる幾つかの関数 が定義されている。 -d オプションを利用しない場合には debug.c getasb.c の2つの ファイルの関数が呼ばれることはない。 7 SimpleScalar Tool and Utility SimpleScalarのためのコンパイラ、バイナリユーティリティ、 ライブラリが用意されている。これらを利用することで、C言語 のソースファイルから SimpleScalarのための実行コードを生成 できる。 7.1 バイナリユーティリティ simpleutils.tar.gz 以下のコマンドによりインストールする。 $setenv IDIR your_install_path $cd $DIR $tar xvfz simpleutils.tar.gz $cd $IDIR/binutils-2.5.2 $./configure --host=i586-linux --target=sslittle-na-sstrix --with-gnu-ld --prefix=$IDIR $make $make install 7.2 コンパイラとライブラリ simpletools.tar.gz こちらは、コンパイル中に幾つかのエラーが発生する。 $unsetenv LD_LIBRARY_PATH $unsetenv TCL_LIBRARY $setenv IDIR your_install_path $cd $IDIR $tar xvfz simpletools.tar.gz $cd $IDIR/gcc-2.6.3 $./configure --host=i586-linux --target=sslittle-na-sstrix --with-gnu-as --with-gnu-ld --prefix=$IDIR $make LANGUAGES=c >>> Error !!! Eedit ccc.p $diff cccp.c~ cccp.c 194c194 < extern char *sys_errlist[]; --- > extern __const char *__const sys_errlist[]; $make LANGUAGES=c gcc -c -DCROSS_COMPILE -DIN_GCC -DPOSIX -g -I. -I. -I./config s dbout.c sdbout.c:57: syms.h: No such file or directory make: *** [sdbout.o] Error 1 Edit sdbout.c soloship :/home/kis/tst/ss/gcc-2.6.3> diff sdbout.c* 57c57 < #include --- > #include soloship :/home/kis/tst/ss/gcc-2.6.3> $make LANGUAGES=c gcc.c:172: conflicting types for `sys_errlist' /usr/include/stdio.h:553: previous declaration of `sys_errlist' make: *** [gcc.o] Error 1 Edit gcc.c soloship :/home/kis/tst/ss/gcc-2.6.3> diff gcc.c gcc.c~ 172c172 < extern __const char *__const sys_errlist[]; --- > extern char *sys_errlist[]; ./cp/g++.c:90: conflicting types for `sys_errlist' /usr/include/stdio.h:553: previous declaration of `sys_errlist' make: *** [g++-cross] Error 1 Edit ./cp/g++.c soloship :/home/kis/tst/ss/gcc-2.6.3> diff cp/g++.c* 90c90 < extern __const char *__const sys_errlist[]; --- > extern char *sys_errlist[]; $../simplesim-2.0/sim-fast ./enquire -f >! float.h-cross $make install $cd $IDIR/glibc-1.09 $./configure --prefix=$IDIR/sslittle-na-sstrix sslittle-na-sstrix Now you have the cross-compiler in $IDIR/bin. Add the $IDIR/bin in your search path. 8. Note SPEC95 binaries compilation flags GNU GCC 2.6.2 with flags "-O2 -unroll-loops". 9. Updates Version 0.3.1: load_nbyte , store_nbyte, as.f[32] Version 0.4: execute.c MUL, LSWなどの実装を変更。 get_hexなどの一部を変更。 Version 0.5: Added simulation option of -e to end specific TC. 10. Thanks I referred to a SimpleScalar Tool Set Version 2.0 and the patch for ss.h and ss.def to execute 132.ijpeg. Reference [1] The SimpleScalar Tool Set, Version 2.0 University of Wisconsin-Madison Computer Science Department Technical Report #1342, June, 1997.