2022年02月26日

PIC32MZEFシリーズのブートローダ機能

 PIC32MZ2048EFGマイコンのブートローダ機能を検討したため、今回はその際に戸惑った点等を備忘録として紹介します。ブートローダ機能を利用するとPICkit4やSNAP等の専用の書き込み治具がなくても、SDカードやUART等からファームウェアを容易に書き換えできるようになります。何かしらの製品に組み込んだ際にユーザ側でファームアップデートすることが想定される場合に便利です。今回は事前にブートローダを書き込んだPIC32MZマイコンにファームウェアの入ったSDカードを挿入すると自動的にファームウェアが書き込まれるブートローダを作成しました。Microchipから様々なブートローダのドキュメントが提供されていますが、いまいち全容を把握するのに苦労したため、ポイントを絞って紹介したいと思います。詳細は各ドキュメントを参照してください。


まずはブートローダ機能を開発する際のポイントを先に紹介します。

■ポイント
@ブートローダ用のプロジェクトとアプリケーションのプロジェクトは分けて作成する
 →リンカファイルが異なるため、ミスを減らすためにプロジェクトを分ける。また、アプリケーション用のプロジェクトでは通常のバイナリ(hex)ファイルの他にbinファイルを生成するスクリプトを追加することでブートローダ読み込み用のbinファイルを追加で生成させる。

Aリンカファイル作成方法
 →Harmony3+bootloaderで生成したプロジェクトでブートローダ用のリンカファイルbtl.ldはそのまま使用可能。一方、アプリケーション側のリンカファイルはp32MZ2048EFG100.ldを編集必須。また、Harmony3+bootloaderで生成したプロジェクトのリンカ設定はbtl.ldファイルでなく、p32MZ2048EFG100.ldファイルとなっているため、p32MZ2048EFG100.ldをプロジェクトから除外してブートローダ用のリンカファイルbtl.ldに置き換えること。

Bクロック設定やデバイスコンフィグは同じ設定にする
 →ブートローダとアプリケーションの切り替え動作が不安定になる。

Cブートローダ用のプロジェクトは最低限の機能に絞る
 →機能を盛り込むとブートローダファームがブートローダ領域に入らない。SDを読み込み専用に設定し、コンソールデバッグ機能は使用しない。

Dブートローダのファームは直接書き込み可能
 →MPLAB IDEからデバッガを介して書き込み可能。既定ではフラッシュ全削除後に書き込みされるため、通常は書き込み直後にブートローダが起動し、アプリケーションの書き込みモードに入る。

Eアプリケーションのファームは直接書き込まずにブートローダから書き込み
 →直接書き込むとフラッシュ全削除してアプリケーションだけが書き込まれる。ブートローダが消されてしまうため、アプリケーション領域に処理をジャンプさせることができず、アプリケーションが起動しない。

■ブートローダ機能のシーケンス
ブートローダ起動→トリガチェック→(書き換えモードの時)ファーム書き換え処理→ソフトリセット
                →(通常起動の時)アプリケーション起動処理→アプリケーション起動

■トリガ種類
ブートローダが起動後にファーム書き換えモードに入るか、通常のアプリケーション起動モードに入るかはIO状態やメモリ上の変数状態で切り替えることができます。

・a)IO状態トリガ
 PIC32マイコンをリセットさせて、起動時のIO状態を見て切り替えます
・b)メモリ上の変数状態トリガ
 アプリケーションが起動している状態でユーザ操作によってファーム書き換えモードにします。ファーム書き換えモードでは特定のメモリ領域に変数を書き込んで部分的なシステムリセット(特定のメモリ領域はリセットされない)をかけることでブートローダを再度起動させて、特定のメモリ領域の変数を見て書き換えモードに入るか、通常のアプリケーション起動モードに入るか切り替えます。




■ブートローダ側の開発流れ
1. MPLAB ] IDEのHarmony3にBootloaderパッケージを追加する。
 Tools→Embedded→MPLAB Harmoney3 Content Managerをクリック。bootloaderとbootloader_apps_sd〜にチェックを入れてパッケージを追加します。

bootloader1.jpg

bootloader2.jpg


2. Harmoney3プロジェクトを作成し、Filesytemタイプのbootloaderを追加する。
 メディアタイプにSDカードを選択し、b)メモリ上の変数状態トリガを有効にするために16バイトの領域を確保する。また、ファイルシステムの設定は自動マウントを有効化して、読み込み専用に設定する。また、32GB以上のSDカードにも対応させる場合はexFatにチェックを入れる。また、クロック設定やデバイスコンフィグはアプリケーション側と共通の設定をします。

bootloader3.jpg

bootloader4.jpg

bootloader5.jpg


3. コード生成後にリンカファイルを設定する。
 Harmoney3プロジェクトでbootloaderを追加すると自動的にbtl.ldとp32MZ2048EFG100.ldの2つのリンカファイルがプロジェクトに追加される。ただ、ビルド設定ではp32MZ2048EFG100.ldが有効となるため、p32MZ2048EFG100.ldをプロジェクトから除外して、btl.ldだけにする。

bootloader7.jpg


4. ファイルシステムコード追記する。
 デフォルトではff.hのビルドに失敗するため、ff.hの38行目くらいに#define __STDC_VERSION__ 199902を定義する。

5. ブートローダコードを実装する。
 詳細はコードをアップロードしてありますのでそちらを参照してください。起動直後にブートローダ機能を実行するか、アプリケーションを起動させるかの切り替えがinitialization.cにあります。

■アプリケーション側の開発流れ
1. 通常通りにMPLAB ] IDEのHarmony3のプロジェクトを作成します。また、クロック設定やデバイスコンフィグはブートローダ側と共通の設定をします。また、リンカファイルを変更して独自のリンカファイルを使用するため、Harmony3のプロジェクトグラフ内のsystem→Device&Project Confguration→Toolchain selectionのAdd linker file to projectのチェックを外します。

bootloader9.jpg

2. リンカファイルp32MZ2048EFG100.ldを書き換えます。

・PROVIDE(_ebase_address = 0x9D000000);の下にPROVIDE(_ebase_vector_offsets = 0x1000);を追加。
・_RESET_ADDR = 0xBFC00000;を_RESET_ADDR = 0xBD000000;に変更。
・_BEV_EXCPT_ADDR = 0xBFC00380;と_DBG_EXCPT_ADDR = 0xBFC00480;を削除。
・_SIMPLE_TLB_REFILL_EXCPT_ADDR、_CACHE_ERR_EXCPT_ADDR 、_GEN_EXCPT_ADDR に_ebase_address を追加して下記のように書き換える。
_SIMPLE_TLB_REFILL_EXCPT_ADDR = _ebase_address + _ebase_vector_offsets + 0;
_CACHE_ERR_EXCPT_ADDR = _ebase_address + _ebase_vector_offsets + 0x100;
_GEN_EXCPT_ADDR = _ebase_address + _ebase_vector_offsets + 0x180;

・メモリ領域(MEMORY)の書き換えとconfig領域の削除
(変更)
kseg0_program_mem (rx) : ORIGIN = 0x9D001000, LENGTH = 0x200000 - 0x1000
kseg1_boot_mem : ORIGIN = 0xBD000000, LENGTH = 0x480
kseg1_boot_mem_4B0 : ORIGIN = 0xBD0004B0, LENGTH = 0x1000 - 0x04B0
(追記)
/* Reserve 16 Bytes to Store Bootloader Trigger Pattern */
kseg0_data_mem (w!x) : ORIGIN = 0x80000000 + 16, LENGTH = 0x80000 - 16
(削除)
アプリケーション側ではコンフィグを行わないため、config領域を削除します。
config_BFC0FF40からboot2lastpageまでを削除します。

・SECTION領域(SECTIONS)のconfig削除
.config_BFC0FF40から始まるSECTIONSを削除する。

・SECTION領域(SECTIONS)の例外処理を削除
ブートローダ側に含まれるため、下記の例外処理を削除する。
.bev_excpt _BEV_EXCPT_ADDR :
{
KEEP(*(.bev_handler))
} > kseg1_boot_mem

・割込みオフセットに_ebase_vector_offsetsを追記

* Interrupt vector table with vector offsets */
.vectors _ebase_address + _ebase_vector_offsets + 0x200 :

・ブートローダ側に含まれるため、下記を削除する
/* The startup code is in the .reset.startup section.
* Keep this here for backwards compatibility with older
* C32 v1.xx releases.
*/
.startup ORIGIN(kseg0_boot_mem) :
{
KEEP(*(.startup))
} > kseg0_boot_mem

・ デバッグ例外処理とコンフィグはブートローダ側に含まれるため、除く処理を追加する。
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.discard) }の下に下記を追加する。
/DISCARD/ : { *(._debug_exception) }
/DISCARD/ : { *(.config_*) }

最終的に書き換え前(書換前リンカファイル.ld)と書き換え後(書換後リンカファイル.ld)のファイルはこのようになります。

3. アプリケーション側で必要に応じて、ソフトウェア的にファーム書き換えモードにするコードを追加します。BTL_TRIGGER_PATTERN で定義された特殊な値をBTL_TRIGGER_RAM_START に書き込んでソフトリセットさせることでリセット後にブートローダが起動し、SDカードからファームを書き換える処理を走らせることができます。

#include "sys/kmem.h"

#define BTL_TRIGGER_RAM_START KVA0_TO_KVA1(0x80000000)
#define BTL_TRIGGER_PATTERN (0x5048434DUL)
#define DCACHE_CLEAN_BY_ADDR(start, sz)
static uint32_t *ramStart = (uint32_t *)BTL_TRIGGER_RAM_START;

SYS_DEBUG_PRINT(SYS_ERROR_DEBUG, "Bootloader Triggered!\r\n");
ramStart[0] = BTL_TRIGGER_PATTERN;
ramStart[1] = BTL_TRIGGER_PATTERN;
ramStart[2] = BTL_TRIGGER_PATTERN;
ramStart[3] = BTL_TRIGGER_PATTERN;
DCACHE_CLEAN_BY_ADDR(ramStart, 16);
APP_SystemReset();

4. プロジェクト設定でbinファイルを生成するコードを追加します。
アプリケーション側のビルド後にhexファイルからbinファイルを生成します。
${MP_CC_DIR}/xc32-objcopy -I ihex -O binary ${DISTDIR}/${PROJECTNAME}.${IMAGE_TYPE}.hex ${DISTDIR}/${PROJECTNAME}.${IMAGE_TYPE}.bin

bootloader8.jpg


これでブートローダ機能の設定および実装は完了です。先にブートローダ側のファームをPIC32マイコンに書き込んで、アプリケーション側のbinファイルをimage.binにファイル名を書き換えてSDカードに入れるとファームが書き換えられ、アプリケーションが実行されます。プロジェクトファイルおよび全体のコードはこちら(code.zip)です。

なお、パッケージを追加するとC:\Users\(ユーザ名)\Harmony3\bootloader_apps_sdcard\apps\sdcard_bootloader内にブートローダ側のbootloaderとアプリケーション側のtest_appがサンプルコードが追加されます。ただ、サンプルプログラム(PIC32MZDA)とREADMEだけでは情報少なく苦労したため、試行錯誤しながら何とか動かすことができました。SDカード以外にもUARTやフラッシュメモリ等からも書き換えできますが、専用ソフトが不要でユーザ側のOS環境に依らず負担が少ないSDカードからの書き換えが汎用性が高く良いと思いました。また、アプリケーション側のリンカファイルを書き換えるとそのままでは簡単にデバッグやブートローダ無の単体動作できないため、元のリンカファイルも残しておいて、読み込むリンカファイルを切り替えるとよいと思います(Lodableに追加する別のデバッグ方法もありますが...)。
posted by Crescent at 00:00| Comment(0) | 組込ソフト | このブログの読者になる | 更新情報をチェックする

2022年02月12日

Rust + Eclipse IDE + GDB

今回はWindows環境でEclipse IDEを使用したRust開発環境の構築方法について紹介します。一般的にはVSCodeを使う流れですが、時代錯誤感はありますが、組込系でも慣れたEclipse IDEで構築しました。

Rust版Eclipse IDEは発展途上or見放された?ようでEclipseの2020-12が最新で以降はRust版が提供されていないようです。そのため、MacやLinux環境では比較的うまく動くようですが、Windows環境ではなかなかすんなり動かないようです。既知の問題点として対処方法含めて紹介されていますが、それだけでは分かりずらい点もあったため、環境構築方法を改めて紹介します。


@Rustインストール
既にRust環境が入っている場合は飛ばしてください。コマンドライン等からcargo --versionと打ってバージョン情報の正しい応答があればインストール済&パスOKです。必要に応じて公式サイトからインストールしてください。

AEclipseインストール
残念ながら2020-12が最新で以降はRust版が提供されていないようです。こちらのページの中央右にあるWindows x86_64をクリックしてダウンロードします。なお、右端のボタンは最新版統合インストトーラのため、Rust版ではありませんので間違えないように注意。直リンクはこちらです。Eclipseはzipを解凍するのみでインストール不要です。適当な場所に解凍したフォルダを置きます。

BGDBインストール
コマンドライン等からgdb --versionと打ってバージョン情報の正しい応答があればGDBインストール済です。「gdbは、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして認識されていません。」と表示される場合はインストールされていないorパスが通っていません。既にCygwin等がインストールされている場合、GDBが入っていればパスを通します。何もインストールされていない場合は最低限としてGDBだけの TDM-GCC Compilerをこちらからダウンロード、インストールします。インストールはzipを解凍し、Cドライブ直下かユーザーディレクトリ直下等にフォルダを配置し、gdb.exeがあるフォルダに対して環境変数のPATHにパスを追加します。

例: C:\Users\(ユーザ名)\gdb\bin

環境変数にパスを追加した後、再度開き直したコマンドライン等からgdb --versionと打ってバージョン情報の正しい応答があればOKです。

Cツールチェーンインストール
コマンドラインから下記の2つのコマンドを順番に打ち込んでツールチェーンをインストールします。

rustup toolchain install stable-x86_64-pc-windows-gnu
rustup default stable-x86_64-pc-windows-gnu


Dコマンドファイル追加
Windows環境ではrust-gdb.exeがそのままでは使用できないため、下記のフォルダにrust-gdb.cmdというファイルを生成し、こちらのサイトに記載されたスクリプトをメモ帳等で書き込みます。念のため、既にスクリプト書き込み済みのrust-gdb.cmdのファイルを圧縮してアップロード(rust-gdb.zip)してあります。

C:\Users\(ユーザ名)\.cargo\bin

Eプロジェクト追加
Eclipse IDEのZipファイルを解凍したフォルダ内のeclipse.exeをクリックし、起動させます。プロジェクトのワークスペースは適当なディレクトリを指定します。File→New→ProjectからRust→Cargo Projectをクリックし、プロジェクト名を入力し、Finishボタンでプロジェクトを作成します。その他の設定はデフォルトでOKです。

rust2.jpg

Fデバッグ設定変更
このままではGDBデバッグ機能が利用できないため、デバッグ設定を変更します。Run→Debug ConfigurationからDebuggerタブをクリックし、GDB debuggerにrust-gdbの代わりに先ほど作成、格納したC:\Users\(ユーザ名)\.cargo\bin\rust-gdb.cmdを指定します。また、Environmentタブをクリックし、Addをクリックして適当な名前と値を入れます。面倒な場合は下記のようにHOGE、hogeと入れます。デフォルトの空白だとエラーになるため、その対処のため、何かしらの文字列を指定します。特に意味はないため、空白以外の文字列であれば構いません。

rust3.jpg

rust4.jpg


Gデバッグ
これで設定完了です。Run→Debugから今まで通りにデバッグできます。

rust5.jpg

Rustでもこれまでと同じようにGDBでデバッグできるようになりました。時代錯誤感はありますが、プラグインやアドオンで重くなりがちなVSCodeよりも各プロジェクトで独立した開発環境を構築できるEclipse IDEの方が個人的に好んでいます。最新版とまでは言わなくとも継続してRust版のEclipseを開発して頂きたいと思いました。
posted by Crescent at 00:00| Comment(0) | ナレッジ | このブログの読者になる | 更新情報をチェックする