前章で、トランジスタと論理ゲートが「計算」を実現することを見た。だが、あの回路には決定的な弱点がある。何も覚えられないのだ。
AND ゲートに 1 と 1 を入れれば 1 が出る。しかし入力を切った瞬間、出力は消える。ゲートは「今この瞬間の計算」しかできない。過去を保持できない回路に、コンピュータは作れない。
この章では、回路が「記憶」と「時間」を獲得する物語を追う。
記憶の正体 — 出力を入力に戻す
論理ゲートに記憶をさせる方法は、意外なほど単純だ。出力を自分の入力に戻す(フィードバック)。
2つのNORゲートの出力を、互いの入力に交差させて接続する。これがSRラッチ(Set-Reset Latch)、最も原始的な記憶素子だ。
┌──────────────┐
S ──┤NOR├──┬─────── Q
└──────┘ │
╲ ╱
╳ (出力を互いに交差フィードバック)
╱ ╲
┌──────┘ │
R ──┤NOR├──┴─────── Q̄
└──────────────┘
動作はこうだ:
- S(Set)に1 → Q が
1になる(セット) - R(Reset)に1 → Q が
0になる(リセット) - S も R も 0 → 直前の状態を保持し続ける ← これが記憶
入力を切っても、フィードバックループが状態を「循環」させ続ける。電気が回路をぐるぐる回ることで、1ビットの情報が保たれる。記憶とは、情報が回路の中を循環し続けている状態なのだ。
| S | R | Q(次の状態) | 意味 |
|---|---|---|---|
| 0 | 0 | Q(保持) | 記憶を維持 |
| 1 | 0 | 1 | セット |
| 0 | 1 | 0 | リセット |
| 1 | 1 | 禁止 | 未定義(使ってはいけない) |
クロック — 全員で歩調を合わせる号令
SRラッチは記憶できるが、問題がある。入力が変われば即座に出力が変わってしまう。何億個もの回路がバラバラのタイミングで反応したら、計算はカオスになる。
そこで登場するのがクロック信号(Clock)だ。一定周期で 0 と 1 を繰り返す、回路全体の「メトロノーム」である。
例えば 3.0 GHz のCPUなら:
1秒間に30億回、全回路が歩調を合わせて動く。厨房で例えるなら、クロックは「料理長の号令」だ。「せーの!」の合図でだけ全員が一斉に次の作業に移る。各自が勝手に動けば厨房は混乱するが、号令に合わせれば統率された動きになる。
クロックの「立ち上がり(0→1の瞬間)」でのみ状態を更新する仕組みをエッジトリガと呼び、これを実装したのがDフリップフロップだ。
┌─────────────┐
D ────┤D Q├──── 出力
│ │
CLK ──▷ │ (立ち上がり │
│ でのみ取込) │
└─────────────┘
- クロックの立ち上がりの瞬間、入力 D の値を取り込んで Q に出す
- それ以外の時間は、入力 D が変化しても Q は変わらない(前回取り込んだ値を保持)
「号令がかかった瞬間だけ、目の前の値を記憶し、次の号令まで守り続ける」。これがフリップフロップの本質だ。
レジスタ — フリップフロップを束ねる
1個のフリップフロップは1ビットしか覚えられない。8個並べれば8ビット、64個並べれば64ビットを同時に記憶できる。この束がレジスタ(Register)だ。
レジスタは、CPUが計算のために使う最速の記憶領域である。メインメモリ(RAM)より桁違いに速い。なぜなら、CPUコアの真横(同じシリコン上)にあり、クロック1周期でアクセスできるからだ。
記憶階層の速度差を体感してみよう:
| 記憶場所 | アクセス時間の目安 | 厨房の比喩 |
|---|---|---|
| レジスタ | ~0.3 ns(1クロック) | 利き手に持った包丁 |
| L1キャッシュ | ~1 ns | まな板の上の食材 |
| メインメモリ | ~100 ns | 冷蔵庫の中 |
| SSD | ~100,000 ns | 倉庫まで取りに行く |
レジスタとメモリでは300倍以上の速度差がある。だから、CPUは「今まさに使うデータ」をレジスタに載せて計算する。
Pythonでレジスタの動きを模す
Dフリップフロップの「クロックごとに値を取り込む」挙動を、シンプルにシミュレートしてみる。
class Register:
"""クロックの立ち上がりで入力を取り込む8ビットレジスタ"""
def __init__(self):
self.value = 0 # 保持している状態
self._next = 0 # 次に取り込む値
def set_input(self, d: int):
self._next = d & 0xFF # 8ビットにマスク
def clock_edge(self):
"""クロックの立ち上がり: このときだけ状態が更新される"""
self.value = self._next
reg = Register()
reg.set_input(42)
print(reg.value) # 0 ← まだ号令(クロック)が来ていないので変わらない
reg.clock_edge() # 号令!
print(reg.value) # 42 ← ここで初めて取り込まれる
入力を与えただけでは変わらず、clock_edge()(号令)が来て初めて値が確定する。この「タイミングの規律」こそ、順序回路の心臓だ。
組み合わせ回路 vs 順序回路
ここまでで、回路には2種類あることが分かった。
- 組み合わせ回路(Combinational):出力が「今の入力」だけで決まる(前章の論理ゲート、加算器)。記憶なし。
- 順序回路(Sequential):出力が「今の入力」+「過去の状態」で決まる(フリップフロップ、レジスタ)。記憶あり=時間の概念を持つ。
コンピュータが「プログラムを順に実行する」「変数に値を覚える」といった動作ができるのは、順序回路が状態を持つからだ。前章の計算能力に、この章の記憶能力が加わって、ようやく「機械」が動き出す。
まとめ — 「おまじない」の消し方
- 記憶=フィードバック:出力を入力に戻すことで、情報が回路内を循環し続ける。これがビットを保持する仕組み
- クロック=全体の号令:一定周期の信号で全回路の歩調を合わせ、カオスを防ぐ
- フリップフロップ=規律ある記憶:クロックの立ち上がりの瞬間だけ値を取り込み、次まで守る
- レジスタ=最速の記憶領域:フリップフロップの束で、CPUの計算の足場になる
「なぜCPUに”GHz”という単位があるのか」「なぜレジスタはメモリより速いのか」——その答えの下地が整った。
次章では、この計算(ALU)と記憶(レジスタ)を組み合わせ、命令を読んで実行する機械=CPUを、自分の手で組み立てていく。
「時間とは、すべてが一度に起こらないようにするための自然の仕組みである。コンピュータはこの原理を、一本のクロック信号として飼い慣らした。」