CPU実験室

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

タイミング修正

このような不具合はカウンタが最初のMC14516の時には起きていませんでした。CPLDに置き換えてからだと思うのですが、ここでMC14500のユーザーズマニュアルに立ち返ってLIFO付の回路例を改めて見てみると、クロックラインに何やらCR積分が入っています。

f:id:O3I:20210411115317j:plain

図面の注釈のところでそのCRの値は「経験的に」決めた、とあります。何ぞそれ?

ヒゲやタイミングで動かない回路にCをペタペタ貼り付けて何とかしちゃうというのはアマチュア的ではありますがともかくやってみます。積分で波形を鈍らせて立上りを遅延させ、立下りはダイオードで急速充電させて遅れをなくすということでしょうが念のためシミュレーションしてみると

f:id:O3I:20210411115643j:plain

f:id:O3I:20210411115655j:plain

C・R=1000×10^-12×10×10^3=10×10^-6、計算通り10usオーダで遅延が得られます。つまりここまでアドレスバスのホールドタイムが稼げることになり、低速CMOSでも充分な値です。もっと短くてもいいはずですがクロック周波数がせいぜい数十kHzなのでこれくらいにしておきます


 

データメモリアクセス

このMC14500ボードにはデータメモリとして1bit×2048word積んでいますが、これを使ったテストプログラムを書いていたら動作がおかしいことに気付きました。

例えば以下のプログラムはREGA_0~REGA_7(800~807番地)にセットされたビットパタンをDOUT0~DOUT7(000~007番地)に出力し接続されたLEDを点灯させるものですが

f:id:O3I:20210410225258j:plain

ビットパタンが何であろうが、最後にビットセットしたREGA_7の0/1状態だけが全ビットに反映されてしまうという謎の不具合です。アドレスを指定して複数のレジスタにセットしているつもりなのですが、あたかもレジスタは1個しか存在せず、アドレス関係なく最後に書込んだデータで置き換えられてしまっているかのように見えます。

 

見通しを良くするためにプログラムを単純化し800番地と801番地のみに連続書込みしているときのデータメモリ周辺の波形ですが、

f:id:O3I:20210410233230j:plain

一応A0=0の800番地とA0=1の801番地にちゃんと書いているようには見えます。ただ書込み信号/WEの立上りタイミングとアドレスの変化点はクロックに同期しており、ひょっとするとアドレスバスのホールドタイムが不足しているのではないか、という心配はあります。

スタック処理

スタックポインタで指し示されたスタックをアクセスする、ということでわりにさらっと書けたのですが、これがサブルーチン1層のプログラムですら動きません。サブルーチンから戻るときにスタックに入っている値がおかしく(0?)で0番地に飛びリセットされるような動きになってしまいます。

配列で宣言したスタックをインデックス変数(スタックポインタ)でアクセスする方法、スタックポインタの算術的な増減など1行1行は大丈夫そう。一番怪しいのは、それらが同時代入文として実行されるときスタックポインタの更新とそれによる参照がどちらが先に実行されるのか、ということです。同時代入なのでどちらが先ということもないのですがどちらかが先でないと値が決まらないし・・

C言語でいうと *ptr++*++ptrの違いみたいなものです。

 

ところがいろいろいじっているうちptrに定数を入れてポインタを固定にしても戻り値がおかしいことに気付きました。もはやインデックスを変数にすること自体がダメなのか、ということで即値でのアクセスに書き換えました。スタックは2段しかないのでif文で分岐させインデックスは即値にしてあります

f:id:O3I:20210406184447j:plain

なんかいろいろ怪しい記述ですが、一応これで動作正常です。使用マクロセルは

 Total macrocells 61 / 64 ( 95 % )

 Total pins 35 / 36 ( 97 % )

で使用ピン数とともにギリギリになりました

 

スタックをフルに使うサブルーチン2層のプログラムはこのような感じ。

LEDON0ルーチンの中からLEDON7ルーチンを呼ぶようになっていてスタックを2段使います。

f:id:O3I:20210406184514j:plain

実行させるとLED0ON→LED7ON→LED7OFF→LED0OFF→ となり期待通りの動きです

サブルーチンテスト(2)

プログラムを実行させるとLED0が点滅するのでサブルーチンは正常にコールされているだろうことはわかります。

CPLDボード上のインジケータでアドレスバスの遷移を見てみると、

f:id:O3I:20210404163032j:plain

アドレス004でサブルーチンコール、アドレス008でサブルーチンからのリタン、アドレス005でループの先頭へのジャンプを繰り返していてコーディング通りの動作です。

ちょっと気になるのはリタンしたときの帰り番地がコール元と同じアドレス004であることです。これはコールの際にその時のプログラムカウンタ値をそのままスタックに退避しているからで確かにその通りの動きなんですがこれではCALL LEDONから抜け出せなくなるんじゃないかと。一般的なCPUの処理だとCALL命令の次のアドレスを退避していたはずです。

 

これはMC14500マニュアルのRTN命令の説明にちゃんと書いてありました

f:id:O3I:20210404160603j:plain

なるほど、RTN実行後の次の命令は無視する、となっています。これでCALL命令が書いてある004番地に戻っても大丈夫ということでした。

 

スタックの深さ1段でうまくいったので次は段数を増やしてみます。12ビットレジスタ×DEPTH段数のLIFO型信号を定義し、あわせて現在のスタックの位置を示すスタックポインタptrを追加しました。PUSH時(CALL)とPOP時(RTN)でポインタを増減させます。

 

f:id:O3I:20210404160615j:plain

さて現在のCPLDでどこまでスタック段数を増やせるでしょうか。マクロ定義DEPTHを変えてコンパイルし消費マクロセルとの関係を見るとこんな感じです。

(横軸:スタック段数、縦軸:消費マクロセル)

f:id:O3I:20210404170033j:plain

現在のEPM7064(マクロセル64個)では2段、EPM7128(マクロセル128個)に差し替えてもせいぜい6段が限度のようです



 

サブルーチンテスト

機能としてはまだ不完全だし、あとどれくらい拡張できるかわかりませんが先ずこれでテストしてみます。

VHDLの合成はピンアサインを指定しないでやっていたのですが実ボードに適用するために今つながっている結線通りにピンプランナーで設定、再コンパイルしたら・・

 Total macrocells 42 / 64 ( 66 % )

 Total pins 34 / 36 ( 94 % )

ひえ~マクロセルがさらに消費されてしまいました。レポートを見るとなんかワーニングが出ています
 Warning (163076): Macrocell buffer inserted after node "reg_Q[5]"
 Warning (163076): Macrocell buffer inserted after node "reg_Q[6]"

コードで検索すると

CAUSE: You turned on the Auto Logic Cell Insertion logic option, or the Insert Additional Logic Cell logic option, to add logic cells to the design. As a result, the Quartus II software inserted a dummy macrocell buffer after the specified node, or the design has an illegal OE source.

何かよくわかりませんが、取り回しが悪いから自動でダミーのマクロセルを挿入してあげたよ。という感じでしょうか。やはりピンアサインは指定してやるよりコンパイラの最適化に任せた方がよさそうです。

 

ともあれ機能は入ったので信号を接続します

MC14500FLAGFピン、ここにはNOPFコマンドを実行するとパルスが出るのでCPLDPUSHピンに接続します。これでNOPF命令をCALL命令と見做すことができます。

一方RTNピンはRTNコマンドでパルスが出るのでCPLDPOPピンに接続でRETURN処理が行われることになります。

 

FLAGFRTN信号はMC14500から直接クリップで取り出します

f:id:O3I:20210402172958j:plain

 

ハードの準備ができたので次はプログラムですが、その前にアセンブラを少し改造しておきました。NOPFCALLNOP0を単なるNOPというようにエイリアスを登録しマシンコードが現れる列を一般的なアセンブラのようにアドレス列の隣に移動しています。アセンブラ自体自作、EXCELのルックアップテーブルを広げるだけなのでここは何とでもなります。

 

さてプログラム本体ですが004-005番地のループでLEDONサブルーチンを連続的に呼びます。006-008番地のLEDONサブルーチンはLED0を点けて消してリタンするだけとなってます。

f:id:O3I:20210402175815j:plain

こうなると一般的なCPUのプログラムにも見えてきます。ただデータ幅は1ビットなんですけど。

スタック実装

カウンタ単体の動作がOKになったのでいよいよLIFOの機能を作りこんでいきます。まずは規模を見るためスタックは1段としスタックポインタは不要にしました。

入力信号として

PUSH・・・・CALLするとき現在のアドレス値をスタックに退避

POP・・・・・RETURNするときスタックの値を現在のアドレス値へ復帰

を追加、退避用のレジスタの実体reg_Sも追加してそのロジックも記述しています。

 

f:id:O3I:20210401195606j:plain

どんどんif~end ifのネストが深くなり、Cのコーディング的にみてif節とelse節は1段インデントを下げ構造的に見やすくしたつもりですがプログラムがだんだん「尖って」きてます。

ここでコンパイルレポートを見てみると

 Total macrocells 36 / 64 ( 56 % )

 Total pins            34 / 36 ( 94 % )

・・ん?12bitカウンタでレジスタ12個、12bitスタックでレジスタ12個、計24個のマクロセル消費と見込んでいましたが何故かすでに36個も消費しています。これはちょっとまずい状況です。スタックの段数が稼げません

解説書を見るとVHDLは冗長な記述をすると、それがロジックにも反映してしまう。条件分岐に漏れがあると不要なラッチを挿入してしまう・・など恐ろしげなことが書いてあります。影響があるかわかりませんが「else~if」の構文は「elsif」に置き換えインデントを減らしてみました(ネストの深さは変わってませんが)分岐構造が判りにくくなってちょっと嫌なんですけど。

 

f:id:O3I:20210401195622j:plain

これをコンパイルしてもTotal macrocells 36 / 64 ( 56 % )で全然変わりません。何が悪いのか・・

クロック整形

鈍っているクロックを整形するためにシュミットトリガを通してCPLDへ注入することにします。規模の大きいPLD/FPGAであれば入力特性を設定できるかもしれませんが、ここでは小さい青のブレッドボードに74HC14を載せ、論理をもとに戻すため2段かましEPM7064に接続してあります。返す返すも回路決め打ちのPLCCアダプタを外注しなくて良かった・・

 

f:id:O3I:20210329161856j:plain

 

この対応の入出力波形です。上がMC14500が出力したなまったクロック。下がCPLDに入力されるクロックで、改善後の立上り時間を測っていませんが74HC14の出力特性をデータシートで見るとtyp4ns、max8nsなので十分高速です。

f:id:O3I:20210329161915j:plain

この回路でプログラムを実行させるとアドレスの跳躍もなく正常に動作するようになりました。