ソフトウェア目線のCPU

CPUを構成する各要素

まず、パソコンの全体像は以下の通り。 この図の内容を理解さえすれば、パソコンが何をする機械なのか概ねわかるはず。

参考 : https://yizhe87.medium.com/from-nand-to-tetris-nand2tetris-project-5-7846812a8dae

  • メモリ
    • 入力
      • writeM(1bit) メモリの値を書き込むかどうかを判定する。1なら書き込む
      • adressM(15bit = 32k) メモリの値を「どこに」書き込むかを指定する。
      • outM(16bit) メモリに「どんな値を」書き込むかのデータ。
    • メモリからの出力 : メモリからの出力データは、基本常に一定。ただし、writeM が1に設定されたときは、outM の値が次の時間ステップから出力され続ける。
  • CPU
    • 入力
      • inM : メモリから出力されるああ値のこと。
      • instruction : どんな処理を実行するべきか?
    • 出力
      • writeM : メモリに値を書き込むかどうか
      • outM : 出力される計算結果
      • adressM : どこに書き込むか?
      • pc : 次に実行する処理が書いてある場所
  • ROM32K
    • 入力 : 次に実行する処理が書いてある場所
    • 出力 : 実行するコード

CPU

CPUがやっていることは、以下の図のとおり

プロセス一覧

  1. もし、入力のinstructionがA命令であれば、CPUは16ビットの命令をAレジスタにロードを行う
  2. もし、入力のinstructionがC命令であれば、
    1. CPUは指定された計算をALUで行う
    2. その計算結果を命令で指定されたレジスタ(A、D、M)に格納する。
      • 計算結果で、Mにデータが格納されれば、writeM出力は1に設定され、CPUのoutM には、ALUの出力が設定され、
      • 計算結果で、Mにデータが格納されていなければ、writeM出力は0に設定される。
  3. もし、reset入力が0の場合、
    • CPUはALU出力と現在の命令のジャンプビットを使用して、次にどの命令をフェッチするか決める。決める方法は以下の通り
      • 現在の命令に、ジャンプ命令が含まれていた場合、ジャンプする宛先は常にAのアドレスに格納されているため、Aアドレスの値にジャンプする
      • ジャンプ命令が特に含まれていない場合、プログラムカウンターをインクリメントし、次の行の値を取得しにいく
  4. もし、reset入力が1の場合、
    • CPUはpc(プログラムカウンター)を0にする

CPUの出力である outMとwriteMは組み合わせゲートで実装されるため、入力後瞬時に変化する。 しかし、別のCPU出力である adressMとpcは、クロックによって制御されるため、次の時間ステップのみで新しい値に更新される。

いずれにせよ、どのタイミングで出力されるにせよ、実際に値が評価されるのは、クロック周波数に合わされたタイミングである。

入出力

  • 入力
    • instruction : 実行する命令(A命令 or C命令)
    • inM : データのこと(通常は、Aアドレスに格納されている値)
    • reset : 現在のプログラムを最初からに戻すか、続けるか
  • 出力
    • outM : 命令のM出力
    • adressM : outMを書き込む宛先
    • writeM : outMにデータが書き込まれたか?
    • pc : 次の命令のアドレス

命令メモリ

命令メモリには、名前の通り、プログラムコードが格納されている。

  • 入力 : CPUのプログラムカウンターから出力された、命令がある場所(adress)が入力されている。
  • 出力 : プログラムの命令コード一行分が出力される

データメモリ

データメモリは名前の通り、データが格納される。 (このデータメモリは、プログラムが格納される命令メモリとは明確に分離されている)

from https://hackaday.io/project/185131-the-hack-computer-from-nand2tetris-on-breadboards/details

データメモリの実装例は、以下のとおり

  • RAM16K : 16000(16K)個のレジスタから構成されるRAMチップ。データが記憶される
  • Screen : 画面のメモリマップとして動作する
  • Keyboard : キーボードのメモリマップとして機能する。

内部

ここで、ALUがこれまでできなかったことを実は実現できるようになっている!

コンポーネントの役割について

1 1 1 a c1 c2 c3 c4 c5 c6 d1 d2 d3 j1 j2 j3
  • instruction[15:13] = 111
  • instruction[12] = a ビット
  • instruction[11:6] = ALU制御ビット
  • instruction[5:3] = destination(どこに結果を書き込むか)
  • instruction[2:0] = jump条件

左上のMUX16

このMUX16は、「A命令なら instruction の値を、C命令なら ALU 出力を A レジスタに入れる」

  • instructionがC命令でかつ、aビットが1の時に(instruction[15] AND instruction[12]) instructionを次の 「A registar」チップに流す。
  • そうでない場合は、ALUから出力される値を次の 「A registar」チップに流す。

ということをしている。

Aレジスタ(A register) = アドレスレジスタ

A命令のとき MUXの出力(=instruction)をAにロードし、C命令のときはAレジスタは通常更新されず、前回の値を保持(ただしdestビットがAを含む場合は別経路でロード)

ここでのcは、 NOT instruction[15](先頭ビットの否定)

  • A命令の時はNOT instruction[15]は1となり、Aレジスタにデータが格納される。
  • C命令の時はNOT instruction[15]は0となり、前回ALUから計算された結果が素通りされて次チップに渡される。
@21

この場合、CPUは「定数21をAレジスタに格納せよ」という意味。

このあとアドレスレジスタ

  • そのままadressMとして、データの格納場所として出力される
  • PCに入力され、次の実行場所として使われる。
  • もう一つのMux16に候補の一つとして引数として渡される。

Note

しかし、Aレジスタの正体は「メモリ(M)レジスタの場所(アドレス)」を表すと言うこと。 そして、MレジスタとAレジスタは別個のものではなく、同じものを指している。 - 場所を指すのがAレジスタ - Aレジスタが指している中身を指すのがMレジスタ

今はたまたま定数が入力されているが、Mレジスタが呼ばれるときは、Aレジスタの場所をMレジスタは見に行っている。

二個目のMUX16

これは、ALUが計算対象とする「値の出どころ」を切り替えるスイッチ。 ALUが「A」か「M(RAM[A])」のどちらを計算に使うか決定する

  • 上側入力: Aレジスタからの入力
  • 下側入力: inMメモリ(RAM)からの入力
  • 選択信号: instruction[12](C命令のaビット)

C命令のaビットは、Aレジスタの値を使うという意味。

Note:AレジスタとMレジスタはいつ同期されるのか?

Mレジスタ(= RAM[A])がAレジスタの指す場所を「見に行く」のは、 Aレジスタが更新された次のクロック周期。

PCチップ

「PCは命令のアドレスを指す」。

  • in 入力 次に設定したい命令アドレス
  • out 出力 現在の命令アドレス(ROMのアドレス入力として使われる)
  • c ジャンプ命令のとき1になり、inの値でPCを上書き。論理式としては(instruction[1] OR instruction[1] OR instruction[1])ジャンプ命令のいずれかのビットが1であれば、PCがAレジスタの値の値にセットされる。

まずは分岐処理

ALUではできなかった分岐処理が、CPUのレイヤーではできるようになった。 なぜできるようになったかと言えば、プログラムカウンタ の概念が導入されたからだ。

CPUにおける分岐処理は、マルチプレクサで分岐処理を行い、プログラムカウンタで次に実行する処理を変更している。

  • サンプルコード (ALUが演算した結果(D)が負(Negative)なら,Aレジスタの値にジャンプ)
D;JLT

page:https://minegishirei.hatenablog.com/entry/2025/04/05/201841