シリーズ「コンピュータの CPU のしくみ」 第3回

回路が「命令をこなす」とは?

同じ拍では、できない

ここまでの 2 回を、短く振り返っておこう。

手順を配線ではなく、0 と 1 のパターンにしてメモリに並べた。1 つぶんの手順が命令だ。 番地のカウンタが番地を 1 つずつ数え、その番地の語を命令の置き場に運ぶ(第1回)。 命令は 8 桁で、上 4 桁が「何をするか」、下 4 桁が「どの番地か」。 上 4 桁をデコーダに通すと、16 本の線のうち 1 本だけが 1 になり、その先の入口が 1 つだけ開く(第2回)。

前回の終わりに、困ったことが 1 つ残った。

命令の置き場に「足す」が入ると、その瞬間から「加算器 → A の箱」の入口は開いている。 だから、次のクロックで A の箱に受け取らせればいい、と思いたくなる。

けれど、次のクロックは、命令の置き場が次の命令を受け取るクロックでもある。 そのときメモリから出ているのは、15 番地の数 5 ではなく、次の命令のパターン 0011 1101 だ。 同じクロックで受け取れば、A の箱は数ではなく、命令を足してしまう。

読むのとこなすのを、どう順番にすればいいか。

2 つの段階

答えは、名前のとおりだ。 読むのと、こなすのを、別々のクロックでやる。

クロックを 2 回で 1 組にする。

  • 1 回目は読む拍。命令の置き場が、メモリから命令を受け取る。番地のカウンタが 1 進む
  • 2 回目はこなす拍。開いている入口の先の箱が、受け取る

これを交互に繰り返す。

そのためには、いまが読む拍なのか、こなす拍なのかを、回路が覚えていなければならない。 覚えるのは 1 桁で足りる。0 なら読む拍、1 なら こなす拍。 この箱を、このシリーズでは拍の状態と呼ぶ。

拍の状態は、クロックのたびに 0 と 1 が入れ替わればいい。 フリップフロップの値の入口に、自分の出口の逆を入れておけば、そうなる。 『記憶のしくみ』を読んだ人は、第6回のカウンタのいちばん下の桁を思い出してほしい。1 の位は、クロックのたびに 0 と 1 を繰り返していた。拍の状態は、あの 1 桁だけを取り出したものだ。

いまどの段階にいるかを覚えておき、決まった順に段階を進めていく回路を、状態機械という。 拍の状態は、段階が 2 つしかない、いちばん小さな状態機械である。 『記憶のしくみ』を読んだ人は、最終回の自動販売機を思い出してほしい。あれは段階が 5 つあった。CPU の真ん中にも、同じ形の輪が回っている。

次に、クロックの線を 2 本に分ける。

  • 読む拍のクロック … クロック AND 「読む拍」
  • こなす拍のクロック … クロック AND 「こなす拍」

AND を 2 個置くだけだ。拍の状態が 0 のあいだは前の AND だけが、1 のあいだは後ろの AND だけが、クロックを通す。

あとは、箱ごとに、どちらのクロックをもらうかを決める。

箱受け取るクロック
番地のカウンタ・命令の置き場読む拍のクロック
A の箱こなす拍のクロック(入口が開いているときだけ)
5 桁目のランプ Cこなす拍のクロック(足すのときだけ)
メモリ(書き込む)こなす拍のクロック(書き込むのときだけ)

入口は、前回と同じだ。命令が届いた瞬間から開いている。 けれど、こなす拍のクロックが来るまで、その先の箱は何も受け取らない。 開いていることと、受け取ることを、分けたのだ。

ついでに、止まる命令もつないでしまおう。 前回、止まる(1111)では「クロックを止める」の入口が開く、と書いた。 クロックの線の根もとに AND を 1 個置き、もう一方の入力に「まだ止まっていない」をつなぐ。このシリーズではクロックの門と呼ぶ。 止まるをこなす拍で、「止まった」を覚える 1 桁が 1 になると、門が閉じる。 それからは、クロックを何度入れても、門より先には届かない。

番地の入口を、選ぶ

もう 1 つ、決めておくことがある。 メモリの番地の入口に、何をつなぐかだ。

読む拍で使う番地は、番地のカウンタだ。 こなす拍で使う番地は、命令の下 4 桁だ。「足す 15 番地」なら 15 番地の数を、加算器に渡したい。

メモリの番地の入口は 1 つしかない。 そこで、入口の手前に「通すか、0 にするか」を 2 組置く。

  • 読む拍なら、番地のカウンタの 4 本を通す
  • こなす拍なら、命令の下 4 桁を通す

2 組を OR で合流させて、番地の入口につなぐ。 どちらを通すかを決めるのは、拍の状態だ。

『記憶のしくみ』を読んだ人は、第8回の「入口を、選ぶ」を思い出してほしい。前回は A の箱の入口で、今回はメモリの番地の入口で、同じ選ぶ箱が働いている。

書き込むときも、拍が大事になる。 書き込みの合図は、こなす拍のクロックと AND してから、番地の線が 1 の語にだけ届ける。 番地の入口が切り替わるのは、クロックが 0 に落ちたあとだ。そのとき合図はもう出ていないので、番地が揺れても、ほかの語は書き換わらない。 『記憶のしくみ』を読んだ人は、第8回の「落ち着いてから、書く」を思い出してほしい。書き込みの合図をクロックと AND する、と書いたのが、ここで本当に使われている。

押して、確かめる

部品がそろった。 第1回の回路と前回の回路を 1 つにつなぎ、3 + 5 のプログラムを動かしてみよう。

命令をこなす シミュレータ

「クロックを入れる」を押すたびに、読む拍とこなす拍が入れ替わります

拍の状態読む拍クロックの門開番地のカウンタ0000 = 0命令の置き場0000 0000番地を選ぶ0000 = 0デコーダ0011000100101111メモリ(16 語)0001 1110メモリ → A の箱閉加算器 → A の箱閉A の箱0000 = 0加算器1110 = 14ランプ C0クロック0

太い線は束(番地と数は 4 本、語は 8 本)。緑の「開」が、いま開いている入口。クロックの門は、止まると「閉」になる

はじめは読む拍。メモリの番地の入口には、番地のカウンタ(0)がつながっている。クロックを入れると、0 番地の語が命令の置き場に届く。

拍ごとの記録

拍段階番地起きたこと
まだクロックを入れていない
番地中身読み方(人間のメモ)
0000(0)0001 1110持ってくる 14 番地
0001(1)0010 1111足す 15 番地
0010(2)0011 1101書き込む 13 番地
0011(3)1111 0000止まる
0100(4)0000 0000
0101(5)0000 0000
0110(6)0000 0000
0111(7)0000 0000
1000(8)0000 0000
1001(9)0000 0000
1010(10)0000 0000
1011(11)0000 0000
1100(12)0000 0000
1101(13)0000 0000数 0(答えを置く場所)
1110(14)0000数 3
1111(15)0000数 5

青い行は、読む拍で使う番地(番地のカウンタ)。琥珀色の行は、こなす拍で使う番地(命令の下 4 桁)。14・15 番地の下 4 桁は、押して替えられる(替えると、はじめからやり直す)

いちばん上の左が拍の状態、右がクロックの門とクロック。 その下の左の列が、第1回の 番地のカウンタ → 番地を選ぶ箱 → メモリ。 右の列が、前回の命令の置き場とデコーダ。 下半分が、こなす側の箱たちだ。前回の入口が 2 つ、A の箱、加算器、5 桁目のランプ C。 メモリの中身は、いちばん下の表にある。

はじめは読む拍。メモリの番地の入口には、番地のカウンタの 0000 がつながっている。

「クロックを入れる」を押してほしい。 クロックが 1 の瞬間、拍の状態の箱から先で緑に光るのは、番地のカウンタと命令の置き場へ行く線だけだ。 0 に落ちると、0 番地の命令 0001 1110(持ってくる 14 番地)が命令の置き場に届き、番地のカウンタは 1 に進む。 拍の状態は「こなす拍」に変わり、番地を選ぶ箱は、命令の下 4 桁の 14 を通している。 表の琥珀色の行も 14 番地だ。

もう一度押す。 今度光るのは、左端を下りていく、こなす拍のクロックの線だ。 「メモリ → A の箱」が開いていたので、14 番地の数 3 が A の箱に入る。 拍の状態は「読む拍」に戻り、番地の入口は番地のカウンタの 1 に戻る。

あとは、同じことの繰り返しだ。

3 回目で「足す 15 番地」を読み、4 回目で A の箱が 3 + 5 = 8 になる。 5 回目で「書き込む 13 番地」を読み、6 回目で、13 番地に 8 が書き込まれる。表の 13 番地を見てほしい。 7 回目で「止まる」を読み、8 回目で、クロックの門が「閉」になる。

13 番地に、3 + 5 の答え 8 が入った。

止まったあとも、クロックを入れてみてほしい。 クロックの丸は 1 になる。けれど、光るのは門までだ。 何度押しても、どの箱も、もう何も受け取らない。 第1回では、止まる命令を読んでも止まらなかった。今度は、本当に止まった。

この図の箱の中では、ゲートが 1876 個働いている。第1回より 172 個増えた。

1 命令、2 拍

いま見たことを、表にしておこう。

拍段階番地命令の置き場A の箱何が起きたか
1読む00001 111000 番地の命令を読む
2こなす140001 1110314 番地の 3 を A の箱へ
3読む10010 111131 番地の命令を読む
4こなす150010 111183 + 5 を A の箱へ
5読む20011 110182 番地の命令を読む
6こなす130011 11018A の箱の 8 を 13 番地へ
7読む31111 000083 番地の命令を読む
8こなす01111 00008クロックの門が閉じる

番地の列を、上から読んでほしい。 0、14、1、15、2、13、3。 読む拍では番地のカウンタの数が、こなす拍では命令の下 4 桁が並んでいる。 番地を選ぶ箱が、拍ごとに入口をつなぎ替えているのが分かる。

(8 拍目の番地 0 は、止まるの下 4 桁 0000 だ。止まるは番地を使わないので、0 番地から何が出てきても、誰も受け取らない)

4 つの命令に、8 拍。 1 つの命令は、読む拍とこなす拍の 2 拍でできている。

読む・見分ける・こなす

この回路は、何をしているのだろうか。

拍の状態が 0 と 1 を繰り返しているだけだ。 0 のときは、番地のカウンタが指す命令を読む。 1 のときは、届いた命令の入口が開いている箱に、受け取らせる。

それだけで、メモリに並べた 4 つの命令が、上から順に 1 つずつ実行された。 3 + 5 をするという手順は、どこにも配線されていない。 配線が決めているのは、「読む、こなす、読む、こなす」という繰り返しだけだ。

読む拍とこなす拍を交互に繰り返すだけで、メモリの中の手順が 1 つずつ実行されていく。

本物の CPU の説明では、この 3 つを英語で呼ぶことが多い。 読むのがフェッチ、見分けるのがデコード、こなすのが実行だ。 この繰り返しを、まとめて命令サイクルともいう。

そして、第1回から約束していた名前を、ここで出そう。 メモリから命令を読み、見分け、こなす。この繰り返しを回している回路を、CPU という。 central processing unit の頭文字で、日本語では中央処理装置だ。 シミュレータの図から、メモリを除いた部分が、このシリーズの CPU だ。

第1回の 4004 も、同じことをしていた。 インテルの資料によると、4004 は 1 語の命令を、750 kHz のクロックの 8 周期でこなす。 最初の 3 周期で番地を送り、次の 2 周期で命令を受け取り、最後の 3 周期で命令を解釈して、こなす。 1 命令にかかる時間は 10.8 マイクロ秒。このシリーズの 2 拍を、もっと細かく 8 つに刻んでいたわけだ。

拍は、いくらでも短くできるわけではない。 クロックを入れてから、番地が切り替わり、メモリから語が出て、加算器を通って、A の箱の入口に届くまで、ゲートを何段も通る。 この回路では、クロックを 0 に落としてから落ち着くまでに、いちばん長いところで 22 ステップかかる。 次のクロックは、それより後でなければならない。 『部品のしくみ』を読んだ人は、第6回の「遅れは、足し算になる」を思い出してほしい。1 拍の長さを決めているのは、あの遅れの足し算だ。

本物の CPU は、読むのとこなすのを少しずつ重ねるなど、もっと急ぐ工夫をしている。けれど、読んで、見分けて、こなす、という骨組みは変わらない。

数を、替える

シミュレータの表の、14 番地と 15 番地の下 4 桁は、押して替えられる。 たとえば 14 番地を 0111(7)、15 番地を 0010(2)にして、もう一度クロックを入れてみてほしい。 8 拍で止まり、13 番地には 9 が入る。

替えたのは、メモリの中の 2 つの数だけだ。 回路は、1 本も変えていない。 第1回で、「メモリの中身を替えれば、回路は 1 本も変えずに、手順が替わる」と書いた。それが、本当に動いた。

では、14 番地と 15 番地を両方 1001(9)にしたら、どうなるだろう。 9 + 9 = 18 は、4 桁には入らない。 13 番地には 18 から 16 を引いた 2(0010)が入り、5 桁目のランプ C が点く。 『計算のしくみ』を読んだ人は、第5回の桁あふれを思い出してほしい。ランプ C は、あふれた 1 桁を、捨てずに覚えている。

14・15 番地の数は 16 × 16 で 256 通りある。 そのすべてで、8 拍で止まり、13 番地に和の下 4 桁が、ランプ C に 5 桁目が入ることを確かめてある。

止まらなかったら

最後に、少し意地悪なことを考えてみよう。

3 番地の止まる命令が、なかったらどうなるだろう。

1111 0000 の代わりに、何も書いていない 0000 0000 が入っていたとする。 上 4 桁が 0000 の命令は、何もしない、だった。 7 拍目でそれを読み、8 拍目は、どの入口も開いていないので、何も起きない。 クロックの門は開いたままだ。

番地のカウンタは、4、5、6……と進む。 4 番地から 12 番地は、何も書いていない。何もしないを読んで、素通りする。 13 番地の 8、14 番地の 3、15 番地の 5 も、上 4 桁は 0000 だ。 数として置いたパターンも、命令として読めば、何もしないになる。

そして、15 番地の次は、0 番地に戻る。 また 3 を持ってきて、5 を足し、13 番地に 8 を書き込む。 それが終わると、また素通りして、また 0 番地へ。 CPU は、同じ 3 + 5 を、いつまでも繰り返す。

番地のカウンタは、1 つずつ進むことしかできない。 だから、手順は、いつもメモリの上から下へ、1 列に流れていく。 15 番地まで行けば 0 番地に戻るけれど、それは、たまたま 4 桁で数えているからだ。

途中から別の番地へ戻ったり、先へ飛ばしたりは、できない。 けれど、番地のカウンタが覚えているのも、ただの 4 桁の数だった。

手順の途中で、別の番地へ飛ぶことはできないか。

次回は、番地のカウンタに、1 を足した数ではなく、別の数を受け取らせてみる。

参考文献