TD4
「CPUの創りかた」
1. はじめに
「CPUの創りかた」という本は、出版されてすぐ本屋で確認し、買うまでもないかとパスした.
大きな書店では,CPUアーキテクチャに関する書籍を見つけることができるが,内容は特定のCPUに関するもの(PIC,H8,ARM等)かアカデミックな内容のものが多く,最近はHDLで記述してシミュレーションするのが流行りなので,実際にハードウェアを製作する必要がない.
この本は,初心者向けに論理回路とか,論理回路の作り方から初めて4bitCPUの設計,74HCシリーズの標準ロジックICを使用した製作までを網羅している.
昔々CPUが出たころにはこのような記事を掲載している雑誌もあったが最近は見ることがなくなっていた.
装丁は,御覧の通り「萌え系」である,
読者層を考えるとこれも戦略なのだろうが,おぢさんはカバーをしないと電車の中ではちょっと読めない.
時々黒板の前でしゃべっているのだが. そこでCPUに興味を持っている聴講生がいたので,入門書としてこの本を紹介した.
書籍から得られるのは理論であり技能は実際に作業を体験することで習得しなければならなず,「理論」と「技能」を習得することで「技術」となる.
ということで「創ってみたら」と勧めたわけだが,勧めるるからには自分も創ってみなければならないだろう.
ということで実際に作ってみた.
2. 方針
はんだ付けの練習も兼ねてというなら本のとおりに作ればよいが,実際に作った例をみるとかなり配線量が多そうだ.
アーキテクチャとインストラクションは同じにして配線量を減らすように回路を変えてみることにした. また、インストラクションセットをよく見てみるとプログラム領域は32byteまで拡張できそうだ.
- ROM
DIP-SW16個 ⇒ 64k(2kx8)SRAMを使う.
DIP-SW 16個を6116に置き換えるので,基板一枚分くらい配線が減るし,プログラム領域も拡張できる.
(32byteしか使わないのに16kのSRAMを使うと「鶏を割くに焉んぞ牛刀を用いん」の感があるが6116も安くなったしまあいいか)
SRAMを使うと,プログラムの入力方法を考える必要があるが,LEDとSWを並べて入力するようにしよう.(懐かしい) - セレクタ
セレクタ(HC154) ⇒ 3-ステートのレジスタを使う
HC154×2を使わないので配線が減る.
オリジナルの実装は,分かりやすさに配慮してか1-2セレクタを使用している.
ここは,3-ステート出力のレジスタを使用し,選択されていないレジスタ出力をHi-Zにすることでセレクタは省略しよう. - A,Bレジスタ(A-Reg,B-Reg)
オリジナルの実装は,ICの種類を増やさないためかカウンタ(HC161)を使用している.
A,Bレジスタに使用する3-ステートのレジスタには入出力ともイネーブル端子を持つHC173を使用しよう.
HC173の出力はM,Nで制御されて M=L and N=L のときデータが出力されて,それ以外はHi-Zとなる.
また,入力はG1,G2で制御されて G0=L and G1=L のとき入力データがラッチされる.
つまりM,NとG1,G2にANDがあるのでゲートも減るかも. - 入力レジスタ(I-Reg),ゼロレジスタ(Z-Reg)
入力レジスタとゼロレジスタはオリジナルの実装は,直接セレクタ(HC145)につながっているのだが,セレクタを使用しないので,別にレジスタを用意しなければならない.
セレクタ(HC145)を2個減らしてレジスタ(HC137)が増えたのではICの数が変わらないので,I-RegとZ-Regを共用することを考える.
HC137にはクリア端子(CLR)があって CLR=H とすることで出力を全てLにすることができる.この機能を使って,Z-RegはI-RegのCLRをHとすることで実現しよう. - プログラム・カウンタ(PC)
オリジナルの実装では,PCはHC161×1だが,プログラム領域を32byteまで拡張するので,HC161×2とする.
拡張部分は1bitなのでD-FFでよさそうだがプリセット回路を考えるとICの数が減らないので結局HC161を使う. - インストラクション・デコーダー
全面作り変えだ.
- クロック・ステップ回路
クロック回路とステップ回路はヒステリシスインバータ(HC14)を使うとIC1個に収まりそうだ.
3. 製作
これが回路図(間違っていたので修正 2014/08/10)
ICは11個なので, サンハヤトのIC用ユニバーサル基板
ICB-96PU(7×3個実装可)に乗りそうだ.
(ぢつは見込みで基板を先に買っていたりする)
余裕があるので,出力にアドレッサブル・ラッチ(Addressable Latch, HC295)を付けておこう.これで出力が8bitまで増やせる.
配線は,0.32mmラッピング用ジュンフロン線を使用して表面に配線した.
この電線は耐熱性があるので,配線を間違って外した場合でも被服が溶けないのが良い.
4. 写真
- ソケットを載せて部品配置を検討
これくらいの規模だとあらかじめ配線プランを考えておいた方が良い.
- パスコン(10μF)列毎に1個
バイパス・コンデンサ(パスコン)は10uF(ケミコン)を列毎に,0.1uF(積層セラミック)をIC毎に付ける.回路図では省略されることがあるが必ず付ける.
- パスコン(0.1μF)IC毎に1個
- 完成
これくらいの配線量になると,間違いがあるので3回チェックした.
- 裏面
今回は表側で配線したので裏面はすっきりしている.白い線はプログラム入力回路に接続する配線.
- はんだ鏝
20年以上使っているはんだ鏝とはんだ, goot(大洋電機産業KK)のCS20を使っている.
- 表示,プログラム入力,電源
左から,出力ポート表示器,プログラム入力回路,ACアダプタ
5. プログラム
IN,OUTはインストラクション実行時にイミディエイト値(即値)の加算ができる. 分かりやすさに配慮してか,書籍では触れていない.
例えば,IN A は20h(0010 0000)だが,入力と同時に1を加算したい場合には,21h(0010 0001)とすればよい.
入力ポートの3bitがHならば分岐する処理を書籍どおりに書くと
IN A 0010 0000
ADD A,8 0000 1000
JC xxxx 1110 xxxx
とするところを
IN A,8 0010 1000
JC xxxx 1110 1000
と1byte節約できる.
プログラム領域が少ないTD4では,この1byteが貴重である.
プログラムの例
サンプルプログラムの条件ジャンプはJC(キャリーが有るときにジャンプする)を使っている。『CPUの創りかた』ではJNC(キャリーが無いときにジャンプする)なのだが、勘違いしたまま配線した。プログラムが動いた後に間違っていることに気が付いたのだが、まあいいかと直していない。(回路図は直しておいた)
なので、↓このサンプルプログラムはTD4では動かない。(^^;(2017/08/12追記)
(↓間違いを発見したので修正した 2014/08/01、2017/08/12)
; ・ナイトライダー
;
LED_ON = 8
LED_OFF = 8
START: ;
OUT LED_ON+0 ; LED_ON(0) 0000: 1010 1000 : A8
MOV B,LED_ON+1 ; B <- LED_ON(1) 0001: 0111 1001 : 79
LOOP1: ;
OUT B ; LED_ON(B) 0010: 1000 0000 : 80
OUT B,LED_OFF-1 ; LED_OFF(B-1) 0011: 1000 0111 : 87
ADD B,1 ; B++ 0100: 0101 0001 : 51
JC LEND1 ; IF B==15 GOTO LEND1 0101: 1110 0111 : E7
JMP LOOP1 ; GOTO LOOP1 0110: 1111 0010 : F2
LEND1: ;
MOV B,6 ; B <- 6 0111: 0111 0110 : 76
LOOP2: ;
OUT B,LED_ON ; LED_ON(B) 1000: 1000 1000 : 88
OUT B,1 ; LED_OFF(B+1) 1001: 1000 0001 : 81
ADD B,15 ; B-- 1010: 0101 1111 : 5F
JC LOOP2 ; IF B!=0 GOTO LOOP2 1011: 1110 1000 : E8
JMP START ; GOTO START 1100: 1111 0000 : F0
↑ダウンロード KnightRider1.avi (1004.0K)
(↓間違いを発見したので修正した 2014/08/01、2017/08/12)
; ・テニス・ゲーム
;
;
LED_ON = 8
LED_OFF = 8
LED_ON_4 = 12
LED_OFF_4 = 4
CHK_SW0 = 15
CHK_SW3 = 8
START: ;
OUT LED_ON+0 ; LED_ON(0) 0000: 1010 1000 : A8
MOV B,LED_ON+1 ; B <- LED_ON(1) 0001: 0111 1001 : 79
LOOP1: ;
OUT B ; LED_ON(B) 0010: 1000 0000 : 80
OUT B,LED_OFF-1 ; LED_OFF(B-1) 0011: 1000 0111 : 87
ADD B,1 ; B++ 0100: 0101 0001 : 51
JC LEND1 ; IF B==15 GOTO LEND1 0101: 1110 0111 : E7
JMP LOOP1 ; GOTO LOOP1 0110: 1111 0010 : F2
LEND1: ;
MOV B,6 ; B <- 6 0111: 0111 0110 : 76
LOOP2: ;
OUT B,LED_ON ; LED_ON(B) 1000: 1000 1000 : 88
OUT B,1 ; LED_OFF(B+1) 1001: 1000 0001 : 81
ADD B,15 ; B-- 1010: 0101 1111 : 5F
JC LOOP2 ; GOTO LOOP2 1011: 1110 1000 : E8
CHKSW: ;
MOV B,0 ; B <- 0 1100: 0111 0000 : 70
IN A,CHK_SW3 ; SW(3) ON? 1101: 0010 1000 : 28
JC MISS ; GOTO MISS, early 1110: 1100 0000 : C1
IN A,CHK_SW3 ; SW(3) ON? 1111: 0010 1000 : 28
JC START ; GOTO START 1 0000: 1110 0000 : E0
; later
MISS: ;
OUT LED_OFF+0 ; LED_OFF(0) 1 0001: 1010 0000 : A0
MOV B,0 ; B <- 0 1 0010: 0111 0000 : 70
BLINK:
OUT LED_ON+4 ; LED_ONF_4 1 0011: 1010 0100 : A4
IN A,CHK_SW8 ; SW(8) on? 1 0100: 0010 1000 : 28
JC $+3 ; 1 0101: 1100 1000 : C8
IN A,CHK_SW0 ; SW(0) on? 1 0110: 0010 1111 : 2F
JC START ; GOTO START replay 1 0111: 1110 0000 : E0
OUT LED_OFF ; LED_ON_4 1 1000: 1010 1100 : AC
JMP BLINK ; GOTO BLINK 1 1001: 1101 0000 : D3
.END
↑ダウンロード TD4_tenis1.avi (923.5K)
6. 配線に使用する線材
以前は,0.5mm構内ケーブル(KTEV)の切端をもらってきて使用していた(白茶が増える.)
この線の被服は耐熱性がないので,付けたり外したりすると被服が溶けてくる. 狭い場所ではピンセットを使いたいのだがピンセットで挟んではんだ付けすると被服が溶けるという問題があったが,安い(ロハ)のと仕上がりが奇麗なので使用していた.
最近は, 表配線が面倒になってきたので,ポリウレタン線(UEW)を使って裏面配線している.
この電線は被服を剥く必要がない(はんだ鏝で被服が溶ける)のと,被服が薄く線径が細いのでかさばらないというメリットがある. しかし,線の色が1色なので目視で配線チェックができない.(細かい物が見えなくなったという身体的問題もある)
ジュンフロン線は耐熱性があるので被服が溶ける心配がない.0.32mmは導線が固く配線しにくい,0.26mmは腰がなく配線がまとまりにくいのだが0.26mmの方が配線しやすいようだ.
始めましてtaku_ohm83と申します。
MSXと同い年の1983年生まれです。
TD4繋がりでここに飛んできました。
というのも、僕はCPUの創りかたは読んだ事なくて、
アーキテクチャの仕様が気になってたどり着いたんです。
74ロジックなら、見ればわかるかなと、回路図を。
確かに回路構成から考えてCPUの勉強するのにはいい教材ですね。
ここのはオリジナルのハードウェアから工夫してあって、参考になります。
一つ気になったのですが、テニス・ゲームの
アドレス0101のマシン語の表記がおかしい気がします。
0101: 1110 10001 ではなくて、
0101: 1110 0111 ではないかと、思いまして。
思い違いだったらすいません。
投稿: taku_ohm83 | 2014年7月31日 (木) 06時57分
始めましてtaku_ohm83さん、コメントありがとうございます。
マシン語確かに変ですね。その他にも間違いを発見しました。
土日で検証してみます
投稿: Yoshi | 2014年8月 2日 (土) 02時26分
こんにちは。
『CPUの創りかた』に書かれている条件分岐命令は「Cフラグが1ではないときにジャンプ」する JNC 命令ですが、本記事には動作が逆のような JC 命令が使われており、「2. 方針」にある「アーキテクチャとインストラクションは同じにして~」とは矛盾している気がします。
『ナイトライダー』のプログラムで、
OUT LED_ON+0 ; LED_ON(0) 0000: 1010 1000 : A8
OUT B ; LED_ON(B) 0010: 1000 0000 : 80
とありますが、『CPUの創りかた』に書かれている命令の説明ではそれぞれ B8, 90 が正しいようです(命令デコーダの回路上どちらでも動作はするようですが)。
同プログラムで、LED_OFF = 8 の条件で
OUT B,LED_OFF-1 ; LED_OFF(B-1) 0011: 1000 0111 : 87
は分かるのですが
OUT B,LED_OFF+1 ; LED_OFF(B+1) 1001: 1000 0001 : 81
となるのが分かりません。 Imm の幅は 4ビットなので 89(あるいは 99) が正しいのでは?
詳しく見ていませんが『テニス・ゲーム』のプログラムにも同様の箇所があるようです。
投稿: 名無し | 2017年8月12日 (土) 00時42分
名無しさんコメントありがとうございます。
〇条件ジャンプ
TD4の条件ジャンプ命令はご指摘のとおりJNC(キャリーが無い場合にジャンプする)です。
実は、作っているときJC(キャリーがあるときジャンプする)と思い込んで作ってしまったので配線はそのままにしてあります。(回路図は直してある。)(^^;
〇OUT命令
ご指摘のとおり『CPUの創りかた』には OUT Bは9xhと説明されていますが、デコーダーは、8xhでも9xhでも同じ動作をします。同様にOUT Imは0Bxhと説明されていますが、0Axhでも0Bxhでも動作します。
プログラムを書くときに、この回路のインストラクション・デコーダの真理値表を見てプログラムを作ったので、それぞれ若番を使ったのだろうと思います。特に意図はありません。
(プログラムを書いているときもほとんど『CPUの創りかた』を見ていませんでした。)
〇OUT B命令
ご指摘のとおり
OUT B,LED_OFF+1 ; LED_OFF(B+1) 1001: 1000 0001 : 81
は間違っているようです。
正しくは
OUT B,1 ; LED_OFF(B+1) 1001: 1000 1001 : 81
ですね。
LED0→LED7方向のときにはBレジスタには 1001~1111が格納されています。
一方、LED7→LED0方向のときには 0110~000が格納されています。なので、LEDを消すために1000を加算する必要はありません。
つまり機械語が正しくてソースが間違っているようです。
LED7→LED0方向のときはBレジスタに-1(1111b)を加えるのですが、1110~1000だとキャリーが出たままになるので、0110~0000にしたのだと思います。
投稿: Yoshi | 2017年8月12日 (土) 05時37分
ご丁寧な解説をありがとうございます。
疑問が氷解いたしました。
投稿: 名無し | 2017年8月12日 (土) 15時23分