前回から引き続き、Moscoviumをデュアルコア化して行きます。今回は割り込みコントローラと、セマフォの実現を支援するI/Oユニットを設計します。
割り込みコントローラINTCはCPUが2個になったことで、各I/Oユニットから出力される割り込み要因を、どちらのCPUに振り分けるか?制御しなくてはなりません。1本の割り込み要因を2個のCPUに同時に要求すると、要因のクリア時、必ず問題が発生するためです。
この制御は、既にINTC162に備わっている割り込みマスクレジスタintmskを使って解決しようと思います(下図)。
上図は各I/Oユニットで発生した割り込み要因が、3つのゲートをくぐって漸くCPUに割り込みとして受け付けられることを解説した図となります。intmskレジスタは上から2つ目の箱にあるINTCの割り込みマスクビットを指しています。
intmskレジスタをCPU毎に2個持っておいて、CPU毎に異なる割り込みマスクビットを設定できるようにします。こうすると、各CPUが自分が受け付けるべき割り込み要因だけを通して、自分が処理しない割り込み要因をマスクすることで、適切に割り込み要因を各CPUへ振り分けることが可能となります。
以下図に割り込み要因(intfct)から割り込み要求(irq,irq2)へ振り分ける割り込みトポロジを示します。
CPU1用のintmsk1レジスタと、CPU2用のintmsk2レジスタは、同じI/Oアドレスに割り付けておき、INTC内部でBUSCからのbmst信号(新設)を判定して切り替えてアクセスさせます。
2個のCPUはメモリバスを通して、同じメモリ(RAM等)にアクセス可能なので、ソフトウェアでCPU間の通信に使用するRAMアドレスの取り決めをしておけば、いくらでも相互通信は可能となります。
ただ、このような方法だと、CPUが特定のRAMアドレスを定期的にポーリングする必要があり、タイムリにあるいはリアルタイムに処理する必要のあるイベントに対処し難いという欠点があります。
そこで、一方のCPUから他方のCPUへ割り込みを要求することが出来るような機構をINTCに持たせておこうと思います。
後ほど、デュアルコア用に拡張した割り込みコントローラのI/Oレジスタ一覧で確認してください。
前節のCPU間割り込みもそうですが、この後で追加するI/Oユニットでも割り込みを発生する必要が生じます。16本の割り込み要因しか処理できないINTC162では足りなくなってきたので、要因数を×2倍の32本に拡張して、INTC322Dとします。
※薄黄色の箱はCPUからアクセス可能なI/Oレジスタです。二重になっているプライオリティエンコーダpencブロック、intmskレジスタなどの箱は、CPU毎に2組の回路が存在することを示しています。CPU毎のレジスタ選択は、メモリバスのbmst信号で切り分けます。
INTC322DのI/Oレジスタ一覧を以下表に示して、各レジスタの説明を続けます。
Addr | Name | R/W | Bit:15〜0 | Memo | |||||||||||||||
0xf060 | intnum | R | INUM[15:0] | interrupt number | |||||||||||||||
intofs | W | OFST[15:0] | offset | ||||||||||||||||
0xf062 | intext | RW | − | − | − | − | − | − | − | − |
EI F1 |
EI F0 |
EI E1 |
EI E0 |
EIT1 [1:0] |
EIT0 [1:0] |
external interrupt | ||
0xf064 | intcpu | RW | − | − | − | − | − | − | − | − |
IC F |
− | − | − |
IC E |
− | − |
IC RQ |
inter cpu interrupt |
0xf066 | intctl | RW | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − |
LE VE |
interrupt control |
0xf068 | intfcth | R |
FR AR 2 |
FT AR 2 |
SM RR 2 |
SM UR 2 |
FR AR 1 |
FT AR 1 |
SM RR 1 |
SM UR 1 |
CE NR |
RT CR |
LO FR |
− | − | − | − | − | interrupt factor high word |
0xf06a | intfct | R |
OV FR 1 |
CM AR 1 |
CM BR 1 |
− |
OV FR 0 |
CM AR 0 |
CM BR 0 |
− |
MT ER |
MR AR |
ST AR |
SR AR |
EI R1 |
EI R0 |
IC R2 |
IC R1 |
interrupt factor |
0xf06c | intmskh | RW |
FR AM 2 |
FT AM 2 |
SM RM 2 |
SM UM 2 |
FR AM 1 |
FT AM 1 |
SM RM 1 |
SM UM 1 |
CE NM |
RT CM |
LO FM |
− | − | − | − | − | interrupt mask high word |
0xf06e | intmsk | RW |
OV FM 1 |
CM AM 1 |
CM BM 1 |
− |
OV FM 0 |
CM AM 0 |
CM BM 0 |
− |
MT EM |
MR AM |
ST AM |
SR AM |
EI M1 |
EI M0 |
IC M2 |
IC M1 |
interrupt mask |
BUSC出力のセレクト信号はbcs_intc_nです。上表の範囲でアサートされるので、INTCユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。
※割り込みレベルとベクタ割り込みに対応した、INTC322DVLコントローラの拡張部分の解説については、こちらをご参照ください。
「→CPU1」、「→CPU2」等として行き先の決まっている割り込み要因もありますが、INTCから見ると同じ割り込み要因の1つに過ぎず、どちらのCPUに割り込み要求すべき要因か知りません。即ち、各CPUでintmsk,intmskhレジスタを設定して、自分が受け付けたい割り込み要因だけを許可、それ以外の要因をマスクしないと、要因(フラグ)をクリアできない割り込みを受け付けてしまい、デッドロックする場合があります。
CPUのデュアルコア化に必要な機能はBUSC,INTC等が対応して吸収したため、その他のI/Oユニットに関しては今までのシングルコア版で設計したユニットをそのまま流用可能です。
回路の設計的には2個のCPUからアクセスが可能、なのですが、本当に2個のCPUからアクセスしてしまって良いのでしょうか?
汎用I/OポートPORT8I8Oユニットの様なI/Oユニットは、例えば、出力ポートの7〜4ビットをCPU1が、3〜0ビットをCPU2が制御する、等の切り分けは可能です。
仮に、第5ビット目をCPU1,CPU2から共有して制御するケースがあったとしても、PORT8I8Oには、出力ポートを1にセットするporsetレジスタと、0にクリアするporclrレジスタが有って、リード-モディファイ-ライト無しで制御可能なレジスタを備えているので、特に問題なく使用可能でしょう。
※リード-モディファイ-ライトアクセスが必要となる様なI/Oレジスタは、デュアルコアでは同時アクセス禁止です。リード-モディファイ-ライトアクセスの間で他方のCPUが同じレジスタにアクセスしてしまうと、確実に誤動作を引き起こすためです(RAMであっても同様)。
※デュアルコア版Moscoviumのプログラミングで、リード-モディファイ-ライトアクセスが必要になった時には、2個のCPUからアクセスしていないことを確認しましょう。
では、UART8N1はどうでしょうか?
CPU1が9,600baud、CPU2が115,200baudに設定して、各々、別な相手と通信したいと思ったところで、UART8N1ユニットは1個しか搭載していないので、同時使用は不可です。
例え通信相手が同じで通信速度を合わせたとしても、2個のCPUが各々勝手に受信レジスタuartrecvにアクセスしてしまうと、「CPU1が1,2,5,7バイト目を読み出して、CPU2が2,3,6,8バイト目を読み出す」なんてことが起きてしまいます。
つまるところ、回路としては2個のCPUからのアクセスは可能だけど、本当に2個のCPUが同時に使うのは、シングルコアの時に作ったほとんどのI/OユニットではNGとなります。
解決策の1つとしては、ソフトウェアでどちらのCPUがどのI/Oユニットを使うのか、予め取り決めておくことです。
予め取り決めておくことができず、I/Oユニットを使用するCPUのIDを動的に変更したい場合、セマフォを構成して、排他的に使用権をやり取りする機構が必要となります。次はセマフォについて考えてみましょう。
英語のセマフォ(semaphore)は、手旗信号など視覚的に信号を伝える手段を指すみたいです。並列コンピューティングの用語では、共有リソースに対するリソース(資源)の管理を指します。
リソース管理の例)
これを複数CPUコアの間で実現するためには、アトミックにテスト-アンド-セットできる機構が必要となります。
セマフォの具体的実装:
Moscoviumでは、1命令でテストとセットを実行したとしても、2つのバスサイクル(テストサイクル,セットサイクル)間に他のCPUアクセスが挟まってしまうため、アトミックに実行できません。
これを実現するには、CPU命令の追加だけでは不足で、バスアービトレーション(バス権調停)などの機構を作り込む必要があります。ラウンドロビンで、交互にバスアクセスが発生する今回のBUSC仕様では、実現できません。
そこで、この様な機構を持ったI/Oユニットを作り、リソース管理を実現しようと思います。
前節で示したレンタルサイクルの例の様に、本来はリソースの最大数Nが有って、あるCPUが「使うよ」と宣言すると、リソースの残数が1減って行くような管理となります。しかし、今回はデュアルコアCPUなので、状態としては以下の2通りしかありません。
一方が占有使用すると他方は使えない、ただそれだけですね。リソースの管理は、1)と2)の間を行ったり来たりするだけです。そこで、2)の状態を、「一方のCPUが占有使用権を取得した🔒」、1)の状態に移行する(戻る)ことを「占有使用権を解放した🔓」と表現することとします。
セマフォによるリソース管理を補助するI/Oユニット(SEMPH5R12Uユニット)は、以下の様なブロック図となります。
※薄黄色の箱はCPUからアクセス可能なI/Oレジスタです。二重になっている箱は、CPU毎に2組の回路が存在することを示しています。CPU毎のレジスタ選択は、メモリバスのbmst信号で切り分けます。
以下、I/Oレジスタ一覧と、レジスタの説明です。
Addr | Name | R/W | Bit:15〜0 | Memo | |||||||||||||||
0xf080 | smphctl | RW | − | − | − | − | − | − | − | − |
SM RF |
SM UF |
− | − |
SM RE |
SM UE |
− | − | semaphore control |
0xf082 | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | reserved |
0xf084 | smphrama | RW | − | − | − | − | − | − | − | − |
semr0 [1:0] |
semr1 [1:0] |
semr2 [1:0] |
semr3 [1:0] |
semaphore for ram 0, 1, 2, 3 | ||||
0xf086 | smphramb | RW | − | − | − | − | − | − | − | − |
semr4 [1:0] |
− | − | − | − | − | − | semaphore for ram 4, reserved | |
0xf088 | smphusra | RW | − | − | − | − | − | − | − | − |
semu0 [1:0] |
semu1 [1:0] |
semu2 [1:0] |
semu3 [1:0] |
semaphore for user 0, 1, 2, 3 | ||||
0xf08a | smphusrb | RW | − | − | − | − | − | − | − | − |
semu4 [1:0] |
semu5 [1:0] |
semu6 [1:0] |
semu7 [1:0] |
semaphore for user 4, 5, 6, 7 | ||||
0xf08c | smphusrc | RW | − | − | − | − | − | − | − | − |
semu8 [1:0] |
semu9 [1:0] |
semu10 [1:0] |
semu11 [1:0] |
semaphore for user 8, 9, 10, 11 | ||||
0xf08e | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | reserved |
BUSC出力のセレクト信号はbcs_smph_nです。上表の範囲でアサートされるので、SEMPH5R12Uユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。
実際、本格的なOSを作ったりとかしない限り、セマフォは必須ではありません。小規模なアプリケーションであれば、予め「UARTはCPU1が使う、各CPUのRAMの使用領域はxxx」等と取り決めておけば済むことです。
あまり難しく考える必要はありませんが、「2個のCPUから有限のリソースを使う以上、リソース管理は必要になる」と言う事だけは頭の隅っこに入れておいてください。そして、何かの折に「セマフォユニットがあったね」と、思い出して、活用していただけたら良いと思います。
セマフォですけど、I/Oユニットに頼らずに実現できた方が応用が利くのですが、CPUとBUSCに結構大変な修正が必要で、検討すべきことも山積みだったため、ちょっと楽な道に迂回してしまいました。次回、もうひと踏ん張りして、シミュレーション検証と実機(FPGAボード)検証まで済ませたいと思います。