単一ワード消去のデータフラッシュを備えたMAXQ7665Cのページ消去可能な(PE)フラッシュのインアプリケーションプログラミング(IAP)
要約
このアプリケーションノートでは、MAXQ7665Cマイクロコントローラ(µC)に内蔵のユーティリティROMを使用して、プログラムフラッシュ、ページ消去可能な(PE)データフラッシュ、およびインアプリケーションプログラミング(IAP)を行う方法について説明します。この情報は、単一ワード消去のデータフラッシュとして構成されたPEフラッシュを備えたMAXQ7665Cフラッシュベースのマイクロコントローラのみに適用されます。
はじめに
このアプリケーションノートでは、ページ消去可能な(PE)フラッシュメモリと単一ワード消去のデータフラッシュを備えたMAXQ7665Cフラッシュベースのマイクロコントローラ(µC)における内部データとプログラムフラッシュの管理方法について説明します。プログラムフラッシュのインアプリケーションプログラミング(IAP)を実行するための一般的な手順を示します。
メモリマップ
この項では、一般的なフラッシュの情報、およびMAXQ7665Cマイクロコントローラのメモリ構成について詳しく説明します。MAX7665デバイスでは、3種類のフラッシュメモリが利用可能です。すなわち、セクタフラッシュ、単一ワード消去、および2ページ消去です。このアプリケーションノートでは、単一ワード消去のデータフラッシュのPEフラッシュを備えたMAXQ7665Cデバイスのみを扱っています。この記事には、セクタ消去のみのデバイスや2ページ消去のデータフラッシュを備えたデバイスは含まれていません。MAXQ7665セクタ消去可能な(SE)デバイスの情報については、アプリケーションノート3575 「MAXQ7665のセクタ消去可能なプログラム/データフラッシュのインアプリケーションプログラミング(IAP)」を参照してください。2ページ消去のデータフラッシュを備えたMAXQ7665ページ消去可能な(PE)デバイスの詳細については、アプリケーションノート3576 「MAXQ7665のページ消去可能な(PE)プログラム/データフラッシュのインアプリケーションプログラミング(IAP)」を参照してください。
表1~表3は、プログラムフラッシュ、ユーティリティROM、およびデータSRAMからコードをそれぞれ実行するときの16KBのMAXQ7665Cデバイスのメモリマップを示します。図1と図2は、16KBと128Bのデータフラッシュのセクタとページの構造を図表にしたものです。これ以外のフラッシュオプションも利用可能です。全リストについては、MAXQ7665Cのデータシートを参照してください。
表1. 16KBメモリ空間—プログラムフラッシュからの実行
表2. 16KBメモリ空間—ユーティリティROMからの実行
表3. 16KBメモリ空間—データSRAMからの実行
図1. 16KBプログラムフラッシュのセクタ/ページ構造
図2. 128Bデータフラッシュのセクタ/ページ構造
データフラッシュを使用してデータを保存
フラッシュは、システムの動作中に一度だけ、または定期的にプログラムする必要のあるシステムデータを確実に保存することができます。EEPROMと同様、MAXQ7665C上のフラッシュはワード消去が可能です。これには通常、10msかかりますが、ワーストケースの状態ではさらに長くかかることがあります。この間、ユーザコードは停止状態となるため、その他の処理を行うことはできません。
データをフラッシュに保存するには複数の手法があり、アプリケーションに適切な方法を選択するときには、いくつかの要因を考慮する必要があります。新しいデータが完全に書き込まれるまで、現在のデータが変更されないことをアプリケーションが保証する必要がありますか? その必要がある場合、何らかのバンクスイッチング手法が必要となります。製品寿命の間に消去/書込みが何サイクル発生しますか? 消去/書込みのサイクル数がデータシートに規定された最大値を超える場合、制限キュー手法を使用し、データフラッシュの複数ページにわたってこれらのサイクルを分散させることによって、消去/書込みの総サイクル数を大幅に増大させることができます。定期的にデータの保存が必要なニーズでは、ほとんどの場合、バンクスイッチングや制限キューの手法によってシステムの信頼性要件と要求を満たすことができます。各手法の簡単な例を以下に示します。
バンクスイッチング
バンクスイッチングは、消去/書込みサイクルの間の、データの消失や破損を防止する効果的な方法です。この方法は、少なくとも1つのデータコピーを常に保持することによって有効に機能します。バンクスイッチングのマイナス点は、制限キューに比べて2倍のデータフラッシュを必要とするという点です。つまり、バンクスイッチングは、実際にはキューサイズが2の制限キュー手法になります。したがって、詳細については、制限キューの実装方法に関する次項を参照してください。
制限キュー
制限キューの手法は、一定の項目数によってキューを制限する方法です。この手法は通常、定期的にデータを処理する場合によく使用されます。
制限キューは、所望のデータブロックサイズに等しい、「エントリ」の固定長キューを作成することによって構成されます。エントリサイズはアプリケーションに固有な値です。また、最も近いページ消去の境界に切り上げる必要があります。消去することのできる最小サイズは2ページ/ワードであることに留意してください。データフラッシュは、アプリケーションの要件に応じて、さまざまな方法で分割することが可能ですが、データフラッシュには2ページ消去の制限があるため、エントリは2ページの倍数に限定されます。たとえば、64 x 16のデータフラッシュは8ワードの8エントリに分けることが可能であり、結果として、表4のようなメモリマップになります。
初期化後、起動ルーチンでキューエントリをスキャンすることで、次に利用可能なキュー内のエントリを知ることができます。キューがいっぱいになると、先頭に戻ることが可能です。データフラッシュのエントリの消去が完了すれば、新しいエントリを書き込むことができます。図3は、制限キュー内のエントリのフローを示しています。
簡単なCのソースコード例については、付録Aを参照してください。
FLASHQueue[ ] | |
Queue Index | Data Flash Address |
7 | 0xC038-0xC03F |
6 | 0xC030-0xC037 |
5 | 0xC028-0xC02F |
4 | 0xC020-0xC027 |
3 | 0xC018-0xC01F |
2 | 0xC010-0xC017 |
1 | 0xC008-0xC00F |
0 | 0xC000-0xC007 |
図3. 制限キューのフロー図
ユーティリティROMのフラッシュルーチン
MAXQ7665Cマイクロコントローラには、フラッシュのプログラミング、消去、および検証のため、ROM(読取り専用メモリ)内にオンチップフラッシュのサポートルーチンが用意されています。これらのルーチンにアクセスするには、2つの方法があります。直接アクセスとルックアップテーブルによる間接アクセスです。最速の方法は直接アクセスであり、ルーチンをじかに呼び出します。これを実行するには、以下に示す行にヘッダファイルを設けます。
u16 flashErasePage(void *); u16 flashEraseSector(void *); u16 flashEraseAll(void); u16 dataFlashWriteE(u16 *pAddress, u16 iData); u16 dataFlashErasePage(void *); u16 dataFlashEraseSector(void *); u16 dataFlashEraseAll(void); u16 dataFlashReadE(u16 *pAddress, u16 iData);
次に、リンカ定義を追加して、各ルーチンに適切なアドレスを割り当てますIARリンカのファイルの場合、追加された行は、次のようになります。
-DflashEraseSector=0x8XXX -DflashErasePage=0x8XXX -DflashEraseAll=0x8XXX . . .
ルーチンごとに0x8XXXを適切なメモリアドレスに置き換えます。他のコンパイラは、このようなリファレンスを追加するのに別の手法を使用する場合があります。
flashWrite()ユーティリティルーチンは、引数の引渡しがC言語に適していないため、Cからじかに呼び出すことはできません。以下に示すような小さなアセンブリルーチンを記述する必要があります。
注:直接アクセス手法では、今後のROMバージョンとの上位互換性は確保されません。
フラッシュのサポートルーチンにアクセスする第2の方法は、テーブル参照を用いた間接アクセスです。この手法は、今後のROMバージョンとの互換性に優れていますが、実行時間が長くなります。以下に示す各ルーチンの後、アセンブリルーチンは、テーブル参照方法を使用して、ユーティリティのROMルーチンのアドレスを取得します。表5は、ユーティリティROMが供給するフラッシュルーチンを示します。ユーティリティのROMルーチンの全体リストについては、MAXQ7665Cのユーザガイドを参照してください。
Routine Number | Routine Name | Entry Point ROMTable = ROM[800Dh] |
Entry Point Physical Address |
1 | flashWrite | ROM[ROMTable] | 0x8XXX |
2 | flashErasePage | ROM[ROMTable + 1] | 0x8XXX |
3 | flashEraseAll | ROM[ROMTable + 2] | 0x8XXX |
4 | moveDP0 | ROM[ROMTable + 3] | 0x8XXX |
16 | flashEraseSector | ROM[ROMTable + 15] | 0x8XXX |
18 | dataFlashWriteE | ROM[ROMTable + 17] | 0x8XXX |
19 | dataFlashErasePage | ROM[ROMTable + 18] | 0x8XXX |
20 | dataFlashEraseSector | ROM[ROMTable + 19] | 0x8XXX |
21 | dataFlashEraseAll | ROM[ROMTable + 20] | 0x8XXX |
22 | dataFlashReadE | ROM[ROMTable + 21] | 0x8XXX |
ルーチン: | u16 flashWrite(u16 *pDest, u16 *pSrc) |
要約: | プログラムフラッシュメモリの単一ページ(32ワード)をプログラムします。 |
入力: | DP[0]—フラッシュメモリ内のディスティネーションアドレス DP[1]—書き込む32ワードのデータを保持するSRAM内のソースアドレス |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。セットされた場合、A[0]には、以下のエラーコードの1つが含まれます。 1: ソフトウェアのタイムアウトによる失敗 2: ハードウェアによって報告される失敗(DQ5/FERR) 4: サポートされていないコマンド SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。 |
以下のアセンブリコードの例では、間接アドレス指定の手法(ルックアップテーブル)を使用してflashWrite()ユーティリティルーチンを呼び出しています。このルーチンはCコードで呼び出されます。
; This routine is callable by C code using the following prototype ; u16 flashWrite(u16 *pDest, u16 *pSrc); ; flashWrite: move APC, #0 ; No auto inc/dec of accumulator. move AP, #2 ; Set ACC to A[2]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #14 ; Add the index to the flashWrite routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. push DP[1] ; Save Frame Pointer on the stack. move DP[0],A[0] ; Move argument 0(dest address) to DP[0]. move DP[1],A[1] ; Move argument 1(src address) to DP[1]. call ACC ; Execute the routine. pop DP[1] ; Restore Frame Pointer. ret ; Status returned in A[0].
ルーチン:: | u16 flashErasePage(void *pAddress) |
要約: | プログラムフラッシュメモリの2ページブロックを消去します。 |
入力: | A[0]—消去する2ページブロック(すなわちページ0とページ1)に位置付けられたアドレス。A[0]には、0x0000~0x001Fの任意のアドレスを記入することができます。/td> |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。セットされた場合、A[0]には、以下のエラーコードの1つが含まれます。 1: ソフトウェアのタイムアウトによる失敗 2: ハードウェアによって報告される失敗(DQ5/FERR) 4: サポートされていないコマンド SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。 |
; This routine is callable by C code using the following prototype ; u16 flashErasePage(void *pAddress); ; flashErasePage: move APC, #0 ; No auto inc/dec of accumulator. move AP, #1 ; Set ACC to A[1]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #1 ; Add the index to the flashEraseSector routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret ; Status returned in A[0].
ルーチン: | Void flashEraseAll(void) |
要約: | プログラムとデータのフラッシュメモリ全体を消去します。このルーチンはRAMからのみ呼び出すことができます。 |
入力: | なし |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。 SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。 |
; This routine is callable by C code using the following prototype ; void flashEraseAll(void); ; flashEraseAll: move APC, #0 ; No auto inc/dec of accumulator. move AP, #0 ; Set ACC to A[0]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #2 ; Add the index to the flashEraseAll routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret
ルーチン: | moveDP0 |
要約: | プログラムフラッシュメモリの単一ワードを読み出します。 |
入力: | DP[0]—フラッシュメモリ内のソースアドレス。プログラムフラッシュを呼び出す0x8000を追加 |
出力: | GRは、指定したアドレスにデータが含まれます。 |
注: | この関数は、引数と戻りのレジスタがCの呼出し仕様に適合していないため、Cからじかに呼び出すことはできません。 |
以下のアセンブリコードの例では、moveDP0をCで呼出し可能なルーチンに変換しています。アプリケーションで速度が必須となる場合は、特定のタスクに合わせて、カスタムのアセンブリ言語ルーチンを記述する必要があります。このようなユーティリティROMルーチンがいくつかあり、フラッシュからの効率的なデータ転送を記述する場合に役立ちます。
; This routine is callable by C code using the following prototype ; u16 flashRead(u16 *pAddress); ; flashRead: move APC, #0 ; No auto inc/dec of accumulator. move AP, #1 ; Set ACC to A[1]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #3 ; Add the index to the moveDP0 routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. push DP[1] ; Save Frame Pointer on the stack. move DP[0],A[0] ; Move argument 0(src address) to DP[0]. call ACC ; Execute the routine. pop DP[1] ; Restore Frame Pointer. move A[0],GR ret ; Data word returned in A[0].
ルーチン: | u16 flashEraseSector(void *pAddress) |
要約: | プログラムフラッシュメモリの単一セクタを消去します。 |
入力: | A[0]—消去するセクタに位置付けられたアドレス |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。セットされた場合、A[0]には、以下のエラーコードの1つが含まれます。 1: ソフトウェアのタイムアウトによる失敗 2: ハードウェアによって報告される失敗(DQ5/FERR) 4: サポートされていないコマンド SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。 |
; This routine is callable by C code using the following prototype ; u16 flashEraseSector(void *pAddress); ; flashEraseSector: move APC, #0 ; No auto inc/dec of accumulator. move AP, #1 ; Set ACC to A[1]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #15 ; Add the index to the flashEraseSector routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret ; Status returned in A[0].
ルーチン: | u16 dataFlashWriteE(void *pAddress, u16 *pData) |
要約: | データフラッシュメモリの単一ワードをプログラムします。 |
入力: | A[0]—書き込むフラッシュメモリのワードアドレス A[1]—フラッシュメモリに書き込むワード値 |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。セットされた場合、A[0]には、以下のエラーコードの1つが含まれます。 1: ソフトウェアのタイムアウトによる失敗 2: ハードウェアによって報告される失敗(DQ5/FERR) 4: サポートされていないコマンド SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。このルーチンはdataFlashReadE()とセットになるルーチンです。 |
以下のアセンブリコードの例では、間接アドレス指定の手法(ルックアップテーブル)を使用してdataFlashWriteE()ユーティリティルーチンを呼び出しています。このルーチンはCコードで呼び出されます。
; This routine is callable by C code using the following prototype ; u16 dataFlashWriteE(void *pAddress, u16 iData); ; dataFlashWriteE: move APC, #0 ; No auto inc/dec of accumulator. move AP, #2 ; Set ACC to A[2]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #16 ; Add the index to the flashWrite routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret ; Status returned in A[0].
ルーチン: | u16 dataFlashErasePage(void *pAddress) |
要約: | データフラッシュメモリの2ページを消去します。 |
入力: | A[0]—消去する2ページブロック(すなわちページ0とページ1)に位置付けられたアドレス。A[0]には、0x4000あるいは0x4001のアドレスを記入することができます。 |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。セットされた場合、A[0]には、以下のエラーコードの1つが含まれます。 1: ソフトウェアのタイムアウトによる失敗 2: ハードウェアによって報告される失敗(DQ5/FERR) 4: サポートされていないコマンド SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。 |
; This routine is callable by C code using the following prototype ; u16 dataFlashErasePage(void *pAddress); ; dataFlashErasePage: move APC, #0 ; No auto inc/dec of accumulator. move AP, #1 ; Set ACC to A[1]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #18 ; Add the index to the dataFlashErasePage routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret ; Status returned in A[0].
ルーチン: | u16 dataFlashEraseSector(void *pAddress) |
要約: | データフラッシュメモリの単一セクタを消去します。 |
入力: | A[0]—消去するセクタに位置付けられたアドレス |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。セットされた場合、A[0]には、以下のエラーコードの1つが含まれます。 1: ソフトウェアのタイムアウトによる失敗 2: ハードウェアによって報告される失敗(DQ5/FERR) 4: サポートされていないコマンド SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。 |
; This routine is callable by C code using the following prototype ; u16 dataFlashEraseSector(void *pAddress); ; dataFlashEraseSector: move APC, #0 ; No auto inc/dec of accumulator. move AP, #1 ; Set ACC to A[1]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #19 ; Add the index to the dataFlashEraseSector routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret ; Status returned in A[0].
ルーチン: | void dataFlashEraseAll(void) |
要約: | データフラッシュメモリ全体を消去します。 |
入力: | なし |
出力: | キャリー:エラー時にセットされ、成功時にクリアされます。 SW_FERR—エラー時にセットされ、成功時にクリアされます。 |
注: | ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、リセットが起動されることなくこのルーチンが完了するようにしてください。 |
; This routine is callable by C code using the following prototype ; void dataFlashEraseAll(void); ; dataFlashEraseAll: move APC, #0 ; No auto inc/dec of accumulator. move AP, #0 ; Set ACC to A[0]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #20 ; Add the index to the flashEraseAll routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret
ルーチン: | dataFlashReadE |
要約: | フラッシュメモリの単一ワードを読み出します。 |
入力: | A[0]—フラッシュメモリ内のソースアドレス。 |
出力: | A[0]—指定したアドレスにデータが含まれます。 |
注: | dataFlashWriteE()とセットとなるルーチンです。 |
以下のアセンブリコードの例では、間接アドレス指定の手法(ルックアップテーブル)を使用してdataFlashReadE()ユーティリティルーチンを呼び出しています。このルーチンはCコードで呼び出されます。
; This routine is callable by C code using the following prototype ; u16 flashReadE(u16 *pAddress); ; dataFlashReadE: move APC, #0 ; No auto inc/dec of accumulator. move AP, #1 ; Set ACC to A[1]. move DP[0], #0800Dh ; This is where the address of the table is stored. move ACC, @DP[0] ; Get the location of the routine table. add #21 ; Add the index to the dataFlashReadE routine. move DP[0], ACC move ACC, @DP[0] ; Retrieve the address of the routine. call ACC ; Execute the routine. ret ; Data word returned in A[0].
インアプリケーションプログラミング
フラッシュを用いたほとんどのシステムで重要となる要件は、システムが最終製品にインストールされている間にファームウェアをアップデートする機能です。これは、インアプリケーションプログラミング(IAP)と呼ばれます。この項では、IAPアプリケーションを作成するための一般的なガイドラインを示します。
上述のユーティリティROMフラッシュのルーチンがフラッシュROMの消去と書込みに必要なすべての動作を実行します。これによって、エンドユーザのアプリケーションがフラッシュメモリに対する操作を行えるようになります。他のサブルーチンの呼出しと同様、ルーチンが完了すると制御はエンドユーザのコードに戻ります。
信頼性の高いIAPを実現するには、ブートローダアプリケーションをメインのアプリケーションから分離する必要があります。この分離によって、再プログラミングシーケンスが完了しなかった場合に再プログラミング手順を確実に再試行することができるようになります。
ブートローダ
ROMは初期化後、アドレス0x0000にジャンプします。したがって、ブートローダアプリケーションのエントリポイントは0x0000に配置する必要があります。ブートローダアプリケーションは、フラッシュのセクタ/ページを必要な数にまで拡張することが可能です。ブートローダアプリケーションで使用されるページはいずれも、ユーザのアプリケーションコードで利用することはできません。フラッシュの消去と書込みを行うときに満たさなければならない特定の要件を表6に示します。
コードを実行しているフラッシュページと同じフラッシュページから消去またはプログラムすることはできません。これは通常、問題にはなりません。フラッシュのブートローダアプリケーションはIAPの間に決して消去してはならないからです。 |
ウォッチドッグを有効にしないでください。あるいはウォッチドッグのタイムアウトを十分に長く設定することによって、flashEraseSector()またはflashErasePage()のルーチンを呼び出す前に、リセットが起動されることなくこのルーチンが完了するようにしてください。消去が完了する前にウォッチドッグのタイムアウトが発生すると、製品がリセットされます。 |
ユーティリティROMにアクセスするためには、システム制御レジスタビットSC.UPAを0に設定する必要があるため、0x8000あるいはこれよりも大きなプログラムメモリからじかにユーティリティROMルーチンを呼び出すことはできません。上位のメモリ(≧ 0x8000)のプログラムからユーティリティROMルーチンにアクセスする必要がある場合、下位のメモリ(0x8000)に置かれたルーチンを通してROMルーチンを間接的に呼び出す必要があります。このため、実質的にブートローダは64KB (32KB x 16)以下に制限されます。 |
図4のフローチャートは、リセット状態から抜け出したときのMAXQ7665の動作を示しています。ROMそのものを診断し、フラッシュの準備が完了していることを確認した後、ROM初期化コードは、直ちにアドレス0x0000にジャンプします。
図4. 簡易ROM初期化のフローチャート
図5のフローチャートは、簡易ブートローダアプリケーションの動作を示しています。簡易アプリケーションのヘッダは、次のようになります。
typedef struct { u16 iSize; // The size of the application in words u32 iCRC; // The CRC of the application u8 ID[8]; // ID string for current application } APPLICATION_HEADER;
このヘッダの情報を使用することによって、ブートローダは、要求がある場合に、メインアプリケーションプログラムの妥当性を検査し、バージョンの識別番号を報告することができます。
図5. 簡易フラッシュブートローダのフローチャート
プログラミングのシーケンスそのものは非常に簡単です。flashEraseSector()やflashErasePage()への呼出しを通じてメインアプリケーションコードを含む各セクタ/ページを消去します。次に、プログラムする必要のある32ワードごとにflashWrite()を呼び出すことによって、一度に1ページを書き込みます。CRC照合の誤る可能性を最小限に抑えるため、アプリケーションヘッダを格納するページを最初に消去し、CRCデータを最後にプログラムするようにしてください。シリアルポートを経由してデータを取得するマイクロコントローラをリフラッシュするための極めて簡易なルーチンは、次のようになります。
/* // VerySimpleReFlash() // As simple as it gets. // Step 1. Wait for erase command, then erase flash. // Step 2. Wait for program command, then program flash one word // at a time. */ void VerySimpleReFlash() { u16 iStatus; // The status returned from flash utility ROM calls s32 iSize; // The size of the main code to program u16 *pAddress = 0x2000; // The starting address of the main application u16 i; InitializeCOMM(); // Can be CAN or UART. WaitForEraseCommand(); // Assume that application starts at the beginning of a sector. for (i=C_START_SECTOR;i<C_SECTOR_END;++i) { iStatus = flashEraseSector(C_ADDRESS_SECTOR_1); if (iStatus != 0) break; } SlowDownWatchdogUpdate(); // If watchdog enabled slow down to prevent timeout SendFlashErasedResponse(iStatus); UpdateWatchdog(); // Prevent timeout. if (iStatus) ResetMicro(); iSize = WaitForProgramCommand(); while (iSize > 0) { u16 iData[32]; Get32WordsFromCOMM(iData); iStatus = flashWrite(pAddress, iData); if (iStatus) break; pAddress += 32; iSize -= 32; UpdateWatchdog(); // Prevent timeout. } SendFlashWriteResponse(iStatus); ResetMicro(); }