MYCPU80でCP/Mを!
超巨大基板の8080互換HCMOS・CPUでCP/Mを走らせてしまおうという、なんとも狂気なプロジェクトです!
[第21回]
●VHDLコードの補足説明
前回はカメレオンロジアナのハードを利用したVHDLコードリストをお見せしました。
CPLD、VHDLについてはその名前程度は以前から知ってはいましたが、今年の始めまではまったくの門外漢でありました。
カメレオンロジアナは何年か前に購入して、そこにはCPLDが搭載されていて、しかもVHDLコードを書き換えることのできる機能も搭載されているので、そのようにすれば自分の考えた回路として使うこともできるらしいということまではわかっていましたが、なにしろずっと多忙でしたので、その機会もないままで来てしまいました。
つまりは昨年暮れまでは、私はVHDLについてはまったく知識ゼロであったわけですが、それが一念発起しましてVHDLの短期速習を行い、なんとか自分の思う通りにカメレオンロジアナを改造することができるようになりました。
そのあたりの経緯につきましては「CPLD+SIMMを使ってUSBプロトコルの解析を!」に書いておりますのでご参照ください。
VHDLも知識ゼロならばISE Design Suiteを使うのももちろん初めてというところからスタートしたわけですので、その経験を生かして、ISE Design Suiteの使い方についても詳しく説明するとよいのですが、しかしそのようなことを書き始めますと、本来のテーマからそれこそ大きく脱線したまま、本線に復帰できるのがいつのことやらわからなくなってしまいます。
それはVHDLについても同様でありますので、ここでは詳しくは書きません。
いずれCPLD入門とか、VHDL入門とかといった連載も書いてみたいと思っております(何時になることやら)。
ああ。
いつまでもよそ様がお作りになったカメレオンロジアナに頼っていてはいけませぬ。
いずれ同様のものを中日電工でも作ってみたいとも思っております。
これまた何時のことになるかわかりませんが、余りあてにしないでお待ちくださいませ。
とりあえず。
前回のVHDLコードリストにつきまして、参考までに簡単に補足説明をいたします。
この回路の目的はMYCPU80を動作させておいて、アドレスバス、データバス、制御信号データをリアルタイムに取得して、それをカメレオンロジアナのメモリ(128K×4バイト)に一旦蓄えたのち、メモリがフルになったら今度はその蓄えたデータをZB28Kに送り、ZB28Kはそれを画面に表示することで、ログファイル(テキストファイル)としてZB28Kと接続したWindowsパソコンのハードディスクに保存するというものです。
そのうちMYCPU80のデータを取得するところから、それをZB28Kに送るところまでをVHDLで記述します。
基本的にVHDLは記述の順序は実行のシーケンスとは関係がありません。
ですので必要な回路ごとに記述することになります。
ただ全然シーケンスが無いかというとそんなことはありません。
取得したデータは順にメモリに書き込んでいかなければなりませんし、メモリがフルになったら、今度はそれをまたメモリの先頭から読み出してZB28Kに送らなければなりません。
そのあたりをどのように記述しているか、というあたりを参考にしていただければ、と思います。
とりたてて長いリストではありませんが、中身は結構濃いと思います。
ここではVHDLの文法には触れません。
とりあえずはそれについてはVHDLの文法について解説していらっしゃるよそのサイトか参考書などをお読みください。
とにかくやっておりますことは。
1)MYCPU80のアドレス、データ、制御信号を8ビット×4個の入力端子から入力する
ここで4バイトなのはカメレオンロジアナのメモリ(CY7C1347G)の構造にあわせたためです。
欲しいデータはアドレスバス上位8ビット、下位8ビット、データバス8ビットと制御信号が数ビットですから、これはもうお誂え向きで好都合でありました。
各データはXC95144XLの入力端子から常時入力されているのですが、それをメモリに書き込むタイミングが重要になります。
2)読み込んだデータをメモリに書き込む
MYCPU80が(MYCPU80の)メモリから命令コードを読んでそれを実行するタイミングごとに、XC95144XLの各入力端子から入力されているデータをメモリ(CY7C1347G)に書き込まなければなりません。
そのタイミングとしてはMEMRD、MEMWR、IORD、IOWRの各信号を使うことにしました。
後にOPcodefetchも加えることにしました。
またMEMRDはそのままでは使えないことが判明しましたので、それには新たに工夫が必要になりました(のちほど説明の予定)。
データをメモリ(CY7C1347G)に書き込むためのタイミングパルスとして、これらの信号を使うためには、信号パルスの極性をよく考えなければいけません。
それにはMYCPU80のタイミングチャートをよく見て考える必要がありました。
3)メモリに書き込んだ後、メモリアドレスを+1する
メモリ(CY7C1347G)のアドレスはパワーONまたはリセット信号入力時にはゼロクリアします。
リセット信号はZB28Kから与えられます。
リセット時はMYCPU80のBUSRQ信号をアクティブにして、MYCPUの命令実行を一時保留させておきます。
CY7C1347Gは128K×4バイトの構成なので、アドレスはA16〜A0の17ビットです。
これは18ビットのバイナリカウンタの出力として与えます。
ここで17ビットではなくて18ビットのバイナリカウンタであるところがキモです。
4)メモリの書き込みサイクルと読み出しサイクルを設定する
CY7C1347GのアドレスとしてはA16〜A0の17ビットを与え、最上位ビットのA17はメモリのREAD/WRITEの切り換えスイッチとして利用します。
A17は下位カウンタが0000になるごとにトグルしますからそれを利用して、MYCPU80からのデータの書き込みサイクル(A17=0)と、それを読み出してZB28Kに送るサイクル(A17=1)の切換えに使います。
A17=1のときはメモリに蓄えたデータを読み出してZB28Kに送っている期間ですから、この間はMYCPU80のデータを読み取ってメモリに書き込むことはできません。
工夫すれば読み出しと書き込みを同時に(厳密には交通整理しながら見かけ上は同時に)行なうことも可能ですが、ZB28K側の処理が圧倒的に遅いために(このことについても後に説明の予定)、そのように工夫することは無駄作業になります。
そこでA17=1の期間はMYCPU80のBUSRQをアクティブにします。
5)ZB28Kへのデータ送出は8ビットのハンドシェークで行なう
ZB28Kへのデータの送出は8ビットパラレルで行います。
CY7C1347Gへのデータの書き込みは1つのアドレスに4バイト同時に行ないますが、それを読み出すときには1つのアドレスから同時に読み出した4パイトのデータを1バイトずつZB28Kに送ることになります。
さらにデータが正しく送られているかの確認のため、4バイトのデータに先立ってCY7C1347Gの現在のアドレス(A17〜A0)も送ります。
つまりMYCPU80の1組4バイトのデータをZB28Kに送るときは1組7バイトのデータとして、それを8ビットパラレルデータ7回の送信として送ります。
このあたりもどうやって実現するかというところに工夫が必要です。
ZB28Kにデータを送るにはZB28K側からのREADY出力と、XC95144XLからのSTROBE出力によって制御します。
ZB28Kは受信可能になるとREADYをHにします。
そしてSTROBEがLになるのを待ちます。
STROBEがLになったら8ビットのデータを読み取って、一旦READYをLにして、またすぐにHにします。
このときに出力されるパルスによってXC95144XLの内部カウンタがカウントアップされます。
XC95144XLは送信可能になるまでSTROBEをHにしたままにします。
データを出力するとSTROBEをLにします。
READYがHのときSTROBEをLにします。
ZB28K側の動作がシーケンシャルに説明しているのに対してXC95144XL側の説明はそのようになっていません。
上の説明ではデータ出力後にSTROBEをLにするというように書いていますが、それはそのココロでありましてVHDLのコーディングでは一見データ出力とは無関係であるかのようにSTROBE信号を記述しています。
実際VHDLコードはZB28K側のREADY出力によって機械的にデータ出力処理が行なわれるようにコーディングしています。
前回のリストを見ていただけるとわかりますように、STROBE出力の定義は
zbkrdymk<=an1(17) and ZBKRDY; ZBKSTB<=not zbkrdymk; |
10 A=0 20 OUT $83,$9A 30 INPUT "OK?"YN$ 40 OUT $82,$FF 50 WAH%=$FF:WAL%=$FF:WD%=$FF 60 IF BIT(IN($82),7)=1 GOTO 60 70 MAL%=IN($80) 80 OUT $83,0 90 OUT $83,1 100 'IF BIT(IN($82),7)=0 GOTO 90 110 IF BIT(IN($82),7)=1 GOTO 110 120 MAM%=IN($80) 130 OUT $83,0 140 OUT $83,1 150 'IF BIT(IN($82),7)=0 GOTO 140 160 IF BIT(IN($82),7)=1 GOTO 160 170 MAH%=IN($80) 180 OUT $83,0 190 OUT $83,1 200 'IF BIT(IN($82),7)=0 GOTO 190 210 IF BIT(IN($82),7)=1 GOTO 210 220 A%=IN($80) 230 OUT $83,0 240 OUT $83,1 250 IF BIT(IN($82),7)=1 GOTO 250 260 B%=IN($80) 270 OUT $83,0 280 OUT $83,1 290 IF BIT(IN($82),7)=1 GOTO 290 300 C%=IN($80) 310 OUT $83,0 320 OUT $83,1 330 IF BIT(IN($82),7)=1 GOTO 330 340 RW%=IN($80) 350 OUT $83,0 360 OUT $83,1 370 OP%=BIT(RW%,1) 380 MR%=BIT(RW%,2) 390 MW%=BIT(RW%,3) 400 IR%=BIT(RW%,4) 410 IW%=BIT(RW%,5) 420 'IF MR%*MW%*IR%*IW%=1 GOTO 440 430 AH%=C% 440 AL%=B% 450 D%=A% 460 'IF AH%<>WAH% GOTO 380 470 'IF AL%<>WAL% GOTO 380 480 'IF D%=WD% GOTO 440 490 PRINT HEX$(MAH%,2);HEX$(MAM%,2);HEX$(MAL%,2);" "; 500 PRINT HEX$(AH%,2);HEX$(AL%,2);" ";HEX$(D%,2);" ";HEX$(RW%,2);" "; 510 WAH%=AH%:WAL%=AL%:WD%=D% 520 IF OP%=0 THEN PRINT " OPCODE":GOTO 580 530 IF MR%=0 THEN PRINT " MEMRD":GOTO 580 540 IF MW%=0 THEN PRINT " MEMWR":GOTO 580 550 IF IR%=0 THEN PRINT " I/ORD":GOTO 580 560 IF IW%=0 THEN PRINT " I/OWR":GOTO 580 570 PRINT 580 OUT $86,0 590 GOTO 60 |