ローダーを使いこなして、分割開発の実行・デバッグの悩みを解決

ローダーとは

組み込みシステムにおけるローダーとは、電源が入り、RESET信号が解除されて動き出す最初のプログラムです。ローダーの役割は、プログラムをメモリ上に読み込むためにメモリコントローラの初期化を行い、プログラムが保存されているフラッシュメモリからSRAMやDRAMなどのメモリに展開して実行するまでの動作を行います。Linuxなどでは、u-bootなどが有名です。最近のマイコンであれば、そのまま実行することもできますが、Cortex-Aに代表されるようなプロセッサーでは、一度メモリに展開してから実行するものがほとんどです。

組み込みシステムを設計して、評価ボード等でソフトウェアも動作確認したら実機で動作検証を行う段階に入ります。ドライバやOS部は、評価ボードがあれば同じことが確認することができます。最終段階では、実機を使って動作検証を行います。RZ/A1Hは、10MBのRAMを持っているので、プログラムはRAMに展開して使用することになります。この講座では引き続き、RZ/A1H Starter Kitを使用していきますので、評価ボードと実機は同一として扱います。

インテリジェントローダーとは

さて、ローダーというと、u-bootなどに代表されるブートローダーが一般的です。今回紹介するローダーは、これまでの組み込みシステム開発で「なんで今までなかったんだ?」と思わせるほどの、インテリジェントローダーです。今回紹介するSOLIDインテリジェントローダーを使えば、ファイルシステムは必要にはなりますが、これまでの組み込みシステムアプリケーション開発につきまとう「ロード時間」や「分割開発」はもちろんですが、より「セキュアな開発」が可能になります。そして何よりも、共同でデバッグをしなくても作業ができるのは、これからの組込みシステム開発では必須になるのではないでしょうか。

SOLIDインテリジェントローダーの仕組み

SOLIDインテリジェントローダーは、アプリケーションタスクを、PCのアプリケーションのように個別にロード・アンロードする方法を提供しています。Windowsを例にすると、Windowsのアプリケーションは、単体で動く場合もありますが、機能が多くなるとアプリケーションのサイズも大きくなってしまいます。そこで考え出されたのが、DLL(Dynamic Link Library)方式です。

DLL方式は、必要なときに必要なライブラリを呼び出すための動的アドレス解決を提供する仕組みを持っており、とても画期的なシステムです。しかし、組み込みシステムで、DLL方式の実装はとても負荷が重く、これまでなんども試みがありましたが、実用レベルには難しいとされてきました。

今回のSOLIDで提供されているローダーは、かなり近いレベルで実現しています。

事前に、ロードされる側のアプリを読み込んでおくことと、お互いのシンボル情報を共有できることで、DLL方式に近い実装が実現しています。

動画では、RZA1Hに搭載されているmicroSDにSOLIDで提供されているファイルシステムを使用して、アプリケーションタスクをロードする方法を紹介しています。このメリットは、仕向け先によってソフトウェアを変更しなければいけない場合や、開発拠点が複数ある場合でも、効率よくデバッグ作業ができます。この機能を使えば、共同デバッグ作業から解放されるのは時間の問題です。

しかも、ロードする側と、ロードされる側のアプリケーションのデバッグ情報を共有することができるので、実際にソースコードがなくても、お互いのシンボル情報を相互参照できるようになっています(図1)。

図図1:シンボルのエクスポート

インテリジェントローダーの詳細はこちら

SOLIDインテリジェントローダーを使うための4ステップ

SOLIDインテリジェントローダーを使うには、4つのSTEPが必要です。

  1. ファイルシステムを有効にしたロードする側のプロジェクトを作成します。
  2. リンカスクリプトを変更したプロジェクトを作成し、ビルドします。
  3. RZ/A1Hに搭載されているMicroSDに2のアプリを保存します。
  4. 1のプロジェクトで、アプリをロードし、実行します。

ここからは、ロードする側のアプリを単に「アプリ」として、ロードされる側のアプリを「サブアプリ」として記載します。

1. ローダーを含んだアプリのプロジェクトをビルド

ローダーを使うには、ファイルシステムを組み込んでおく必要があります。プロジェクトを作成するときに、ビルド済みのファイルシステムを選びましょう。

ローダーは、2つのAPIを指定するのみです。

SOLID_LDR_LoadFile
SOLID_LDR_CanExec

そのほかとして、よく使うAPIのシンボルをエクスポートすることができます。

SOLID_LDR_RegisterSymbol

このAPIを使用すると、syslogなどのAPIをロードされるアプリで呼び出した場合、ロードした側のモジュールで実行することができます(図2)。まるでパソコンのような使い勝手を実現しており、とても便利な機能です。

図図2:ローダーのAPIを呼び出している例

ファイルシステムを含んだプロジェクトが出来上がれば、Starter KitのRZ/A1Hに搭載されているMicroSDが使うことができます。

【SOLIDのFileSystemの簡単な使い方例】

						int fd;
						int ret_size;

						SOLID_FS_Open(&fd, “¥¥FATFS¥¥SD¥¥temp.txt”, O_WRONLY | O_CREATE);
						SOLID_FS_Write(fd, “SOLID_OS”, 8, &ret_size);
						SOLID_FS_Close(fd);
					

このコードで、MicroSDにtemp.txtが作られて、中身は「SOLID_OS」が書き込まれています。このMicroSDへのアクセスは、SDA(SD card Associates)のライセンスの関係上、SPIによるアクセスのみです。ファイル名は、8.3形式がプラットフォーム依存が少ないので、おすすめです。

2. サブアプリをビルドする

ファイルアクセスができたことが確認できれば、サブアプリをロードする環境は整っています。

まずは、サブアプリのビルドをしましょう。サブアプリは、通常のアプリケーション新規作成で作成しますが、リンカスクリプトであるldファイルが異なります。新規作成時に作られるboot.ldとsolid_cs.ldは削除します。その代わりに、こちらのファイルを使います(図3)。

図図3:アプリのみの新規作成時から、設定を変えたサブアプリのプロジェクト

【app1.ld】

						OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm","elf32-littlearm") 
						OUTPUT_ARCH(arm)

						ENTRY(app1_main)

						SECTIONS {
						    . = 0x00010000;

						    .text ALIGN(4K) : {
							*(.text .stub .text.* .gnu.linkonce.t.* .glue_7t .glue_7)
						    }

						    .rodata ALIGN(4K) : {
						        *(.rodata .rodata.* .gnu.linkonce.r.*)
						
						        . = ALIGN(4);
						        _ctor_list1 = . ;
						        KEEP (*(SORT(.init_array.*)))
						        KEEP (*(.init_array))
						        LONG (0);
						
						        _dtor_list1 = . ;
						        KEEP (*(.fini_array))
						        KEEP (*(SORT(.fini_array.*)))
						        LONG (0);
						    }
						
						    .Arm.extab : {
							*(.Arm.extab* .gnu.linkonce.armextab.*)
						    }
						
						    .Arm.exidx : {
							*(.Arm.exidx* .gnu.linkonce.armexidx.*)
						    }
						
 						   .data ALIGN(4K) : {
							*(.data .data.* .gnu.linkonce.d.*)
						    }

							/*
						    .bss ALIGN(4K) : {
							*(.sbss .sbss.* .bss .bss.* .gnu.linkonce.b.*)
							*(COMMON)
						    }
						
							 *  DWARF debug sections.
							 *  Symbols in the DWARF debugging sections are relative to
							 *  the beginning of the section so we begin them at 0.
							 */

							/* DWARF 1 */
							.debug			0 : { *(.debug) }
							.line			0 : { *(.line) }

							/* GNU DWARF 1 extensions */
							.debug_srcinfo	0 : { *(.debug_srcinfo) }
							.debug_sfnames	0 : { *(.debug_sfnames) }

							/* DWARF 1.1 and DWARF 2 */
							.debug_aranges	0 : { *(.debug_aranges) }
							.debug_pubnames	0 : { *(.debug_pubnames) }

							/* DWARF 2 */
							.debug_info		0 : { *(.debug_info) }
							.debug_abbrev	0 : { *(.debug_abbrev) }
							.debug_line		0 : { *(.debug_line) }
							.debug_frame	0 : { *(.debug_frame) }
							.debug_str		0 : { *(.debug_str) }
							.debug_loc		0 : { *(.debug_loc) }
							.debug_macinfo	0 : { *(.debug_macinfo) }

							/* SGI/MIPS DWARF 2 extensions */
							.debug_weaknames 0 : { *(.debug_weaknames) }
							.debug_funcnames 0 : { *(.debug_funcnames) }
							.debug_typenames 0 : { *(.debug_typenames) }
							.debug_varnames  0 : { *(.debug_varnames) }
						}
					

リンカスクリプトは、仮想アドレスの0x00010000番地から実行するようにしています。もし、複数のサブアプリをロードして使用する場合は、サブアプリごとに、ロードするアドレスを指定してください。

3. MicroSDにアプリを保存

ここまで準備ができれば、サブアプリのビルドを行います。ビルドが終わったら、フォルダを開き、.outファイルをmicroSDにコピーします。今回は、app.outとしています。

4. アプリを共存させたデバッグ

ロードする側のアプリもビルドができて、ロードされる側のサブアプリも準備ができれば、一緒に動かしてみます。

まずは、ボードにMicroSDを挿入して、ロードする側のアプリをデバッグします。これまで作ったタスクやOSのリソースは起動しています。アプリをロードするポイントで、デバッガを停止します(図4)。

図図4:サブアプリのエントリーポイントでデバッガを止めているところ

サブアプリをロードする時に、サブアプリ内で使用している共通シンボル(この場合は、「syslog」)をアプリ側から参照とアドレスの解決を明示的に行なっています。その時のコードが、下記のシンボルの登録です。サブアプリをロードする前に、このシンボルをエクスポート登録を行っておくと、ロードされる側のサブアプリで該当する関数コールした場合に、ロードするアプリ内のシンボルを参照してくれます。

						void setup_symbol(void)
						{
							SOLID_LDR_RegisterSymbol("syslog_wri_log", (SOLID_ADDRESS)syslog_wri_log);
							SOLID_LDR_RegisterSymbol("syslog", (SOLID_ADDRESS)syslog);
							SOLID_LDR_RegisterSymbol("malloc", (SOLID_ADDRESS)malloc);
							SOLID_LDR_RegisterSymbol("free", (SOLID_ADDRESS)free);
							SOLID_LDR_RegisterSymbol("MainValue", (SOLID_ADDRESS)(&MainValue));

							return;
						}
					

実際に、ロードする側のコードを示します。流れとしては、シンボルのエクスポート登録、サブアプリが入っているファイル名を指定します。SOLID_LDR_LoadFIle()で指定されたサブアプリをロードします。MMUにより、仮想アドレスのエントリポイントにロードされ、SOLID_LDR_CanExec()で、実行可能状態に遷移します。そして、エントリーポイントの関数を呼び出すことでサブアプリが実行されます。

						void ldr_task(intptr_t exinf)
						{
							dly_tsk(2 * 1000 * 1000);
						
							/* exportするシンボルを登録 */
							setup_symbol();
						
							/* サブアプリケーションのLOAD */
							sprintf(filename, "\\FATFS\\SD\\%s", LOAD_APP_NAME);
							int ret = SOLID_LDR_LoadFile("APP1", filename);
						
							if (ret == SOLID_ERR_OK)
							{
								SOLID_ADDRESS addr;
								ret = SOLID_LDR_CanExec("APP1", &addr);
								if (ret > 0)
								{
									/* サブアプリケーションのエントリ関数コール */
									void (*pFunc)(void) = (void (*)(void))addr;
									MainValue = 0x12345678;
									pFunc();
								}
							}
						   
						   slp_tsk();
						}
					

サブアプリ実行時には、ソースコードを含むファイルがデバッガ上に表示され、ステップ実行や変数の表示、例外のキャッチもできるので、開発効率が向上します(図5)。

図図5:サブアプリの例外もキャッチ

サブアプリでべき乗計算した後に、他のOSリソースが動いている様子がわかります(図6)。

図図6:サブアプリの実行とOSリソースが動いてる様子

ネットワーク経由のアプリ更新も可能

SOLIDには、今回使用したファイルシステムと同様に、TCP/IPネットワークのスタックである LwIPが入ってます。LwIPで動作する FTPクライアントやFTPサーバーの動作も、RZ/A1H Starter Kitで動作します。microSDやUSB以外にも、FTPサーバーを動かして、FTP経由のモジュールの更新、なんてことも可能です。

まとめ

今回は、SOLIDのインテリジェントローダーを使いこなして、分割開発の実行・デバッグの悩みを解決を中心に解説してみました。インテリジェントローダーは、ファイルシステムを組み込むことで、仕向け先ごとのアプリや、共同デバッグ時の効率的な進め方に応用が期待されます。ぜひ、SOLIDを活用して、効率的なデバッグ方法を体験してください。

前の記事を読む