4 bit RISC CPU
2012年12月4日
RISCアーキテクチャを用いたCPUに変更する事になりそうだ。
新しいCPUの仕様に従って、回路をこつこつと設計していたのだが、7割ほど出来上がったところで問題が発生した。どうやら、今の設計では、小規模のCPLD(マクロセル64以下)には収まりそうもなく、比較的規模の大きいCPLDか、FPGAの規模になってしまいそうである。
こうなると、まず最初のCPUをCPLDの中に納めるという短期目標が難しくなり、トランジスターで組むという最終目標はほとんど達成できそうもない。今のまま進めても、CPLDレベルや汎用ロジックのレベルでは何とかなるかも知れないが、最初の設計の段階で手を打っておかないと、後で一からやり直しということになりかねないように思う。
CPUをなるだけ簡単にするという事で真っ先に思い浮かぶのはRISCアーキテクチャで、4ビットのCPUもこれで出来ないかと、少し考えてみた。
汎用レジスターは4ビットの物が12個(FLは、フラグを格納)。他に12ビットのプログラムカウンタ。これだけで、何とかなりそうだ。
コマンドは固定長16ビットで、演算命令とジャンプ命令のみ。この構成で、以前の仕様に在った次の動作が行えるかどうか、検証してみる。
1) Load to register
演算命令を用いて、任意のレジスターに0を加えて別のレジスターに保存すれば良い。メモリーへのアクセスも、同じ方法で行える((X0Y0Z0)など)。
2) Calculation
普通に、演算命令を実行する。
3) 条件分岐
FL(フラグレジスター)に、スキップフラグを用意する。FLに対して演算命令を実行することにより、キャリーフラグ・ゼロフラグの結果がスキップフラグにコピーされるようにする。スキップフラグが立っているときは、次の命令がNOPになる。
4) NOP
FLのスキップフラグをクリアするような演算命令(ANDを用いる)をNOPとする。
5) Increment, decrement
1を加える(減ずる)ような演算命令を実行する。
6) Load literal to register
レジスターのうちどれか一つ(X2の予定)の値が必ず0xFであるようにしておけば、AND演算をそのレジスターとリテラル値の間で行うことで、そのリテラル値を代入できる。なお、0x0もしくは0xFは、ANDもしくはOR演算を用いて代入することが可能。
7) Jump
実装されたジャンプ命令を実行する。
8) Push, pop
X2, Y2, Z2を、スタックポインターとして使用する。すなわち、pushは、Z2をデクリメントした後に(X2Y2Z2)への書き込みで、popは(X2Y2Z2)から読み込んだ後にZ2をインクリメントすれば、実行できる。
9) Call, return
復帰するアドレスをX3, Y3, Z3に格納した後にjump命令を実行すれば、callと同じ動作になる。Returnするときは、X3Y3Z3にジャンプする。サブルーチンを入れ子で使う場合は、X3, Y3, Z3を上記の方法でスタックに格納する。
10) 任意の用途に使える、6つのレジスター(A, B, C, S, H, L)
X0, Y0, Z0, X1, Y1, Z1の6つが相当する。(X0Y0Z0), (X1Y1Z1)を用いた間接メモリアクセスも可能。
ということで、何とかなりそうである。これ用のアセンブラーの作成に少し手間がかかりそうだが、そこさえクリアーすれば、回路の設計や半田付けなどは、飛躍的に楽になるはず。
新しいCPUの仕様に従って、回路をこつこつと設計していたのだが、7割ほど出来上がったところで問題が発生した。どうやら、今の設計では、小規模のCPLD(マクロセル64以下)には収まりそうもなく、比較的規模の大きいCPLDか、FPGAの規模になってしまいそうである。
こうなると、まず最初のCPUをCPLDの中に納めるという短期目標が難しくなり、トランジスターで組むという最終目標はほとんど達成できそうもない。今のまま進めても、CPLDレベルや汎用ロジックのレベルでは何とかなるかも知れないが、最初の設計の段階で手を打っておかないと、後で一からやり直しということになりかねないように思う。
CPUをなるだけ簡単にするという事で真っ先に思い浮かぶのはRISCアーキテクチャで、4ビットのCPUもこれで出来ないかと、少し考えてみた。
汎用レジスターは4ビットの物が12個(FLは、フラグを格納)。他に12ビットのプログラムカウンタ。これだけで、何とかなりそうだ。
コマンドは固定長16ビットで、演算命令とジャンプ命令のみ。この構成で、以前の仕様に在った次の動作が行えるかどうか、検証してみる。
1) Load to register
演算命令を用いて、任意のレジスターに0を加えて別のレジスターに保存すれば良い。メモリーへのアクセスも、同じ方法で行える((X0Y0Z0)など)。
2) Calculation
普通に、演算命令を実行する。
3) 条件分岐
FL(フラグレジスター)に、スキップフラグを用意する。FLに対して演算命令を実行することにより、キャリーフラグ・ゼロフラグの結果がスキップフラグにコピーされるようにする。スキップフラグが立っているときは、次の命令がNOPになる。
4) NOP
FLのスキップフラグをクリアするような演算命令(ANDを用いる)をNOPとする。
5) Increment, decrement
1を加える(減ずる)ような演算命令を実行する。
6) Load literal to register
レジスターのうちどれか一つ(X2の予定)の値が必ず0xFであるようにしておけば、AND演算をそのレジスターとリテラル値の間で行うことで、そのリテラル値を代入できる。なお、0x0もしくは0xFは、ANDもしくはOR演算を用いて代入することが可能。
7) Jump
実装されたジャンプ命令を実行する。
8) Push, pop
X2, Y2, Z2を、スタックポインターとして使用する。すなわち、pushは、Z2をデクリメントした後に(X2Y2Z2)への書き込みで、popは(X2Y2Z2)から読み込んだ後にZ2をインクリメントすれば、実行できる。
9) Call, return
復帰するアドレスをX3, Y3, Z3に格納した後にjump命令を実行すれば、callと同じ動作になる。Returnするときは、X3Y3Z3にジャンプする。サブルーチンを入れ子で使う場合は、X3, Y3, Z3を上記の方法でスタックに格納する。
10) 任意の用途に使える、6つのレジスター(A, B, C, S, H, L)
X0, Y0, Z0, X1, Y1, Z1の6つが相当する。(X0Y0Z0), (X1Y1Z1)を用いた間接メモリアクセスも可能。
ということで、何とかなりそうである。これ用のアセンブラーの作成に少し手間がかかりそうだが、そこさえクリアーすれば、回路の設計や半田付けなどは、飛躍的に楽になるはず。
コメント
Katsumi (2014年8月4日 18:29:44)
フラグレジスターの案としては、このレジスターを下位ビットからbit0, bit1, bit2, bit3としたとき、bit1がキャリー、bit2がゼロ。
ADC F,0,Fで、C=1の時に、bit0が立つ。
ADC F,15,Fで、C=0の時に、bit0が立つ。
ADD F,4,Fで、Z=1の時に、bit3が立つ。ただし、Z=0となる。
ADD F,12,Fで、Z=0の時に、bit3が立つ。ただし、Z=1となる。
NOPには、AND F,6,Fを割り当てる。bit0とbit3をクリアー。
ADC F,0,Fで、C=1の時に、bit0が立つ。
ADC F,15,Fで、C=0の時に、bit0が立つ。
ADD F,4,Fで、Z=1の時に、bit3が立つ。ただし、Z=0となる。
ADD F,12,Fで、Z=0の時に、bit3が立つ。ただし、Z=1となる。
NOPには、AND F,6,Fを割り当てる。bit0とbit3をクリアー。