やまいしの製作記

作ったものを記録します

ほぼNANDだけで作る4bit自作CPU

はじめに

こんにちは。岩と申します。ほぼNANDだけで4bitCPUを作ってみたので紹介します。

なぜNANDなのか

NANDゲートには完全性があり、全ての組み合わせ論理回路はNANDゲートで作ることができます。CPUは組み合わせ論理回路の塊なので、NANDゲートをいっぱい用意すればCPUが作れるというわけです。また2入力NANDゲートの機能を持つCMOSロジックIC・74HC00は現在でも生産されており、DIP品もあり価格・入手性・製作性ともに優れています。というわけで、今回は74HC00縛りで自作CPUに挑戦することにしました。

コンセプト

コンセプトは簡単に作れてそれっぽく動くNAND-CPUです。なるべく簡単にするためbit数は4とし、アーキテクチャはTD4ベース、ROMをRAMとマイコンの組み合わせで代用する等の工夫 (妥協) をしました。またそれっぽく動くようにLEDをいっぱい繋げて、TD4アーキテクチャに少しだけ手を加えました。多機能で実用的なNAND-CPUは先駆者がいらっしゃったのでこういう方向になりました。(というのは言い訳で私に技術力と根気がなかっただけ...)

アーキテクチャ

TD4を参考にして図のような構成としました。TD4からの変更点は、レジスタ間演算が可能なようにABレジスタを分離した点と、減算命令とJNZ命令の追加、プログラムカウンタの (強引な) 拡張です。

命令一覧

  • MOV A,IM
  • MOV A,B
  • MOV B,IM
  • MOV B,A
  • OUT IM
  • OUT B
  • OUT A
  • IN A
  • ADD A,IM
  • ADD A,B
  • SUB A,IM
  • SUB A,B
  • JAS IM
  • JNZ IM
  • JNC IM
  • JMP IM

全16種です。4bitCPUとしては普通な命令セットですが、JAS IMというオレオレ独自規格な命令が追加されています。これはMレジスタにIMデータを格納する命令です。Mレジスタは各種ジャンプ命令実行時に読み込まれ、ジャンプ先アドレスの上位4bitとなります。JAS命令と各種ジャンプ命令を組み合わせることで、強引にプログラムカウンタを8bit化しています。どうしてこうなったかというのは2バイト命令に対応するのが面倒くさかったからです。

物理的仕様

全回路を11枚の秋月Bサイズ基板に分割し、2x20のロングピンソケットを左右に1個ずつ使って縦積みしました。各基板および回路の仕様は後述します。

各部の詳細

レジスタ

74HC00でエッジトリガD-FFを構成し、入力にロード信号で制御されるマルチプレクサを置いた構造を4bit分並列にしています。ロード信号がLのときはデータバスから信号を取り込んで記憶し、Hのときはフリップフロップ自身の出力を入力に取り込み保持します。A、B、M、OUTレジスタで同じ基板を使いまわせるようにジャンパパッドを設けて配線の一部を可変にしました。

プログラムカウンタ

基本的にレジスタと構造は同じですが、ロード信号がHのときは出力に+1した値を入力に取り込みカウントアップを行う点が異なります。8bit分あるので1枚の基板に収まらず2枚構成としました。

命令デコーダ

4bitの命令コードから各レジスタのロード信号5本、マルチプレクサ制御信号1本×2個、ALU制御信号2本を制御するデコーダです。気合で74HC00×9個に収めましたが、面倒くさくてカルノー図すら書いていないのでもっと減らせるのかもしれません。

マルチプレクサ

4bit2入力マルチプレクサ×2個を1つの基板に載せました。特に解説することはないです。

ALU

全加算器を4bit分繋げて、制御信号に応じて片方の入力を反転して+1することで減算機能を実現、さらに入力側にマルチプレクサを置いてAスルー、Bスルー、A+B、A-Bの4つの演算を可能にしました。また、分岐命令用にキャリーとゼロ出力を用意しました。使った74HC00は20個となりました。

フラグレジスタ

ALUのキャリー、ゼロ出力を記憶するD-FFです。TD4をベースとしたため1クロックごとにデータを取り込んでしまうので、JNC、JNZ命令はADD、SUB命令の直後に実行しないといけない制約がついています。

ROM、クロック発生、リセット、電源

ROMはRAMで代用し、電源起動時にだけマイコンのEEPROMからRAMに書き込む仕様としました。RAMにマイコンとトライステートバッファがくっついているので厳密にはこのCPUはNAND-onlyではありません。まあこれぐらいは許してください...。RAMはデジットで入手したHM6116、マイコンArduinoを焼いたATmega328Pです。クロックやリセットについてはTD4をほぼ踏襲し、変更点は外部クロック/リセット入力に対応した点くらいです。電源は今風にUSB-typeCの5V給電とし、電源と同じコネクタにUSBシリアル変換ICを載せてUARTでプログラムを流し込めるようにしました。ズルしてる感はありますが運用はとっても楽になりました。

アセンブラの自作

Pythonで適当に簡易アセンブラを作ってみました。GUITkinter、シリアル通信はpyserialを使いました。私はプログラミングに関しては完全素人なので要所要所でボロが出てしまっていますが、まあ見た目それっぽくなったしヨシとしましょう。これのせいで3日くらいPythonアンチになっていました。

このCPUができること

  • LEDちかちか
  • ラーメンタイマー
  • 4bitの加減算
  • フィボナッチ数の計算

  • (サウンドカード追加で) 256ステップ以内の簡単な音楽演奏

  • (足回りとモタドラ、フォトリフレクタ搭載で) ライントレーサとか?

あとがき

使ってみたらABレジスタだけでデータをやりくりするのはやはり厳しく、動作中にRAMにアクセスできるようにしとけばよかったと思いました。他にも何個か課題はありますがとりあえず見た目はそれっぽいしほぼNANDで動いているのでヨシとしましょう。 NAND-CPU、最近流行っている (?) らしいので、皆さんもNANDでCPUを作ってみてください。