今では昔話にしか出てきそうにない MC68000 ですが、今更ながら動かして遊んでみました。 折を見て追記していきます。
MC68000 は米 Motorola 社(現 Freescale 社) が 1979 年に市場に投入したマイクロプロセッサ(MPU)です。 現在でもあまり古さを感じさせないコンセプトで、16ビット CISC プロセッサはこの時点ですでに完成されていたことがわかります。 16 ビットと書きましたが、この MPU は内部では完全に 32 ビット動作となっていて、16 ビットなのは外部とやりとりするデータバスだけです。
ワンチップマイコンではなく、むき出しの CPU なので、メモリをはじめとする周辺回路は自分で接続する必要があります。 今回は ROM 1MB、RAM1MB の構成で動作するように作ってみました。単品のプロセッサなので、 メモリはアドレス空間の許す限り自由に配置できます(MC68000 のアドレス空間は 16MB)。 この点はワンチップマイコンとは対照的です。
ROM/RAM が 1MB だとかなり厳しいですが、一応試作ボードで uClinux を動作させることができました。
実際に動作させてみた感触としては、立派な設計だが今となっては遅い CPU といったところです。 現在では SH2 や ARM といったマイコンがあり、1 クロックあたり 1 命令前後の処理をしますが、 MC68000 は 10 クロックで 1 命令くらいだと思います。同一クロックで動作する H8300H マイコンの半分くらいのパフォーマンスではないかと思います。
PC の環境は Ubuntu 12.04 です。gcc ツールチェーンの構築や uClinux カーネルのビルドは Ubuntu デストリビューションに依存しないと思うので、他の Linux あるいは Windows でも同様の作業は可能と思います。
多分作る人はいないと思うので、概略だけ書きます。
CPLD はアドレスデコーダとタイマー割り込み発生、割り込みアクノリッジサイクル等を実装しています。 アドレスデコーダを CPLD 内に実装しているため、メモリの配置はかなり自由に変更できます。 CPLD のソースコードをここに置いておきます。
写真では 120ns のフラッシュとなっていますが、今は 90ns のフラッシュに載せ替えています。 よって CPLD にメモリアクセスのウェイトサイクルを入れていません。
/RD 信号は全てのメモリに接続していますが、/WR 信号は CPLD 内で /WRU と /WRL に分け、 上位 8 ビットと下位 8 ビット別々に書き込みができるようにします。
メモリは 8 ビット品 2 個をデータバスの上位 8 ビットと下位 8 ビットに接続します。 MC68000 はバス幅が 16 ビットなので、アドレスの最下位は A0 ではなく A1 となります。 よって MC68000 の A1 をメモリの A0、A2 を A1... というようにメモリの方は一つ数字を減らした アドレス線に接続して行きます。
uClinux を動かすにはタイマーが必要なので、CPLD 内にバイナリカウンターを実装し、 これを簡易タイマーとして使用しています。CPLD のレジスタ節約のために、 写真では見えませんが、メモリの下に 74LS161 を 2 個実装し、これをクロック周波数を 1/256 するプリスケーラとしています。
(他に注意点等思い出したら追記します)
今回は ROM にフラッシュメモリを使用しているため、MC68000 自身にフラッシュ書き換えをさせたいと思い、ブートローダを作成しました。
ブートローダのソース一式をここに置いておきます(tar.gz形式)。
PC側の送信プログラムをここに置いておきます(Linux用)。
写真左端にあるジャンパースイッチで、ブートローダを起動するか、 ターゲットプログラムを起動するかを選択します。ブートローダを起動した場合は PC 側のソフトから プログラムを受信し、フラッシュ ROM を書き換えます。
ブートローダは通常フラッシュ ROM の最初のセクタに書き込まれたままです。 ブートローダ自体を更新する場合は、いまのところデバイスを取り外してフラッシュライターを使っています。
ブートローダは ROM の最初にいますが、MC68000 の場合、例外ベクタテーブルのアドレスを ROM の先頭以外に変更できないため、ターゲットプログラム側で例外処理を行う場合、 ブートローダからターゲットプログラムの例外処理ルーチンのアドレスにジャンプさせる必要があります。 この部分は正直 MC68000 の使いにくいところなので、MC68000 系 CPU で遊ぶ場合は例外ベクタテーブルの先頭番地を変更可能な MC68010 以降の CPU を使用した方が楽だと思います。
ROM/RAM 1MB の用途が思いつかなかったので uClinux を動かしてみようと思いました。
uClinux カーネル (2.4) を MC68000 用にビルドするのは予想以上に困難でした。
ツールチェーン構築の要点をいくつかまとめておきます:
-rw-rw-r-- 1 ki ki 8540 8月 13 00:24 README-20030314.txt drwxr-xr-x 7 500 513 4096 2月 9 2002 STLport-4.5.3 -rw-rw-r-- 1 ki ki 12109 8月 13 00:19 STLport-4.5.3.patch -rw-rw-r-- 1 ki ki 739943 8月 13 00:19 STLport-4.5.3.tar.gz -rw-rw-r-- 1 ki ki 1737 8月 13 01:27 binutils-2.10-allow-gcc-4.0.patch -rw-rw-r-- 1 ki ki 9835 8月 13 02:38 binutils-2.10-ar_overflow.patch -rw-rw-r-- 1 ki ki 34520 8月 13 00:20 binutils-2.10-full.patch -rw-rw-r-- 1 ki ki 5570368 8月 13 00:21 binutils-2.10.tar.bz2 -rwxrwxr-x 1 ki ki 21127 8月 13 00:30 build-uclinux-tools.sh drwxrwxr-x 2 ki ki 4096 8月 13 00:27 elf2flt -rw-rw-r-- 1 ki ki 54852 8月 13 00:20 elf2flt-20030314.tar.gz -rw-rw-r-- 1 ki ki 88841 8月 13 00:20 elf2flt-cygwin-020612.patch -rw-rw-r-- 1 ki ki 1782 8月 13 00:20 gcc-2.95.3-arm-mlib.patch -rw-rw-r-- 1 ki ki 16003 8月 13 00:20 gcc-2.95.3-arm-pic.patch -rw-rw-r-- 1 ki ki 716 8月 13 00:20 gcc-2.95.3-arm-pic.patch2 -rw-rw-r-- 1 ki ki 489 8月 13 00:21 gcc-2.95.3-cygwin-020611.patch -rw-rw-r-- 1 ki ki 57065 8月 13 00:21 gcc-2.95.3-full.patch -rw-rw-r-- 1 ki ki 1666 8月 13 00:32 gcc-2.95.3-m68k-zext.patch -rw-rw-r-- 1 ki ki 444 8月 13 01:34 gcc-2.95.3-open-mode.patch -rw-rw-r-- 1 ki ki 572 8月 13 00:21 gcc-2.95.3-sigset.patch -rw-rw-r-- 1 ki ki 12911721 8月 13 00:23 gcc-2.95.3.tar.gz -rw-rw-r-- 1 ki ki 927 8月 13 00:21 genromfs-0.5.1-cygwin-020605.patch -rw-rw-r-- 1 ki ki 20543 8月 13 00:21 genromfs-0.5.1.tar.gz drwxrwxr-x 16 ki ki 4096 8月 13 00:26 linux-2.4.x drwxr-xr-x 16 ki ki 4096 3月 15 2003 uClibc -rw-rw-r-- 1 ki ki 4372 8月 13 00:21 uClibc-0.9.19.patch.gz -rw-rw-r-- 1 ki ki 2070430 8月 13 00:24 uClibc-20030314.tar.gz
linux-2.4.x elf2flt-20030314.tar.gz uClibc-20030314.tar.gz
uClinux Kernel 2.4 の MC68000 対応箇所についてはここにまとめました。 主に MC68000 用の記述が入っていないことが原因です。 まだまだあったと思いますが、今回の試作ボードならではの問題が多いので割愛します。
MC68000 にはトレースビットというデバッグ用のステータスビットがあり、これを 1 にセットすると 1 命令実行する毎に例外(割り込み)を発生させることができます。 これを利用して、GDB とシリアル通信しながらソースレベルデバッグを行うことが可能です。
GDB とシリアル接続でデバッグ可能なベースプロジェクトをここに置いておきます(tar.gz 形式)。 m68k-gdbstub.c が GDB とシリアル通信する部分です。インターネットから拾ってきたソースを改変しています。
今回は PC とのシリアル通信に FT245 の仮想 COM ポート(linux では ttyUSBx)を使用しています。 ボード側で使用するシリアル通信ハードウェアによって、通信部分を変更する必要があります。
main に入って必要な初期化を行った後、gdbstub() をコールします。ここで GDB と通信するための初期化を行い、TRAP #1 命令を実行し、GDB 通信コンテキストに移行します。
GDB との通信はこのコンテキスト内(例外コンテキスト)で行い、ターゲットプログラムの実行は RTE によって例外から抜けることで行います。例外コンテキストに入る際にレジスタ等の値を 全て保存し、デバッガからのレジスタ値操作はこの保存されたレジスタ値を変更します。 そして RTE 前にこの保存レジスタ値をレジスタに復帰させて(ステップ実行を行うときは トレースビットを1にセットして) RTE すれば処理の続きが実行できる仕組みです。
現在の gdbstub の制限事項として、現状プログラムを GO した場合、GDB 側からこの実行を停止できるのはシリアル通信割り込みが許可されている場合だけです。 ターゲットプログラムが全割り込み禁止している状態ではシリアル通信割り込みも発生しないため、 プログラムの実行を GDB から停止させることができません。
ROM はフラッシュメモリであることを想定しています。フラッシュメモリに書き込まれたターゲットプログラムをデバッグする場合、 GDB はブレークポイント設定のためにフラッシュメモリのコードを書き換えます。 このとき現在の gdbstub の実装ではコード書き換えが発生したセクタをまるごと更新します。 時間がかかるのと、フラッシュメモリの書き換えサイクルをそれなりに消費しますが、一応動作しています。 フラッシュメモリの書き換えサイクルを消費したくない場合は RAM にターゲットプログラムをロードして デバッグできるようにビルドする必要があります。
まだバグがある感じで、たまに処理が戻って来ないことがあります。 この辺は追々改善して行きます。
2MB の DRAM ボードを製作し、 MC68000 に接続してみました。
512kB の DRAM を 4 個使用して 2MB としています。512kB の SRAM が手に入るのに、なぜ敢えて 512kB の DRAM を買ったのか我ながら不明です。 これはもう一生使わないかもしれないと思ったので、敢えてこの機会に使い切ってしまうことにしました。
想定していた用途は uClinux カーネルのデバッグでした(実際はこれを使う前に問題が解決してしまいました)。 GDB でソースレベルデバッグしてみるのところに書いたように、ROM にプログラムを書き込んで GDB でデバッグすると、ブレークポイントを設定するたびにフラッシュの書きが発生するので、 デバッグする間だけ DRAM 領域にプログラムをロードしてデバッグしてはどうかと考えて作りました。 (実際はおそらくメモリを 1MB も使うプログラムは書かないので、使うことはあまり無さそうです)
CPLD の実装がショボく、 DRAM のアクセス時は MC68000 を少しウェイトさせています。 実装を改善するとノーウェイトでいけるかもしれません。
MC68000 である程度開発できるようになったので、何かゲームを作るなり勝手に移植するなりしてみたいと思っています。
現在の MC68000 ボードは、下の写真のように MBM29F040-90 (90[ns])と HM628512BLP-7 (70[ns]) になっています。メモリ IC の下に余分なボードをかましているように見えますますが、これはもともと SSP-123 というピッチ変換基板(幅が DIP32 より広い)用にフットプリントを考えていて、途中で DIP32 を使うように変更したためです。SOP32、TSOP32 ともに DIP32 にピッチ変換できることがわかったので、SSP-123 は使わないことにしました。MBM29F040 の TSOP32 → DIP32 ピッチ変換ボードは、自分でテキトウにデザインして基板業者に作ってもらいました(実は一回失敗した)。