今日頂くのはデュアルコアCPUマイコンです。
その昔CPUは動作周波数の向上に労力のかなりな部分を費やしていました。ムーアの法則に乗っかって、どんどん動作クロックは高速化したのですが、ムーアの法則がドン詰まって以降、今度は、コア数増加の道をたどります。もっとも、コア数増加は ⇒ 論理規模の増大 ⇒ 面積の増大、とつながり、結局ムーアの法則に帰り着くので、これも長くは続きませんでした。
それでも、1個のCPUがすべての周辺リソースを同時に並行に使うことはできないので、複数のCPUコアが並列して処理した方が、周辺リソースの効率的な運用ができる点で、マルチコア化には一定の利点が存在します。アリかナシかで言ったら、断然アリなのです。
世の中には例えば、演算処理に特化した32ビットMPUと、周辺装置のリアルタイム処理に長ける16ビットMCUを組み合わせる様な、AMP(Asymmetric Multi-Processing、非対称並列処理)というシステムも存在しています。適材適所、得意な処理を得意なCPUに実行させる方式です。
一方、PCやスマホなどで使われるマルチコアCPUは、SMP(Symmetric Multi-Processing、対称並列処理)と呼ばれます。各CPUに性能面や機能面の優劣が無く、上下関係も、主従関係も存在しない対称なシステムのことです。
今回は、FPGAボード(Sipeed Lichee Tang Primer)にデュアルコアCPUマイコンを作り込んで見ます。1本のバスにすべてのメモリとI/Oユニットがぶら下がって、上下関係や主従関係のない全く同じCPUが2個存在する、SMP型のデュアルコアにしてみようと思います。
やっぱり、最初はCPUから検討を始めないとなりませんね。使用するCPUはもちろん、オリジナル16ビットCPUコアMoscoviumです。
でも、意外なことに、CPUでは何もやることは無いのです。今まで作って来たシングルコア版のCPUそのものでOKです。
ただ、さすがにIDくらいは振っておきましょう。今回はシステムの中に2個のCPUが存在して、リセット解除で同時に同じプログラムメモリから命令フェッチして起動します。そのまま放置すると、2個のCPUがひたすら同じプログラムをフェッチして、同じ処理を実行してしまいます。
状態レジスタsrに、IDを読み出せるビットを2ビット追加します(下図)。
IDフィールドからは以下のCPU IDが読めるようにしておきます。
2個のCPUで動作するプログラムを2本別々に用意するのは面倒極まりないため、命令ROMは1個だけで、2個のCPUからは全く同じROM(従ってプログラム)が読めることとします。プログラムでSrレジスタのIDフィールドを読み出し、シングルコア版、デュアルコア版(CPU1またはCPU2)を判定し、適切なコードに分岐させます。
※以降、2個のCPUをCPU1、CPU2等と呼称します。
CPUで変更すべきはこれだけです。お〜しまいっ!
デュアルコアCPUなのに、CPUが何もしない(何も変更しない)分、周りの回路はそりゃぁ大騒ぎ🎉となります。以下、変更が必要なユニットについて、個別に議論を進めます。
リソースを効率良く活用できるよう色々なバストポロジが考えられますが、今回はシンプルに、以下図の様なトポロジで設計します。
今までのシングルコア版と同じで、マイコンの中にバスは1本だけ(メモリバス)となります。すべてのI/OユニットやRAM,ROM等のメモリはこのメモリバスにぶら下がっています。一方、能動的にバスにアクセスする「バスマスタ」としてのCPUが2個になります。
バスは1本しかないので、2個のCPUから同時にバスアクセスすることはできず、いずれかのバスマスタが排他的に時分割でバスアクセスを実行することが出来ます。
バスを使用してバスアクセスする権利のことを「バス権」と呼びます。バスステートコントローラBUSCが、CPU1,CPU2からのバスアクセス要求を受け、バス権を調停後、バス権を勝ち取ったCPUがバスへアクセスします。バス権を獲得できなかったCPUは、バスが空くまで待たされます。
例えば、バス権の優先順位をCPU1>CPU2等の様に固定してしまうと、SMPとしての対称性が損なわれてしまうので、優先順位をラウンドロビンと言う方法で動的に制御します。
以下図は、2個のCPUが同時にバスアクセスする様子をシミュレーション(iverilog,GTKWave)によって確認したタイミング図です。
※クリックすると拡大してご覧になれます。
上のシミュレーション波形は、以下の様にCPU1,CPU2から同時にpush/popして、連続したメモリアクセスを発生した場合の動作です。
pushwm r0,r1,r2,r3 // 連続4回書き込み
popwm r7,r6,r5,r4 // 連続4回読み出し
メモリバス仕様をご確認いただいて、brdy,badr,bcmd,bmst信号等の動作を追っていただくと、信号名cpuid(橙色着色)の様に、CPU1とCPU2が交互にバスアクセスしながら並行して動作している様子が解ると思います。
繰り返しになりますが、バスは1本だけなので、ある一瞬で見ると、いずれか1個のCPUだけがアクセス可能です。バスにぶら下がるRAMやI/OユニットのI/Oレジスタに関して言えば、全く同時に2個のCPUからアクセスされることは無いので、今まで作ってきたユニットをそのまま流用可能です。
ただし、命令ROMについては、命令フェッチが2個のCPUから同時に起きるので、後ほど検討して、デュアルコアに対応する必要があります。
CPUを設計した時に取り決めたメモリバス仕様(下図参考)ですが、今後、どちらのCPUからのアクセスかを知って、動作を変える必要のあるユニットが登場するので、アクセス中のCPU IDを示すbmst信号を追加します。
メモリバス追加仕様:
CPU IDを知る必要のないユニットは使わなくて良い「オプション」信号なので、今まで設計して来たUART8N1、SYSTIM等のI/Oユニットはそのまま流用可能です。
CPUがldw命令などを使ってデータとしてアクセスするオペランドアクセスに関しては、メモリバス経由のアクセスとなるので、RAMや他のI/Oユニットと同じで今まで通り流用可能です。
問題はCPUが命令を読み出すためにアクセスする、フェッチアクセスです。
Moscoviumはハーバードアーキテクチャなので、オペランドアクセスはメモリバス、フェッチアクセスはフェッチバスを使ってアクセスします。シングルコア(従来)版のROMは、物理的に1つしかアクセスポートを持たないシングルポートROMマクロを使っていたので、Moscoviumのステートマシンでフェッチアクセスとオペランドアクセスが同時に起きない様に作り込んであります。
デュアルコアCPUになったことで、例えばCPU1のフェッチアクセスと、CPU2のオペランドアクセスが同時に発生するケースが起こり得ます。そもそも、2個のCPUが各々異なるアドレスへのフェッチアクセスを実行するため、フェッチアクセスの競合はもっと高頻度で発生します。
現在のROM構成では、同時アクセスは不可なので、何らかの対策が必要です。
解決策の例:
今回使うFPGAボード(Anlogic EG4S20BG256)では、デュアルポートRAMマクロを使用可能なので、2)の解決策を選択します。もし、デュアルポートRAMマクロを持たないFPGAボードやプロセス等で実現する場合は、1)で回避することも可能です。
以下、今回使用する4KバイトROM用デュアルポートRAMマクロの設定情報です。TD IDEのIP Generatorで以下図の様に生成してください。
※「#16-7 Moscoviumにブートローダを」で対応したブートローダ機能を実現するため、作成するのはROM用ですが、RAMマクロです。
※ROM容量8Kバイトおよび16Kバイト版を使う場合には、上の設定で、「Memory Size ⇒ Depth」欄を4,096または8,192として作成してください。ファイル名も合わせて変更(ram8kbd_matまたはram16kbd_mat)することをお忘れなく。
16ビット×2,048ワード=4Kバイト版です。8Kバイト、16Kバイト版が必要な場合には、ワード数とモジュール名を所望の値に変更して生成してください(下表)。
`define | ROM容量 | ROM用RAMマクロ名 | サイズ |
MCOC_ROM_16K | 16Kバイト | ram16kbd_mat(.v) | 16ビット×8,192ワード |
MCOC_ROM_8K | 8Kバイト | ram8kbd_mat(.v) | 16ビット×4,096ワード |
なし | 4Kバイト | ram4kbd_mat(.v) | 16ビット×2,048ワード |
※ROM用ですが、デュアルポートRAMマクロを生成してください。
上の様にして作ったデュアルポートRAMマクロに以下の様なバスラッパを被せて、都合4本のバスからのアクセスを制御しています。
見た目上は4ポートROMです。CPU1とCPU2からはフェッチバスの同時アクセスがあり得るので、デュアルポートRAMマクロが必要です。各CPUで見ると、メモリバスとフェッチバスの同時アクセスは存在しないので、時分割でどちらかのアクセスが発生するのをラッパが制御しています。
メモリバス側のアクセスでは、CPU1からのアクセス時とCPU2からのアクセス時で、使用するデュアルポートRAMのポート(AまたはB)が異なるため、前節のBUSCで拡張したbmst信号を使って切り分けしています。
※実装ではメモリバスが集約されて1本になり、bmst信号でポートを選択するので、メモリバス、フェッチバス1、フェッチバス2の、3ポートROMになっています(「4ポート」は、上図見た目の話)。
CPUとバス周りの設計が終わったところで、一旦小休止です。次回は割り込み制御とI/Oユニットの設計を進めます。