Cmod A7ボード(FPGA)を使って、オリジナル16ビットCPUコアMoscoviumの開発を進めています。
前回から、1つのマイコン上に多数のCPUコアを集積する、多コア化をテーマに考察を重ねています。
前回、密結合メモリの導入によって、CPUから占有アクセス可能な高速メモリを確保できました。
更に、CPUで実行すべき処理に想像を巡らせると、CPU間で通信する場合や、データを共有したり等、共通なメモリやI/Oユニットにアクセスしたいケースは必ず発生します。全CPUがメモリバスにアクセスできることが望ましいです。この場合、メモリバスは1本に限定されるので、複数のCPUからアクセスするためには何らかの方法で調停する必要が生じます(下図)。
上図は、最大7個のPUNIT階層が、バス権調停回路(BUSA)を通してメモリバスへアクセスする事を示した図です。この階層をPolycore階層と命名します。
最終的には、このPolycore階層がデュアルコア(下図)となることで、最大14コアのポリコア版マイコンが完成します。
2コア(Polycore)×7コア(PUNIT)=14コア
上図のCPU1およびCPU2と記した箱が、Polycore階層によって置換されます。
これにて、14コアの全体像と、そこから1系統のメモリバスへアクセスするトポロジが完成しました。
Polycore階層に於いて、バス権調停コントローラBUSA716の設計を進めて行きます。
今回はデュアルコアなんてものではなく、最大14コアを目指すので、よくよく考えて進めて行かないと、「出来上がったは良いけれど全く機能しないごみ」が完成してしまいかねません!例えば下に示すボトルネック(再掲)は、どうでしょうか?
CPUが多数存在して、それらが一斉にバスステートコントローラBUSCおよびメモリバスを通して、共有メモリ(ROM/RAM)や周辺I/Oユニットへアクセスに行きます。考えるまでも無く、メモリバス(BUSC)がボトルネックとなることは明らかです。
最大14個のCPUが同時にバスアクセスを要求した場合、優先順位の高いCPUから順にバスアクセスして行き、最後に優先順位の一番低いCPUがアクセスできるまでに、13バスサイクルの間、待たされてしまうでしょう。いや、まだ13バスサイクル後にバスアクセスできれば良いのですが、それは保証されません。
なぜなら、最初にバスアクセスを実行した優先順位の高いCPUが次の処理に移った結果、新たなバスアクセスを要求するからです。高優先順位CPUからの再要求は、低優先順位CPUの要求を追い越して、またしても、高優先順位CPUが先にバスアクセスを実行します。低優先順位CPUは、ずっと待たされます。
そうなのです。14コアもあるバス系に於いて、固定優先順位は、すぐに破綻します。優先順位の低いCPUは、いつまでも待たされ、ハングアップしてしまうからです。
デュアルコア版Moscoviumを開発した時には、ラウンドロビンと言う方法で優先順位を変化させる工夫をしました。
ラウンドロビン:
ラウンドロビンと言う方式は、デュアルコアだからこそ比較的容易に実装できましたが、4コアに拡張するだけで、一気に複雑さが増します。CPU1,CPU2,CPU3,CPU4の優先順位組み合わせは順列の数だけあるので、
4P4=4!=4×3×2×1=24通り(!は階乗演算子)
に上ります。少なくとも、24状態のステートマシンが必要です。状態遷移も複雑、かつ、優先順位に従ったバス権調停回路も複雑な事になるでしょう。えっと、これが今回14コアなので、状態の数は14P14通りです。計算は省きますが、現実的な数ではありません。ラウンドロビンは却下(不採用)です。
これまでの考察によって、優先順位は可変であって、かつ、統計的・平均的に全てのCPUコアが等しく同じ優先順位でアクセスできなくてはならない、ことが解りました。1つの解になるであろうラウンドロビンは複雑すぎて不適切だということも判明しました。
そこで、以下の規則でバス権を調停する案を考えてみました。
変動優先順位方式:
バスサイクルが発生する度にArbCntがカウントアップして変化して行くので、バス権優先順位も規則的に変化します。一定の時間が経過すると、統計的・平均的に全CPUのバス権優先順位は等しくなります。しかも、ラウンドロビンより、圧倒的に単純な論理で実現可能です(カウンタが1個あれば良い)。
バス権調停とは、複数のCPUから要求されたバスアクセスに対して、優先順位に従って1個のCPUを選択する処理の事を指します。割り込みレベルを導入した時、複数の割り込み要因の中から、1つの割り込み要因を決定するのに使ったトーナメント(勝ち抜き戦)回路と同じ仕組みを応用できます。
割り込みと異なるのは、勝ち抜く条件です。以下にバス権調停の勝ち抜き条件をまとめます。
勝ち抜き条件:
バス権調停回路が働くPolycore階層には最大7個のPUNITが存在するので、バス権の調停は7個 → 1個を選び出す処理です。
従って、UnitId[2:0]とArbCnt[2:0]は3ビット(0〜6)の信号線です。割り込みの時と同様なトーナメント🏆によって1個を選び出すので、上の勝ち抜き戦回路を6個、3段(=ceil (log2 (7コア)))使います。勝ち抜く条件には3ビットの大小比較回路を1個含みますが、それほど大きな回路では無いはずだし、従って伝搬遅延も速いと考えられます。
コンパイル(論理合成・配置配線)して、実際にCmod A7ボードへ書き込めるかどうか?は、さておいて、一旦、Moscovium-BS×14コアのデザインを使い、Vivadoのシミュレーションで動作確認しておきたいと思います。Cmod A7ボードへ書き込もうとすると、コンパイルの結果、LUT数やRAMマクロ数が規定範囲内に収まるかどうかなどの制限が付きますが、シミュレーションなら制限なく実行可能です。
以下のプログラムを全14コアで同時に実行させ、共有RAMへの書き込みを同時に実行することで、バス権調停が平均的に分散する事を確かめたいと思います。
実験用プログラム:mcoc_poly_share.asm
※アセンブルに必要なINCLファイル等は、こちらからダウンロードすることができます。アセンブル方法などについては、こちらをご参照ください。
上プログラムの処理内容は、以下の通りです。
プログラムの処理内容:
もし、バス権優先順位に偏りがあれば、CPU間のカウントアップデータが32以下の値でばらつきます。
偏りなく、平均的に満遍なく、バス権優先順位が分散すれば、カウントアップデータは全てが32または、その近辺の値となって終了するはずです。
実験サンプル:シミュレーション結果ログ
0 32
1 32
2 32
3 32
4 32
5 32
6 32
7 32
8 32
9 32
10 32
11 32
12 32
13 32
14 32
Test Pass
CPI: 1079/ 248( 0)= 4350/1000
finish: 63000 ns
シミュレーションしてみたところ、上ログの通りCPU ID番号と、カウントアップした値が15個表示されました。ID=0は存在しないので、ID=0と共に表示されたデータは、上に記した処理内容3)で書き込んだ、実行停止フラグ値です(非0なので、実行停止を示すフラグ)。カウントアップの上限値32を実行停止フラグとして使っています。
ご覧の通り、全14個のCPUコアが32回づつ書き込みできた事が判ります。バス権が満遍なく割り当てられた結果であり、期待通りの動作です。Good。
一応、バス権調停アルゴリズムの効果(有効性)を確かめるため、ArbCntに当たるカウンタを停止(=0固定)して、シミュレーションしてみた結果が以下です(RTLを改変して試行したため、皆様のお手元では確認できません)。
実験サンプル:シミュレーション結果ログ
0 32
1 32
2 32
3 32
4 32
5 16
6 17
7 16
8 16
9 1
10 1
11 1
12 1
13 1
14 1
Test Pass
CPI: 564/ 248( 0)= 2274/1000
finish: 42400 ns
ログの見方は、最初に説明したのと同じです。カウンタArbCntを停止したため、CPU番号の小さい物がより優先順位が高い、固定優先順位となります。CPU IDの小さな、言い換えると優先順位の高いCPUは、32までカウントアップできましたが、それ以外のCPUはアクセス数が制限されて、16前後のCPU(バス権獲得率およそ50%)や、1で終了して実質1回もアクセスできなかったCPU(バス権獲得率ほぼ0%)が発生しました。
この結果をもって、ArbCntを使ったバス権調停アルゴリズムの効果を確認できました。
多コア化(目標14コア)に向けた全体像が見えてきました。次回、割り込みなど、CPU周辺機能の設計を進めます。