先日の記事 で Intel 8088 のマイコンボードの制作の検討を始めた事を書きましたが,その中で早速一つ課題が出てきたのでそれをネタに記事を書いてみます。
今回は「CPUのバスの速度とI/Oの速度の差」についてです。
i8088のバスのイミングチャートを見ながら,I/Oの動作がCPUの動作に間にあうか?や解決方法を書いて行こうかと思います。
1.CPUやデバイスの動作速度はそれぞれ違う
さて,「速度」の話をする上でまずは Intel 8088 の動作速度をおさらいです。
Intel 8088 には 5MHz,8MHz といったクロックの製品があり,セカンドソース(互換品)の中にはもっと高速に動作する物もあります。
するとCPUからのI/Oやメモリーの読み書きも5~8MHzを基準行われるのですが(詳細本項内で後述),早速その速度に追いつかないデバイスが見つかりました。
というのが,この部分・・・
グラフィック液晶モジュールです。
使おうと思っているのは秋月電子通商で販売されているSUNLIKE製の SG12864 というモジュールです。
一時,電子工作で話題になったモジュールですね。
参考:バックライト付グラフィック液晶表示器 128×64ドット SG12864ASLB-GB
が,データシートを見るとデータやの読み書きが1MHz程度まででしかできないようです。
まずはその辺を見てみましょう。
データシートより読み書きのタイミングチャートの抜粋です。
この液晶モジュール,値の読み書きはR/#W信号(#は反転という意味, ̄の事)で読み込みか書き込みか?を,#C/D信号(端子名はD/I)でデータなのかインストラクション(コマンド)なのか?を決定し,CS1B,CS2B,CS3信号(端子名はCS1,CS2)でデバイスをアクティブにした状態でE信号(イネーブル)をLowに落とすと書き込み,読み出しが行われます。
余談ですが,データシートの表記がCS1信号などはHやLとなっているのに対し,E信号のみ「H→L」という表記になっています。
その他,タイミングチャートの内容からしても,どうもE信号のみエッジ動作,他はレベル動作のようです。
コンピュータなどのディジタル回路では動作のタイミングで「エッジ動作」と「レベル動作」の2種類があります。
エッジ動作:HからL,またはLからHへ変化する「瞬間だけ」回路が動作します
レベル動作:H,またはLの「区間中」回路が動作します
これを誤ると回路が意図した通りに動作しない事があります。
一般的にコンピュータのI/Oではレベル動作で動作条件やデータを準備し,エッジ動作で確定するといった動かし方が多いです。
さて,図を見ると図中に矢印と英文字で各タイミングの時間が示されています。
E信号のはダウンエッジ(HからLに変化する瞬間動作)ですから立下り(HからLに変わる瞬間)から次の立下りまでが読み書きの周期となります。
図中では「tCYC」がちょうどその位置に引かれていますから,その値をデータシートから読みます。
タイミングチャート下の表を見ればその時間は「min. 1000nsec」となっていますから,このモジュールは最大1MHzで動作する事になります。
さて,対するCPUの方はどうでしょう?
読み込みのサイクルから見てみましょう。
何だかごちゃごちゃした感じですが,見るのは赤の「READ CYCLE」の部分です。
その中でも読み込みを指令するのは「#RD信号」ですから#RDを見ます。
ここで一つ8086や8088の基礎知識なのですが,8086や8088はバスの一連の動作(バスサイクルと呼びます)が4つのクロック構成されています。
それぞれ,1回目のクロックの期間T1,2回目のクロックの期間をT2・・・と呼んでいます。
この図の上にあるT1,T2,T3,T4は4つで構成されるバスサイクルのどの部分か?という意味です。
さて#RD信号,図中TCLRLに示す通り,T2(2回目のクロックのサイクル)の始まりを起点に少し遅れて#RDがLとなり有効になっています。
その後,図中TCLRHに示す通り,T3の終わりを起点にまた少し遅れて#RDがHに戻り読み込みが終了します。
ここで表のとおり,TCLRL,TCLRHとも値いに大きな差はなく,#RD信号は概ね2CLK(クロック2回分)の間Lになる事になります。
では書き込みはどうでしょう?
またごちゃごちゃしていますが,こちらも赤の「WRITE CYCLE」を見ます。
その中でも読み込みを指令するのは「#WR信号」ですから#WEを見ます。
すると,図中TCVCTVに示す通り,T2の始まりを起点に少し遅れて#WRがLとなり有効になっています。
その後,図中TCVCTXに示す通り,T3の終わりを起点にまた少し遅れて#WRがHに戻り下記込みが終了します。
ここで表のとおり,TCVCTVやTCVCTXの値いに差はなく,読み込みの時と同様に#WR信号も概ね2CLKの間Lになる事になります。
そして先に書いた通り,8086や8088は4クロックで1回のバスサイクルを終えます。
言い換えれば読み込みにしても書き込みにしてもCPUのクロックの4分の1の速度で行われている事がわかります。
さらに言えば,4CLK中2CLKが#RD,#WRの有効期間ですからちょうど半分が有効期間となります。
これはHとLの比率(デューティー比)は1:1でもあり,ちょうど綺麗にCPUのクロックの4分の1で動作している事なります。
というわけで・・・
8088のクロックは5MHz,または8MHzで,その4分の1で動くので読み書きは1.25MHz,または2MHz相当,液晶は1MHzで動作していますから,I/Oである液晶モジュールはCPUの速度に追いつかない事になります。
ダメじゃん。・・・というのが今回の話。
5MHzの場合はバスサイクルが1.25MHzなので正直動いちゃうとは思います。
ただ,8MHzの場合はバスサイクルが2MHzですからさすがに動かないでしょう。
というわけで何とかする方法を考えます。
2.CPUはちゃんと待ってくれる,READY信号を使う
さて,デバイスがCPUの速度に追いつかない事は分かりましたが,だからと言ってデバイスに合わせてCPUのクロックを落としてはせっかく高速に動作したはずのCPUがもったいないですね。
問題はI/Oの速度ですから,I/Oのアクセス時だけ速度を落としてくれればメモリーへのアクセスや演算は高速に動作できます。
そんな都合のいい事できるのか?と思うかもしれませんが,8086や8088にはその機能がちゃんと備わっています。
というか,CPUがI/Oの処理完了を待つ機能はZ-80や8080にも備わっています。
コンピュータの黎明期から・・・というより,黎明期に戻れば戻るほどI/Oというのは動作が遅く,黎明期からI/O待ちが必要だったんですね。
もっとも,これらの機能,いくら5MHzや8MHzが当時としては高速だったと言っても今となっては非常に低速,10MHzで動作するI/Oが容易に手に入る現在ではI/O待ちの機能を使う事は稀です。
そのため,すっかり忘れられているのを見かける事もあります。
私自身,今まで大体間に合ってしまっており,実際に問題になったのは今回が初めてですね・・・
というわけで,今回は私もTryalです。
さて,具体的にはどうやるのか?というと,8086や8088には「READY」という信号があります。
この信号をT2サイクル完了までにLに落とせばT3サイクルの後に「Twait」という待機のサイクルが挿入されます。
その様子が書かれているのが下図です。
図はデータシートにあった図に少し書き足したものです。
動作としては見ての通りです。
READYは「CPUの周りの回路の準備ができました」とCPUに通知する信号です。
それがL,切れてしまうのでCPUは動作を中断するという物です。
その後,READYがHに戻るとCPUは準備が完了したと判断し,動作を再開します。
まさにI/Oのに合わせてCPUに待ってもらう信号となります。
余談ですが,Z-80や8080の場合は#WAIT信号という信号を使います。
こちらはもうちょっと直接的な名前ですね。CPUに「待って!」と通知する信号といった名称になっています。
8080にもREADYという端子はあるのですが,こちらは「CPUが準備完了」という信号になっており,8088とは真逆の信号となっています。
このREADY信号は8237等のDMACにも備わっています。DMAが速すぎるという場合も同様の方法でI/O完了を待たせることができます。
さて,やり方は分かったところで実例を見てみましょう。
多分,セオリーのような回路があるのだと思うのですが見つからず,Z-80の#WAITを参考にしつつバスサイクルを見ながら下図のような自分なりに回路を描いてみました。
回路の動作としては,まずU2,U3のNANDによりIO/#M信号とアドレスデコーダーの出力より今回のデバイスに対し読み書きしようとしている事を検知します。
この時,アドレスデコーダーの出力を確認していますから,対象デバイスのみが待機の対象となります。
その後,U4のD-FFでクロックに同期してラッチ,その反転出力でREADY信号をLへ,デバイスとしては待ってほしい旨をCPUに通知します。
その後に続くU6~U9の長~いD-FFはクロックの回数を数えるDelay(遅延)です。
ここでは4個接続しているので8MHz用です。タイミング的に・・・おそらく1個多いと思います。3個でいいかもしれません。
5MHzの時はU6~U8の中で1個D-FFを減らしてください。
この時,U1,U5のNANDで#RD,#WRがちゃんと落ちている事を確認して遅延を行っています。欲しいのは#RD,#WRのパルス幅ですから。
そしてDelayで意図した時間経過後,#Q(U5がNANDで反転しているので)の出力でORによりREADY信号をHへ,準備完了をCPUに通知します。
デバイスREADYを受け,CPUはT4サイクルへ移行,処理を再開します。
その後,IO/#M,CSがLに落ちると#CLRがLに落ちるので回路がクリアされます。
・・・とこんな感じでしょうか?あ,8284通してくださいね。
DelayのD-FFが長いので74AC574のようなDラッチで代用できないかも考えましたが,クリアが面倒だったので74AC74のままとしました。
あ,そうそう,今回のバスはそこそこ速いので74HCではなく74ACを使ってます。
また,8088はTTLレベルですので,レベルを優先するのであれば74ALSとするか,プルアップしたり74ACT541等を通したりする等してCMOSにインターフェースてください。(これはまた後日にでも)
そのほかこんな回路でもよさそうです。
クロックの立ち上がりと立下りを使った回路です。ちょっとDDRっぽい回路です。
こちらは「2クロックウェイトを挿入する」という動作になります。
U9ばREADYを戻すタイミングの調整用です。
こちらはバスのタイミングの意図するものに近いので,8284でもASYNで入れてしまって大丈夫だと思います。
クロックに同期しているのでREADYを直接CPUに入れることもできますが,その場合はU9~U10の間に30ns程度のディレイを入れてください。データシート上は何故かホールドタイムが規定されています。
いずれの回路も8086,8088用として設計していますが,Z-80も似たような回路でI/O待ちできます。
Z-80は8080を参考に作っているCPUなので,多分8080もZ-80と同じ回路で動くと思います。
さて,デバイスにアクセスした時だけウェイトをかける(CPUを待たせる)事はできるようになったのですが,これ,大団円とはいきません。
単発のコマンドの送信などはこれでもいいのですが,1画面丸々更新するような場合はちょっと困ってしまいます。
というのが,今回の液晶モジュールはキャラクターROM等を搭載しないのグラフィックタイプです。
そのためビットマップのような形で画面に表示するデータを送ってやる必要があり,”A” という文字を表示したいだけでも,すべてドット絵のような形で送ってやる必要があります。
かつ今回の液晶は128×64ピクセルのモノクロの液晶で,縦に8byte,横に128byteのデータがあり,画面全体を更新する場合は合計1024byte(1KiB,1kbyte)のデータを転送する必要があります。
1024回もウェイトがかかってしまったらたまったものではありません。
これにより,DMAでやろうがこんなにウェイトされてしまったら折角の8MHzのCPUやDMAが台無し(といっても8237もそんなに高速なDMAではありませんが)です。I/O命令で送ろうとしても(Z-80と比較して)ちょっと物足りない8088のI/O命令では転送に時間がかかってしまいます。
そんなこんなを考えると,恐らくですが全画面の転送のために2msec程度CPUがつきっきりになると思います。
こりゃたまらん。
というわけで,もう一押し改善してみます。
3.バッファーを設ける
さて,単発のコマンドの送信等は正常に送れるようになりましたが,大量のデータの転送に時間がかかりすぎる課題が残っています。
こちらを解決して行こうと思います。
さて何がいいだろう?と言えばバッファーを付けてしまうのが手っ取り早いですね。
かつバッファーをRAMとしてメモリー空間にマップしてしまえば,CPUにはメモリーの一部に見えますからブロック転送命令も使えます。
これは,後々CPUを80286にステップアップする場合にも有利です。
というわけで,こんな回路を考えてみました。
何だか複雑に見えた人もいるかもしれませんが,アドレス線やデータ線が多いだけでそんなに難しい回路ではありません。
補足ですが,この回路まだ完全ではありません。CPUのメモリー空間にマップされたバッファーとしてふるまい,コマンドによりバッファーの内容を自動でまとめて液晶モジュールに送る部分だけです。
といっても回路図だけでは「何の事やら?」ですね。
少し,動作を説明していきます。
通常はU1~U3のバススイッチによりRAM(U4,HM6264)はCPUのバスに接続されています。そのため,CPUから見れば単なる1KiBのRAMに見えています。
そのため,I/O命令ではなくメモリーの転送命令で読み書きができ,ブロック転送命令で効率よく値を書き換える事ができます。
かつ,画面をCPUから自由に書く事ができます。これは80286等の高速なCPUにステップアップした際,8237等の思ったほど速度が出ないDMAを使うより有利になります。
さて,画面のデータの準備が終わったら転送を行うのですが,この転送は周辺にあるカウンターやロジック回路が自動でまとめて行ってくれます。
この回路,単純にバッファーというより,ちょっとしたIOプロセッサ的な機能までついてます。
Go信号によりU12のDラッチをラッチすると,U1~U3のバススイッチがRAMをCPUのバスから切り離します。
そして内部の転送用の回路がU18~U19のバススイッチによりRAMが接続されます。
U13~U14のDラッチはクロックの同期とカウンターのタイミング生成用で,ラッチするに連れ,液晶モジュール転送用の低速なクロックが液晶モジュール,カウンターと供給されて行きます。
クロックが供給されると,クロックにより液晶モジュールのE信号を駆動,メモリーから読み出された値を取り込ませます。
また,クロックがカウンターにも供給されていますから,転送するたびにカウンターがインクリメントされます。
そのカウンターの出力がRAMのアドレスに接続されていますからデータを送り終わるたびにアドレスが進んでいく事になります。
結果,クロックのたびに新しくデータを読み出し転送する,という動作を繰り返します。
その後,カウンターの11ビット目の出力,すなわち転送量のカウントが10ビット(1024byte=1KiB)をオーバーフローした事によりU12~U14のラッチを解除,ラッチ解除によりカウンターをクリアし,U1~U3やU18~U19のバススイッチが切り替わりRAMがCPUのバスに復帰,再びCPUから読み書きができる状態になり,一連の動作が完了します。
さて,今回はメモリーに画面を書いておき一斉に転送する構造としましたが,この回路の1byteの物を作ればコマンドの送信等もバッファーを搭載できます。
その場合は,カウンターやバススイッチを省略できるので回路も簡単でもあります。
すると,ここまで作るか?という疑問はあります。
ここはチョット悩ましいところで,使い方にもよる部分です。
とはいえ,この規模であればCPLDやFPGAに入れてしまうのは容易です。かつ単に趣味で作るだけであればそんなに大きな回路ではなく,程よい作り応え(?)で楽しみにはちょうどいいいかな?とも思えます。
どの回路で行くかは検討要ですが,とりあえず技術的にはクリアです。
4.検討までを終えてみて
さて,バスに間に合うか?の確認からセオリー(?)な2例の解決策の検討までを通して記事に書いてみましたが,ハードウェアとしてはこんな感じです。
ソフトウェアのサイト向けであるため概要やポイントのみの内容となっていますが,ソフトウェアを実行しているハードウェアはこんな検討や設計を経て出来上がっていたりします。
逆にハードウェアの事を勉強中の方へは,概要はこんな感じだよ!といった感じです。
とりわけ,私自身再確認で調べてみて感じたのが,昔に比べて情報が減ったように感じました。
正直,ジオシティーズが落ちたのが・・・いや何でもないです。
今回書いたバスやI/Oの速度の検討は,うっかり忘れがちな部分でもあります。
動かしてみたら何だか動かない,プログラムも間違ってない,ロジックアナライザで見ても正しく送られてるのになぜ?といった結果になり,迷宮入りのように感じてしまう事もあります。
メモリーや特定のCPU用の専用LSIは「8MHz品」といった感じで周波数で書いてあるので間違える事は少ないのですが,今回のような汎用のモジュール品で特定のCPUにだけにつながるわけでけではない部品は要注意です。
というわけで駆け足ではありましたが,バスとI/Oの速度の注意点,検討の大まかな流れ,その解決策についてでした。
大雑把な内容でしたが誰かの参考になれば幸いです。それではまた (・ω・)ノシ









