PC-6001: February 2009アーカイブ

現在の設計を出しておきます。 一応、CPLDが容量の範囲内でプログラム可能であることはチェックしてあります。

20090226-28pin-CPLD.PNG

ただ、正直言って設計にはいまひとつ満足していません。 XC9536XLを2つ使うというのは、価格との折り合いをつけたものです(XC9536シリーズは1個350円くらい)。 ピン数およびチップ数のバランスを考えると、これがもっとも小規模なのです。

この設計だと、CPLD内にレジスタが1つしか取れないため、タイミングの関係でPC側の動作を少々変更する必要があります。 現在テスト中の回路では、コマンドのoutを行ったあと、busyチェックのためのinを行う前にnopを1つ入れる必要があります。 これは動作速度の関係で今のところ避けられないのですが、レジスタを1つにした場合、もう少し時間を見る必要があります。 AVRがデータ入力を行ってからbusyデータを出力する必要があるためです。 現在は入出力のレジスタが分かれているため、busyデータ出力をしてからデータ入力をしています。

回路を簡単にする、かつ安価に作る、かつ必要十分な機能を実装する、という制約を満たすのはなかなか難しいものです。 


SDカードインタフェースはほぼ期待通りに動作しています。 ハードを設計して、作って、ソフトを設計して、作ってというのは結構長い道のりですが、動いたときはうれしいものですね。 いつもはソフトしか作らないので、またこれも格別なものがあります。 とはいっても、今回は西田さんにもかなり助けられていますが。 ありがとうございます。

さて、次の課題はブートアッププロセスです。 現在のインタフェースはメモリを積んでいないため、テストプログラムはカセットインタフェースから読んでいます。 ベルーガのカートリッジと一緒に挿せばROMからブートアップが可能ですが、拡張ユニットを作るのが(配線が)面倒なので、まだやっていません。

現在のインタフェースに使っている部品は以下のとおりです。 安価で手に入りやすい部品のみ使用しています。

  • ATmega8L (88でも動作可)
  • 74HC373
  • 74HC374
  • 74HC4078
  • 74HC32
  • レギュレータ 48M033F
  • コンデンサ 0.1u x 1, 10u x 1
  • 抵抗 1k x 1, LED x 1

これまでのアイデアは、CPLDを使ってロジックをまとめ、SRAMを同時搭載することによりブートアップ時にデータをSRAMに転送することです。 理論的には全く可能なのですが、意外に多ピンのCPLDは高いです(SRAMは安い)。 というか、もともとCPLDは(上記の部品に比べれば)結構高価です。 部品代もあるので、あまり高価な部品は使いたくありません。

AVRで扱えず、ロジックで必要になるのは以下の部分です。

  1. 入出力用のラッチ(8ビット x 2)
  2. I/Oポートのデコード
  3. SRAM初期化用アドレス発生
  4. アドレスバス・データバスのバッファリング

このうち、1, 2については現在既に行っています。 3, 4が問題で、入出力数が多いので小さなCPLDでは無理があります。 ATtiny2313 + 74HC244/245数個という組み合わせもありますが、DIPでは基板に乗り切らなさそうです。

ここは金を使わず頭を使って(?)、何とかうまい方法を編み出したいものです。


なかなか苦しんでいたAVR版SDカードインタフェースですが、ファイルのデータ読み込みまで動作させることに成功しました。

結局outの割り込み(INT1)の最後でペンディングしているin割り込み(INT0)を解除し、かつステータスデータをINT0割り込みで出すようにすることでタイミングが取れるようになりました。

データの入力はinを連続させて取得できるようにしているので、こちらも動作タイミングが重要です。 AVRによるSDカードからのオンデマンド1バイト読み込みには、現在のところ80クロック強かかっているので、Z80側はそれに合わせて読み込ませます。 現在使っているAVRは8MHz動作なので、約4MHzのZ80では1バイトあたり40クロック強かけることになります。

なお、現在の設計では、クロックポートを空けてあります。 そのため、ATmega88を使えば、外部クロックで20MHz駆動が可能なので、その場合は16クロック強/バイトということになり、INIRなどを使った連続読み込みも可能になるかと思います。

まだ読み込みデータの取りこぼしなど細かいところをチェックしていませんが、10kB/sec以上は出ています。 最適化はまだ行っていないため、今後ロジックを詰めることにより高速化が可能です。

ジョイスティックポート版がおよそ2kB/sec(CRT KILLで4kB/sec)なので、やはりかなり高速化しますね。

それと、FATの処理をAVR内部で行っているので、Z80側のプログラムは非常に小さくなります。 ルートディレクトリのファイル読み込みだけなら1kB以下の容量でプログラムできます。


AVRでは、割り込み処理中にも割り込み"要求"自体が禁止されていなければ、外部からの割り込みに対して要求が発生するようです。 コメントにもあるように、その割り込みは保留(pending)され、現在処理中の割り込み処理が終了した後で処理されるようなのです。 このあたりが情報源です。

概念として、「割り込み要求(ハードウェアで処理される)」と「割り込み許可」は独立して動作しているということのようです。

そのため、割り込み処理中の別の割り込み要求を完全に禁止したければ、割り込み要求ビットをクリアしておく必要があります。

具体的には、今回はINT1の処理中のINT0割り込みを禁止したいので、INT1の処理内でステータスデータを出力する部分で、

GIFR = _BV(INTF0);

とします。

ステータスデータを出力する前にクリアしてしまうと、データを取りこぼすことがあり、出力後にクリアするとステータスデータを2度読んでしまうことがあります。 なんとも厄介ですが、これで一応正常に動作しているように見えます。


追記: と思ったのですが、まだ時々ずれる(ステータスデータを2度読む)ことがあります。 一体どうせいっちゅうねん...。


さて次は、いよいよファイルデータのオープンと読み込みです。 SRAM容量の関係で、読み込み処理をオンデマンドで行う必要があります。 そのため、inの連続で処理するためには、処理時間を一定にすることが必要で、以前の実装(1セクタを一気に読む)では通用しません。


どうもうまく動いてくれないSDカードインタフェースですが、問題はクリアになりました。 わかりにくいので、シーケンス図にします。

sequence.png

この図で、PCからのリクエストがin/out命令で、OUT buffer/IN bufferがそれぞれフリップフロップやラッチです。

最初にコマンドをout命令で送出し、その後inを繰り返してデータを取得するという典型的な例です。 コマンド処理中はbusyをあらわすステータスがIN bufferに入っているので、PC側からはポーリングしながら待ちます。

問題は、赤い部分です。 ここは本来、INT1の処理の最後で書き込んだステータスが返されるはずなのですが、実際にはステータスは稀にしか返りません。 なぜか、ここでINT0の出力が返ります。

しかし、ときどきステータスも返ることがあるため、INT0の出力データの最初のバイトにステータスを埋め込んだとしても、データのずれが発生してしまいます。

なんでこんなことが発生するのか、まだ解明できていません。 busyが返るin命令でも割り込み信号自体はAVRに伝わっているので、それが何か影響しているのかもしれません。 プログラム上は、INT0/INT1の処理中(=割り込み処理中)は割り込みは禁止になっているはずなのですが。

なお、プログラムのメインループはsleepによりアイドル状態(スリープではない)の無限ループとしています。

while (1) {
  sleep_mode();
}

こんな感じです。 基本的に割り込みでしか動作しません。


SDカードインタフェースのハードウェアが出来上がったので、早速実際のSDカード入出力を作り始めています。 まずは、試しにディレクトリ情報を読み出しています。

今回は、AVR側にFATシステムを入れることにより、高速化を狙っていますが、1ポートのインタフェースで通信するのはなかなか難しいことがわかりました。

たとえば、BASICで入出力を組んでやると、特に問題なくSDカードからデータが読めます。 しかし、マシン語になると、処理によってはZ80からの要求がシビアで、AVR側の処理が追いつきません。


PCからのデータ出力時(コマンド)

PCからoutでデータを出力したら、それに反応してAVRが動作するようにしています。 処理中の場合はステータスとしてBUSYを示す信号を出すなどして本体側を待たせればいいわけですが、BUSYを出すまでの時間が問題です。 普通のAVR-GCCではレジスタ退避をするのでとても追いつきません。 そのため、割り込みベクタ部は一部アセンブリで組んでいます。

以前の調査で、割り込みルーチンの開始までの反応時間(ベクタテーブルからのrjmp含む)は8MHzでおよそ8クロックであることがわかっています。

BUSYシグナルを出すためには、アセンブリでも現状で12クロックほどかかるので、合計20クロック。4MHz相当で10クロックです。 out命令終了から次のin命令まで10クロックということは、間に1命令程度挟めば普通に間に合うことになります(in命令自体の入力動作が命令処理の後ろのほうであるため)。

これはほぼうまく動くようになりました。


PCへのデータ入力時(レスポンス)

こいつが問題です。

なるべく入力割り込みの処理を軽くして、連続するin命令に対応できるようにしようとしています。

ディレクトリ情報などは、SRAM内で保持してもたいしたことはないので、コマンド実行時に結果を用意しておいて、1バイトづつ返せば、割り込み処理自体もそれほど時間はかからないでしょう。

ところが実際は、けっこうwaitを入れながらでも、入力データのとりこぼし(というか、ずれ)が生じます。 PCからはINRQ信号で割り込みをかけ、AVRがそれに対し「次のデータ」を返す(でないと間に合わない)のですが、割り込み処理中の多重割り込みはしていないので、完全なハンドシェイクにはなりません。 AVRとZ80がうまく同期していないんですね。

特に、コマンド実行直後のステータス情報を取得する部分(連続するinの最初のバイトの受信)でうまく同期が取れていないようです。 busy状態からデータ入力に変わるところなので、タイミングが微妙なのです。 ステータス情報の確実な受け渡しのために、ステータスにPCからのackを返せばいいかもしれませんが、それに対してまたbusy状態になって...きりがありませんね。

また、セクタデータを読み出す際は、ATmega8では1セクタ分(512バイト)のバッファが取れないので(既にFATで512バイト使っているため)、オンデマンド読み込みをするしかなさそうです。


データもステータスも一つのポートでやり取りしなければならないのが根本原因のひとつなのですが、ここまできた以上なんとかするしかないですね。 完全に1バイトづつハンドシェイクする方法もあるのですが、なんかそれは負けた気がする...。


何とか手持ちの資料とかいただいた資料とかを駆使して、RS-232Cコネクタの信号線を類推(?)してみました。

本体のカバーをあけると、以下の写真のようになっているところがあります。

Photo-0070.jpg

3つコネクタが並んでいますが、左からCN10(スピーカ)、CN4、CN5です。このCN4とCN5に、写真上にねじ止めされているRS-232Cユニットからのケーブルを接続します。

まず、CN4です。in/out方向は本体側から見てです。

  1. RESET(out)
  2. D0(inout)
  3. D1(inout)
  4. D2(inout)
  5. D3(inout)
  6. D4(inout)
  7. D5(inout)
  8. D6(inout)
  9. D7(inout)
  10. ^IORD(out)
  11. ^IOWR(out)
  12. Career Detect(in)

次、CN5です。

  1. ^CS0(out)
  2. A0(out)
  3. NC
  4. ^RxRDY(in)
  5. Clock(x2)(out)
  6. Clock(x1/2)(out)
  7. GND
  8. +5V(out)
  9. +12V(out)
  10. -12V(out)

正しい保証は全くありません。

RESETは正論理のようです。 IORDとIOWRは、それぞれIORQ or RD, IORQ or WRがデコードされたものなので、このままI/Oリクエストとして扱うことができます。 ^CS0は8251の^CSに接続されているChip Selectなのですが、この信号が本体内でどのように作られているのかはよくわかりません。

以上が正しいとすると、前回の想定はほぼ正しいことになります。 アドレスのデコードはおろか、I/Oリクエストのデコードも不要になるので、かなり論理が簡単になることが期待できます。 ただし、I/Oポートは0x80/0x81(またはそのイメージ)に固定されます。

これでSDカードインタフェースを作ったとすると、拡張スロット用の回路からHC32とHC4078を排除することができるので、かなりお手軽です。

ただ、メモリの接続はできないので、やはり通信系のインタフェースを実装するのが適切なのではないかと思います。


西田さんに作っていただいたプリント基板上に、部品を実装しました。 ICソケットを使った関係で両面の半田付けにてこずったりしましたが、完成しました。 回路図は以前設計したものです。

Photo-0068.jpg Photo-0069.jpg

さすがプリント基板、仕上がりが美しい~。

ちなみに、元の回路に入れ忘れていたLEDを追加実装しています。 あと、SDカードのGNDの配線が一つ抜けていたのはジャンパを飛ばしました。

実はPC-6001初代の場合、ISPコネクタの位置が悪く、スロットの入り口にぶつかってしまいます。 まぁ何とかなるレベルではありますが。

ソフトウェアのほうは、とりあえず以前ユニバーサル基板で実装したときに動作させたもの(リード)は試して、うまく動作するところまでいきました。

全機能の実装はこれからです。

今回作ってみて思いましたが、プリント基板の場合は表面実装部品のほうが作りやすそうですね。


PC-6001をいろいろと拡張すると、拡張スロットが足りなくなります。 拡張スロット自身はパラレル結線のみで拡張できるので自作可能ですが、スロットごとに50本の結線をするのはなかなか骨です。 プリント基板を作れれば問題ないんですけどね。

そこで思いついたのですが、RS-232C用のコネクタが使えるではありませんか。 PC-60m61をPC-6001初代に実装して使えた経験から、少なくともPC-6001/mk2/6601では同じ仕様になっているのではないかと思います。

RS-232Cなど実装せずにコネクタを遊ばせている人がほとんどでしょうから、実質的に開きスロットとして扱えるでしょう。

ただ拡張スロットと異なり、特定のI/Oが既に割り当てられているので、アドレス線に自由にアクセスしたりすることは不可能です。 逆に言えば、I/Oリクエストのデコードが不要となるし、少量(63バイト)ですが専用のバッファもワークエリアに割り当てられています(0xfbf9-0xfb38)。 さらに、受信割り込みもデフォルトで存在します(ベクタは0xfa04, 0xfa05)。

これで何をするかといえば、RS-232Cの代わりということで、やっぱりネットワークですよね。 Ethernetなら、MicrochipのENC28J60を使ってSPIでやり取りするか、RealTekのRTL8019ASを使って8ビットバスでやり取りすることができそうです。

ENC28J60のSPIクロックは0Hzまで許容、バッファ8kB、秋月で600円(28DIP)。 RTL8019ASはバッファ16kB、秋月で800円(100QFP)。 Z80から直接ドライブするのならRTL8019ASのほうが高速になると思いますが、ENC28J60のほうが回路の作成は楽そうです。

もっとも、RealTekのチップは、そこら辺にあるジャンクのEthernetボードからいくらでも(?)毟れそうですね。 ただ、手元にあったチップはRTL8029で、これはPCIバス用でした。 ところで、PCIのイーサネット基板にはRTL8092という16DIPの部品が乗っているのですが、これは何でしょうね? データシートを検索しても見つかりませんでした。

ENC28J60の場合は、外付け部品も、

  • バッファ(HC125)
  • 25MHzクロック
  • 3.3V降圧
  • パルストランス内蔵のRJ-45コネクタ(秋月で300円)
  • 数個の抵抗およびコンデンサ
くらいでよさそうです。

問題は、本体側のコネクタの仕様がわからないことくらいですね。 8251の仕様から考えれば、以下が出ているはずなのですが。

  • 8bitデータバス
    多分Z80のデータバス
  • RESET
    多分Z80のリセット信号
  • CLK
    多分Z80のクロック信号
  • Control/Data select
    多分A0(I/Oポート0x80-0x81のイメージが0x82-0x8fに出ているため)
  • RD
    多分Z80のRD信号
  • WR
    多分Z80のWR信号
  • CS
    う~ん、何だろう? Z80のIORQかな?
  • Vcc
    +5V
  • GND
    0V

で、本体のピン番号がわからないんですよね。

って、それがわからなきゃ作れないですね。:-) PC-60m61を分解すればわかるかなぁ。


いろいろトライ&エラーで、最適化の方法がだんだんわかってきました。

一番簡単なのは、「出力信号ごとにプロセスを分ける」ということです。

たとえば、昨日の例なら、次のようにします。

  process (ATOB, G)
  begin
    if G = '0' and ATOB = '0' then
      BUSA <= "ZZZZZZZZ";
      Reg <= BUSA;
    elsif G = '0' and ATOB = '1' then
      BUSA <= BUSB;
    else
      BUSA <= "ZZZZZZZZ";
    end if;
  end process;  process (ATOB, G)
  begin
    if G = '1' and ATOB = '1' then
      BUSB <= Reg;
    else
      BUSB <= "ZZZZZZZZ";
    end if;
  end process;
  

ただし、この例は簡単すぎるので、マクロセルを含めリソースの消費量は変わりません。

あと、バスに複数の信号を選択的に入れたい場合、マルチプレクサが挿入されます。 マルチプレクサ一つにつきマクロセル一つを消費するようで、今回の試作ではこれが大きく響いています。 2つある8ビットのデータバスの両方とも、複数のデータソースを持つためです。

実際にはフィッティング時の最適化で、「必ずこれによりマクロセルを消費する」ということは言えないようですが。 わかっている範囲で、単体でマクロセルを消費するロジックは以下のとおりです。

  • マルチプレクサ
  • 3ステートバッファ
  • XORゲート

レジスタは必ずしも単体でマクロセルを消費しないようです。

こんな感じで最適化を行った結果、次のようになりました(XC9536ではオーバーしてフィッティング結果が出ないので、XC9572でフィッティングを行っています)。

Macrocells     Product Terms    Function Block   Registers      Pins           
Used/Tot       Used/Tot         Inps Used/Tot    Used/Tot       Used/Tot       
42 /72  ( 58%) 96  /360  ( 27%) 77 /144 ( 53%)   16 /72  ( 22%) 31 /34  ( 91%)
  

マルチプレクサを低レベルのロジックで手組みすると、少し減ります。 すなわち、

if COND1 = '0' and COND2 = '0' then
  BUSA <= BUSB;
elsif COND3 = '0' and COND4 = '0' then
  BUSA <= Reg;
end if;
  

とする代わりに、

if (COND1 = '0' and COND2 = '0') or (COND3 = '0' and COND4 = '0') then
  BUSA(7) <= ((COND1 nor COND2) and BUSB(7)) or ((COND3 nor COND4) and Reg(7));
  BUSA(6) <= ((COND1 nor COND2) and BUSB(6)) or ((COND3 nor COND4) and Reg(6));
  ...
end if;
  

のように、低レベルな記述に手で変換してやります。 すると、以下のようになりました。

Macrocells     Product Terms    Function Block   Registers      Pins           
Used/Tot       Used/Tot         Inps Used/Tot    Used/Tot       Used/Tot       
40 /72  ( 56%) 94  /360  ( 26%) 82 /144 ( 57%)   16 /72  ( 22%) 31 /34  ( 91%)
  

目標とするマクロセル数は36。惜しいですねぇ。


CPLDの設計を始めて、とたんに壁にぶつかりました。 マクロセルの消費量が予想より多く、手持ちのXC9536XLでは足りなさそうなのです。

ロジックはそれほど難しいものではないのですが、2つの8ビットレジスタ(ラッチ)と、2つの3ステートバッファを使っています。 これがどうもマクロセルを喰うようです。

たとえば、双方向3ステートバッファとして次のようなものを考えてみます。 74245と同じです。

entry TristateBuffer is
  Port (
    ATOB: in std_logic;
    G: in std_logic;
    BUSA: inout std_logic_vector(7 downto 0) bus;
    BUSB: inout std_logic_vector(7 downto 0) bus;
  );
end TristateBuffer;
architecture Behavior of TristateBuffer_body is
begin
  process (ATOB, G)
  begin
    if G = '0' and ATOB = '0' then
      BUSA <= "ZZZZZZZZ";
      BUSB <= BUSA;
    elsif G = '0' and ATOB = '1' then
      BUSA <= BUSB;
      BUSB <= "ZZZZZZZZ";
    else
      BUSA <= "ZZZZZZZZ";
      BUSB <= "ZZZZZZZZ";
    end if;
  end process;
end Behavior;
  

これを合成すると、以下のような結果が得られます。

Macrocells     Product Terms    Function Block   Registers      Pins           
Used/Tot       Used/Tot         Inps Used/Tot    Used/Tot       Used/Tot       
16 /36  ( 44%) 32  /180  ( 18%) 20 /72  ( 28%)   0  /36  (  0%) 18 /34  ( 53%)
  

既にマクロセルを16個使っています。 バッファ1つにマクロセル1つ消費する格好です。

これに単純なラッチを追加してみます。

architecture Behavioral of TristateBuffer is
  signal Reg: std_logic_vector (7 downto 0);
begin
  process (ATOB, G)
  begin
    if G = '0' and ATOB = '0' then
      BUSA <= "ZZZZZZZZ";
      BUSB <= "ZZZZZZZZ";
      Reg <= BUSA;
    elsif G = '0' and ATOB = '1' then
      BUSA <= BUSB;
      BUSB <= "ZZZZZZZZ";
    elsif G = '1' and ATOB = '1' then
      BUSA <= "ZZZZZZZZ";
      BUSB <= Reg;
    else
      BUSA <= "ZZZZZZZZ";
      BUSB <= "ZZZZZZZZ";
    end if;
  end process;
end Behavioral;
  

すると、結果は次のとおりです。

Macrocells     Product Terms    Function Block   Registers      Pins           
Used/Tot       Used/Tot         Inps Used/Tot    Used/Tot       Used/Tot       
16 /36  ( 44%) 40  /180  ( 22%) 20 /72  ( 28%)   8  /36  ( 22%) 18 /34  ( 53%)
  

ラッチ部分にレジスタがアサインされ、マクロセルは増大していませんね。

しかし、これはかなりシンプルな例で、if条件を複雑にすると一気に消費マクロセルが増えてしまうようです。

たとえば、今回の試作では、それぞれ独立に合成すると、以下のようになります。

  • バスに関係のないデコードに5マクロセル
  • メモリアクセスのための3ステートバッファに17マクロセル
  • I/O用に8ビットのラッチを2個入れると、16マクロセル

合計は38マクロセル(これでもオーバーですが)なのですが、全部を一気に合成すると56マクロセル消費します。 もちろん条件判断漏れで余計なラッチが合成されないようにプログラムしてです。 しかも、ifの条件の順番を変更すると、使用マクロ数が変動したりします。う~ん。

そんなわけで、なかなか詰め込みも苦労しますが、限られたリソースで何とかするのに慣れているので、これはこれで最適化のし甲斐がありますね...。 最適化病?


西田さんの作られた基板を送っていただいているので(感謝!)、到着するまでの間、CPLDを使った実装について考え始めています。

まず、手持ちのCPLDがXC9536XL-PC44なので、それでどこまでできるか考えてみました。 結果、次のような課題が明らかになりました。

  • このチップは外部I/Oが34ピンしかないため、アドレス線をフルデコードすることは無理。 そのため、アドレスは外部でラッチするか、本体のアドレスカウンタを利用する変態実装をする必要がある。
  • (変態実装時)VHDLのコーディングが悪いのか、メモリ系とI/O系を同時にハンドリングするとマクロセル(36個)が不足する。 まぁそれ以前に、変態実装時はアドレスの同期が結構大変そうですけど。

単純に考えると、I/Oは以下のとおりです。

  • PCからのアドレスバス(16)
  • PCからのデータバス(8)
  • PCからのMREQ, IORQ, RD, WR(4)
  • SRAMへのアドレスバス(15)
  • SRAMおよびAVRへのデータバス(8)
  • SRAMへCS, WE, OE(2)
  • AVRへの入力および出力の割り込みリクエスト(2)
  • AVRからのINIT(PC/SRAMセレクト), WR, RD信号(3)

合計58ピンとなります。 SRAMを使うので、EXRAS/EXCASや、CS2/CS3はなくても動作するのではないかと思います。

もっと規模の大きなCPLDを使えばいいだけなんですが、手持ちにないのと、回路を複雑にしたくないので迷っています。 また、規模の大きいものはかなり値が張るようです。XC9536XLが320円なのに対し、規模を大きくしてピン数の同じXC9572が800円、100ピンのものは1000円します。

やたらI/Oを喰うのはアドレスバスで、これを外部で任せれば、CPLD内で必要なのはSRAMデコードのためのA15, A14およびI/O用のA3~A0(と他のアドレス線をORしたもの)くらいなので、初期化時のアドレス発生を安価なAVRに任せたほうが全体として安くなりそうです。

または、9536を2個使う構成も考えられますね。アドレスの発生とバッファ用に1つ使ってもいいかもしれません。

ROMレスシステムの回路を試しに描いてみました。 複雑度の雰囲気を掴むだけなので、正しい保証はありません。

まずは40ピン系(ATmega164/324/644)。

20090204-40pin.PNG

次に、28ピン系(ATmega8L/88/168/328)。 カウンタはATtiny2313を使ったほうが安くなりますが、わかりやすさのために2段のカウンタを使っています。

20090205-28pin.PNG

おっと、初期化時は本来A14をHにしなければいけませんね。 または本体からのA14にNOTを入れるか。

それにしても、かなり複雑ですね。 CPLDで実現するとなると、ゲート数やレジスタ数の割にI/Oが多いので、バッファを使わないともったいないことになるかもしれません。


西田さんからのコメントがあったので、ROMレスシステムについて考えてみました。

基本的な考えは、

  • SRAMをROM領域(0x4000-0x7fff)に確保しておく
    (実際には32kB分のSRAMを0x4000-0xbfffに配置してRAM拡張を兼ねる)
  • 起動時にSRAMにブートアップ用のプログラムを転送し、そこから起動する

以前、私もAVRのプログラム領域、またはSDカード内にブートアップ用の(Z80の)プログラムを入れておいて、そこから起動することも考えてはいたのですが、直接AVRからZ80に読むにはタイミングが間に合わないのでそれ以上考えていませんでした。

しかし、西田さんのアイデアのように、いったんSRAMに転送してしまえば問題はなさそうです。

ブートアッププログラムはAVRに焼きこんでもいいのですが、SDカードに入れておけば、カードの差し替えで起動方法を変えられるのでさらに便利そうです。

なんかSDカードからのブートアップなんて格好いいかも? :-)

課題は次のとおりです。

  • デコードおよび調停が複雑になる
    メモリ系のピン(MREQ, RAS, EXCAS等)のデコードが必要なほか、アドレスの上位も扱う必要があります。
    また、SRAMに書き込んでいるときには本体からの読み書きを停止する必要があります。 いよいよCPLDの出番ですかね。
  • PC-6001が起動してROMからブートアップするタイミングまでにSRAMへの書き込みが間に合わない可能性がある。 特にSDカードを使った場合。
    これは、電源を入れてブートアップを読み込んだ後、手動で本体をリセットすればいいのですが、格好悪いですね。
  • 28ピン系のAVRでは入出力が厳しい。
    仮に可能な限りI/Oを共用をしたとしても、28ピン系のAVRで使えるI/Oは23本です(リセット含む)。 データ線に8本、アドレス線(ROMの16kB分, 14本)、制御線(ブートアップ中であることを示す, 1本)を出したとすると23本。 う~んぎりぎり。 この場合SDカードからの読み込みはI/O不足でできません。 時間分割すれば不可能ではないでしょうが、なんか不毛かも。 SDカードから読み込みたければ、素直に40ピン系のAVRを使ったほうがいいでしょうね。

というわけで、けっこう変えなきゃいけません。 これまでの設計はなるべくシンプルにしてきましたが、実際BIOSが必要であることを考えると(BelugaやPC-6006のカートリッジと同時使用する場合を除き)、メモリの扱いも必要なんですね。

とりあえずシンプル版?とROMレス版、2本立てで考えを進めてみようかと思います。