3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ブレッドボードで作る 8bit CPU のキットを組み立てました

Last updated at Posted at 2024-06-11

はじめに

Build an 8-bit computer from scratch
https://eater.net/8bit

の動画をみながら、専用のキット を組み立てて CPU の勉強をしました。

image.png

8bit.gif

動機

ソフトウェアのプログラミングのお仕事を25年くらいしています。直近15年くらいはいわゆる 組み込み系 が中心だったのですが、組み込み系の中ではかなり高レイヤーに位置する ユーザーインターフェース が専門で、OS とかコンパイラとかハードウェアは僕に取っては 動いていて当然のもの でした。

少し前に 基礎から学ぶ-組込みRust を読んで、Rust やマイコン自体やマイコンでのプログラミングに触れたことで、(僕からみた)低レイヤーに興味を持ち、色々勉強をはじめ、簡単な回路を設計し、カスタム基板を作成し、 朝の待ち合わせ用の簡易的な時計 を自分で作れるようになったりしていました。

その流れで、会社の同僚が紹介してくれたのが前述の Ben Eater の「8-bit computer」で、最初は YouTube の動画をひたすら見ていたのですが、見れば見るほど作りたくなってしまい、せっかくなので公式のストアからキットを買い(たった数日で届きました!)、自分で組み立てることにしました。

得たもの

  • CPU を構成する基礎的な電子部品や電子回路の知識
  • ロジックICや様々なIDの知識
  • 基本的な CPU の設計や構成
  • 簡単なアセンブラの知識

クロックモジュールの作成 - Clock module

555 タイマーでクロック信号を作る

555タイマー というタイマーICの 無安定モード を利用して、CPU のクロック信号を作っていきます。

555タイマーの内部構造の解説を聞きながら、無安定モードの回路をブレッドボード上に作成し、LED を点滅させることができました。

シングルクロックボタンを作る

ボタンの チャタリング 対策として、なんとまた555タイマーが登場します。

今回はボタンが押されたときに555タイマーの 単安定モード が機能するように回路を組み立てました。

トグルボタンを作る

プッシュ式もしくはスライド式のトグルスイッチを利用して、上記2つの信号を選択できるようにします。

今回も、このスイッチのチャタリング対策として555タイマーを利用します。
3回目は 両安定モード で、シンプルに555タイマーの中の SRラッチ を利用する形になります。

切り替え機能を作る

トグルボタンの出力と(反転したものを)自動、手動の出力と AND し、両者を OR して1つの出力にまとめます。将来のために、コンピューターを停止する HLT という入力も追加しておきます

元の動画はクロック信号のところにLEDをつないでいますが、それによってクロックの電圧が下がって困る場合には、以下のように余ってるInverterを2回経由するといいかもしれません。

電子部品の基礎知識 - Background

本格的にブレッドボードで CPU を作りはじめる前に、基本的な部品のおさらいをしました。

半導体とは

共有結合のシリコン結晶にリン(P)を混ぜたりホウ素(B)を混ぜたりして結晶中を価電子が動くようになって、両者を組み合わせるとダイオードが完成します。

トランジスタとは

NPN の形で半導体を作ると、トランジスタになります。

ロジックゲート

トランジスタとスイッチを組み合わせて、以下の回路を作ってみました。

SR ラッチ

トランジスタでロジックゲートを作ったので、モジュール化されたロジックICで次のステップに進みます。

D ラッチ

Set/Reset を、Data/Enable にしてみました。

D フリップフロップ

Enable のところを RC回路 にして、クロックの立ち上がりの一瞬だけデータを取り込むようにしました。

レジスター - Registers

バスアーキテクチャ

バスと呼ばれる8本の線を複数のモジュールに接続し、モジュール間でデータの共有を行うアーキテクチャで CPU を作ることを学びました。

1つのモジュールがバスにデータを共有し、他のモジュールがそこからデータを読むのですが、ちょっとタイミング関係が難しそうです。

トライステートロジック

High/Low の他に、接続していないという状態を持てることで、必要のないときはバスには関わらないようにできることを学びました。

1bit レジスター

D フリップフロップ(74LS74)を利用して1ビットを任意のタイミングでクロックに合わせて保持するレジスター回路の作り方を学びました。

74LS74 他のICがキットに含まれていなかったため、ここの実践はスキップしました。

8bit レジスター

74LS173 という4bitのDレジスターICを2個利用して、8bitのレジスターを作成しました。74LS173には出力にトライステートの機能が用意されていますが、レジスターの中身をLEDで可視化するためにそれは利用せず、別途 74LS245 を使うことにしました。

レジスターの動作確認

レジスターモジュールを複数個組み立て、バスを介して8bitデータのやりとりができるようになりました。

演算装置 - ALU

2進数の足し算をロジックゲートで行う方法

XOR で 1bit の足し算ができて、繰り上がりは AND で計算できる(半加算器)。
そこに XOR, AND, OR を追加すると、下からの繰り上がりを考慮した計算もできる(全加算器)。

負の数の表現

最上位bitを±として使ってみて、1の補数を試して、2の補数にたどりつきます。

演算装置の設計

ALU にAレジスターとBレジスターを直接つないで、加算を行う設計にします。
直前に足し算の回路を自作したので、ここでは4bitの足し算を行う 74LS283 を採用し、2個で8bitの計算ができるようにしました。

減算が少々難しいのですが、

  1. 2の補数を生成する
  2. 加算する
    という仕組みで解決します。

2の補数は、ビット反転をして1を足すので、まず XOR でビット反転に対応します。

演算フラグ 入力 XOR 出力
足し算(0) 0 0(そのまま)
足し算(0) 1 1(そのまま)
引き算(1) 0 1(反転)
引き算(1) 1 0(反転)

1を足すのは、全加算器のキャリー入力を有効にすることで対応します。

とてもスマートに解決することができました。

演算装置の実装

上記を頑張って組み立てます。

演算装置の修正

1つ前の実装にバグがあったようなので、原因を調査して修正をしました。

低レイヤーのデバッグは難易度が高いですね。

次のステップで、実際に自分の回路にもバグが見つかり同じように修正をすることになりました。

演算装置のテスト

A = A + B の計算を演算装置で繰り返すことでテストをしました。

RAM - Random access memory (RAM) module

RAM の概要

8bit の読み書きができる回路の配列で、インデックスをアドレスと呼び、2進数のアドレスから特定のアドレスを表す信号に変換して特定の場所の記憶装置を有効化しているようです。

レジスタをたくさん並べてもいいのですが、様々な理由で、トランジスタとコンデンサを組み合わせたもので記憶をし、情報が消えないように定期的にコンデンサの充電をする DRAM という方式が一般的なようです。

メモリを自作するのは今回は避けて、 74189 という、4bit × 16 = 64bit のメモリICを採用します。
8bit 必要なので、2個使います。

全体で16バイトのRAMになります。

RAM の実装

74189 の出力が反転されているので、74LS04 を2個使って信号を元に戻します。

また、バスとの入出力にまたトライステートバッファ(74LS245)を使います。

RAM のアドレスレジスタ

バスからメモリのアドレス(4bit)を取得するための仕組みをAレジスタと同じようにDレジスタIC(74LS173)を使って実装します。

メモリアドレスはバスへの出力はしないため、トライステートバッファは必要ありません。

それから、RAM は手動でも操作するので(プログラミングモード)、DIPスイッチでも操作できるようにします。

切り替えは、74LS157というICで行うことにしました。

RAM のデータ操作

メモリのアドレスと同じスイッチで手動での書き込みに対応します。
回路もほぼ同じですが、コンピューターの動作時の書き込みはクロック信号と同期して動作する必要があるので NAND を利用して一工夫しています。

RAM の動作確認

RAM の出力を動画と同じように下位3bitでしか行ってなかったのですが、5bit目と6bit目が入れ替わってしまっていて、私も苦労して原因を特定して修正しました。

また、動画の最後で、メモリの書き込み用の信号を RC 回路に変更してクロックの立ち上がりのエッジのタイミングで書き込むように変更しています。

プログラムカウンタ - Program counter

JK flip-flop

SRラッチを拡張して、セット/リセットが両方1のときに対応した回路を作成しました。

レースコンディションの調査

しかし、意図したようには動作しませんでした。

Master-slave JK flip-flop

SRラッチを2段にして、実用的なフリップフロップを作りました。
富豪的な解決方法ですね。

そして、一旦自分で作ったら、モジュール化されたIC(74LS76)を使うことにするようです。

バイナリカウンター

74LS76 を利用すると、クロック信号の倍の周期で切り替わる信号が作れます。
それを並べると、2進数のカウンターになります。

プログラムカウンターの設計

ジャンプやカウントアップの切り替え機能も実装されているモジュール化されたバイナリカウンターIC(74LS161)を採用します。

バスとのやりとりはここでも 74LS245 を利用します。

プログラムカウンターの実装

出力レジスタ - Output register

7セグデコーダー

ロジック回路で0〜Fを7セグの信号に変更してみました。無理。。。

EEPROM での実装

AT28C16 という 2K x 8bits の EEPROM を利用して、00~0F に、対応するセグメントのデータを入れます。

EEPROM の動作を学ぶために、データを読み書きする回路を作ってみます。

自分で工夫をして、書き込みデータも DIP スイッチでできて、7セグ表示にも対応してみました。

Arduino Nano で EEPROM に書き込みをする

Arduino Nano 33 BLE ヘッダー付き もしくは互換があるものを利用して、PCでプログラミングしたデータを簡単に EEPROM にかけるようにします。

IOピンの数の関係で、11bit のアドレスをそのままは扱わず、8bitのシフトレジスター(74HC595)を2個利用しています。

3桁の7セグ表示に対応する

555タイマーと、JK flip-flop のバイナリカウンタと 74LS139 デコーダーの組み合わせで4桁をそれぞれ高速に表示し、人間の目には残像で4桁とも常に表示されているように見えるという魔法が登場しました。

バスで各モジュールを結合する - Bringing it all together

出力レジスタがバスからデータを読むために 74LS273 を採用しました。

また、取り込みのコントロールラインとクロックを AND したものを取り込み信号として利用しています。

お手本はバスの8本を中央に並べる形になっていますが、少し工夫をして、CPU 全体で利用する以下のものも並べるようにしました。

  • クロック
  • ↑を反転したもの
  • リセット
  • ↑を反転したもの

コントロールシグナルの概要

各モジュールに存在するコントロール線(I/O など)を一ヶ所に集めて操作しやすくします。

CPU の制御回路 - CPU control logic

命令セット

8bit のうち、上位4bitを命令コード、下位4bitを引数として命令体系を構築します。

アドレス 命令/データ バイナリデータ
0000 LDA 14 0001 1110
0001 ADD 15 0010 1111
0002 OUT 1110 0000
...
1110 28 00011100
1111 14 00001110

インストラクションカウンター

各CPU命令のマイクロインストラクションを逐次実行するためのカウンターを作成します。

バイナリカウンタ(74LS161)を利用し、クロックをカウントアップするのですが、処理のタイミングを考慮し、クロックを反転してソースとして利用します。

ついでにデコーダー(74LS138)で、各ステップを信号として独立して扱えるようにします。

カウンタ自体は16まで数えられますが、マイクロインストラクションの最大は5なので、
0から4まで数えたらカウンタICのリセットに信号を送るようにします。

インストラクションの実装

EEPROM を利用して、インストラクションカウンターと命令コードをアドレスにとり、コントロール信号をデータとして出力するようにします。
コントロールの信号が15個あるため、EEPROM を2つ利用します。
データピンを個々のコントロール信号線とつないで、自動でマイクロインストラクションが実行されるようにします。
EEPROM の中身は手動で書き込みました。

リセット回路

リセットボタンを作成し、反転した信号も作成し、各モジュールのリセット機能と接続しました。

インストラクションカウンターのリセット信号とも連携して動作するようになっています。

後半は全体の電源の話をしていました。

Ben Eater おすすめのブレッドボード BB830 は、mouser から購入可能 です。でも透明のが届くかも。

Arduino で EEPROM を書き込む

プログラミングでマイクロコードが管理できるようになり、インストラクションを簡単に増やせるようになりました。

CPU 命令の追加

以下の命令を追加しました

  • SUB
  • STA
  • LDI
  • JMP

チューリング完全にしよう

コンピューターとは、についてのお勉強をしました。

CPU のフラグを管理するレジスタ

Carry フラグと Zero フラグを Dレジスタに保持するようにし、保持用のコントロール信号も追加しました。またマイクロインストラクションで利用できるよう、EEPROM にフラグを接続しました。

条件ジャンプ

フラグを考慮した形でマイクロインストラクションを書き換え、JC, JZ が利用できるようになりました!

完成

以上で Ben Eater の 8bit CPU のキットが完成しました。

試行錯誤

フィボナッチ数列の計算を効率よく行おう

11バイトで計算ができるのも十分すごいのですが、上記のコードを書いていたら、
「あれ?SUMレジスタの値を、AレジスタとBレジスタに交互に入れていけば計算できるのでは?」と閃きました。
ということで、フィボナッチ数列の計算のための専用命令を2つ追加し、わずか3バイトで計算ができるようになりました。

最大公約数の計算

16バイトでなんとかなるのすごい。

キット組み立てに利用した道具の紹介

(アマゾンのリンクはアフィリエイトになっています)

ケーブルの長さを測るやつ

ブレッドボードの電源ラインをつなぐやつ

抵抗とかコンデンサをブレッドボードにちょうど良く加工しやすくるすやつ

ロジックテスタ

ロジックテスタを作ってみよう!【MR-LOGIC-TESTER】

もっと早く買っておけばよかった。。。

オシロスコープ

ラジオペンチ

ツノダ(Tusnoda) King TTC 先曲りラジオペンチ 125mm RB-125

ニッパー

ホーザン(HOZAN) 精密ニッパー フルフラッシュカットタイプ 細銅線用 N-55

ケーブルストリッパー

エンジニア オートワイヤーストリッパー PAW-41

これは超便利です。

抵抗内蔵LED

抵抗つなぐのが面倒くさくて、大人買いして遠慮なく使いました。

ケース

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?