TA 高田正法 mtakada@mtl.t.u-tokyo.ac.jp B10 CPUを作る 3日目 SPIMの改造 TA 高田正法 mtakada@mtl.t.u-tokyo.ac.jp
はじめに このスライドは以下の場所にあります http://www.mtl.t.u-tokyo.ac.jp/~mtakada/jikken_b10/
注意!! PCSpimディレクトリの下にあるファイルは、Windows用インターフェースに関するものなので、今回書き換える必要はありません srcディレクトリの下を見ましょう 以下のファイルは自動生成されるものなので、書き換えない方が無難です y.tab.c y.tab.h lex.yy.c
SPIMのソースを書き換える前に 命令の仕様を決めましょう
命令の仕様を決める前に MIPSの命令セット 以下ではこの単語を使います 全ての命令の長さは32bitです 命令の形式も数種類しかありません ニーモニック形式 add $t1, $t2, $t3 といった表記 バイナリ形式 014b4820 といった、命令がメモリに格納されるときの形式 アセンブル ニーモニック形式→バイナリ形式の変換 逆アセンブル バイナリ形式→ニーモニック形式の変換
命令のバイナリ形式 I型(Immediate型 lw, bne, addiなど) J型(Jump型 jaなど) 固定部分 パラメータ部分 I型(Immediate型 lw, bne, addiなど) J型(Jump型 jaなど) R型(Register型 add, mulなど) 31 26 25 21 20 16 15 0 命令コード Reg(src) Reg(tgt) 即値(アドレス含) 31 26 25 0 命令コード ジャンプ先アドレス 31 26 25 21 20 16 15 11 10 6 5 0 命令コード Reg(src) Reg(tgt) Reg(dst) 新命令もどれかの形式に合わせるとラク
ニーモニック-バイナリ対応の例 I型 R型 bne $t1, $t2, LABEL ($t1!=$t2→LABELへ) コンピュータの設計下巻Appendix参照 I型 bne $t1, $t2, LABEL ($t1!=$t2→LABELへ) R型 add $t1, $t2, $t3 ($t1 = $t2 + $t3) SPIMマニュアル10ページ参照 000101 01001 01010 分岐先相対アドレス(16bit) 固定部分 パラメータ部分 000000 01010 01011 01001 00000 100000 2進->16進変換 014b4820
新命令の仕様 (例) 名前 命令形式 オペランド 表記例 機能 書式 sub R型 レジスタ3つ sub $t2, $t0, $t1 2つ目のレジスタの値から3つ目のレジスタの値を減算し、結果を1つ目のレジスタへ格納する 書式 31 26 25 21 20 16 15 11 10 6 5 0 000000 reg番号2 reg番号3 reg番号1 00000 100010
新命令の仕様決定の流れ 命令の名前を決める 命令の形式を決める 命令のバイナリ表記を決める 他の命令と被らないように SPIMマニュアルまたはコンピュータの構成と設計下巻Appendix参照 命令の形式を決める I型、R型、J型のどれか? 命令のバイナリ表記を決める 7ページの”固定部分”について、他の命令と被らないように適当な値を決める コンピュータの構成と設計下巻 Appendix参照
SPIMのソースを書き換える前に SPIMに関する予備知識
書いたプログラムが動くまで C言語の場合 アセンブリ言語の場合 Cのソースファイル 実行ファイル(バイナリ) コンパイル 実行 アセンブラソースファイル 実行ファイル(バイナリ) アセンブル 実行 ニーモニック形式の命令が並んだもの
プログラムを動作させるための道具 ソースファイル ソースファイルをバイナリに変換するためのプログラム 実行する環境 C言語/アセンブリ言語その他 ソースファイルをバイナリに変換するためのプログラム コンパイラ/アセンブラ 実行する環境 PC実機 シミュレータ SPIMは両方の機能を持っている (アセンブラ+シミュレータ)
新命令の追加 命令の追加をするには以下の2つの作業が必要です アセンブラ部分への、新命令翻訳機能の追加 正しくバイナリへ変換できるようにする正確には、バイナリ→ニーモニック変換部分の対応も必要(画面に表示するため) シミュレータ部分への、新命令実行機能の追加 新命令が正しく解釈され実行されるように
ソースファイルの中身 srcディレクトリの中にある、”README”に簡単な説明があります 以下では、わかりにくいと思われる次のファイルについて、簡単に解説してみます op.h parser.y
SPIMのソースを書き換えましょう まずは共通部分
print_inst_internal関数、 op.h op.hは、SPIMがサポートする命令のテーブルになっています ここに新命令を追加しましょう 他のファイル内でincludeして使われています OP ("sub", Y_SUB_OP, R3_TYPE_INST, 0x00000022) 命令のバイナリ表記。 ただし、レジスタ番号や即値、 アドレスなどの部分は全て 0で置き換えたもの。 命令のタイプ。他の命令の 定義を見て、似たようなもの にしましょう。inst.cの、 print_inst_internal関数、 inst_encode関数、 inst_decode関数で 使われてます。 命令の名前 SPIMソース内での 識別名。Y_命令名_OP にするのが無難
SPIMのソースを書き換えましょう アセンブラ部分
SPIMのアセンブラ部分 spim_utils.c 及び、inst.cが中心 ファイルを開くと、spim_utils.c の read_assembly_file関数が呼ばれます while (!yyparse ()) ; が、アセンブラ部分のメインループです 内部では、各命令に対し、命令の形式に応じて r_type_inst関数や、i_type_inst関数など(inst.c内)が読み込んだソースファイルに書いてある順に呼ばれます
parser.yの書き換え(1) – parser.yとは 謎のスクリプト言語だと思ってください 命令解釈は、450行目付近から始まるASM_CODE:部分で行っています
parser.yの書き換え(2) – 簡単な説明 例 意味: BINARY_OP_I DEST_REG SRC1 SRC2 というパターンを見つけたら、r_type_inst関数を呼び出しなさい BINARY_OP_Iは、1360行目付近からはじまるBINARY_OP_I の右側に書いてある命令のいずれか | BINARY_OP_I DEST_REG SRC1 SRC2 { r_type_inst ($1.i, $2.i, $3.i, $4.i); }
parser.yの書き換え(3) – 作業内容(1) 命令を追加するには 前半の %token~部分に、新命令の記述を追加してください op.hと同じ識別名に 次のページへ続く (前略) %token Y_DIV_S_OP %token Y_HOGE_OP %token Y_HOGE2_OP %token Y_JALR_OP (後略)
parser.yの書き換え(4) – 作業内容(2) ASM_CODE: で始まる部分(450行目付近)に、右のような記述を追加 R型は上のように I型の場合は、下のように /* R型の例 */ | Y_HOGE_OP DEST_REG SRC1 SRC2 { r_type_inst ($1.i, $2.i, $3.i, $4.i); } /* I型の例 */ | Y_HOGE2_OP SRC1 SRC2 LABEL i_type_inst_free ($1.i, $3.i, $2.i, (imm_expr *)$4.p);
parser.yの書き換え(5) – 書き換え例 ASM_CODE: Y_HOGE_OP DEST_REG SRC1 SRC2 { r_type_inst ($1.i, $2.i, $3.i, $4.i); } | Y_HOGE2_OP SRC1 SRC2 LABEL i_type_inst_free ($1.i, $3.i, $2.i, (imm_expr *)$4.p); | LOAD_OP DEST_REG ADDRESS i_type_inst ($1.i == Y_LD_POP ? Y_LW_OP : $1.i, (以下略)
他にも… inst.c内に、アセンブラに相当する部分が存在します 数点書き換えなければならない点があります
SPIMのソースを書き換えましょう シミュレータ部分
シミュレータ部分の書き換え 実はほとんど書き換える必要はありません run.cや、mem.h、reg.hなどが中心になっています いかにも各命令について何かやってそうなところを探し出して、書き換えてやりましょう