TOPPERS/ASPのターゲット依存部をRenesas FSPで実装してみた
概要
使おうとしている基板でTOPPERS/ASPを使いたいと思っても、その基板向けのターゲット依存部がないと、自分で実装する必要があります。
Renesas製のRAシリーズの基板であれば、FSPというRenesasから提供されるソフトウェアスタックを利用して、比較的簡単にターゲット依存部を実装することができます。
また、Visual Studio Code(以降VSCode)の拡張機能がRenesasから提供され、FSPが使えるようになっているので、VSCodeに対応したTOPPERS/ASPを使って開発環境を整えることができます。
この説明では、Renesas製の評価基板EK-RA6M5とEK-RA8M2にFSPを使用して移植し、別の基板への移植の参考になるよう手順を紹介します。
ソースコードは下記にあります。
https://github.com/exshonda/asp3_fsp
TOPPERS/ASPの移植
TOPPERS/ASPをターゲット基板に移植する場合、CPUアーキテクチャに依存するコードと、ターゲット基板に依存するコードが必要になります。コンパイラについても使うものに依存したコードが必要になりますが、今回の開発環境ではGCCとLLVMだけに対応した環境ですので、コードは共通で使えます。
| 移植対象 | コードのパス | コンパイラ依存 |
|---|---|---|
| CPUアーキテクチャに依存するコード | asp3/arch/arm_m_gcc/ |
Cortex-M、GCC/LLVMに依存 |
| ターゲット基板に依存するコード | asp3/target/ |
GCC/LLVMに依存 |
CPUアーキテクチャに依存するコードについてはさらに細かく、common/がCortex-M共通で、ra6m5_fsp/とra8m2_fsp/がそれぞれのターゲットのチップ向けに分かれています。
ターゲット基板に依存するコードについてはさらに細かく、ek_ra6m5とek_ra8m2にそれぞれのターゲット向けに分かれています。
アーキテクチャ依存部
CPUアーキテクチャに依存するコードは、EK-RA6M5がarm Cortex-M33で、EK-RA8M2がarm Cortex-M85なので、TOPPERS/ASPに付属するコードが使用できます。
TOPPERS/ASPに付属するコードは、下記のCortex-Mアーキテクチャに対応していて、__TARGET_ARCH_THUMBマクロ定義で設定します。今回の基板では両方とも値は5になります。
| アーキテクチャ | コア | __TARGET_ARCH_THUMB |
|---|---|---|
| ARMv6-M | M0/M0+/M23 | 3 |
| ARMv7-M | M3/M4/M7 | 4 |
| ARMv8-M | M33 | 5 |
| ARMv8.1-M | M85 | 5 以上 |
ターゲット依存部
次に、ターゲット基板に依存するコードは、先述のFSPというソフトウェアスタックを利用することができ、FSPの構成をスマートコンフィギュレーターという付属のGUIツールで行えます。
ターゲット基板に依存するコードには、CPUのクロック設定やピン設定、割り込みハンドラ定義、OSで使うタイマーの設定や処理、ログ出力などに使うUARTの設定や処理を記述します。
スマートコンフィギュレーターを使うと、この部分をFSPを使ったコードとして生成してくれるので、周辺レジスタの設定方法を調べたり、割り込みハンドラの位置や番号を調べて実装する手間が省けます。
| 実装内容 | ファイル | 処理内容 |
|---|---|---|
| 初期化処理 | target_kernel_impl.c | CPUのクロック設定やピン設定 |
| 割り込みハンドラ定義 |
target_kernel.py -> kernel_cfg.c
|
標準方式では、静的APIで定義された割り込み構成をkernel_cfg.cにコード生成する |
| タイマードライバ | target_timer.c | ティックレスタイマー処理、signal_timeの呼び出し |
| シリアルドライバ | target_serial.c |
asp3/syssvc/serial.cのターゲット依存部、オープン、クローズ、文字送信、文字受信、割り込みを使わないポーリングによる文字送信 |
TOPPERS/ASPの標準的な移植では、割り込みハンドラ定義はcfgファイルの静的API(DEF_INHやCFG_INT)で記述し、カーネルコンフィギュレーターがkernel_cfg.cにベクターテーブルや割り込み設定情報を生成します。
TOPPERSカーネルコンフィギュレーターはRubyスクリプトで実装されていますが、アーキテクチャやターゲットに応じてRubyスクリプトを変更する必要があります。
FSP対応版では、Renesas拡張機能に付属するPythonを利用するため、TOPPERSカーネルコンフィギュレーターをPythonスクリプトで実装しています。別途Rubyをインストールする必要がなくなりました。
上記のtarget_kernel.pyは、Ruby版ではtarget_kernel.trbというファイル名でした。
スマートコンフィギュレーターの生成コード
ターゲット依存部が必要とするコードは、ほとんどスマートコンフィギュレーターで生成できます。
FSP対応のターゲット依存部は、TOPPERS/ASPから要求される実装をFSPのAPI呼び出しで補完する程度になります。また他の基板への移植時も同じような構成でプロジェクトを作成すれば、大きな変更なしで使えます。
| 実装内容 | ファイル | 実装 |
|---|---|---|
| 初期化処理 | target_kernel_impl.c | 生成コードで初期化済みなので、シリアルのオープンのみFSPドライバの呼び出しで実装 |
| 割り込みハンドラ定義 | target_kernel.py -> kernel_cfg.c | スマートコンフィギュレーターの生成コードにベクターテーブルや割り込み優先度が出力されるので、それを利用するよう実装 |
| タイマードライバ | target_timer.c | GPTで構成しコールバックにtarget_hrt_handlerを指定、カウンタ値取得や、次回割り込み時刻設定はFSPドライバの呼び出しで実装 |
| シリアルドライバ | target_serial.c | EK-RA6M5はSCIで、EK-RA8M2はSCI_Bで構成し、コールバックにtarget_uart_handlerを指定、オープン・クローズ処理や、送受信処理はFSPドライバの呼び出しで実装 |
「Generate Project Content」ボタンを押下すると、コードが生成されます。
スマートコンフィギュレーターでプロジェクトを作成すると、生成されるフォルダra_genに下記のファイルが生成されます。TOPPERS/ASPが要求する、ターゲット依存部が用意されるのでこれを利用します。
| ファイル | 内容 |
|---|---|
| bsp_clock_cfg.h | クロック設定 |
| common_data.c/.h | FSPの共通定義 |
| hal_data.c/.h | ドライバ構成 |
| main.c | メイン処理(hal_entryの呼び出し) |
| pin_data.c | ピン設定 |
| vector_data.c/.h | 割り込みハンドラ定義 |
初期化処理
スマートコンフィギュレーターが生成する、コードがCPU初期化処理を行ってくれます。プロジェクト作成時にsrc/hal_entry.cというファイルが生成されるので、TOPPERS/ASPの開始sta_kerを呼び出せばOSが開始し、静的APIで構成したタスクが開始します。
sta_kerは割り込み禁止状態で呼び出す必要があります。
void hal_entry(void)
{
/* TODO: add your own code here */
__asm("cpsid f":::"memory");
sta_ker();
}
割り込みハンドラ定義
TOPPERS/ASPの標準的な方式では、カーネルコンフィギュレーターが割り込み構成を生成します。一方、今回のFSP方式では、スマートコンフィギュレーターもIRQ番号・優先度・コールバック設定を生成します。TOPPERS/ASP側とFSP側の両方で同じ割り込みを定義すると設定元が二重になるため、FSPドライバ用の割り込みはスマートコンフィギュレーター側を正とし、cfgファイル側ではDEF_INHやCFG_INTで別途定義しない方針にしました。
ただし、TOPPERS/ASPでは割り込みの入口処理と出口処理が必要です。そのため、スマートコンフィギュレーターが生成したベクターテーブルをCPUのVTORに直接設定するのではなく、TOPPERSのコンフィギュレーターが生成した_kernel_vector_tableをVTORに設定し、各IRQはいったんcore_int_entryに入ります。
core_int_entryは現在の例外番号を取得し、スマートコンフィギュレーター由来の__Vectorsに対応付けたexc_tbl[intnum]()を呼び出します。
タイマードライバ
タイマードライバは、FSPのAPIを利用した実装になっているので、スマートコンフィギュレーターで、GPTを巡回タイマーとして設定すれば、タイマードライバを変更する必要はありません。
TOPPERS/ASPはティックレスタイマーとなっています。ティックレスタイマーでは、32bitのカウンタを0〜0xFFFFFFFFを巡回(ラップアラウンド)するタイマーを利用します。
このカウンタをOSのシステム時刻として使うため、1カウント1μsとするのが理想です。サンプルアプリではタイマーに指定する値の単位を1μsと想定しています。例えばslp_tsk(1000)の引数1000は、1msを想定しています。ティックレスタイマーでは、OSが必要と思った時間で割り込みを行うので、定期的な割り込みがなく、時間の精度を上げることができます。なので1msのタイマーだと価値が失われます。
EK-RA6M5やEK-RA8M2ではGPTが適していたので、こちらを使っていましたが、スマートコンフィグレータでは、1カウント10nsの設定から1μsに変更できないようなので、ドライバ側で100倍や100分の1して使うようにしました。
ただし、100倍すると0xFFFFFFFFから0に、カウントアップする時に、不連続になるので、内部では64bit相当で保持し、ASP3には1μs単位の32bit HRTCNTとして返しています。
OSがタイムアウトなどの次に発生させたい契機の時刻をタイマーのカウンタで設定します。設定したカウンタで割り込みを掛けるため、GPTではコンペアA割り込みを利用します。
TOPPERS/ASPはタイムアウトの処理を行うため、タイマードライバからのsignal_time関数の呼び出しを期待していますので、FSPに設定したコールバックハンドラtarget_hrt_handlerで、signal_timeを呼び出すよう実装します。そのほかtarget_hrt_get_currentで返す値に応じて、次のsignal_time呼び出し要求の時刻がtarget_hrt_set_eventで指定されます。即時呼び出しが必要な場合、target_hrt_raise_eventでタイマー割り込みを要求します。これらの処理をFSPのGPTドライバAPIで実装しています。ほかの基板に移植する際にGPTを使うのであれば変更なしで使用できます。EK-RA6M5とEK-RA8M2で実装の違いはありません。
シリアルドライバ
シリアルドライバはTOPPERS/ASP付属のsyslog関数を使用する際に必要になりますが必須ではありません。TOPPERS/ASP付属のsample1は、syslog関数を使用するので、実装してあります。
EK-RA6M5はSCI、EK-RA8M2はSCI_Bを使用します。チャネルはそれぞれSCI7とSCI8を使っています。ボーレートなどの設定は、TOPPERS/ASPとしての要求はありません。サンプルは115200bps、データ8bit、ストップビット1bit、パリティなしの設定です。
| ボード | タイプ | チャネル |
|---|---|---|
| EK-RA6M5 | SCI | SCI7 |
| EK-RA8M2 | SCI_B | SCI8 |
TOPPERS/ASP付属のシリアルドライバは、ターゲット非依存部とターゲット依存部に分かれた構成になっています。ターゲット非依存部はタスクから扱いやすいよう文字列送受信のインターフェイスを持ち、送受信が完了するまでのブロック処理やバッファの管理を行います。ターゲット依存部は単一文字送受信など、CPUペリフェラルレジスタ操作や、割り込み処理などを実装します。
EK-RA6M5はSCI、EK-RA8M2はSCI_Bと、CPUのシリアルが、違ったタイプのペリフェラルを使用し、レジスタ構成も違いますが、FSPが同じように使えるAPIを提供してくれるので、おおむね同じコードとなっています。
シリアルドライバのターゲット非依存部の要求に従った実装をするため、FSPのAPIでは対応できず、ペリフェラルレジスタを操作する必要がありました。ほかの基板への移植時も同様に、CPUシリアルのタイプに応じた変更が必要になると思います。
まとめ
スマートコンフィギュレーターでターゲット依存部のほとんどが生成されるので、FSP対応版のターゲット依存部の実装を使えば、大きな変更なしにTOPPERS/ASPへ移植しやすくなると期待できます。


