
昨今の半導体不足や開発期間の短縮といった状況の変化により、組み込み開発において実機の手配が困難になるケースが増えています。このような背景から、ハードウェアに依存しないソフトウェアオリエンテッドな開発スタイルへの移行が注目されています。
本記事では、IARシステムズのウェビナー「実機なしでもデバッグテストを進める方法」の内容を基に、IAR Embedded Workbenchのシミュレーターを最大限に活用し、実機なしでも効率的に開発を進めるためのデバッグ・テスト手法について詳しく解説します。
組み込み開発におけるシミュレーターの重要性
これまでの組み込み開発では、開発者人数分の実機を用意し、外部委託先への手配やスケジュール調整など、ハードウェアに依存した開発スタイルが一般的でした。しかし、シミュレーターを導入することで、以下のようなメリットが得られます。
- 実機台数の削減: 必要な時のみ実機を使用し、最低限の台数で開発を進めることが可能になります。
- 開発初期段階からのテスト: 評価ボードが手元にない場合や、試作ボードにない機能を先行してテストできます。
- 自動化テストの構築: シミュレーターは自動テスト環境を構築しやすいため、テストの効率化と品質向上が図れます。
- 再現困難な条件のテスト: 実機では再現が難しい特定の条件も、シミュレーター上で容易に再現し、繰り返しテストを行えます。
IAR Embedded Workbench シミュレーターの豊富な機能
IAR Embedded Workbenchは、すべてのアーキテクチャ向け製品でシミュレーターを標準装備しています。プロジェクトオプションでデバッガーの設定を「シミュレーター」にするだけで、すぐにデバッグを開始できます。
シミュレーターでは、一般的なデバッグ機能(ブレークポイント設定、ステップ実行など)に加えて、以下の高度な機能が利用可能です。
- 高度なトレース機能
実機でのトレースは、高価なICEが必要であったり、通信インターフェースの帯域幅制限によるデータ欠損が発生したりする課題がありました。しかし、シミュレーターではデータの欠落が一切なく、完全なトレースデータを取得できます。
- 命令トレース: すべての命令を順に取得し、複雑な問題の原因特定に役立ちます。
- 関数トレース: 関数の入りと抜けのポイントのみを取得し、問題発生箇所の絞り込みに有効です。
- タイムライン表示: リアルタイムにコールスタックを視覚的に表示し、関数の呼び出し状態を容易に把握できます。
- 関数プロファイラー
関数の使用状況の統計を取ることで、実行中に最も多くの時間を費やすボトルネック関数を特定し、コードの最適化に役立てることができます。
- コールトレース: 命令のフルトレースを解析し、関数の呼び出しとリターンを判定します。コール回数や関数内部での実行時間、子関数を含めた実行時間などを詳細に分析でき、過度なコンテキストスイッチの特定に適しています。
- フラットトレース: 関数の呼び出しやリターンに関係なく、トレースされた命令を対応する関数に割り当てて処理時間を計測します。プログラムカウンターが関数のスコープ内にあった時間をカウントし、ボトルネックとなっている関数を見つけるのに役立ちます。
- コードカバレッジ
シミュレーターでは、コードカバレッジの取得も可能です。これにより、すべてのコードがテストされたかどうかを確認したり、コードの複雑度を理解したりするのに役立ちます。
- カバレッジは、カバレッジウィンドウ、逆アセンブラ画面、エディター画面で確認できます。
- コマンドラインでの実行も可能で、自動テストと連携してカバレッジ結果を保存することもできます。
- メモリ構成と検証
シミュレーターは、正しくターゲットシステムをシミュレートするためにメモリ構成に関する情報が必要となります。デフォルトのメモリ構成を使用するだけでなく、以下のような方法で独自の設定も可能です。
- デバイス記述ファイル(.ddf)のカスタマイズ: デフォルトのDDFファイルをコピーし、メモリマップを独自にカスタマイズして使用します。
- デバッグイメージからの情報利用: ビルド時に生成されるデバッグイメージに含まれるセグメント情報をもとにメモリを設定します。
- 手動設定: メモリ構成画面から新たなメモリゾーンを手動で定義できます。
また、アクセス違反のメモリアクセスチェック機能も備わっており、開発中の問題を早期に発見できます。
- 割り込みシミュレーション
シミュレーターには、割り込みを発生させるための割り込みシミュレーターが搭載されています。デバイスがサポートする割り込みリストから割り込みを選択し、以下のように設定できます。
- 単一発生: 特定のタイミングで一度だけ割り込みを発生させます。
- 繰り返し発生: 指定した間隔で繰り返し割り込みを発生させます。
- 強制発生: 任意のタイミングで強制的に割り込みを発生させます。
シミュレーターを使いこなすC-SPYマクロ
C-SPYマクロは、シミュレーターを最大限に活用するために非常に重要な機能です。簡単なスクリプトを作成することで、複雑なデバッグ機能を実現できます。
C-SPYマクロの主な機能
- ファイル操作: ファイルの読み書きなどが可能です。
- メモリのリード/ライト: メモリの内容を読み書きできます。
- ブレークポイントの設定/解除: プログラムの実行中にブレークポイントを動的に操作できます。
- Cスタイル記述: シンプルなCスタイルでスクリプトを記述できます。
- 自動実行・手動実行・ブレークポイント連携: 作成したマクロファイル(.mac)は、デバッガーに登録することで、様々なタイミングで実行可能です。
C-SPYマクロの活用例
- RAMメモリの初期化: 特定のテスト条件に合わせてRAMの内容を初期化するマクロを作成できます。
// sim_data_map.macの例
// データファイルからRAMへデータをロードするマクロ
CollectData() {
__var _fileHandle;
__var byte, address;
address = 0x20000000;
_fileHandle = __openFile("input_data.txt", "r"); // データファイルを開く
while ( (byte = __readFileByte(_fileHandle)) != -1 ) // ファイルから1バイトずつ読み込む
{
__writeMemoryByte(byte, address, "Memory”); // RAMアドレスに値を書き込み
address++; //アドレスをインクリメント
}
__closeFile(_fileHandle); // ファイルを閉じる
__message "Data loaded to RAM at 0x20000000 from input_data.txt\n";
}
- 変数の値のログ出力: 特定の変数の値をログファイルに出力するマクロを作成し、実行中のデータ変化を追跡できます。
// log_variable.macの例
__var _fileHandle;
// デバッグ開始時にログファイルを開く
__execUserSetup() {
_fileHandle = __openFile("variable_log.txt", "w");
__message "Variable log file opened.\n";
}
// 特定の変数値をログに書き込む
LogMyVariable(myVar) {
__fmessage _fileHandle, "MyVariable: ", myVar, "\n";
}
// デバッグ終了時にログファイルを閉じる
__execUserExit() {
__closeFile(_fileHandle);
__message "Variable log file closed.\n";
}
- デバッグ設定の自動化: 割り込みシミュレーターの設定や各種ブレークポイントの設定などをマクロで自動化できます。
// setup_advanced.macの例
__execUserSetup()
{
// USART1受信割り込みを4000サイクル後に発生、その後1000サイクル毎に繰り返す
__orderInterrupt("USART1", 4000, 1000, 0, 1, 0, 100);
__message "USART1 setup for simulation.\n";
// UART1_DRレジスタへのリードアクセス時にマクロ関数を呼び出すイミディエイトブレークポイント
__setSimBreak("USART1_DR", "R", "Access()");
__message "Immediate breakpoint on UART1_DR read access set.";
// ソース中のUartReceiveHandlerラベルにコードブレークポイントを設定
__setCodeBreak("UartReceiveHandler", 0, “1”, “TRUE”, “”);
__message "Code breakpoint at UartReceiveHandler() function.";
}
// UART1_DRレジスタへのアクセス時に実行されるマクロ関数
Access() {
__var val;
// データファイルから値を読み込み、UART1_DRレジスタに代入する例
__readFile(_fileHandle, &val); // 実際のファイル読み込み
USART1_DR = val; // レジスタに値を代入
__message "Access() called. USART1_DR = 0x", val:%X, “\n”;
}
- CPUレジスタのダンプ: 特定のタイミングでCPUレジスタの値をダンプし、デバッグ情報を取得できます。
// dump_registers.macの例
DumpCPURegisters() {
__message "--- CPU Registers Dump ---";
__message "R0: 0x", #R0:%x; //CPUレジスタは名前の前に「#」をつけて参照可能
__message "R1: 0x", #R1:%x; //「:%x」を付けて16進出力を指定
// 他のレジスタも同様に追加
__message "-------------------------";
}
- シーケンシャルブレークポイント: ある条件が順番に成立した場合にブレークするような高度なブレークポイントを設定できます。例として、10秒と0.1秒の2つのタイマ割込みを持つシステムを考えます。低速のイベントが2回トリガされた後に高速のイベントがトリガされた場合にブレークをしたいとします。低速イベントにはブレークポイントが設定されており、このブレークポイントが呼び出されたのが2回目かをチェックするマクロを呼び出します。2回目の場合、マクロは高速イベントにもブレークポイントを設定します。
SetSequentialBreakpoint() {
if (SlowInterruptCount++ == 2)
{
brk = __setCodeBreak(“Fast_Interrupt_Handler”, 0, “1”, “TRUE”, “”);
}
}
- 周辺機器のシミュレート: イミディエイトブレークポイント(プログラムを一時停止してマクロを実行し、実行を継続するブレークポイント)を活用することで、特定のレジスタへのアクセスをトリガーにマクロ関数を実行し、ファイルからデータを読み込んでレジスタに代入する、といった周辺機器のシミュレーションが可能です。
効率的な開発のためのビルド構成の活用
IAR Embedded Workbenchのビルド構成機能は、一つのプロジェクトに対して複数のビルド構成を持つことを可能にします。これにより、目的(実機テスト用、シミュレーション用など)に応じて異なるプロジェクトオプションを設定し、柔軟な開発が実現できます。
- ビルド構成の作成: ベースとなるビルド構成から簡単に新規作成でき、分岐後は互いの変更が反映されないため、管理が容易です。
- シミュレーター専用コードの管理: シミュレーター用のビルド構成にスタブ関数などを追加し、実機テスト用のビルド構成ではそれらを無効化(ビルドから除外)するといった使い分けが可能です。
- リダイレクト機能: シミュレーターでのみコールしたいスタブ関数などをリンカーのリダイレクト機能で指定できます。これにより、実コードではGPIOなどにアクセスする部分を、シミュレーターではスタブ関数に置き換えてシミュレーションを行うことが可能です。
まとめ
IAR Embedded Workbenchのシミュレーターは、単なるデバッグツールに留まらず、開発の初期段階からテストを効率的に進めるための強力な味方となります。特に、半導体不足や開発期間短縮が求められる現代の組み込み開発において、その重要性はますます高まっています。
本記事でご紹介したシミュレーターの豊富な機能とシスパイマクロ、ビルド構成の活用方法をぜひ開発現場でご活用いただき、効率的で高品質な組み込み開発を実現してください。
ご不明な点やさらに詳しい情報が必要な場合は、お気軽にお問い合わせください。