Cmod A7ボード(FPGA)を使って開発しているオリジナル16ビットCPUコアMoscoviumに於いては、前回の記事で漸く全てのCPUコアを移植完了しました。
今回のテーマは、デュアルコア版の移植です。
Moscoviumマイコンのデュアルコア版は、同じ機能を持ったCPUが2個🌜🌛集積される、SMP(Symmetric Multi-Processing)型のマイコンです(下図)。
最初にデュアルコア版を設計した時(「#17-1 双頭の狼」)には、まだMoscoviumコアしか存在していませんでしたが、その後、32ビットに拡張したNihoniumコアと、論理規模を縮小したTennessineコアが追加されています。
「`define MCOC_DUAL」を有効化すると、CPUが2個インスタンスされるのですが、他にも、2種類のI/Oユニットがインスタンスされる様に、なっていました。
デュアルコアCPUを使うに当たって便利なI/Oユニットではあるのですが、必須機能では無いので、コンパイルオプションを追加して個別にインスタンスするか否かを選択可能な様に改定します。
コンパイルオプション(追加):
これら2つのI/Oユニットはデュアルコア版に必須の機能ではありませんが、これらのI/Oユニットを使うためにはデュアルコアであることが必須なので、デュアルコア版以外の場合には有効化できません。
セマフォと言うのは、資源管理に使用する技術です。
デュアルコア版の様に複数のCPUから、使用可能な数が限定された資源へ、同時にアクセスする場合、「今から使うよ」、「今使い終わったよ」という、やり取りをCPU間で実行して、使用可能な数を超過しないよう制御するために使います。
CPUが1個の場合でも、FreeRTOSの様に、マルチタスクプログラミングが可能な場合、複数のタスクから、使用可能な数が限定された資源へ、同時にアクセスする場合に必要となります。
この様な資源管理を実行しなくてもプログラミング可能な場合があるため、必ずしも必須の機能ではありません。
Moscoviumマイコン用のSEMPH5R12Uユニットは、150LUT程の回路です。めちゃくちゃ大きな回路ではありませんが、必要ない回路にLUTを消費したくないケースもあるでしょうから、コンパイルオプションを追加して、選択できるようにしておきます。
2個のCPU間で情報をやり取りするためのI/Oユニットです。
複数のマイコン間で通信しながら1つの機能を果たそうとする場合、例えば、UART等を使って接続し、互いに通信しながら処理を進めます。他にも、I2CやSPI等と言った通信手段が良く使われます。
デュアルコア版の場合、同じチップ上に集積された2個のCPU間で通信できれば良いので、わざわざUARTやI2C等のシリアル通信規格を使う必要がありませんし、パラレル通信でバンド幅を広げた方が速くて使い易いです。
この様な用途のため、2個のCPU間で相互にデータを送受信できる様、双方向のFIFOバッファを使って通信するI/OユニットがICFF16ユニットです。
ただ、CPU間の通信は、共有メモリを介して実行することもできるので必須ではありません。このI/Oユニットも、コンパイルオプションを追加して、個別に有り/無しを選択できるようにしておきます。
また、これまでICFF16ユニットには、FPGAメーカが提供するFIFOマクロを使っていたのですが、FIFOマクロはRAMマクロを消費するため、1Kバイトとか、2Kバイトとか、かなり大きなFIFOと、なっていました。
こんなに大きなFIFOは必要ないため、こちらで設計した小容量FIFO回路を使う様に変更します。
FIFO:
結果、ICFF16ユニットは、FIFOに使う分散RAMのLUT数も含めて140LUT程の回路となりました。
Cmod A7ボード用Moscoviumマイコンの設計データ(Vivadoプロジェクト)は、GitHubにて配布しております。以下バナーよりアクセスしてください。
ダウンロード方法などについては、トップのREADME.mdをご覧ください。
今回までに、以下のI/Oユニットを移植してリリースしました。
今回リリースしたI/Oユニットによって、以下の型名が解禁されました。
後ほど実験でnhoc113dc0416.vhを使いますので、コンパイルしてCmod A7ボード(FPGA)に書き込んでおいてください。
デザイン(回路)をCmod A7ボードに書き込む手順については、こちらをご確認ください。
Vivadoでのコンパイル(論理合成・配置配線)結果を以下にまとめます。
コンパイル結果:
上述の通り、32ビットのNihoniumコアが約3,900LUT/1個で、それを2個搭載したNHOC113DC0416全体が10,010LUTでした。
最大20,800LUT使用可能なXilinx Artix-7(XC7A35T)ですので、試しにスーパスケイラ版Nihonium-SSコアを使ってデュアルコア版をコンパイルしてみたところ、LUT数が最大値20,800個を超えてしまい、エラー終了してしまいました。
ただ、もう一息、な、感じなので、今回追加したコンパイルオプションなどを使って、不要なI/Oユニットを外していくと、収まらないことも無いです(1^^;)っ。
※Xilinx Artix-7シリーズの6入力LUTの実力なのか、中華製Primerボードでは全くお話にならなかったNihonium-SSデュアルコア版も、もう一息で書き込むことができるくらいになっています。良き。
さすがに、Nihonium-SS程の高性能CPUを2個も積むのは無理がある様なので、もう少し考えた方が良さそうです。そちらはまた、別なテーマとします。
実験には、またまたまた、円周率計算プログラムを使います。
円周率計算プログラム:calc_pi.asm
手元にある、デュアルコアCPUで分散処理したプログラムは、これだけだから……。アセンブル後、Cmod A7ボードへ転送してお使いください。
以下実験中の様子です(写真は使い回し)。
実験サンプル:
【pi,5116】
OK+ start calculation
OK+ Klingenstierna's formula
OK+ dual core cpu edition
OK+ pi=3.
1415
9265
3589
:
: (中略)
:
0141
6314
9648
OK+ digits=5116 (size=1280)
OK: pi time[us]=0x012cba49
5,116桁の円周率を計算させました。円周率5,000桁が、こちらの期待値と一致することを確認できました。前回、スーパスケイラ版で計算させた円周率5,000桁と比べても、演算時間(pi time[us])が短縮しており、デュアルコアに処理を分散した効果が見えています。
デュアルコア版Nihoniumコアの動作確認終了、OKです。
前節実験で使用した円周率計算プログラム(calc_pi.asm)では、多倍長固定小数の逆正接atan()関数計算ルーチンにて、スタックポインタ上にスタックフレームを確保してサブルーチン内で使用する、局所変数を使っています。
32ビットのNihonium用としてパフォーマンスを最大化するため、スタックポインタをロングワード境界に乗せるようにプログラムしていたつもりが、そうなっていませんでしたorz。→ロングワードスタック
犯人は、サブルーチン分岐、jalw命令とrtnw命令。そうだよ、この子たち、戻り先アドレスを1ワード(2バイト)データとしてスタックに積むから、その時、スタックポインタが奇数ワードアドレスに変化してしまうんだ……。
Nihoniumでロングワードスタックを使うなら、jall命令とrtnl命令を使う必要があったのです。
対処案:
今回のプログラムで問題だったのは、逆正接atan()関数を計算するサブルーチン1か所だけ、だったため、2)の回避策を採用しました。
全CPUコアの移植が完了して、概ね、キーとなる回路はCmod A7ボードへの移植を完了しました。まだ、いくつか移植未済のI/Oユニットが残っていますので、追々移植作業を続けながら、そろそろ、Cmod A7ボード特有のリソースを使った機能強化にも取り組んで行きたいと考えています。