メモリとレジスタとスタック #2

はじめに

レジスタについて、前回はパタヘネ本を使って勉強しました。 x86-makanai.hatenablog.jp

今回は下記の本を読んでみようと思います。

コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方

コンピュータシステムの理論と実装 ―モダンなコンピュータの作り方

本題

仕事から帰ってきて、ご飯食べてまたーり索引からレジスタのページに飛んだら、前回よりも複雑な話になりました。
今回は、レジスタアーキテクチャの中でどのように機能するのか、ということを書こうと思っていたんですが、甘くなかったです。。。
下手すると、#5くらいまでこの本を読むことになるかもしれないので、もし読まれる方は、#5があがってからまとめて読むのが良いかもしれないですね。
では、読んでいきましょう。

 1章と2章で構築した論理演算や算術演算の回路はすべて、組み合わせ回路(combinational circuit)と呼ばれる。組み合わせ回路は、入力値の組み合わせだけによって、関数の値が決定する。このどちらかと言えば単純な回路は重要な処理(たとえば、ALUなど)をたくさん行うことができるが、状態を保つことができない。コンピュータは値を計算するだけでなく、その値を保存し呼び出すことができなければならない。そのため、時間が経過してもデータを記憶することのできる記憶素子を備える必要がある。この記憶素子は順序回路(sequential circuit)から構築することができる。

と、導入がこんな感じでした。この話は3章なので1章と2章は何なんだ?って思われる方は、ぜひ買ってみてください。見ての通り、文章が簡潔なので 非常に読みやすいです。
続いて、その記憶素子のアーキテクチャの背景について。

 「記憶する」という行為は本質的に時間に依存する行為である。つまり、「前に記憶したものを思い返す」という行為が記憶することの本質である。そのため、情報を記憶するための回路を構築するには、時間の経過を表す方法を考案しなければならない。

と、まあ言っていることはわかるけど、時間という概念的な話を計算機科学だとどう表現するのか、いまいちイメージがわきません。 ここからは、下記の流れで説明が進んでいきます。

クロック

著作権を考えても、引用しすぎるのははばかられますが、文章が簡潔で無駄がないため、すべて引用しちゃいます!

 ほとんどすべてのコンピュータでは、継続的に変化する信号をマスタクロックが送信することによって時間の経過を表現する。実際のハードウェアにおける実装はオシレーターに基づくのが一般的である。このオシレーターはふたつのフェーズ ー 0/1、low/high、tick/tockのようなラベル付けがされるーを絶え間なく行き来する。tickの始まりから次のtockの終わりまでに経過した時間を周期(cycle)と呼ぶ。このクロックの1周期がタイムユニット(単位時間)としてモデル化される。現在のクロックフェーズ(tickまたはtock)は2値信号によって表すことができる。ハードウェアの回路網を使って、この信号はプラットフォームの隅から隅まで、すべての順序回路に送られる。

クロックが時間経過の概念を表しているということは初耳でした。クロック数によって時間という幅を表現しているんですかね。それを正しい解釈として読み進めていきます。

フリップフロップ

 コンピュータで使われる順序回路の中で、最も基本となる回路はフリップフロップ(frip - flop)だる。このフリップフロップにはいくつか種類があるが、本書ではD型フリップフロップ(data flip - flop、DFF)と呼ばれるタイプを用いる。

え?フリップフロップって種類があるの??って思ってしまったので、wikiで調べました。
フリップフロップ - Wikipedia

では、続き。

 このD型フリップフロップ(以降、DFFと表記する)のインターフェースは1ビットのデータ入力と1ビットのデータ出力である。さらにDFFにはクロック入力があり、このクロック入力にはマスタクロックからの信号が絶えず送られる。データ入力とクロック入力が合わさることで、DFFは「時間に基づく振る舞い」が可能になる。この時間に基づく振る舞いは、out(t) = in(t - 1)という式で表される。ここで、inとoutはゲートの入力値と出力値を、tは現在のタイムユニットを表す。言い換えれば、DFFは単にひとつ前のタイムユニットの入力値を出力しているだけである。

レジスタ

さて、ここでやっとレジスタの話。

 レジスタとはデータを”格納”したり、”呼び出し”たりすることができる記憶装置である。レジスタは、伝統的なストレージの振る舞いであるout(t) = out(t - 1)を実現する。一方、DFFはひとつ前のストレージの振る舞いであるout(t) = in(t - 1)を実現する。

ここから、設計の話になりますが、0と1だけで世界を形作っている仕組みでもあるので、引用します。

 そのため、図3-1の左下のように、DFFの出力を単に入力に送信すれば、レジスタを実装することができるだろう。おそらく、時刻 t における出力は、時刻 t - 1 における値を出力すると思われる。よって、記憶装置に求められる最低限の機能はこれで達成できた、と思うかもしれない。しかし、それではうまくいかない。

図3-1って出てきていますので、図を描いて見ました。
上から順に、①フリップフロップ、②間違った設計、③1ビットレジスタとなります。 f:id:x86_makanai:20170525230701p:plainf:id:x86_makanai:20170525230712p:plainf:id:x86_makanai:20170525230718p:plain 面白いですね。

 図3-1の左下に示した図は正しい設計ではない。まず第一に、新しいデータの値をこの回路に読み込む方法が明らかではない。なぜなら、どのタイミングでinワイヤからデータを読み込み、どのタイミングでoutワイヤからのデータを読み込むのかということをDFFに指示する方法が存在しないからである。回路設計において一般的に言えるのは、内部ピンの入力数は1にしなければならない。つまり、ひとつのソースだけから入力データが送られるようにする必要がある。

 

 以上の考察から、図3-1の右下の図に示される解法ー正しい解法、そして素晴らしい解法-が導かれる。見てのとおり、入力の曖昧さを取り除くには、回路設計にマルチプレクサを導入するのが自然な方法である。さらに、マルチプレクサへの「選択ビット(select bit)」はレジスタ回路への「読み込みビット(load bit)」の役割を担うことができる。もしレジスタに新しい値を保持させたいならば、その新しい値を入力inに入れ、「読み込みビット」であるloadに1を設定すればよい。また、もし内部の値をレジスタに保持させたいならば、loadビットを0にすればよい。

マルチプレクサもよくわかんなかったのでwikiで調べてみました。
真偽値の表がわかりやすい!
マルチプレクサ - Wikipedia

 これまでのところ、1ビットを記憶する基本的な仕組みは達成できた。続いて、任意の幅のレジスタについて考える必要があるが、これは簡単に作ることができる。というのは、多ビットのレジスタは1ビットレジスタを必要な数だけそろえて、それらを配列上に並べて構築することができるからである(図3-2)。そのようなレジスタの設計では、(width)ー保持すべきビットの数ーをパラメータとして考えなければならない。このパラメータの値の候補としては、たとえば16、32、64などの数字が用いられる。そのような多ビットのレジスタの持つ値は、一般的にワード(word)と呼ばれる。

ということでした。なかなか難しい内容になりそうな雰囲気がプンプンしていますね。ただ、記憶・記録には時間という概念が必要であること。そして、クロック入力でその時間を表現することがよくわかりました。
このBitレジスタを配列上に持つことで、多ビットになる、と。いやー、よくわかった。プロセッサが絡んでないので、命令はないけれど、レジスタに数値(オペランド)はどのようにして格納され、保持されるのか、ということの理解は多少はできた気がします。
これ以上は、他の書籍に期待しますが、実際に作ってみないと真に理解はできない気もしますね。
思ったのですが、メモリやレジスタ、スタックについて調べていくと、CPUのことも理解を深めていく必要があると思うので、そちらも念頭に入れてこれから読んでいこうと思います。
最終的には、ハードウェアの動きをプログラムで表現できればいいなー、なんて思っています。CPUのコントローラがいて、レジスタのモデルがいて・・・。他にやっている人がいそうですが。
気長に考えます。

おわりに

では、今回は一旦ここまでとします。
長々と読んでいただきありがとうございました。
次はメモリ、カウンタ、時間についてです。
その後は、この書籍の名前とおり、実装の話になります。楽しみですね。