2026-10-30
最近、低レイヤーに興味があり、『コンピュータシステムの理論と実装』(通称 nand2tetris)を読み進めています。 今回は第1章「Nandひとつから、すべてが始まる」で学んだことと、そこから興味が広がって調べた半導体の世界についてまとめてみました。
課題は nand2tetris の Web IDE で解いていて、ブラウザだけで回路を書いてテストまでできるので手軽に始められます。
Nandは Not + And を縮めたもので、「Andして、そのあとNotする」という意味です。つまりAndの結果をひっくり返したものになります。
| a | b | And | Nand |
|---|---|---|---|
| 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 0 |
両方が1のときだけ0になる、というシンプルな関数ですが、Nandだけでどんな論理回路でも作れるというのがこの章のポイントです。
第1章では、Nandを材料にして15個のチップ(関数)を作りました。下の表は、それぞれの材料・役割と、JavaScriptで書くとどうなるかを自分なりに整理したものです。
| 関数 | 材料 | すること | JS | できること(使う場所) |
|---|---|---|---|---|
| Nand | 最初からある | 両方1のときだけ0 | !(a && b) | すべての関数の材料 |
| Not | Nand | ひっくり返す | !a | −x を作る。ALUの nx, ny, no |
| And | Nand, Not | 両方1のときだけ1 | a && b | 足し算の繰り上がり(carry)。値を通す・止める |
| Or | Not, And | どちらか1つでも1なら1 | a || b | FullAdderで2つのcarryをまとめる |
| Xor | And, Not, Or | 違うときだけ1 | a !== b | 足し算の右の桁(sum) |
| Mux | Not, And, Or | sel で a か b を選ぶ | sel ? b : a | どのデータを使うか選ぶ |
| DMux | Not, And | sel で in を a か b に流す | sel ? {a:0, b:in} : {a:in, b:0} | どこにデータを送るか選ぶ |
| Not16 | Not × 16 | 16ビットをひっくり返す | ~x | ALUの nx, ny, no |
| And16 | And × 16 | 16ビットをビットごとにAnd | x & y | ALUの x&y |
| Or16 | Or × 16 | 16ビットをビットごとにOr | x | y | 16ビットのOr計算 |
| Mux16 | Mux × 16 | 16ビットの a か b を選ぶ | sel ? b : a | ALUで「0にする/しない」「反転する/しない」を選ぶ |
| Or8Way | Or × 7 | 8本のどれか1つでも1なら1 | bits.some(b => b) | ALUの zr(結果が0かどうか) |
| Mux4Way16 | Mux16 × 3 | 4つから1つ選ぶ(sel 2ビット) | [a, b, c, d][sel] | メモリから読み出す場所を選ぶ(3章) |
| Mux8Way16 | Mux4Way16 × 2, Mux16 | 8つから1つ選ぶ(sel 3ビット) | [a, …, h][sel] | メモリから読み出す場所を選ぶ(3章) |
| DMux4Way | DMux × 3 | 4つの出口のどれかに流す | メモリに書き込む場所を選ぶ(3章) | |
| DMux8Way | DMux, DMux4Way × 2 | 8つの出口のどれかに流す | メモリに書き込む場所を選ぶ(3章) |
普段書いている && や ? : が、実はこういう小さな部品の組み合わせでできていると思うと面白いです。また、ここで作った関数の多くは、後の章で作るALU(計算をする部品)やメモリでそのまま使われます。
課題では、これらのチップを HDL(Hardware Description Language) で書いていきます。HDLは、ハードウェアの設計図を文字で書くための言語です。
ここでいう設計図とは「何の部品を何個使って、どこをどうつなぐか」のことで、HDLの1行は「部品を1個置いて、線をつなぐ指示」になります。たとえば、Nandを2つ使ってAndを作るとこう書けます。
1行目のNandで a と b のNandを計算し、その出力 nandOut を2つ目のNandの両方の入力につなぐことで、結果をひっくり返しています(Nandの両方の入力に同じ値を入れるとNotになる)。
プログラミング言語と違うのは、配線図なので行の順番は関係ないという点です。上から順に実行されるのではなく、すべての部品が同時に存在していて、線でつながっているだけ、というイメージです。
HDLで書いたものが現実ではどうなるのかが気になったので、Tinkercad で実際に回路を組んでみました。
実際のハードウェアでは、Nandが実装されたICが最初の部品になります。今回使ったのは 74HC00 というICで、14本のピンがついていて中にNandが4つ入っています。
Nand1つには次の3本のピンがあります。
これが4つ分で12本、さらに電源用の2本を足して、合計14本のピンになっています。

74HC00と同じピン配置の7400チップです。(画像: Audrius Meskauskas , CC BY-SA 3.0 )
Notは、スイッチを1個のNandの入力1と入力2の両方につなぐだけで作れます。
ここでいう「つなぐ」は電気を通すということで、スイッチをONにすると入力1, 2の両方に電気が届くようにします。 両方の入力が1になるとNandの出力は0、スイッチがOFFなら出力は1になるので、入力をひっくり返すNotになります。

Andは、HDLで書いたものと同じようにNandを2つ使って作ります。つなぎ方は次の通りです。

スイッチを2つともONにしたときだけLEDが光ることを確認できました。HDLの1行が、ブレッドボード上の1本1本の配線に対応しているのが実感できます。
このハードウェア上の関数は、電気の流れそのものが計算になっているものです。そして、ソフトウェアが最後に頼る、唯一の「本当に計算する場所」になります。
JavaScriptの関数も、最終的にはこういう回路に電気が流れて計算されています。ソフトウェアは自分で計算しているのではなく、ハードウェアの中にある関数のどれを使うかを 0 と 1 で選んでいるだけなんだな、と感じました。
ここまで来ると、実際のCPUはどう作られているのかが気になってきたので、少し寄り道して調べてみました。
CPUにはNandが数百万個入っていて、1枚のシリコンの板の上に、トランジスタも配線も全部まとめて焼き付けて作られているそうです。
この動画 が分かりやすかったです。とんでもなく精密に作られていて驚きました。
Nandひとつからコンピュータの部品を組み立てていく第1章でしたが、そこから半導体のビジネスやNVIDIAの話まで興味が広がって、いろいろ面白いことを知ることができてすごく良かったです。 次の章以降も引き続き進めていこうと思います。