スライド 1

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