B10 CPUを作る 1日目 解説 TA 高田正法 [email protected] 1 はじめに 下のページに目を通すようにしてください。 http://www.mtl.t.u- tokyo.ac.jp/~mtakada/jikken_b10/ このスライドも上記の場所にあります。 2 今日説明する内容 実験の目的、内容 この実験で扱う道具について SPIM MIPSアーキテクチャ 課題1~3を行うために必要な知識 レジスタ 命令 システムコール MIPS用プログラム記述の例 3 実験の目的 RISC型プロセッサの構造、動作を理解する 命令セットを理解する 命令をMIPSシミュレータ”SPIM”に追加すること によって、プロセッサの動作に関する理解を深め る 4 実験の内容 MIPSの命令セットを理解する アセンブラでプログラムを書く 課題1~3 プロセッサの動作を理解する 構成図を見て考える 課題4~5,8 シミュレータを用いて、命令追加を行う 課題6~7 シミュレータと実機の違いについて考える 課題8 5 SPIMとは? RISCプロセッサ MIPS R2000のシミュレータ 以下の機能をサポート アセンブリ言語からの直接読み込み プロセッサの状態表示 ステップ実行 簡単な入出力 6 MIPSとは? RISC型プロセッサの一種 機能が単純な命令のみを持ち、複雑な機能は命 令の組み合わせでサポートする メモリを扱う命令が、他の命令と分離されている のが特徴 32個の32bitレジスタを持つ レジスタ:計算をするための作業領域 7 メモリとレジスタの違い メモリ ○ ○ × × 大きい(2^32Byteなど) アクセスする領域(アドレス)も変数 演算命令で直接扱えない 遅い レジスタ ○ ○ × 演算命令で直接扱える 速い 小さい(32bitレジスタが32個) × アクセスする領域(レジスタ番号)は命令内で明示 8 レジスタの種類(1) – 汎用レジスタ $t0~$t9 ユーザー用のレジスタ サブルーチン(関数)を呼び出す際には、呼び出 す側で値を保存しておく必要がある $s0~$s7 ユーザー用のレジスタ 呼び出されたサブルーチン側で、中身を保存する 必要がある 9 レジスタの種類(2) – 関数呼び出し用 $a0~$a3 サブルーチン(関数)を呼び出す際に、引数をこれ に入れる $v0~$v1 サブルーチン(関数)からの戻り値をここに入れる $ra サブルーチン(関数)を呼び出した側の、プログラ ム番地を保存する 10 レジスタの種類(3) – 特殊なレジスタ $zero 常に0が入っているレジスタ $sp スタックの先頭のアドレスが格納されるレジスタ 11 レジスタの種類(4) – 使わないもの 使ってはいけないレジスタ $at 疑似命令を展開する時に使われるレジスタ ここでいう疑似命令: ソースファイルに書くことが可能であるが、 実際には複数の実在する命令に展開される命令のこと(例:blt) $k0~$k1 OS予約レジスタ 気にしなくて良いレジスタ $gp, $fp 12 プロセッサの命令の種類 メモリ←→レジスタのデータのやり取りをする命令 いわゆるload/store レジスタを用いて計算をする命令 算術演算、論理演算その他 次に実行する命令を変える命令 条件分岐命令 C言語のif, for, whileなどで使われる 無条件ジャンプ命令 C言語での関数呼び出しなどで使われる 13 1Word(32bit) load/storeの例 1Word(32bit) 1Byte1Byte1Byte1Byte1Byte1Byte1Byte1Byte x lw x+1 x+2 x+3 x+4…(番地) $t0, 4($t1) メモリの($t1 + 4)番地に書かれている値(32bit)を、$t0レ ジスタに格納する $t0 = *($t1 + 1); +1 なのは、+4番地=32bit向こう=1要素進んだところだから sw $s2, 0($t0) $s2の中身(32bit)を、メモリの($t0+0)番地に格納する *($t0) = $s2; 14 演算命令の例 addi $t0 or $s0 $t0, $t1, 1 = $t1 + 1; $s0, $s1, $s2 = $s1 | $s2; 15 制御命令の例 bne $t0, $t1, LABEL != $t1 ならば、次からは、LABEL番地から始 まる命令列を実行する if( $t0 != $t1 ) goto LABEL; $t0 jal LABEL $raに、jal命令の番地+4を格納し、次からは LABEL番地から始まる命令列を実行する サブルーチン(関数)呼び出しに使われる 16 syscallの使い方 SPIMでは、キーボードからの入力/画面への 出力を、syscallを使って行います $v0にシステムコール番号を入れることによっ て、数種類の機能を実現できます 詳しくは、SPIMマニュアルの8ページを参照 してください 17 システムコールの例 $t0(整数型)の中身を画面に表示 li move syscall # $v0 = 1; # $a0 = $t0; メモリの$t0番地から始まる文字列を表示 li move syscall $v0, 1 $a0, $t0 $v0, 4 $a0, $t0 # $v0 = 4; # $a0 = $t0; プログラムの実行を終了 li syscall $v0, 10 18 MIPS用プログラムの 記述例 19 具体例 このような関数を考えましょう numという配列が渡されたときに、先頭n要素の和を返す関数 int sum(int n, int num[]){ int sum = 0; int i; for( i = 0; i < n; i++ ){ sum += num[i]; } return sum; } 20 C言語からアセンブリ言語へ C言語の変数は、主に2種類 static変数 プログラムのどこからでも見ることのできる変数 コンパイル時に、静的にメモリに割り当てられる local変数 同じ関数内でのみ参照可能 実際には以下のいずれかに割り当てます レジスタ ←ほとんどこちらだけで事足ります メモリのスタック領域(※坂井先生の資料8ページ参照) 21 ソースファイルの書式 .data #お約束 ここに、static変数に対応する部分を ”.word”等の疑似命令を使って書く .text .globl main main: #お約束 #お約束 #お約束 ここからプログラムを書く # “#”から行末まではコメントになります 22 for文の展開 for( i = 0; i < n; i++)をアセンブリ言語で書きましょう nは、$a0に入っていると仮定 iは$t0に割り当てましょう li $t0, 0 loop_start: 何か処理… addi $t0, $t0, 1 bne $t0, $a0, loop_start # i = 0; # i++ # if( i != n ) goto loop_start; 23 メモリからの読み出し $a1に、先頭の要素のアドレスが入っていると仮定しましょう $t1に、現在の要素のアドレスを入れましょう $t2に、各要素の値を一時的に格納しましょう $v0に、合計の値を格納しましょう li $t0, 0 # i =0; li $v0, 0 # sum = 0; move $t1, $a1 # $t1 = $a1; loop_start: lw $t2, 0($t1) # $t2 = *($t1); add $v0, $v0, $t2 # $v0 = $v0 + $t2; addi $t1, $t1, 4 # 次の要素は4番地(32bit)先 addi $t0, $t0, 1 # i++; bne $t0, $a0, loop_start # if( I != n ) goto loop_start; 24 関数ができました sum: li li move loop_start: lw add addi addi bne jr $t0, 0 $v0, 0 $t1, $a1 # i =0; # sum = 0; # $t1 = $a1; $t2, 0($t1) $v0, $v0, $t2 $t1, $t1, 4 $t0, $t0, 1 $t0, $a0, loop_start $ra # $t2 = *($t1); # $v0 = $v0 + $t2; # 次の要素は4番地(32bit)先 # i++; # if( i != n ) goto loop_start; # 関数呼び出し元へ 25 この関数の仕様 配列に入っている各要素の和を計算する 要素数 $a1: 先頭のアドレス $v0: 計算結果 $a0: 26 呼び出し元を書きましょう こんなプログラムだとしましょう int numOfElements=4; int elements[] = {1, 2, 3, 4}; // static変数 // static変数 void main(){ int s = sum( numOfElements, elements); printf( “%d”, s); } 27 呼び出し元 main: lw la jal $a0, numOfElements $a1, elements sum # $a0 = numOfElements; # $a1 = &(elements[0]); # $v0 = sum($a0, $a1); move li syscall $a0, $v0 $v0, 1 # $a0 = s; # # prinf(“%d”, s); li syscall $v0, 10 # exit(); 28 static変数部分 今回は、numOfElementsとelementsがstatic変数 numOfElements: elements: .data .word 4 # int numOfElements=4; .word 1, 2, 3, 4 # int elements[]={1, 2, 3, 4}; これで、numOfElementsが”4”の入ったアドレ ス、elementsが1, 2, 3, 4が入ったメモリの先 頭のアドレスを指すようになります 29 完了 http://www.mtl.t.utokyo.ac.jp/~mtakada/jikken_b10/ にある sum.sがこのプログラムの完成品です SPIMで読み込んで、実行してみましょう .word部分の値を変えて、結果が変わることを確 認してみましょう 30 駆け足になりましたが 各内容についての詳細は以下を参照してください レジスタの一覧 SPIMマニュアル 10ページ 命令の一覧 SPIMマニュアル 13ページ~ システムコールの一覧 SPIMマニュアル 8ページ~ .data部分に書くことのできるもの SPIMマニュアル 7ページ~ 31
© Copyright 2024 ExpyDoc