リトライするCPUの構想。(CPU設計にデーター通信のリトライシーケンスを応用) 著者:吉田作太郎

リトライするCPUの構想。(CPU設計にデーター通信のリトライシーケンスを応用) 著者:吉田作太郎
本文
信頼性の高いCPUの研究(リトライするCPU)

目次
1.リトライシーケンスのあるCPUについての構想。 2
2.構成と動作。 4
3.コマンドとレジスター構成。 6
4.コマンドの解説。 7
(1)条件ジャンプ。(アブソリュート アドレス) 7
(2)条件ジャンプ。(相対 アドレス) 7
(3)無条件ジャンプ。(アブソリュート アドレス) 7
(4)無条件ジャンプ。(相対 アドレス) 7
(5)PUSH レジスター。 8
(6)POP レジスター。 8
(7)CALL 飛び先アドレス。(サブルーチンコール) 8
(8)RET。(リターン) 9
(9)IRQ発生時。 9
(10)IRET。(リターンインターラプト) 9
(11)「AC1 ← AC1(演算)AC2」。 10
(12)「転送命令、(IY)+ ← (IX)+」。 12
(13)「転送命令、(IY)ー ← (IX)ー」。 12
5.CPUのレジスター構成再考。 13
6.暴走するCPUをソフトウエアによって対処する方法。 14




1.リトライシーケンスのあるCPUについての構想。

コンピュータのCPU自身の異常動作についての対策について考えた。
ソフトウエアで、対応できる事柄は、各処理を実行した後にチェックライトしながら二度演算しなおすとか、CPUが、プログラムカウンターの異常でインストラクションのインタープリトが、異常になっても、アセンブラ命令の中にあるNOP(ノー オペレーション)命令の活用で、インタープリトする機能を正常にしなおせる様にするとか、CPUが、異常を検知した時(メモリーエラーや、ウオッチドックタイマーによるタイムアウトなどによる)、ソフトウエアで、異常処理を実行してもらうなどの一般的な対応がある。

私は、CPUをコミュニケーションする装置と考え、エラーを検出したら、コマンドを実行するCPU自信の内部状態が遷移する各フェーズに対して、リトライシーケンスを実行する事により、障害を克服する手段を考えた。(リトライシーケンスを行っても異常の場合はCPU異常を通知しなければならないが、ここでは、それについて述べない。)
エラー検出に関して言えば、同じ装置にCPUを二つ以上置いて、互いの内部の状態を比較し、常に同一の状態になっているかをチェックする事があげられる。

さて、CPUについて、命令の実行から考えると以下の機能が、必要である。
 ① データーの転送(メモリー転送)
 ② データーの演算(A+1 → Aなど)
 ③ プログラムの分岐(JMP,CALL命令、割り込み処理の実行)
これを再実行(リトライ)すると、次の問題が、生じる。
 ① 再実行による対応は、OK
   但し、移動先をまちがえた場合、相手先のメモリーを壊す。
 ② 2項演算を行っているものは、全て演算内容が壊れる。
   例) A+1→A をリトライすると 
     ((A+1)+1)→Aとなり
     演算内容が、破壊される。
 ③ プログラムカウンターの変更、
   スタックポインターの変更及び、データー転送がある。
   この時、PC(プログラムカウンター)、
   SP(スタックポインター)というCPU
   内部のレジスターに演算動作がされる。
   その演算の保証が、必要である。
この問題の解決方法について考える。
 ① リトライシーケンスは、可である。
   但し、移動先をまちがえて、
   壊れてしまったメモリーへの対応は、対策はない。
 ② 2項演算を使わず、3項演算を使って対応。
   どうしても、二項演算が、必要な場合、
   二つのフェーズに分けて演算する。
   例) 「A+1→A」 →
        (第一フェーズ、3項演算)   A+1→B
        (第二フェーズ、データー転送)  B→A
     という2つの独立したフェーズに分離して、命令を実行する。
 ③ 分岐命令をPC,SPの演算命令と、
   データー転送命令とに分けて、問題を整理
して考える。



2.構成と動作。

さて、これら命令を実行するCPUの構造について考える。

 ┌─CPU────────────┐  ┌───―───┐
 │┌───┐  ┌──────┐ │  │ プログラム │
 ││コント│←─┤命令フェッチ│ │  │       │
 ││ローラ│  │レジスター │←┼────┌───┐ │
 │└───┘  └──────┘ │┌──→│命 令│ │
 │       ┌──────┐ ││ │ └───┘ │
 │       │プログラム │ ││ │   │   │
 │       │カウンター │─┼┘ │   ↓   │
 │       └──────┘ │  │   ↓   │
 │       ┌──────┐ │  ├───―───┤
 │       │スタック  │ │  │       │
 │       │ポインター │ │  └───―───┘
 │       └──────┘ │  │       │
 │       ┌──────┐ │  ├───―───┤
 │       │演算    │ │  │       │
 │       │レジスター │─┼───→┌──―─┐│
 │       └──────┘←┼────│データー││
 └────────────────┘  │ └──―─┘│
                     │       │
                     └────―──┘
の構造になっている。
これは、次の様な動作をする。
 ① PC(プログラムカウンター)に示されたプログラム命令を
   命令フェッチレジスターに入れる。
   この時、PCは、1命令分、カウントアップする。
 ② 命令フェッチレジスターの内容をコントローラが、
   理解し、命令を実行する。
   この時、PC,SP,演算レジスター、メモリーの状態は、
   命令フェッチレジスター
   の内容を基にデーターを変化させる。
これを式にすると、
 ① (命令フェッチレジスター) ← (PC)+
 ② 命令の実行
の二つのフェーズに分かれて命令を実行する。
ここで、注意しなくてはいけないのは、①の段階で、正常なのに、エラー検出して、リトライすると、PCが、移動して、再実行したい命令の次の命令を実行してしまう。
これは、PCが、二項演算の要素を持っているためである。
ここで、PCを三項演算として、取り扱うために、PCの値を一時保管してもらうために、WPC(ワークPC)なるレジスターを作る。
そして、命令の実行を次の様に展開する。
 ① (命令フェッチレジスター) ← (PC), WPC ← PC
 ② 命令フェッチレジスターの実行
 ③ PCの移動「PC←WPC+実行した命令の長さ」
この3つのフェーズの各段階は、それぞれリトライしても大丈夫だが、その再実行の相関関係を表にしてみると、次の様になる。
┌────┬─────────────────┐
│    │   エラーリトライ       │
│フェーズ├─────┬─────┬─────┤
│    │①の再実行│②の再実行│③の再実行│
├────┼─────┼─────┼─────┤
│ ①  │  ○  │  X  │  X  │①から、再実行可
├────┼─────┼─────┼─────┤
│ ②  │  ○  │  ○  │  X  │①から、再実行可
├────┼─────┼─────┼─────┤
│ ③  │  X  │  X  │  ○  │③をリトライ
└────┴─────┴─────┴─────┘するしかない
     ( ○:再実行可/ X:再実行不可 )
これより、1命令をフェーズに分かれての再実行は、①②か、③のフェーズに分かれて可能となる。



3.コマンドとレジスター構成。

この考え方を基に、ノイマン型コンピュータの命令を再度、考え直す。
(基本的に、2項演算のフェーズのある項目を3項演算に分解して、物事を考える。)
ノイマン型のコンピュータの主なCPUの動作は、次の通りである。
 ☆ データー転送
 ☆ データー演算
 ☆ JMP命令(無条件、条件ジャンプ)
 ☆ サブルーチンコール
 ☆ 割り込みサービス
これらについて、基本的なコマンド実行についての解説をしながら、CPUの基本的なレジスター構成を模索する。
まず、最初に考えられる必要なレジスターは、次の通りである。
IFR:インストラクション フェッチ レジスター
           (コマンドをフェッチするために必要)
ILR:インストラクション レングス レジスター
           (フェッチしたコマンドのレングスを記憶するためのレジスター)
FR :フラグレジスター
PC :プログラムカウンター
WPC:ワーク プログラムカウンター
SP :スタックポインター
WSP:ワーク スタックポインター

これを基に、今まで示した3つの段階(フェーズ)に合わせて、各コマンドの実現のための、CPU内部のデーターの動きをさぐってみる。
ここでは、一つのフェーズにある複数のコマンドは、同時に実行できる様にできている。



4.コマンドの解説。

(1)条件ジャンプ。(アブソリュート アドレス)

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② IF 条件一致
      THEN  WPC ← JMP先(IFRの内容により)
      ELSE  WPC ← PC + ILR
   ENDIF

 ③ PC ← WPC

(2)条件ジャンプ。(相対 アドレス)

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② IF 条件一致
      THEN  WPC ← PC + 相対JMP先
                      (IFRの内容により)
      ELSE  WPC ← PC + ILR
   ENDIF

 ③ PC ← WPC

(3)無条件ジャンプ。(アブソリュート アドレス)

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② WPC ← JMP先(IFRの内容により)

 ③ PC ← WPC

(4)無条件ジャンプ。(相対 アドレス)

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② WPC ← PC + 相対JMP先(IFRの内容により)

 ③ PC ← WPC

次に、サブルーチンコール命令を考える前に、よく使われるスタック操作命令について考える。

(5)PUSH レジスター。

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② (SP- レジスターレングス) ← レジスター
    WSP ← SP - レジスターレングス
    WPC ← PC + ILR

 ③ SP ← WSP
   PC ← WPC

(6)POP レジスター。

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ②  レジスター ← (SP)
    WSP ← SP + レジスターレングス
    WPC ← PC + ILR

 ③ SP ← WSP
   PC ← WPC

次にサブルーチンコールについて考える。

(7)CALL 飛び先アドレス。(サブルーチンコール)

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② (SP- プログラムカウンターレングス)←PC+ILR
    WSP ← SP - プログラムカウンターレングス
    WPC ← IFRにある飛び先アドレス

 ③ SP ← WSP
   PC ← WPC

(8)RET。(リターン)

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ②  WPC ← (SP)
    WSP ← SP + プログラムカウンターレングス

 ③ SP ← WSP
   PC ← WPC

次にインターラプト(IRQ)発生について考える。
メモリー、レジスター間の転送が、2つあるので、(9)と(10)の実行では、・のフェーズに2クロックが、最低限、必要である。

(9)IRQ発生時。

 ① インストラクション フェッチ 無し

 ② (SP- PCレングス ー 0) ← PC + ILR
   (SP- PCレングス ー FRレングス) ← FR
    WSP ← SP - PCレングス ー FRレングス
    WPC ← IFRにある飛び先アドレス

 ③ SP ← WSP
   PC ← WPC

(10)IRET。(リターンインターラプト)

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ②  FR  ← (SP)
    WPC ← (SP + FRレングス)
    WSP ←  SP + FRレングス+ PCレングス 

 ③ SP ← WSP
   PC ← WPC

ここで、データー演算命令について考える。
ここでは、3項演算を中心として問題を進めているので、演算レジスターは、まず、3つ必要である。
また、レジスター間のデーターの移動に対しても演算レジスターが、必要とされるので、最低限、演算レジスターは、合計4つ必要である。
しかし、今まで、2項演算の要素を、フェーズ分けしてきて、リトライシーケンスを可能としてきていた。
だから、ここでも、2項演算でも、今までのフェーズを利用したリトライシーケンスが、可能な演算方法を考えてみる。
ここでは、その方法を説明する。
その説明のために、ここでは、レジスターを3つ登場させる。
AC1:アキュムレータ1
AC2:アキュムレータ2
WKR:演算ワークレジスター

(11)「AC1 ← AC1(演算)AC2」。

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② WPC ← PC + ILR
   WKR ← AC1(演算)AC2

 ③ PC  ← WPC
   AC1 ← WKR

次にデーター転送命令について、考える。
データー転送には、転送元と転送先がある。
まず、ここでは、I/OポートのデーターのREAD/WRITの信頼性については、対象としない。
I/OポートのデーターREADは、スイッチの入力などのチャタリングなどの様なノイズが、発生する事が予想され、不確定な値になり続ける場合がある。
また、データーWRITは、書いたデーターを読み直す事は、一般にはできないためである。
CPUを中心としたデーター転送には、4つの種類がある。
 Ⅰ.メモリー → レジスター(最低限1クロック必要)
 Ⅱ.レジスター→ メモリー (最低限1クロック必要)
 Ⅲ.レジスター→ レジスター(最低限1クロック必要)
 Ⅳ.メモリー → メモリー(メモリーアクセスを2度行うため最低限2クロック必要)
ここで、データー演算命令実行時には、(11)で使われた・以外の転送も、本来は必要である。
さて、転送元のメモリーのデーターの信頼性のチェックには、2度読みが考えられる。
また、転送先のデーターの信頼性に関しては、一度、転送したデーターを読み直す事が、考えられる。
ここにもバッファとしてのワークレジスターの必要性を感じる。
但し、ここでは、複数のCPUを使ってデーターをチェックしているので、エラーは、そこからも検知できるのでチェックは、省略できるが、その中で省略できない事は、CPUが、メモリーへデーター転送した時に、一度書き込んだ命令を読み直す事ぐらいであろう。
但し、この時、CPUが、データーの書き込み先をまちがえた場合、壊れてしまったメモリーのデーターへの対応策は、CPU独自のハードでの対策だけでは、不可能である。(ソフトウエアを含めた障害対策が、必要である。)

 さて、CPUには、アドレスレジスターをインデックスレジスターとして、活用するととても便利である。
ここでは、メモリーtoメモリー転送に便利なレジスターを取り入れて、コマンドを2つ作ってみる。
IX :インデックスレジスターX
IY :インデックスレジスターY
WIX:ワークインデックスレジスターX
WIY:ワークインデックスレジスターY
さて、(IY)←(IX)へ、データー転送時、メモリーアクセスを2度行うためこの部分に最低限2クロックが、必要である。




(12)「転送命令、(IY)+ ← (IX)+」。

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② (IY) ← (IX)
    WIX ← IX + 転送1ワードレングス
    WIY ← IY + 転送1ワードレングス
    WPC ← PC + ILR

 ③ PC  ← WPC
   IX  ← WIX
   IY  ← WIY

転送命令には、逆アドレス方向の転送命令も必要なので、次の命令も付加する。

(13)「転送命令、(IY)ー ← (IX)ー」。

 ① インストラクション フェッチ IFR ←─┬──(PC)
                  ILR ←─┘
 ② (IY) ← (IX)
    WIX ← IX ー 転送1ワードデーターレングス
    WIY ← IY ー 転送1ワードデーターレングス
    WPC ← PC + ILR

 ③ PC  ← WPC
   IX  ← WIX
   IY  ← WIY



5.CPUのレジスター構成再考。

ここで、今までリトライするCPUに必要であった基本的なレジスター構成は、以下の様になる。

IFR:インストラクション フェッチ レジスター
    (コマンドをフェッチするために必要)
ILR:インストラクション レングス レジスター
    (フェッチしたコマンドのレングスを記憶するためのレジスター)
FR :フラグレジスター
PC :プログラムカウンター
WPC:ワーク プログラムカウンター
SP :スタックポインター
WSP:ワーク スタックポインター

AC1:アキュムレータ1
AC2:アキュムレータ2
WKR:演算ワークレジスター

IX :インデックスレジスターX
IY :インデックスレジスターY
WIX:ワークインデックスレジスターX
WIY:ワークインデックスレジスターY

プログラマーが、変数として、ユーザが、活用できるレジスターは、
FR,SP,AC1,AC2,IX,IY
その他デバックに注目しなければならないレジスターは、
PC
である。
最後に、リトライするCPUには、3つのフェーズが、必要で、最低限3クロック必要である。
ここで、CPUに命令フェッチするパイプライン処理を追加した場合①のフェーズを短縮できるので、最高1/3近くまでしか処理時間を短縮できないだろうと思われる。



6.暴走するCPUをソフトウエアによって対処する方法。

 CPUを動かす時、異常なコードをCPUのインストラクションが解析すると、エラートラップを発生させて、CPUに設定されたメモリー領域のプログラムを実行するという方法論が使えるCPUがある。
こういうCPUは、また、CPUのエラーとラップを利用して、OSを作るのに利用したりするものだ。
我々は、CPUの暴走は、その様なハードウエアによってだけ対処できない訳では無い。
対処する方法は、ソフトウエアにNOP命令を埋め込んで利用する方法論が考えられる。
 ハードウエアの異常時には、CPUのインストラクション解析する部分、または、PC(プログラムカウンター)が異常なコードを解析してしまう事があると仮定してみよう。
そういう壊れたインストラクション解析する場合、それを正常にもどすには、何もプログラムを実行しないNOP命令を連続的に多用して使えば、CPUが正常に戻った時、NOP命令を実行し続けて、CPUの異常暴走をくい止める事ができるだろうと仮定してみる。
更に、異常時対処するためのNOP命令のグループを実行した後、どこかのエラー処理にジャンプして適切な異常処理を実行した後、また、元のプログラムを実行してもらうか、もしくは、リセットからシステムを始める様にすれば、それは、CPUが暴走する時のエラーリカバリー処理となるだろう。
 この様に、NOP命令とは、不必要な命令ではない。
エラー処理、フェルセーフ、フォールトトレランスなる処理をする時、NOP命令が何もしない命令だからこそ有効に使えるアセンブラ命令としての使い道があるものである。
 その他、CPUが暴走する場合でも、そのエラー対策を行えるソフトウエアについて考えてみる。
作ったソフトウエアにどこまで動いたかを記述するチェック・ポインター領域を設ける事が考えられる。(チェック・ポインター領域には、どこまでプログラムを実行しているか?その時のメモリー内容やCPUのレジスター内容をメモリーに記述する。)
CPUが異常な動作をしたら、とりあえずエラー処理にPC(プログラム・カウンター)にジャンプ先を設定して、今まで、どこまで処理を進めてきたのかだとか、その時のCPUのレジスターやメモリー内容はどうであったかだとかをプログラムがチェック・ポインターを通過して実行した時点で記述したメモリー内容やCPUのレジスター内容を復旧させて、問題無くプログラムを再開させるための工夫をしてゆく事が考えられるだろう。
 現在は、もっと複雑な事象に対応するエラープログラムというのがあるかもしれないが、
信頼性のあるソフトウエア開発の基礎とは、以上の事が考慮されていると考えられる。




"リトライするCPUの構想。(CPU設計にデーター通信のリトライシーケンスを応用) 著者:吉田作太郎" へのコメントを書く

お名前
メールアドレス
ホームページアドレス
コメント