By Shawn Prestridge, IAR Systems Software Inc. Co-author Colin Flournoy, IAR Systems Software Inc. IAR Embedded Workbench と STM32F207ZG-SK を 使用したブートローダの構築の実例 Download application project (zip) ソフトウェアが複雑になるにつれ 出荷後に発生する問題に対処するため ファームウェアはフィールドで自分自身を書き換えできるように設計されるようになりました ファームウェアの書き換えはブートローダが行います この記事では IAR Embedded Workbench と評価キット STM32F20ZG-SK を使用してブートローダを構築する方法や問題点について解説します ブートローダに要求される機能は様々で 設計の実際について議論することは難しいと言えます 書き換えのためのデータを取り込む方法ひとつをとっても様々で マイコンの周辺デバイスの USB 同じく周辺デバイスの Ethernet を使用した TCP/IP または古めかしい RS232C などだったりします ブートローダはいろいろな場面で必要になる機能なので 設計を始める前に 使用するマイコンで使えるブートローダのサンプルがすでに作られていないかどうか マイコンメーカに確かめてみることをお勧めします もしそれが手に入るなら ブートローダの構築の手間をかなり省くことができます 選択するアーキテクチャによっても微妙な差があります 例えば デバイスが ARM か Cortex-M3 かでリセットベクタが異なります またデバイスメーカにより ベクタテーブルを再配置できるもの できないものがあります ここでは 話を簡単にするために ひとつのデバイスとその評価キットを実例とすることにします 評価キット STM32F20ZG-SK は Cotex-M3 デバイスを搭載しています この記事ではこのボードが手元にあり デバイスにプロジェクトをダウンロードする方法をすでに理解していて EWARM のデバッガ C-SPY のマクロファイルシステムが デバッガをスタートする前にどのようにレジスタの値などを操作することができるかなどについて知識があることを前提とします 図 1: STM32F207ZG-SK 評価キット
Page 2 ワークスペースはプロジェクト管理の最上位であり IAR Embedded Workbench を起動すると自動的にオープンします ワークスペースには複数のプロジェクトを登録することができるので ここではメインのアプリケーションとブートローダのプロジェクトを登録します このワークスペースの中で どの順番でプロジェクトをビルドするか決めることができます ここでは メインのアプリケーションプロジェクト ( キット用に用意されたプロジェクトをベースにしたもので プロジェクト名は LCD) ブートローダの順にビルドすることにします なぜなら ブートローダをビルドする時にアプリケーションのイメージデータをロードする予定だからです これは IAR Embedded Workbench IDE のバッチビルドの機能により実現でき プロジェクトメニュー > バッチビルドをクリックすることで起動することができます ( 図 2 参照 ) バッチを編集し ビルドするプロジェクトを追加したり プロジェクトの順番を変えたりすることも マニュアルでビルドを行うこともできます 図 2: バッチビルド アプリケーションとブートローダを同一のプロジェクトに置くことはどうしてもできません 1つの理由は IAR Embedded Workbench が ( 特に ブートローダとアプリケーションのスタートアドレスが離れている場合 ) 非常に大きなバイナリファイルを作ってしまうことです ブートコードの最後とアプリケーションの最初のコードの間に周辺デバイスのアドレスがあると 空きエリアのデータを周辺デバイスに書こうとしてフラッシュローダが誤動作することがあります このことが問題にならないとしても ブートローダはコードを受け取る静的に配置された配列を持つのでアプリケーションの走行時に必要なメモリ領域を圧迫してしまいます 2つの独立したプロジェクトにすることにより リンカは効果的にメモリを配置することができます もう1つの理由は ブートローダとアプリケーションが同じプロジェクトに属していると これらを別々に変更することができないことです つまり 片方を変更するということは もう片方も変更することになり デバッグに時間がかかってしまうことです 基本的な設定この例では メインのアプリケーションプロジェクトを特定のアドレス ( この例では 0x0801000) より上側に置き ブートローダを下側に置きます (0x08000000 より開始 ) 一度この設定を行うと ブートローダ プロジェクトのバイナリイメージを作成することができます この方法の有利な点は ブートローダのバイナリイメージをアプリケーションに取り込むことができ デバッガでアプリケーションとブートローダを同時にダウンロードするこができることです まとめると
Page 3 ブートローダのプロジェクト : 1. ブートローダのプロジェクトを作成します 2. ブートローダのコードと固定データをブートローダ用に確保した空間 ( このプロジェクトでは 0x08000000 と 0x08000FFF の間 ) に配置します ( 図 3 参照 ) 図 3: リンカの設定 3. ブートローダ プロジェクトの出力コンバータ ( プロジェクトメニュー > オプション > 出力コンバ ータ > 出力 ) を バイナリファイルを生成するように設定します ( 図 4 参照 ) 4. バイナリファイルは メモリの内容のそのもので フォーマットやアドレス情報を含みません 図 4: 出力コンバータの設定
Page 4 アプリケーションプロジェクト : 1. メインのアプリケーションのプロジェクトを作成します (app) この記事では STM32F207ZG- SK 用評価キットに付属のサンプルプロジェクトである LCD プロジェクトを使用します 2. プログラムコードと固定データを アプリケーション用に確保した場所に配置します この例では ブートローダの次の部分 0x08001000 からフラッシュメモリの最後 0x080FFFFF(STM32F207ZG の場合 ) までです 次にリンカ設定ファイル (.icf) に以下の行を追加し place at address mem:0x08000000 { section.bootloader };.bootloader セクションを配置します この行は フラッシュメモリの最初の部分に ブートロー ダのバイナリイメージを配置することを意味します ( 図 5 参照 ) 図 5: リンカの設定 2 3. プロジェクトメニュー > オプション > リンカ > 入力と選び ブートローダ プロジェクトで作成したバイナリイメージを読み込むように設定します リンカはバイナリイメージを定数の配列のように扱い リンカ設定ファイルで指定したアドレスにそのままコピーします さらにこのバイナリイメージのシンボル名と所属するセクション名を設定し このシンボルが他の部分から参照されていなくても配置されるように設定します この記事のプロジェクトでは シンボル名を bootloader 所属するセクション名を.bootloader アラインメントは 4 とします ( 図 6 参照 )
Page 5 図 6: リンカの入力 デバッグアプリケーションのデバッグは通常の場合と同じですが デバイスとアーキテクチャの違いにより多少の変更が必要になる場合があります デバッグを開始する前にもう一度整理すると以下のようになります 1. アプリケーションの追加入力である ローバイナリファイルを生成するブートローダ プロジェクトを作成します 2. アプリケーションのプロジェクトは プロジェクトオプションでこのローバイナリファイルを追加入力とするように設定します 3. 両方のプロジェクトのリンカ設定ファイルは コードとデータが重なり合わないように修正します それから デバッグ対象がアプリケーションだけでなくブートローダも同時にデバッグしなくてはならないことを デバッガに知らせなくてはなりません STM32F2xx にはベクタテーブルのベースアドレスを指定するレジスタがあり ブートローダからアプリケーションにジャンプする時に このレジスタを書き換えてベクタテーブルを持ち替えます さて デバッグ対象のプログラムはアプリケーションのリセットベクタのアドレスではなく ブートローダのリセットベクタよりスタートすることをデバッガに知らせなくてはなりません これは デバッガのマクロファイルを呼び出すことにより実現できます (.mac) 以下が デバッガに対して ブートローダを先に実行し 後からアプリケーションに移動することを知らせるマクロです この記事よりリンクされているプロジェクトサンプル内には test.mac というマクロファイルが含まれていて アプリケーションのプロジェクトオプションに追加されています マクロファイルの名前は 拡張子が.mac であれば自由につけることができます このファイルを使用して デバッガに対し デバッグ対象のプログラムがブートローダにあるリセットベクタにある Reset_Handler というアドレスからスタートすることを知らせます このファイルで スタックポインタの初期化も行います 下記が このマクロファイルの内容で 以上のことをどのように行っているか理解することができます
Page 6 // test.mac ファイル execuserreset() { MSP = *(int*)0x08000000; } PC = *(int*)0x08000004; // スタックポインタをブートローダのベクタテーブルの先頭アドレスにセット // プログラムカウンタをブートローダのリセットハンドラの先頭アドレスにセット マクロファイルの編集が終わったら プロジェクトメニュー > デバッガ > 設定タブのページでプロジェクトに追加し 有効にします プログラムのテストの最初のステップは ブートローダがデバッガ内で正常に動くことを確認することです この目的のために 下の図の run to main のチェックははずしておきます 図 7: デバッガの設定アプリケーション (app) には ローバイナリイメージを追加済みですが ローバイナリイメージのデバッグ情報が含まれていません デバッグ情報を追加してソースレベルデバッグを可能にするには プロジェクトメニュー > オプション > デバッガ > イメージタブを開き ブートローダの.out ファイルを指定します.out ファイルには実行コードだけでなくデバッグ情報も含まれています この.out ファイルは通常プロジェクトファイル \ ビルド構成名の下の exe フォルダに作成されます デバッグ情報をのみが必要なので デバッグ情報のみ のチェックを入れて オフセットは 0 にします
Page 7 図 8 : デバッガ イメージの設定 設定が済んで ダウンロードしてデバッグ アイコンをクリックすると フラッシュメモリをプログラム中です のウィンドウが2 回表示されます これは正常な動作で アプリケーションだけでなくフラッシュローダも書き込まれていることを表わしています 次に マクロファイルにより プログラムカウンタがブートローダのベクタテーブルのリセットアドレスに設定されます ブートローダからシングルステップでアプリケーションプロジェクトに入ることは 問題なくできます アプリケーションプロジェクトを経由して 2つのプロジェクトをダウンロードしたことを思い出してください これは ブートローダのソースファイルではなく アプリケーションのソースファイルに直接アクセスしていることを意味します デバッグ中 ブートローダのソースファイルをクリックすると ビルド設定ないしプロジェクトを切り替えるかどうか聞かれます これは 両方のプロジェクトのデバッグをしたいのですから 好ましいことではありません これを避けるには ブートローダのコード (main 関数 ) に到達するまでアセンブラのシングルステップで進むか 命令コードのところにブレークポイントを置きます 表示メニュー > ブレークポイントを開き ウィンドウ上で右クリックし 新規ブレークポイントを選択し 新しい コードブレークポイント を追加します
Page 8 図 9: コードブレークポイント ブートローダ プロジェクトの main の最初の部分はブートローダ プロジェクトのマップ (.map) ファ イル内で表示されますから 適切なブレークポイントを選択するのに役立ちます これがうまくいくと run アイコンをクリックするとブートローダのメイン関数のどこかでブレークすることができます ブートローダをどう実装するか の議論の範囲は広いのですが デモンストレーションの目的で 一番簡単なサンプルを作成しました これは 入力の状態によらず 常にアプリケーションにジャンプします ジャンプは アプリケーションのリセットハンドラを指す関数へのポインタを介して行われます この部分のコードは 以下に含まれます //in bootloader main.c int main(void) { //jump to application via function pointer } int * address = (int *)0x08001004; set_msp(*(int*)0x08001000); *(int*)0xe000ed08 = 0x08001000; ((void (*)())(*address))(); while(1){ }; // アプリケーションのリセットハンドラへのポ インタ // スタックポインタをアプリケーションのスタ ックエリアに設定 // VTOR: ベクタテーブルをアプリケーション のものに設定 // ポインタ関数としての再キャスト // アプリケーションは実行しない ここは STM32F2xx の情報を取り始める部分です ブートローダがアプリケーションにジャンプする前に ベクタテーブルを移動しなくてはなりません 現在のところ ブートローダとデバイスはブートローダのベクタテーブルは永久に 0x08000000 番地にあり続けると信じています ブートローダのコードは ベクタテーブルアドレスをアプリケーションが最初にリンクされた時の 0x08001000 番地にリセットします このことにより アプリケーションのコードが正しく動き ベクタテーブルも適切な位置にセットされます
Page 9 ここまでで道のりの半分で これからメインアプリケーションに到達し正しく走らせることができるかどうか検証する必要があります アプリケーションの main 関数のどこかにブレークポイントを設定してあればそこでブレークし LCD 表示を確認し 再度走らせることができます 実行アイコンをクリックし ブートローダがアプリケーションへ至るまでの走行をコントロールしているところを確認してください まとめると ほとんどの基本的な種類のブートローダは実現可能です ブートローダは常にアプリケー ションにジャンプします このプロジェクトは以下の 3 つことを例示 実証していますが 他の条件や ユーザの入力によりもっと複雑なブートローダに拡張することができます 1. ブートローダの骨格の作り方 2. ブートローダとアプリケーション コードを統合し ターゲットにダウンロードし同時にデバッグ する方法 3. 実ハードウェア上で動きかつ 実用途向けに改造可能であること ブートローダは フィールドでプログラムを書き換え可能にする有用なツールです この記事を参考に して ご自身のブートローダを作成する際の参考にして頂ければ幸いです 以下のリンクは この記事 で使用したプロジェクトファイルです Download application project (zip)