これだけ技術が進んでも、意外と大変な作業となるのが「移植」という仕事です。
開発当初から、「これと、これと、この環境で動作するマルチプラットフォーム開発を行う」等と決めて進めた場合なら、まだ良いのかもしれません。しかし、一旦開発が終了した後で、何らかのソフトウェアを別な環境(別なOS等)に移植するのは、大変骨が折れます。
ハードウェア開発(RTL)では、どうなのでしょうか?
今まで、中華製のFPGAボード(Primerボード)を使って開発していたオリジナル16ビットCPUコアMoscoviumの、新規購入したFPGAボード(Cmod A7ボード)への移植を通して確認して行こうと思います。
前回、ソフトウェア(C言語)とハードウェア(Verilog-HDL)を例に、異なる環境への「移植」をテーマに考察しました。今回は、以下のテーマから再開します。
移植の一手順と言えば、その通りなのですが、Cmod A7ボード(Xilinx Artix-7 FPGAチップ)に適応するため、新規に対応が必要となるのが以下の項目です。
この中でSRAMへの対応は、また別の機会に譲るとして、その他のリソースに対する対応を個別に検討して行きましょう。
Primerボードでは、FPGAチップに入力されていた24MHzの源発振クロックを、そのままMoscoviumマイコンのクロックとして使いました。
Cmod A7ボードの源発振クロックは12MHzです。Primerボードと比べて半分の周波数で、遅いです。そのまま使うと、Moscoviumマイコンのパフォーマンスが半分に低下してしまいます。
なお、Cmod A7ボードの回路図で確認すると、12MHzクロックはUSB⇔UART変換チップ(FTDI)にも接続されています。どうやら12の倍数は、USB1.1(通信速度12Mbps)やUSB2.0(通信速度480Mbps)などに起因(整数逓倍)した、丁度良い周波数らしいです。
Cmod A7ボードの源発振12MHzクロックをPLLで×2逓倍すれば、Primerボードと同じ24MHzになります。
Vivadoの「Flow Navigatorサブウインドウ ⇒ PROJECT MANAGER ⇒ IP Catalog」を開き、「PLL」で探してみたのですが、それらしきものが見つかりません。
キーワード「clock」で探し直した所、「Clocking Wizard」という項目が見つかりました。これかな?MMCM(Mixed-Mode Clock Manager)という名前のIPマクロらしいです。
上図は、入力クロックを源発振クロックの12MHz、出力クロックを×2逓倍の24MHz、リセット無し、クロック安定出力信号有り、で作成したMMCMマクロの要約画面です。
このIPマクロを使った場合、クロック制約は自動的に宣言されるため、制約(XDC)ファイルでクロックを設定しようとすると、二重に宣言されてしまうので注意してください。複数のクロックを処理するため、コンパイルに時間が掛かったり、異なる波形、異なる名前で宣言した場合には、クロック間の書き込みバイオレーションが出たりする場合もあります。
: (XDCファイル抜粋)
## 12 MHz Clock Signal
set_property -dict {PACKAGE_PIN L17 IOSTANDARD LVCMOS33} [get_ports sys_clock]
#create_clock -period 80.000 -name sys_clock -waveform {0.000 40.000} -add [get_ports sys_clock]
:
上コード例の様に、制約(XDC)ファイルのcreate_clockは、コメント(#)しておきます。
set_propertyの方は、sys_clock端子を12MHzクロック入力端子L17に割り当てる制約なので、残します。
※念のため、IMPLEMENTATION後のタイミング結果報告で、所望のクロック制約が適応されている事を確認👉しておきましょう(下図、ご参考)。「誰かが定義しているだろう」と、互いに思ってしまって、「誰も定義していなかった」なんてことは、「チーム開発あるある」ですから。
Cmod A7ボードには、2個のユーザスイッチが搭載されています。
Moscoviumマイコンでは、通常のシステムリセットと、ブートモードで起動するブートリセットの、2種類のリセットを使い分けるため、2個のスイッチが必要です。以下の通り、ユーザスイッチ2個共、リセットスイッチとして割り当てて使います。
ユーザスイッチの割り当て:
上に示した2種類のリセットは、スイッチを押した時だけ、発動します。従って、FPGAボードに回路を書き込んだ直後は、リセットが掛からない状態で起動しているのです。
ところが、FPGAの場合、回路を書き込んだ直後、全てのF/Fが0で初期化された状態で起動する様です。Primerボードでも、そうだったので、FPGAのデファクトになっている様です。
従って、リセットで初期化したいF/Fは、0で初期化する論理にしておくと、FPGAに回路を書き込んだ直後、疑似的にリセットがかかった状態で動作を開始することができます。
よって、Moscoviumマイコンの内部リセット信号rst_nは、負論理としてあります。
以下図にリセット信号と動作モードを生成する、システムコントローラSYSCA7ユニットのブロック図を示しておきます。
入出力信号仕様:
Cmod A7ボードでは、前節で生成したMMCMマクロから出力された、クロック安定信号(locked_clk)を使ってリセット信号を生成しています。クロック出力が安定するまでの間、リセットがかかり続けるので、書き込み直後のF/F=0を期待しなくても、しっかりリセットがかかる機構です。
Cmod A7ボードには、ユーザが制御可能なオンボードLEDが3個搭載されています。2個は単色のLEDですが、残り1個はRGB LEDです。RGB LEDは、普通にLED-R/LED-G/LED-Bの3個を個別に制御するタイプなので、実質、3個のLEDだと考えると、合計5個のLEDが搭載されています。
Cmod A7ボードのリファレンスマニュアルページによると、以下の様な記述を見かけました。
「RGB LEDは、明るすぎるので、PWMを使って明度制御する事を推奨」
回路図で確認したところ、確かに、RGB LEDは単色LEDに比べて電流制限抵抗値が小さく設定されている様です。
LEDを光らせるだけなら汎用I/OポートがあればOKで、適切な汎用I/OポートをLEDのつながった端子に割り当てておけば良いです。しかし、使い始めると、明るさを制御したり、RGB LEDの場合は、R,G,B各LEDの輝度を制御することで、色を自由に変化させたりしたくなるのは予測できます。
どうやら、LED駆動用としてPWM出力を用意しておくのが賢い対応の様です。そこで、LED駆動用の専用PWMタイマを設計しておこうと思います。
LED駆動用の専用タイマユニットを設計します。名前は、LED駆動用PWMタイマTIMLED5ユニットとします。以下にブロック図を示します。
※薄黄色の箱はCPUからアクセス可能なI/Oレジスタです。
PrimerボードのオンボードLEDは、普通に汎用I/Oポートを使って光らせていました。PORT8I8Oユニットが提供する8出力端子の内、下位3ビット(PORT[2:0])がRGB LEDの各LEDに接続されていました。その接続を前提としたプログラムもいくつか作ってきました。
Primerボードで作成したのと同じプログラムがそのまま使えて機能するように、TIMLED5にI/OポートPORT[2:0]の制御信号を接続しておいて、Primerボードと同様に、汎用I/OポートでもRGB LEDを光らせることが可能な回路にしておきます。
なお、LED駆動信号LED*(*=1,2,R,G,B)は、HIGH出力でLED点灯、LOW出力でLED消灯となる、正論理で出力しています。Cmod A7ボードのRGB LEDは、点灯/消灯の論理が逆(負論理)なので、Moscoviumマイコンの最上位モジュールから出力する時に反転しています。
FPGAボードによって異なる可能性のある、LEDの駆動論理は、MCUの最上位モジュールで調整すれば良いので、TIMLED5ユニットとしては、正論理で駆動信号(PWM)を出力する仕様としました。
以下表にTIMLED5ユニットのI/Oレジスタ一覧を示し、その下に解説を続けます。
Addr | Name | R/W | Bit:15〜0 | Memo | |||||||||||||||
0xf160 | tledctl | RW | − | − | − | − | − | − | − | − | − | − |
LE D2 |
LE D1 |
RG BE |
LE DG |
LE DR |
LE DB |
led timer control |
0xf162 | tledintc | RW | − | − | − | − | − | − | − | − | − | − | − |
LO FE |
− | − | − |
LO FF |
interrupt control |
0xf164 | tledpsc | RW | PERI[15:0] | prescaler period | |||||||||||||||
0xf166 | tleddtyr | RW | − | − | − | − | − | − | − | − | DUTYR[7:0] | led red duty | |||||||
0xf168 | tleddtyg | RW | − | − | − | − | − | − | − | − | DUTYG[7:0] | led green duty | |||||||
0xf16a | tleddtyb | RW | − | − | − | − | − | − | − | − | DUTYB[7:0] | led blue duty | |||||||
0xf16c | tleddty1 | RW | − | − | − | − | − | − | − | − | DUTY1[7:0] | led 1 duty | |||||||
0xf16e | tleddty2 | RW | − | − | − | − | − | − | − | − | DUTY2[7:0] | led 2 duty |
BUSC出力のセレクト信号はbcs_tled_nです。上表の範囲でアサートされるので、TIMLED5ユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。
I/Oレジスタ仕様:
制御すべきLED数5個に対して、LEDタイマ(カウンタ)は1個なので、PWM周期は共通です。デューティレジスタは5個独立して持っているので、すべて異なるデューティに設定可能です。
本来、デューティレジスタはダブルバッファ構成にしておいて、LEDタイマのオーバフロウ発生タイミングで更新するのが一般的です。しかし、本ユニットのデューティレジスタはシングルバッファです。書き込んだ瞬間に、新しいデューティが有効となり、その1周期は、意図しないデューティでPWMが出力される場合があります。
グリッチを嫌うモータ制御用のPWM出力ではないため、論理規模縮小を優先した結果(仕様)です。
PWM出力するため、8ビットのLEDタイマ1個と、16ビットのプリスケーラカウンタ1個を搭載しています。
なお、無駄な電力を消費しない様に、必要のない時、LEDタイマおよびプリスケーラカウンタは停止しています。停止中は、LEDタイマ、プリスケーラカウンタ共、0にリセットされています。
LEDタイマのカウント条件:
wire tcnt_tim_enb=(
(lctl_rgb_enb && (lctl_ledr_enb || lctl_ledg_enb || lctl_ledb_enb)) ||
(!lctl_rgb_enb && (|gpio_port_oe[2:0])) ||
(lctl_led1_enb || lctl_led2_enb) );
ちょっと複雑な条件なので、RTLを、そのまま示してしまいました(1^^;)っ。
一言で言うと、「いずれかのLEDを光らせようとした場合」に、LEDタイマをカウントアップします。リセット解除直後など、いずれのLEDも光らせようとしていない場合、LEDタイマは停止しています。
LEDタイマが停止した状態では、LOFFフラグがセットされる事は無いので、LOFR割り込み要求が発生することもありません。割り込みを要求する場合、上のtcnt_tim_enb=1となるような条件を成立させる必要があります。
もし、LEDタイマだけをカウントアップさせて、かつLEDを光らせたくない場合、デューティレジスタに0を設定して、PWM出力をLOW固定する方法があります。
当初から意識して設計して来たことも手伝って、何とか、新しいCmod A7ボードでのMoscoviumマイコンの姿が見えてきました。次回、シミュレーションと実際のFPGAボードで動作検証を実施できると思います。