CPU実験室

誰も見向きもしない古いCPUをいじって動かしてみようというプロジェクトです

バスタイミング変更

電源起動時の動作が不安定なのはROMのアクセスタイミングが合ってない可能性があります。現状CPUのアクセスタイミングを取る/RdCEnと/AckはCPLDで常時Lでアサート状態になってます。/RdCEnは信号説明によるとリードトランザクションの完了を示すものとされていて

詳細をタイミングチャートで見ると/RdCEnをSysclkの立上がりでサンプリングしてアサート状態であればその周期の立下りでリード完了、/RDをネゲートするようです

最初から/RdCEnがLだとトランザクション開始1周期後、sysclk=20MHzなら50ns後にいきなりROMを読んでしまう訳でROMのアクセススピードに間に合ってません。ROMのマーキングがHN27C4096ACC-10なのでおそらく100ns。

そこで/RdCEnをちゃんと作ることにします。ROMへの読出し信号RDEN&ROMCSをD-FF2段で遅延させ/RdCEnとして遅れた分だけStallサイクルが挿入されるはず

修正したロジックの波形を見てみました。GNDの取回しでリンギングが酷いのと/RdCEnの前縁にカーソルを当てるべきでしたが1.5sysclk、75ns遅延してるのが判ります

ch1(黄):SYSCLK(20MHz)、ch2(水色):/(RDEN&ROMCS)、ch3(紫):/RdCEn

 

最小ループ

ROMを挿したボードを電源ONし早速制御ピン、データバス、アドレスバスをオシロで触ってみましたが結論から言うとNGでした・・

/ROMCSはLになりっぱなしでROMは常に選択され、ROMの出力イネーブル/OEはパルスが出ています。アドレスバスはA0、A1がパタパタしていて一見連続的にROMアクセスされているようですがA5,A6,A13が常時Hになってしまっているのでこれは0x1FC02060付近でループしていることになります。

このアドレスのROMは未使用領域で0xFFFFなので全データバスはHに張り付いていて期待する動作とは全く違います

プログラムはこれ以上単純にはならないし後はハード設計か部品の不良なわけで手始めにCPUをダメ元で79R3081E-40から79R3081-50に差換えてみました

(黄色く変色した警告シールから「動いていた」実績はある?)

ところが期待はしてなかったのですが何故かこれが大当り。アドレスバスはA0だけ変化してA1以降はは全てL。データバスはD27,D25,D24,D23,D22,D21,D20が振動し他は全てLでこれはアドレスが0番地と1番地で交互に0x0BF00000⇔0x00000000がデータバスに出力され正しく最小ループが回っているということになります。

ch1(黄):アドレスA0、ch2(水色):ALE

電源ON時のパワーONリセットでは変なアドレスでループしてしまう3081Eに似た状態に陥るのですが通電中にリセットボタンを押すと必ずこの正常状態で安定します。

いったい何が違うのでしょうか?3081EはMMUが内蔵されているのでメモリマップが初期化してないのか、40MHz品と50MHz品でタイミングマージンが違うのか、はたまた単純にCPUが壊れてるのか?

ROM焼き

バイナリコードが出来上がったのでこれをROMに配置していきます

まず32bitのコードを上位下位16bitに分割し2個の16bit幅ROM:27C4096に焼き付けますが要注意なのは数値表現のバイトオーダーです。IDT79R3081はリセット時の設定によってビッグエンディアン/リトルエンディアンの切替えができ、現状はビッグエンディアン設定になっています(GALのロジックで設定可能)

これをROMに焼くとき8bit幅ROMであればmod4でアドレスを間引いて4個のROMに分ければいいですが16bit幅ROMだとワード内のアライメントを考慮しなければなりません。

自作ROMライタはx86系のリトルエンディアンを想定していて下位アドレス(偶数アドレス)がROMのD7..D0、上位アドレス(奇数アドレス)がROMのD15..D8に接続されるので「F0/0B」とバイトスワップしてセットしなければなりません

今回のプログラムは小さく修正1か所なので手打ちでできてしまいますが今後大きいコードを読み込むようになったらROMライタの機能にエンディアン切替を入れた方がよさそうです

上位下位16bitROMを焼いて基板にセットしました

 

答え合わせ

ハンドアセンブルの結果が正しいか、gnuアセンブラも通しておきました。構文はGCC開発環境の構築で用意したスタートアップルーチンとほぼ同じです

test.s:

永久ループの次にあるNOPは遅延スロットで実行される命令として入れてあります。MIPSにはNOP専用命令があるんだ、とおもったらこれはシフト命令のエイリアスのようです

「ゼロレジスタを0回左シフトしてゼロレジスタに代入」という何の意味も影響もない命令がうまい具合に(というかそういう設計で)0x00000000にアサインされています。このコードをアセンブルしてリロケータブルオブジェクトにした後、これと同じようなリンケージファイルmips.ldで実アドレスにロケートします

最初アセンブラソースに実アドレス.org 0x1fc00000を指定したところ、アセンブラがしばらく沈黙した後500MB超えの巨大オブジェクトが出来てしまいました。

・・・これと同じ。.orgディレクティブは「スタートアドレスを決める」のではなく「セクションの先頭からどれだけずらすか」という意味なんですね。

セクションごとにロケートされたELFファイルを逆アセンブルすると

たしかにジャンプ命令のバイナリコード0x0bf00000が出力されています。nopが2行生成されましたが1個目はソース通り。2個目はただのパディングのようです

初コーディング

どうやらCPUは生きていてアドレスラッチも動いているようです。ここでROMを挿して意味のあるコードを動かせればCPUのコードフェッチやデータバスの確認ができます。

最初のプログラムはいろいろなCPUでさんざんやってきたこれです:

自分自身に戻るだけの一番簡単なプログラムでリセットベクタに置けばバスの挙動を観察できます。

既にMIPSプロセッサのGCC、アセンブラといった開発環境は用意できているのでアセンブルすれば一発ですが勉強のためにハンドアセンブルもやってみました。それでIDTのマニュアルを探っているのですがデータシートやハードマニュアルがほとんどでインストラクションマニュアルというのが見当たりません。ハードマニュアルにあるのはこういうサマリ部分だけでした。

おそらくMIPSアーキテクチャ準拠なので詳細は本家を当たってくれということかもしれません(日本語ではここがわかりやすかったです)

ここで問題になるのは命令長は32bit固定なのでジャンプ命令のOPコードに6bitあてがうと引数を26bit長しか取れません。つまり引数は全メモリ空間の絶対アドレス即値は指定できず、かといってPC相対アドレスでもありません。

たとえばリセットベクタ0x1FC00000にこの命令を置く場合

飛び先アドレス32bitをどうやって26bitに圧縮するのか?というと「擬似直接アドレッシングpseudodirect addressing」という妙なことをやるようです

まず実効アドレスは32bit①ありますが命令長は32bit(4byte)固定で命令途中に飛ぶことはあり得えず下位2bitは捨てることができこれで30bit②になります←これは納得

次に上位4bitをバッサリ捨て「その命令が置かれたアドレス上位4bit」と同じであると想定して26bit③になります←これはPC相対のようなもので如何なものかとも思いますが

これにジャンプ命令のOPコード6bit(000010)④を最上位側に連結すれば32bitのバイナリコード⑤0x0BF00000が得られます

 

CPU実装

ロジックを書き込んだGALとCPLDを基板に挿入。これで既に実装したデバイスの信号行き先が確定するのでいよいよ御本尊のIDT79R3081も安置しました

∫dtマークが3ツぞろいでカッコいい。実は基板裏面にも2ツあります

ソケット段階で電源関係が正しく結線されていることは確認済なので電源も投入してしまいます。+5Vは約750mA程度になり放置するとCPUも結構熱くなります。ただ煙が出たりはないので先ずはOK。信号周りを素早くオシロで当たっていきました。

なおこのパッケージはマーキングを正立させると1番ピンが右に90度回転した位置になります。なのでデータシートにあるpinout図と首っ引きで実機のピンをプローブするとき誤ったピンに触れてしまうことがあります。期待するロジックレベルでないので何度ヒヤッとしたことか・・

 

現状ではROMが無いので電源ONで暴走状態になってるとおもいますがROMの最下位アドレスA4(ch1)とALEアドレスラッチイネーブル(ch2)をプローブしてみるとアドレスバスはちゃんとドライブされていて、CPUが「なんかしてそう」なことは確認取れました

 

GALロジック

このボードにはGAL16V8で構成したもう一つプログラマブルロジックがあります。回路図中ではCPUの左下で割込み信号INT(0..5)に接続されています

割込み要因:シリアル通信、タイマ割込みの接続先をアサインしているだけでGALなしで直結させてもいいように見えますがR3081ではこれらのピンがCPUの動作モード設定を兼ねていてリセット時の割込み信号レベルを読取る仕組みがあります

電源ON直後からリセット解除後しばらくの間は設定したいベクタを出力し、その後は割込み要因に接続すればソフトウェアDIPスイッチ的な働きになる筈です

この機能は追って作り込みますが今の時点ではすべての割込み出力をネゲート(H)に固定するだけのロジックにしておきます