独自のCPUを創ってみる(3)の続き
さてさて、ここらで考えられるCPUの機能を思いつくまま挙げてみる。(チェック・リスト)
- 任意アドレスのメモリ読み込み・書き込み
- レジスタに定数を設定、レジスタ間のデータ移動
- 算術演算、論理演算、シフト演算、キャリー付き加減算
- 条件分岐、無条件分岐(相対アドレス/絶対アドレス)
- スタックポインタ(SP)とpush/pop
- サブルーチンコール、リターン
- 割り込み、割り込みからの復帰、割り込み禁止・解除
- フラグなど、CPU状態の保存・復帰(マルチスレッド・マルチタスク実現)
- テスト&セット命令 (mutex実現のため)
- ブレークポイントのための命令。1ワードでソフト割り込みするなど
- バイト単位、16bit単位のアクセス、符号拡張命令
- SPに定数を加減算、SP相対アドレッシング(高級言語のローカル変数実装)
- 分岐テーブルに基づくジャンプ(オブジェクト指向言語のメソッド継承実装)
- forループ、break、switch文、関数呼び出しなどの実装に便利な機能など
- 乗除算、浮動小数点演算、多倍長演算、もしくはその効率的実装
- BCD演算の効率的実装
- 高速なブロック転送
- バレルシフタ
- 複数レジスタの一括push/pop
- 大量データの一括演算(MMX命令みたいな)
- 保護モードとソフトウェア割り込み
- 保護モード用のレジスタの効率的な退避・復帰(仮想マシンの実装)
- 仮想メモリ、メモリの保護
- HALT命令(低消費電力対応など)
1~10 まではほぼ必須の機能。11以降はあればうれしい、という感じ。
それぞれ、今考えているv9のcpuで出来るかどうか考えてみる。
1. 任意アドレスのメモリ読み込み・書き込み
⇒ ○
以下のopraとoparでできる。
wire opra = (opaddr == 1); // Rd = Ri op [im32]
wire opar = (opaddr == 5); // [im32] = Ri op (Rj+im8)
MV Rd, [im32]
MV [im32], Rd
⇒ ○
直接的に定数を設定する機能はないが、定数をどこかのメモリに設定しておいてopraでレジスタに設定できる。
MV Rd, [im32]
定数が-128~127の範囲内であれば、RSB演算と組み合わせて以下のように1命令で実行できる。
// 8: RSB Rd = Rj - Ri オペランド逆転減算
RSB Rd, Ri, (Rj+im8) ⇒ Rd = (Rj+im8)-Ri なので、Ri=Rj にすると Rd = im8 になる。
レジスタ間の移動は下記のoprrでできる。
wire oprr = (opaddr == 0); // Rd = Ri op (Rj+im8)
MV Rd, Rj
3. 算術演算、論理演算、シフト演算、キャリー付き加減算
⇒ ○
16種類の演算に含まれている。
4. 条件分岐、無条件分岐(相対アドレス/絶対アドレス)
⇒ ○
R15がプログラムカウンタなので、
MV R15,[im32]
がジャンプ、
MV R15,(R15+im8)
が相対ジャンプになる。ここでも、できれば MV R15, im32 とイミディエイトアドレッシングで直接値を指定したいところだがそのような命令はない。定数テーブルが命令アドレス±512バイトの範囲内であればoprxを使って
wire oprx = (opaddr == 2); // Rd = Ri op [Rj+im8*4]
wire oprx = (opaddr == 2); // Rd = Ri op [Rj+im8*4]
MV R15, [R15+im8*4]
により定数を取り込める。多くの場合は相対ジャンプで済むので、あまり問題無いだろう。
条件分岐にするには、条件付き実行フラグを利用すればよい。
5. スタックポインタ(SP)とpush/pop
⇒ ○
6. サブルーチンコール、リターン
⇒ △
超重要機能だが、サブルーチンコールのための機能がない。
一応、以下の3命令で実行できる。
RSB R0, R0+#4, R0
ADD [R12-#4]-, R0, R15
MV R15, [R15+im8*4] 又は MV R15, [im32] など
リターンは
MV R15, [R12]+
となる。条件付きのサブルーチンコールもできる。
普通のCPUなら1命令でできるサブルーチンコールが3命令になってしまう上に、
R0などの関係ないレジスタが一つ必要になる。
2つ目のADD [R12-#4]-, R0, R15は、リターンアドレスをpushしている。
単にpush R15なら1命令でできるが、これだと戻りアドレスが4少なくなってしまう。
これはなんとかしないと、使いづらいだろう。
7. 割り込み、割り込みからの復帰、割り込み禁止・解除
⇒ ×
8. フラグなど、CPU状態の保存・復帰(マルチスレッド・マルチタスク実現)
⇒ ×
9. テスト&セット命令 (mutex実現のため)
⇒ ×
以下のopxxiを使えば、1命令で読み込みと書き込みができる。
後はフラグの使い方とうまい演算を組み合わせれば実現できそうだ。
wire opxxi = (opaddr == 4); // [Rd]+ = [Ri]+ op [Rj+im8*4]+
10. ブレークポイントのための命令。1ワードでソフト割り込みするなど
⇒ △
1命令でジャンプするには、RSB命令で8bitイミディエイトデータを取り込む。
wire oprx = (opaddr == 2); // Rd = Ri op [Rj+im8*4]
12. SPに定数を加減算、SP相対アドレッシング(高級言語のローカル変数実装)
⇒ ○
13. 分岐テーブルに基づくジャンプ(オブジェクト指向言語のメソッド継承実装)
⇒ ○
14. forループ、break、switch文、関数呼び出しなどの実装に便利な機能など
⇒ ?
後日、コンパイラを作ってみて検証したい。
15. 乗除算、浮動小数点演算、多倍長演算、もしくはその効率的実装
⇒ △
乗算だけは入れてみた。まだ試してないが。
16. BCD演算の効率的実装
⇒ ×
17. 高速なブロック転送
⇒ ○
18. バレルシフタ
⇒ ×
19. 複数レジスタの一括push/pop
⇒ ×
サブルーチンの実装や、マルチスレッドのタスク切り替えなどに使える魅力的な機能。
20. 大量データの一括演算(MMX命令みたいな)
⇒ ×
21. 保護モードとソフトウェア割り込み
⇒ ×
Linuxのような本格的なOSを動かすには必須の機能。
でもこのCPUには贅沢な機能だろう。
22. 保護モード用のレジスタの効率的な退避・復帰(仮想マシンの実装)
⇒ ×
23. 仮想メモリ、メモリの保護
⇒ ×
24. HALT命令(低消費電力対応など)
⇒ ×
必須の機能のうち、まだないものは下記がある。
サブルーチンコール、リターン
割り込み、割り込みからの復帰、割り込み禁止・解除
フラグなど、CPU状態の保存・復帰(マルチスレッド・マルチタスク実現)
テスト&セット命令 (mutex実現のため)
ブレークポイントのための命令。1ワードでソフト割り込みするなど
5. スタックポインタ(SP)とpush/pop
⇒ ○
下記のoprxi, opxrd を使うことで、任意のレジスタをスタックポインタとして利用できる。
push Rn をするには MV [R12-#4]-, Rn で、pop Rn は MV Rn, [R12]+ で実行できる。
wire oprxi = (opaddr == 3); // Rd = Ri op [Rj+im8*4]+
wire opxrd = (opaddr == 7); // [Rd+im8*4]- = Ri op Rj
⇒ △
超重要機能だが、サブルーチンコールのための機能がない。
一応、以下の3命令で実行できる。
RSB R0, R0+#4, R0
ADD [R12-#4]-, R0, R15
MV R15, [R15+im8*4] 又は MV R15, [im32] など
リターンは
MV R15, [R12]+
となる。条件付きのサブルーチンコールもできる。
普通のCPUなら1命令でできるサブルーチンコールが3命令になってしまう上に、
R0などの関係ないレジスタが一つ必要になる。
2つ目のADD [R12-#4]-, R0, R15は、リターンアドレスをpushしている。
単にpush R15なら1命令でできるが、これだと戻りアドレスが4少なくなってしまう。
これはなんとかしないと、使いづらいだろう。
7. 割り込み、割り込みからの復帰、割り込み禁止・解除
⇒ ×
8. フラグなど、CPU状態の保存・復帰(マルチスレッド・マルチタスク実現)
⇒ ×
9. テスト&セット命令 (mutex実現のため)
⇒ ×
以下のopxxiを使えば、1命令で読み込みと書き込みができる。
後はフラグの使い方とうまい演算を組み合わせれば実現できそうだ。
wire opxxi = (opaddr == 4); // [Rd]+ = [Ri]+ op [Rj+im8*4]+
10. ブレークポイントのための命令。1ワードでソフト割り込みするなど
⇒ △
1命令でジャンプするには、RSB命令で8bitイミディエイトデータを取り込む。
wire oprr = (opaddr == 0); // Rd = Ri op (Rj+im8)
RSB R15, R15, (R15+im8)
アドレス0~127の32ワードがソフト割り込みベクタのようになるが、
これらのアドレスには32bitジャンプ命令を置く必要があるため、
実質的にはソフト割り込みは16個しか使えない。
アドレス-128~-1も活用できる。これらのアドレスは0xFFFFFF00~0xFFFFFFFF
に位置する。
後述のようにR0をゼロ・レジスタにすれば、
wire oprx = (opaddr == 2); // Rd = Ri op [Rj+im8*4]
MV R15, [R0+im8*4]
により、より直接的に-512~+511の256ワードをソフト割り込みベクタに利用できる。
ただし、ソフト割り込みとして利用するには自動的に割り込みを禁止する機能も
必要になるかも知れない。
そうしないと、ソフト割り込み直後に割り込みが二重にかかる危険がある。
11. バイト単位、16bit単位のアクセス、符号拡張命令
⇒ ×
PCは32bitあり、アドレス出力にはこのまま32bitが出力されている。
32bitアドレスにアクセスする以下のアドレッシングモードにおいても、
下位2bitはそのままアドレス信号に出力されている。
⇒ ×
PCは32bitあり、アドレス出力にはこのまま32bitが出力されている。
32bitアドレスにアクセスする以下のアドレッシングモードにおいても、
下位2bitはそのままアドレス信号に出力されている。
wire opra = (opaddr == 1); // Rd = Ri op [im32]
wire opar = (opaddr == 5); // [im32] = Ri op (Rj+im8)
従って、4で割り切れないアドレスにアクセスしたときにこれらの意味合いを
持たせるように使うことはできる。
しかし、上位24ビットないし上位16ビットをマスクしたり、
符号拡張したりする命令はないので、個別に実装しなければならない。
12. SPに定数を加減算、SP相対アドレッシング(高級言語のローカル変数実装)
⇒ ○
SPは汎用レジスタなので、普通にできる。
13. 分岐テーブルに基づくジャンプ(オブジェクト指向言語のメソッド継承実装)
⇒ ○
テーブルアドレスをRjに設定しておけば、以下の命令で分岐テーブルに基づくジャンプになる。
wire oprx = (opaddr == 2); // Rd = Ri op [Rj+im8*4]
MV R15, [Rj+im8*4]
14. forループ、break、switch文、関数呼び出しなどの実装に便利な機能など
⇒ ?
後日、コンパイラを作ってみて検証したい。
15. 乗除算、浮動小数点演算、多倍長演算、もしくはその効率的実装
⇒ △
乗算だけは入れてみた。まだ試してないが。
16. BCD演算の効率的実装
⇒ ×
17. 高速なブロック転送
⇒ ○
あまり高速ではないが、一応、ループ1回あたり2命令でできる。
⇒ ×
19. 複数レジスタの一括push/pop
⇒ ×
サブルーチンの実装や、マルチスレッドのタスク切り替えなどに使える魅力的な機能。
20. 大量データの一括演算(MMX命令みたいな)
⇒ ×
21. 保護モードとソフトウェア割り込み
⇒ ×
Linuxのような本格的なOSを動かすには必須の機能。
でもこのCPUには贅沢な機能だろう。
22. 保護モード用のレジスタの効率的な退避・復帰(仮想マシンの実装)
⇒ ×
23. 仮想メモリ、メモリの保護
⇒ ×
24. HALT命令(低消費電力対応など)
⇒ ×
必須の機能のうち、まだないものは下記がある。
サブルーチンコール、リターン
割り込み、割り込みからの復帰、割り込み禁止・解除
フラグなど、CPU状態の保存・復帰(マルチスレッド・マルチタスク実現)
テスト&セット命令 (mutex実現のため)
ブレークポイントのための命令。1ワードでソフト割り込みするなど
後日、よく検討しよう。
0 件のコメント:
コメントを投稿