公開日:2021/07/24
title.png

17th Season


#17-2 双頭の狼(knee)

 前回から引き続き、Moscoviumをデュアルコア化して行きます。今回は割り込みコントローラと、セマフォの実現を支援するI/Oユニットを設計します。

割り込みコントローラ(INTC)

 割り込みコントローラINTCはCPUが2個になったことで、各I/Oユニットから出力される割り込み要因を、どちらのCPUに振り分けるか?制御しなくてはなりません。1本の割り込み要因を2個のCPUに同時に要求すると、要因のクリア時、必ず問題が発生するためです。

 この制御は、既にINTC162に備わっている割り込みマスクレジスタintmskを使って解決しようと思います(下図)。

a159_irqack.png

 上図は各I/Oユニットで発生した割り込み要因が、3つのゲートをくぐって漸くCPUに割り込みとして受け付けられることを解説した図となります。intmskレジスタは上から2つ目の箱にあるINTCの割り込みマスクビットを指しています。
 intmskレジスタをCPU毎に2個持っておいて、CPU毎に異なる割り込みマスクビットを設定できるようにします。こうすると、各CPUが自分が受け付けるべき割り込み要因だけを通して、自分が処理しない割り込み要因をマスクすることで、適切に割り込み要因を各CPUへ振り分けることが可能となります。

 以下図に割り込み要因(intfct)から割り込み要求(irq,irq2)へ振り分ける割り込みトポロジを示します。

a172_inttplg.png

 CPU1用のintmsk1レジスタと、CPU2用のintmsk2レジスタは、同じI/Oアドレスに割り付けておき、INTC内部でBUSCからのbmst信号(新設)を判定して切り替えてアクセスさせます。

CPU間割り込み

 2個のCPUはメモリバスを通して、同じメモリ(RAM等)にアクセス可能なので、ソフトウェアでCPU間の通信に使用するRAMアドレスの取り決めをしておけば、いくらでも相互通信は可能となります。
 ただ、このような方法だと、CPUが特定のRAMアドレスを定期的にポーリングする必要があり、タイムリにあるいはリアルタイムに処理する必要のあるイベントに対処し難いという欠点があります。

 そこで、一方のCPUから他方のCPUへ割り込みを要求することが出来るような機構をINTCに持たせておこうと思います。

 後ほど、デュアルコア用に拡張した割り込みコントローラのI/Oレジスタ一覧で確認して下さい。

デュアルコア版割り込みコントローラ

 前節のCPU間割り込みもそうですが、この後で追加するI/Oユニットでも割り込みを発生する必要が生じます。16本の割り込み要因しか処理できないINTC162では足りなくなってきたので、要因数を×2倍の32本に拡張して、INTC322Dとします。

a172_intc322d.png

※薄黄色の箱は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ユニットはバイトアクセス禁止です。必ずワードアクセスしてください。

 「→CPU1」、「→CPU2」等として行き先の決まっている割り込み要因もありますが、INTCから見ると同じ割り込み要因の1つに過ぎず、どちらのCPUに割り込み要求すべき要因か知りません。即ち、各CPUでintmsk,intmskhレジスタを設定して、自分が受け付けたい割り込み要因だけを許可、それ以外の要因をマスクしないと、要因(フラグ)をクリアできない割り込みを受け付けてしまい、デッドロックする場合があります。

I/Oユニット

 CPUのデュアルコア化に必要な機能はBUSCINTC等が対応して吸収したため、その他の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ユニットを作り、リソース管理を実現しようと思います。

セマフォユニットSEMPH5R12U

 前節で示したレンタルサイクルの例の様に、本来はリソースの最大数Nが有って、あるCPUが「使うよ」と宣言すると、リソースの残数が1減って行くような管理となります。しかし、今回はデュアルコアCPUなので、状態としては以下の2通りしかありません。

 一方が占有使用すると他方は使えない、ただそれだけですね。リソースの管理は、1)と2)の間を行ったり来たりするだけです。そこで、2)の状態を、「一方のCPUが占有使用権を取得した🔒」、1)の状態に移行する(戻る)ことを「占有使用権を解放した🔓」と表現することとします。


 セマフォによるリソース管理を補助するI/Oユニット(SEMPH5R12U)は、以下の様なブロック図となります。

a172_semph5r12u.png

※薄黄色の箱は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ボード)検証まで済ませたいと思います。


btn_arduino.png

btn_prev.png btn_next.png

home