クロックまわり下調べ

Z80の動作の理解にあたって手動クロックで各マシンサイクルを追いかけていこうと思っているので、そのために調べたことをメモ。


資料を読んでいると「nMOS版は最低クロック周波数がある」という記述あり。WikipediaによるとnMOS版はダイナミック・ラッチという機構を使っているのでクロックを止められないとのこと。

Unlike the original nMOS version, which used a few dynamic latches, and therefore could not be stopped for more than a few thousand clock cycles.

で、ダイナミック・ラッチというのはDRAMのメモリセルのようなものらしい。要するにコンデンサを使って情報保持してるからリフレッシュが必要? データシートでAC特性を調べると、tw(φL) max = 2000[nsec] がこの辺の事情を表してそう(電荷が抜けるので一定時間以上Lにできない)。

f:id:marlesan:20161005022034p:plain

でもパルスをHにできる時間は∞なので、一応クロックを止められるような気はする。ボタンを押すごとに1μsecくらいのパルスを出す回路を作って実験してみよう(555が使えそうだけど、こんなに短い時間のパルスも出せるのかな…)。

  • 【追記】555だと出力パルス幅より短い入力トリガを与えないとダメっぽい(時定数コンデンサが充電されて内部F/FのRがHになる時、トリガパルスが終わってないとSもHのままなので)。74HC123がデータシートによるとnsオーダーの単発パルスを出せるようなので買おう。

ちなみに↑のZ80のデータシートは2.5MHzのものですが、4MHzでもmax側の値は同じのようです。

メモリまわり下調べ

f:id:marlesan:20161002023911j:plain:w250
Z80写真再掲。
これは純粋にCPUというだけのICで、メモリもありません。メモリがなければプログラムもありません(ストアドプログラム方式ってヤツだ!)。というわけで、まずはメモリについて学ばなければいけません。

「メモリ」というだけだと相当に意味の広い言葉ですが、ここでは「コンピュータ基板に直接乗せるICとしてのメモリ≒CPUがアドレス指定で直接アクセスできるメモリ」という文脈で使います。すると、メモリはROMとRAMの2種類に大別でき、以下のように使い分けるようです。

  • ROM (Read Only Memory)
    • 電源を切っても内容が保存される(不揮発性)
    • 内容の書き換えに制限あり(回数・方法など)
  • RAM (Random Access Memory)
    • 電源を切ると内容が消える(揮発性)
    • 読み書きが自由自在
    • ROMもランダム(任意位置)アクセス可能なので、慣習的な呼称

典型的にはプログラムをROMに置いて、プログラムが利用する一時的なデータをRAMで読み書きします。ここで唐突にスーパーファミコンのカートリッジを開けてみると、ROMとRAMが載った基板が出てきます。ROMには今言ったようにゲームのプログラムが書き込まれていますが、RAMは一時データの読み書き用ではなく(そのためのRAMは本体側にある)セーブデータの保存用です。RAMにバックアップ用電池を繋げることで不揮発性と自由な読み書きをなんとか両立させているのですね。

f:id:marlesan:20161003020555j:plain:w300
ドラクエ5の中身)

さて、このRAM、スーファミカセットの部品としてはセーブデータのバックアップ用ですが、RAMはRAMなのでひっぺがせばZ80のメインメモリとしても普通に使えるはず。何事も手を動かすのが大切ということで、早速弄ってみましょう。

f:id:marlesan:20161003223721j:plain:w300

特殊ドライバーでガワを開け、半田こてと半田吸い取り線で外し、HY6264A という8kBytesのSRAMを入手。新単語ですね。SRAMDRAMの違いについてまとめておきます。

  • DRAM(Dynamic RAM)
    • メモリセル(1bitの記憶回路)が1個ずつのFETとコンデンサで構成される
    • メモリセルの構造が単純なので面積あたりの容量を稼げる
    • コンデンサに貯まった電荷で0/1を区別するが、そのままだと放電してデータが消えるため絶えず「リフレッシュ」という動作が必要(Z80にはこのための回路が内蔵されている)
  • SRAM(Static RAM)
    • メモリセルがフリップフロップで構成される
    • DRAMに比べメモリセルの構造が複雑で、集積度が低い
    • その代わりリフレッシュのような面倒な手間がかからない

勉強のために是非めんどくさそうなDRAMを使ってみたいのですが、今回の用途に丁度いいもの(DIPで~64kB)は入手困難なよう。SRAMは普通に買えます。そして実は、ハードオフでジャンクカートリッジを買ってきて分解するより普通に買った方が安いです(例えばこれは32kBで@294)。

閑話休題
ひっぺがしたRAMに対してデータの読み書きをしてみます。基本は以下の2点。

  • 書き込み
    • アドレスバスに書き込み先アドレスを、データバスに書き込むデータを乗せてWE(Write Enable)信号をアクティブにする
  • 読み込み
    • アドレスバスに読み込み元アドレスを乗せ、データバスを読み込んだデータの出力先に接続してOE(Output Enable)信号をアクティブにする

実際にコンピュータシステムの一部として使う場合は上記操作のタイミングが超!重要になるようですが、手でぽちぽち実験するなら次のような適当な回路で動作しました。

f:id:marlesan:20161004002757j:plain:w400

データバスの取り扱いだけ要注意。データ入力の信号をバスに直結させると、読み込み時にRAMから出てきたデータとスイッチからの入力データが衝突してしまうため、間に74HC540を入れてます。このICはバッファ(ある出力信号が駆動できるIC数=ファンアウト数を増やすために通す)ですが、入力ピンのG1かG2をアクティブにすると出力がハイインピーダンス(Hi-Z)になる機能もついてます。Hi-Zは「すごく抵抗高い」状態なので回路的には開放しているのと同じです。スマートとはいい難いですが、G1の入力をスイッチで切り替えられるようにし、WEを押すときは信号を通し、OEを押すときはHi-Zにして入力信号をバスから切り離し衝突を回避しています。ちなみにこの74HC540はTD4の部品のひとつで、製作当時は間違ってHi-Zにしてしまう配線ミスをして「ていうか3ステート(出力がH/L/Hi-Zの3状態ある)って何に使うの…」とか思ってました。こういうときに使うんです。

写真では入力をデータバスまで通しているので、この状態で青ボタンを押すと現在設定中のアドレス(上位5bitはGNDに落としているので 0b0000011111111)にデータ 0b10010101 を書き込みます。逆にRAM内のデータを読み込むには、540をHi-Z状態にし、読み込みたいアドレスを設定して赤ボタンを押します。このRAMはWEもOEも非アクティブだとデータピンはHi-Zになるため、赤ボタンを押している間だけ指定アドレスのデータ信号でLEDが光ります。ぱちぱち。

もうSRAMは極めたね。

いや、実はチップセレクト信号という超重要な項目が残ってました。が、今回はもう長く書きすぎたのでまたの機会に……。

12ステップ完遂

久々の更新です。
12ステップで作る組み込みOS自作入門』を完遂。「そもそもOSって何のためにあるの?」というところから学べて、とてもためになりました。RubyJavascriptでイチャコラとカジュアルなプログラミングに親しんでいただけだった私が、今や「OSのソースを読む」とか「デバイスドライバを書く」とかいうハッカーめいた領域に手を出せそうな気になっています(気分だけか!)。

さて次は、『はじめる組込みLinux』に興味津々です。CPU自作、OS自作を体験してコンピュータ世界の俯瞰図ができあがってきたところで、既製システムにガッツリ触れるのはかなり勉強になりそうです。

ですが!

そろそろ「はんだ付けしたい」「LEDチカチカさせたい」欲求が溜まってきたので、いったん物理層に帰りたいと思います。
題材はこれです。

f:id:marlesan:20161002023911j:plain:w400

Z80

私が生まれる前から存在するCPUです。これを使って何がしかのシステムを作りたいのです(欲を言えばゲーム機を…)。Z80高専時代に教育用ボードで触れたはずですが全く覚えてないし、メモリや周辺コントローラを自分で配線してコンピュータシステムを作るなど、どんな学校でもやらないでしょう。おそらくハンドクロックでプリミティブな動作を勉強するところから始まるので、かなり気長なプロジェクトになりそうですが、飽きるまでは突っ走ってみます。

ステップ6まで終了

第1部ブートローダ編が終わりました。思ったよりサクサク進めたのは確実に『CPUの創りかた』をこなしてたおかげです。『12ステップで作る〜』は、ステップ6までは章の前半でソフト開発を進め、後半でCPU原理を解説するという構成が多いのですが、この後半部分がびっくりするくらいすんなり頭に入ってきます。なんせブレッドボードとユニバーサル基板で2回作りましたからね。CPU(ドヤ)。メモリマップドI/Oなど『創りかた』では発展項目として省略されていた話題もあって無理なく知識を増やせている感じがあります。

肝心のソフトウェアの方はというと。C言語を書くのなんて高専の授業以来で、「ポインタってこうやって使うものだったのかぁ」などと感心しながら写経している状態です。自力で同じものを再開発しろというのは現状無理ですが、理屈自体は理解できてるので、実際に手を動かして作業していることが後々意味を持ってくれることを信じましょう。

環境設定以降ここまででドハマりした部分が一つあるのでメモしておきます。XMODEM通信でファイルをブートローダに送信する手順で、cuを使っている場合、本では~Cからlsx filenameとコマンド(lsx がなければ sx)を打つよう書いてありますが、Ubuntuのcuには~Cの操作がありません。たぶんFreeBSDとの違いでしょう。代わりに~+でうまく行きました(完全に等価な操作なのかは調べてないので不明…)。

$ cu -s 9600 -l /dev/ttyUSB0
Connected.
kzload (kozos boot loader) started.
kzload> load
~[マシン名]+sx kozos /** ここ! **/
Sending kozos, 11 blocks: Give your local XMODEM receive command now.
Bytes Sent:   1536   BPS:172                             

Transfer complete

XMODEM receive succeeded.
kzload> run
starting from entry point: ffc020
Hello World!
> echo good!
 good!
> exit
~[マシン名].
Disconnected.
$

ステップ7からさらに内容が濃くなる雰囲気です。ので、さすがに英語の勉強にシフトしたいと思います。開発は1日1時間まで!

12ステップで作る組み込みOS自作入門

本とハードが届いてみたらめちゃ面白そうだったので、思わず始めてしまいました。TOEICなんてどうでもええ!

著者も書いている通り環境構築が一番の壁のようで、私も大いに躓きました。先人たちの情報をググりまくることでStep1を無事クリアできたので、私自身もトライ&エラーの跡を記録しておきたいと思います。GNUツールでの開発はおろかLinuxも素人なので的はずれな情報になってしまうかもしれませんが。

モノの準備

まずは私のマシン環境の紹介から。

  • DELL Inspiron 14 5000 (ノートPC)
  • Ubuntu 16.04 LTS 日本語 Remix

64bit環境です。次に購入したもの。

変換ケーブルを直接ボードに挿せば延長ケーブルいらないやん!と思ってたんですが、固定用のネジ部分がかちあって挿せません。ネジを外して無理やり挿しても通信が不安定になる、ということなので、素直に延長ケーブルも買いましょう。私は短さで選びました。
ネジ+スペーサーはマイコンボードにつける足です。これがないとむき出しの端子が足替わりになってしまいます。持ってない人は是非合わせての購入をオススメ。

f:id:marlesan:20160904050732j:plain:w400
こんな感じで開発してます。

開発環境の構築

はじめにボソっと書いたとおりLinux素人なもので、「あるソフトウェアが必要なときにそのソースファイルを取得して自環境でビルドする」という手順自体ほぼ初めて触れました。だいたい、次のような感じです。

  1. 作業用フォルダを作り、そこに移動
    • ホームに直接ソースをDLして作業すると後で泣きたくなると思います
  2. ソースファイル(圧縮されている)をダウンロード
  3. 解凍し、展開されたフォルダに移動
  4. build フォルダを作り、そこに移動
  5. ../configure でMakefileを作る
  6. make でビルド実行
  7. sudo make install で、ビルドされたバイナリをシステムにインストール

今回ビルドしたのは binutils, gcc, kz_h8write の3つでした。正確に言うと、他にもたくさんビルドしまくりましたが、結局必要なのはこの3つでした。順に使用したコマンドなど書きつけていきます(覚えてるかな…)。

Guake

その前に、使ってるターミナルを紹介しておきます。F12でパッパッと出し入れできる便利なヤツです。

$ sudo apt install guake

binutils

binutils はバージョン 2.27 を選択。本で紹介されてるものより新しいです。はじめ「lex っていうコマンドが使えないぜ」とエラーになったので、flex をインストールしています。この手のエラーを吐かれたら指摘されたコマンドを実行してみると、インストールすべきパッケージの候補を教えてくれます。

$ sudo apt install flex

改めて binutils を入れます。lex 以外の要因でもエラーでビルドが止まりましたが、これは本の助言通り --disable-werror のオプションを足してやり過ごしました。

$ cd ~/workshed /* 普段使ってる作業フォルダ */
$ mkdir src     /* 今回の作業フォルダ */
$ cd src
$ wget http://core.ring.gr.jp/pub/GNU/binutils/binutils-2.27.tar.gz
$ tar zxvf binutils-2.27.tar.gz
$ cd binutils-2.27
$ mkdir build
$ cd build
$ ../configure --target=h8300-elf --disable-nls --disable-werror
$ make
$ sudo make install

新しい binutils を使う場合の指示が公式サポートページにあるので対応しましょう。

gcc

ここが鬼門でしょう。binutils と同じノリで「新しい方がいい!」と 5.4.0 のビルドを試みましたが数時間格闘した末に諦めました。素人は素直に本と同じ 3.4.6 をビルドするのが賢いと思われます。ただし、64bit環境では本にない手順が必要です。これも公式サポートページで詳細を確認してください。

以下のコマンドリストにはこのパッチを当てる手順も入っています。

$ cd ~/workshed/src
$ wget http://core.ring.gr.jp/pub/GNU/gcc/gcc-3.4.6/gcc-3.4.6.tar.gz
$ tar zxvf gcc-3.4.6.tar.gz
$ cd gcc-3.4.6
$ gedit gcc/collect2.c /* 本で解説されている修正を行う */
$ wget http://kozos.jp/books/makeos/patch-gcc-3.4.6-x64-h8300.txt
$ patch -p0 < patch-gcc-3.4.6-x64-h8300.txt /* 64bit環境用のパッチを適用 */
$ mkdir build
$ cd build
$ ../configure --target=h8300-elf --disable-nls --disable-threads --disable-shared --enable-languages=c --disable-werror
$ make
$ sudo make install

ビルドが結構長いのでコーヒーでもキメながら待ちます。

kz_h8write

シリアル通信でH8/3069FのフラッシュROMに書き込みを行うツールです。h8write の改良版ということで最初からこちらを利用しています(Ubuntu + h8write だと書き込みごとにプロセスのkillだかマシン再起動が必要らしい…)。

配布サイトです。ブラウザから kz_h8write-v0.2.1.zip をダウンロードして作業フォルダに移動させました。

$ cd ~/workshed/src
$ unzip kz_h8write-v0.2.1.zip
$ cd PackageFiles/src
$ make

kz_h8write というバイナリがビルドされます。これはKOZOSのプロジェクトフォルダに配置する想定なので make install しません。また、ブートローダー用Makefileの H8WRITE の値を kz_h8write に対応するのを忘れないようにします(バイナリの名前を h8write に変えるでもよさそうです)。

というわけで開発環境の構築はほぼ終わりです。「ほぼ」というのは、USB-シリアル変換ケーブルを使う場合、そのセットアップも必要になるかと思います。以下、はじめに紹介したBUFFALOのBSUSRC0605BSをUbuntuで使う場合の手順をまとめます。

BSUSRC0605BS ドライバのインストール

正確にはこの製品に使われているチップのドライバが必要です。いや、もしかしたらデフォルトで入ってるかも…。確かめる前にドライバを手動で導入したのでわからないんですよね。変換ケーブルをUSBポートに挿して$ dmesgを実行し、

[87560.045514] usb 1-3: new full-speed USB device number 11 using xhci_hcd
[87560.235826] usb 1-3: New USB device found, idVendor=0403, idProduct=6001
[87560.235839] usb 1-3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[87560.235847] usb 1-3: Product: USB HS SERIAL CONVERTER
[87560.235854] usb 1-3: Manufacturer: FTDI
[87560.235859] usb 1-3: SerialNumber: FT0H4ELF
[87560.240220] ftdi_sio 1-3:1.0: FTDI USB Serial Device converter detected
[87560.240394] usb 1-3: Detected FT232BM
[87560.240744] usb 1-3: FTDI USB Serial Device converter now attached to ttyUSB0

こんな感じのログが末尾にあれば使える状態だと思います。どうも反応ないらしいとなったら、次に進んでください。

ドライバはこちらから環境に合わせて入手します。ここでは Linux-64bit です。

$ cd ~/workshed/src
$ wget http://www.ftdichip.com/Drivers/D2XX/Linux/libftd2xx-x86_64-1.3.6.tgz
$ tar zxvf libftd2xx-x86_64-1.3.6.tgz
/* 以下、展開された中にある ReadMe.txt に従った手順 */
$ cd release/build
$ sudo -s
# cp libftd2xx.* /usr/local/lib
# chmod 0755 /usr/local/lib/libftd2xx.so.1.3.6
# ln -sf /usr/local/lib/libftd2xx.so.1.3.6 /usr/local/lib/libftd2xx.so
# exit

あとはコードをゴリゴリ書いていくのみ!

f:id:marlesan:20160904050814j:plain:w400
Step1完遂、歓喜のリセット連打。たぶん皆やる。

TD4完成!

8月中という設定にギリギリ間に合いました。
こちら完成品になります。どどん。

f:id:marlesan:20160831004022j:plain:w250 f:id:marlesan:20160831004025j:plain:w250

配線の汚さは言い訳不可能のようですね。それは次回への課題ということで、今はTD4がちゃんと動作してることを喜びましょう。
★動いてるところ: 4bitCPU TD4 - YouTube

本の作例ではI/OはDIPスイッチとLED・ブザーを直接組み付けているところ、Arduinoなどの開発ボードみたいにピンソケットで入出力できるようにし、ついでにミニブレッドボードも載せてみました。これにより汎用性がグッと増している!……はず……。しかし、ブレッドボード裏の両面テープを使う日が来るとは思ってませんでした。貴重な体験です(大げさ)。

反省点というか備忘録というか。

  • フラックスはガンガン使っていこう。
  • IC用パターンの基板はほぼICだけ使うときに有用。今回のように普通の回路が載ったりLEDたくさんつける場合は、はんだ付けの工数減より配線の窮屈さの方が勝ってました。
  • ラッピング線の整形方法を確立したい。
  • ピンそろったはICが浮かないように押さえて、ゆっくり締める。雑に使うと足が斜めになってソケットに挿すとき変な力がかかってしまう。
  • 白LED眩しすぎる(1.5kΩもつけたのに…)。クロック表示用の白LEDがそうなってるけど、使うならマットタイプ。

さて次のプロジェクトは。Arduinoの仕組み解明という流れを汲むなら『12ステップで作る組込みOS自作入門』に続こうかなと考えてます。かなりソフト寄りになりそうですが。
あくまで電子工作にこだわるならラジオやオーディオに挑戦したいですね。これは『Make: Electronics』に電磁誘導の原理から学ぶ章があるので、そこから手をつけます。

どっちにしろ、9月はTOEIC受験のため英語の勉強に時間を使うので、活動再開は10月になりそうです。

これからが本当の地獄だ…

f:id:marlesan:20160822011719j:plain:w250 f:id:marlesan:20160822011721j:plain:w250

ROM完成&LED配置まで。ROMさえ乗り越えれば、という考えだったけど残り作業の方が面倒くさそう。最短配線だと詰むので順番と線を這わせるルートを計画する必要があるます。さらに、ここから先は完成まで動作チェックできないのも辛い。できるけどかえって非効率というのが正確かも。こういうテストクリップたくさん欲しいな。

ROM部分はバッチ動いてます。プログラムカウンタの74HC161だけ配線してクロックでカウンタ増加→対応するアドレスのROMデータが74HC540から出力される、という動作を確認。このとき540が配線ミスでハイインピーダンス出力になってたのと、手動クロック信号にチャタリングが出てたのがバグでした。

チャタリングは「クロックの立ち下がりで161の出力値が変わる!」というブレッドボードの時に原因究明を諦めた現象が再発したので、オシロでキャプチャ。 f:id:marlesan:20160822013949p:plain:w400
シュミットインバータの入力は問題なくて、出力だけがこんな風にガタガタいってました。出力ピン~トグルスイッチ間のラッピングワイヤを基板に押し付けるとガタガタ、離すと正常波形になるというバグで、根本原因は…不明(;・`ω´・)
ワイヤの被覆は変形こそあったけど無事っぽくて、はんだ付け部分もショートしてない。信号の乗り移りってやつかな?とも思いますが、この手のアナログっぽいエラーは今のところ理解不能です。結局、問題のワイヤを外してルートをちょっと変えて再配線したら直りました。