クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

命令が一つずつ実行される仕組み(フェッチと実行)

このレッスンのあとで: 1つの命令について、フェッチ、デコード、実行の流れを追うことができる。

50分のレッスンコマンド9件コンパイラとアーキテクチャのラボ:gcc、flex、bison、RISC-V を1台の VM で

1秒に30億回の同じ手順

会社で配られるノートパソコンのプロセッサは、1秒に30億回ほどの手順を進めます。その1回1回は、どれも同じ小さな手順のくり返しです。

次の命令を読み、何をするのかを調べ、それを行い、次へ進む。1つの命令でこの手順を追えるようになれば、どんなプログラムでも追えます。

このレッスンのゴール

メモ

1つの命令について、フェッチ、デコード、実行の流れを追うことができる。

キーワード

ことばいみれい
プログラムカウンタ次の命令の番地を持つレジスタpc
命令レジスタ取り出したばかりの命令を置いておく場所
フェッチ次の命令をメモリから読むこと
デコード命令をフィールドに分けて、意味を調べること
実行命令が求める仕事を行うこと
オペコード命令の種類を表すフィールド

学生が入力するセルは、このプレビューでは空白です。

同じ手順をくり返す

プログラムカウンタ pc は、次の命令の番地を持っています。フェッチでは、その番地の語を命令レジスタへ読み、pc に4を足します。

デコードでは、語をフィールドに分けます。命令の種類はどれか、どのレジスタを使うか、を調べます。実行では仕事を行い、答えをレジスタへ書き戻します。そして、またフェッチに戻ります。

アーキテクチャのフォルダへ移る

コマンド
cd ~/arch

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

ラボの add.s をアセンブルする

コマンド
riscv64-linux-gnu-as add.s -o add.o

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

リンクする

コマンド
riscv64-linux-gnu-ld add.o -o add

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

エミュレータのフェッチとデコードを見る

コマンド
qemu-riscv64 -d in_asm ./add

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

エミュレータが取り出したもの

qemu はメモリから5つの語を取り出し、1つずつ解読しました。各行には、番地、16進数の語、解読した結果が並んでいます。

番地は 0x000100b0 から 0x000100c0 まで、4ずつ増えています。ここの命令はどれも4バイトだからです。li が消えていることにも注目しましょう。プロセッサが見るのは addi a0,zero,2 だけです。

デコードは語をフィールドに分ける

addi a0,zero,2 の語は 00200513 です。32ビットを5つのフィールドに分けます。下の7ビットがオペコードで、19 は「レジスタと即値の計算」を表します。

その上に、答えを入れる rd が5ビット、細かい種類が3ビット、入力の rs1 が5ビットあります。上の12ビットが即値です。a0 は x10、zero は x0 なので、この語は「x10 = x0 + 2」という意味です。

例: 00200513 のフィールド

ビットフィールド値意味
31 から 20imm000000000010(2)即値の2
19 から 15rs100000(0)入力は x0、zero
14 から 12funct3000足し算
11 から 7rd01010(10)答えは x10、a0 へ
6 から 0opcode0010011(19)レジスタと即値の計算

学生が入力するセルは、このプレビューでは空白です。

命令を32ビットで見る

コマンド
python3 -c "print(format(0x00200513, '032b'))"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

opcode、rd、rs1、即値を取り出す

コマンド
python3 -c "print(0x00200513 & 127, (0x00200513 >> 7) & 31, (0x00200513 >> 15) & 31, 0x00200513 >> 20)"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

実行のあと、pc は先へ進む

実行では、足し算、ロード、比較などの仕事を行います。ほとんどの命令は、pc をフェッチが進めた4つ先のままにします。条件が合った分岐だけは、pc に新しい番地を書きこみます。飛ぶとは、それだけのことです。

-d exec を付けると、qemu は実行した命令ごとに1行を出します。角かっこの中の2つ目の数が、RISC-V の pc です。

実行した命令ごとに1行

コマンド
qemu-riscv64 -one-insn-per-tb -d exec,nochain ./add

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

よくある間違い

注意

「プログラムカウンタは、実行した命令の数を数えている」と考える人が多くいます。何も数えていません。持っているのは番地、つまり次の命令がある場所です。ループの中では、同じ番地に何度も戻ります。

このマシン自身のプロセッサを1歩ずつ

gdb は、ふつうのプログラムを止めて、1命令ずつ進められます。手で打つと、gdb は入力を待ち続けます。-batch とコマンドの並びを渡すと、それを実行して終わるので、Run で使えます。

次の Run は main で止まり、その命令を表示し、stepi で1命令進めて、次の命令を表示します。Arm のプロセッサなら番地は4進みます。x86 では命令の長さが1から15バイトなので、進み方が変わります。

ラボの loop プログラムを作る

コマンド
gcc -O2 -o loop loop.c

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

main で止めて、1歩進める

コマンド
gdb -batch -iex 'set debuginfod enabled off' -ex 'break main' -ex run -ex 'x/i $pc' -ex stepi -ex 'x/i $pc' ./loop

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

さらに挑戦

命令のレッスンで作った times が、何個の命令を実行するかを、実行する前に計算してください。ループの前に3個、1周で3個を5周、ループのあとに2個です。

それから qemu-riscv64 -one-insn-per-tb -d exec,nochain ./times 2>&1 | wc -l を自分で打って比べます。5回ずつ出てくる番地は、どれでしょうか。

このラボとソースについて: コンパイラとアーキテクチャのラボ:gcc、flex、bison、RISC-V を1台の VM で

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。