μT-Kernel 3.0 BSP の boot2 セクションはどこからやって来た? (前編)
Raspberry Pico (以下 Pico) 向け μT-Kernel 3.0 BSP のリンカスクリプトを眺めると、外部フラッシュメモリの先頭に boot2 というセクションが配置されています。その実体を探ると、boot_2nd.c というファイルで定義されているboot2[]
という配列に格納された、数値の羅列であることがわかります。これが一体どこから来たのか?を調べました。
目次
本稿を読むうえでの約束事
- μT-Kernel 3.0 BSP のソースコードは、pico_rp2040 ブランチのタグ v1.00.00.B5-pico_rp2040 で確認しています。
- リンカスクリプトの場所: etc/linker/pico_rp2040/tkernel_map.ld
- boot_2nd.c の場所: kernel/sysdepend/cpu/rp2040/boot_2nd.c
- Pico SDK のソースコードは、タグ 1.5.1 で確認しています。以下、ソースファイルのパスは pico-sdk/ ディレクトリを起点として表記します。
- Pico Examples のソースコードは、タグ sdk-1.5.1 で確認しています。以下、ソースファイルのパスは pico-examples/ ディレクトリを起点として表記します。
- Pico SDK のビルド方法は、公式ドキュメント Getting started with Raspberry Pi Pico に記載されているやり方にしたがいます。コンフィグレーション等は何も変更しません。
% cd pico-examples % mkdir build % cd build % export PICO_SDK_PATH=../../pico-sdk % cmake .. % cd blink % gmake
- 「Usage Requirements」や「インターフェース・ライブラリ」という言葉がたびたび登場します。これらについては、次の過去記事を参照してください。
はじめに結論
- boot_2nd.c の配列
boot2[]
は、Pico SDK のセカンド・ステージ・ブート・ローダのオブジェクトコードを格納したもの。 - セカンド・ステージ・ブート・ローダのソースコードは Pico SDK の pico-sdk/src/rp2_common/boot_stage2/boot2_w25q080.S。
セカンド・ステージ・ブート・ローダってなに?という方は [1] や [2] などを参照してください。外部フラッシュメモリの先頭 256 バイトに格納されており、ブートシーケンスのフラッシュ・セカンド・ステージにて Quad SPI 通信を使って読み出されます。
1. について本当にそうなのか確認してみましょう。Pico Examples に含まれる blink をビルドし、.boot2
セクションのデータをダンプしてみます。
% arm-none-eabi-objdump -s --section=.boot2 blink.elf
blink.elf: file format elf32-littlearm
Contents of section .boot2:
10000000 00b5324b 21205860 98680221 88439860 ..2K! X`.h.!.C.`
10000010 d8601861 58612e4b 00219960 02215961 .`.aXa.K.!.`.!Ya
10000020 0121f022 99502b49 19600121 99603520 .!.".P+I.`.!.`5
...
100000f0 00000000 00000000 00000000 74b24e7a ............t.Nz
外部フラッシュメモリの先頭 (0x10000000 番地) から0x100000ff 番地まで、256 バイト分のデータが表示されています。エンディアンに注意して眺めれば、boot_2nd.c の配列とデータが一致していることがわかると思います。
2. については、このあと長々と説明します。
概要
当該ソースファイルの目星をつける
Pico に搭載されている外部フラッシュ・メモリは、Pico のデータシート [3] Appendix B の回路図から W25Q16JVUXIQ であることがわかります。そこで、「W25WQ16」などのキーワードで Pico SDK のソースコードを grep すれば、pico-sdk/src/rp2_common/boot_stage2/boot2_w25q080.S がヒットします。このファイルの先頭のコメントから、これがセカンド・ステージ・ブート・ローダのソースコードとみて間違いないでしょう。
ところが、じゃあ boot2_w25q080.S がアセンブルされてリンクされるんだなと思って、ビルドを行ったディレクトリでオブジェクトファイルを探しても、boot2_w25q080.o とか boot2_w25q080.obj みたいなファイルは見つかりません。代わりに、boot2_w25q080.S と同じディレクトリにあった compile_time_choice.S からアセンブルされたと思われる compile_time_choice.S.obj が見つかります (場所は pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir)。
どうやら compile_time_choice.S がその名のとおり、ビルド時にコンパイルするソースファイルを boot2_*.S の中から選んでいるようです。実際、compile_time_choice.S を覗いてみると、マクロPICO_BOOT_STAGE2_ASM
にセットされたファイルをインクルードするようになっています。というわけでPICO_BOOT_STAGE2_ASM
にセットされるのはどのファイルなのか?を探ることにします。もちろん、それが boot2_w25q080.S であることを期待しています。
blink から Pico SDK を呼び出す
ユーザー・アプリケーション (ここでは Pico Examples の blink) が Pico SDK を呼び出すところから出発しましょう。Pico SDK を使用するための設定が、Pico SDK README.md の Quick-start your own project 第 2 項に書かれています。ここには 4 パターンの方法が挙げられていますが、 Pico Examples では 1 つ目のやり方でやっています。となると、まず読むべきは Pico Examples のトップディレクトリにある CMakeLists.txt です。
pico-examples/CMakeLists.txt より引用出典: raspberrypi/pico-examples (GitHub)... # Pull in SDK (must be before project) include(pico_sdk_import.cmake) ... # Initialize the SDK pico_sdk_init() ... # Add blink example add_subdirectory(blink) ...
※ 以下、括弧なしの行番号はオリジナル・ソースファイルの行番号、括弧ありの行番号は引用部分の行番号を表します。
4 行目 (4 行目) で pico-examples/pico_sdk_import.cmake をインクルードします。CMake では、インクルードされたファイルはその時点で読み出されて実行されます。インクルードされた pico_sdk_import.cmake は、さらに pico-sdk/pico_sdk_init.cmake をインクルードしています。これによりpico_sdk_init()
マクロを Pico Examples 側から呼び出すことが可能になります。
実際にpico_sdk_init()
を呼び出しているのが 19 行目 (8 行目) です。これにより、環境変数PICO_SDK_PATH
にセットされたディレクトリがadd_subdirectory()
されます。CMake では、add_subdirectory()
されたディレクトリはプロジェクトツリーに追加され、その中に置かれている CMakeLists.txt が直ちに実行されます。PICO_SDK_PATH
は事前にセットしてあり、その値は../../pico-sdk
です (前出の「本稿を読むうえでの約束事」を参照)。つまり、この時点で pico-sdk/CMakeLists.txt が実行されることになります。
(pico-sdk/CMakeLists.txt を処理したのち) 23 行目 (12 行目) で blink ディレクトリをadd_subdirectory()
して、pico-examples/blink/CMakeLists.txt を実行します。blink の CMakeLists.txt では、blink が pico_stdlib というインターフェース・ライブラリに依存していることの記述があります。
pico-examples/blink/CMakeLists.txt より引用出典: raspberrypi/pico-examples (GitHub)add_executable(blink blink.c ) # pull in common dependencies target_link_libraries(blink pico_stdlib) ...
ここでのポイントは次の 2 点になります。
- Pico Examples 側から
pico_sdk_init()
マクロが呼ばれている。 - blink が Pico SDK のインターフェース・ライブラリ pico_stdlib に依存している。
Pico SDK の初期化 (pico-sdk/pico_sdk_init.cmake)
先ほど登場した pico_sdk_init.cmake をもう少し詳しく見てみましょう。
pico-sdk/pico_sdk_init.cmake より引用出典: raspberrypi/pico-sdk (GitHub)... include(pico_pre_load_platform) ... macro(pico_sdk_init) if (NOT CMAKE_PROJECT_NAME) message(WARNING "pico_sdk_init() should be called after the project is created (and languages added)") endif() add_subdirectory(${PICO_SDK_PATH} pico-sdk) endmacro() ...
45 行目 (3 行目) で pico-sdk/cmake/pico_pre_load_platform.cmake をインクルードしています。このファイルの中で、PICO_PLATFORM
という変数に値をセットしています。デフォルトはrp2040
です。次の節で出てくるので、頭の片隅に入れておきましょう。
ところで、CMake の変数にもスコープ (適用範囲) があります。本稿では割愛しますが、詳細は公式ドキュメント [4] を参照してください。また、pico-sdk/pico_sdk_init.cmake で定義されているpico_register_common_scope_var()
およびpico_promote_common_scope_vars()
という 2 つのマクロも参考になると思います。
51 ~ 56 行目 (6 ~ 11 行目) でpico_sdk_init()
マクロを定義しています。やっていることは簡単で、環境変数PICO_SDK_PATH
で指定されたディレクトリをadd_subdirectory()
コマンドでプロジェクトに追加しています。前の節でも触れたとおり、環境変数PICO_SDK_PATH
には、すでに../../pico-sdk
がセットされています。これで、blink から Pico SDK を使用する準備が整いました。
Pico SDK に突入!
ここからは Pico SDK の説明に入ります。まず、先ほどpico_sdk_init()
マクロを実行したことで pico-sdk ディレクトリがadd_subdirectory()
され、pico-sdk/CMakeLists.txt が読み込まれます。その 42 行目では、さらに pico-sdk/src ディレクトリをadd_subdirectory()
しています。というわけで pico-sdk/src/CMakeLists.txt を見てみましょう。
pico-sdk/src/CMakeLists.txt より引用出典: raspberrypi/pico-sdk (GitHub)if (NOT PICO_PLATFORM_CMAKE_FILE) set(PICO_PLATFORM_CMAKE_FILE ${CMAKE_CURRENT_LIST_DIR}/${PICO_PLATFORM}.cmake CACHE INTERNAL "") endif () ... # Initialize board related build/compile settings include(${CMAKE_CURRENT_LIST_DIR}/board_setup.cmake) ... include(${PICO_PLATFORM_CMAKE_FILE}) ...
6 行目 (2 行目) のCMAKE_CURRENT_LIST_DIR
は CMake の組込み変数で、今まさに処理している CMakeLists.txt が置かれているディレクトリ (この場合は pico-sdk/src) の絶対パスが格納されています。そのとなりの変数PICO_PLATFORM
には、上で書いたとおりrp2040
が格納されています。
これらのことを踏まえると、ここでやっていることは次の 2 点になります。
- pico-sdk/src/board_setup.cmake をインクルードする。詳細は次の節。
- pico-sdk/src/rp2040.cmake をインクルードする。rp2040.cmake はさらに rp2_common.cmake をインクルードする。詳細は次回。
board_setup.cmake でやっていること
このファイルでやっていることは、使用するマイコンボードのボード名を変数PICO_BOARD
にセットすることです。自分で選ぶ場合は、pico-sdk/src/boards/include/boards に収められているヘッダファイルの中から選んで、拡張子 ".h" を除いた文字列を環境変数PICO_BOARD
にセットしておけばよいようです (2,3 行目)。何も指定しなければ、デフォルトのpico
になります (7 行目)。ここでセットされたファイルは、次に出てくる generic_board.cmake から参照されます。ちなみに、このディレクトリにある pico.h を見てみると、67 行目で次のように定義されており、フラッシュ・セカンド・ステージのソースコードが boot2_w25q080.S であろうことが感じられます。
pico-sdk/src/boards/include/boards/pico.h より引用出典: raspberrypi/pico-sdk (GitHub)#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1
21 ~ 29 行目では、ボードごとに専用の CMake スクリプトをインクルードしています。PICO_BOARD_CMAKE_DIRS
で指定されたディレクトリ (デフォルトでは pico-sdk/src/boards) で ${PICO_BOARDS}.cmake をサーチし、存在すればそのファイルをインクルードします。存在しなければ、pico-sdk/src/boards/generic_board.cmake をインクルードします。
最後に 31 行目で ${CMAKE_CURRENT_LIST_DIR}/boards/include (つまり pico-sdk/src/boards/include) をリストPICO_INCLUDE_DIRS
に加えています (リストについては CMake リファレンス・マニュアル [5] を参照)。PICO_INCLUDE_DIRS
に登録されたディレクトリは、インターフェース・ライブラリ pico_base_headers の Usage Requirements として追加されます。
blink と pico_base_headers の間には、target_link_libraries()
コマンド、あるいはpico_mirrored_target_link_libraries()
関数を使って次のような依存関係が定義されています。
よって pico_base_headers の Usage Requirements が blink まで伝播して、blink のビルドの際に pico-sdk/src/boards/include がインクルード・パスに追加され、変数PICO_BOARD
で指定したヘッダファイルをインクルードすることができるようになります。
generic_board.cmake でやっていること
このファイルのおもな目的は、board_setup.cmake で選択された ${PICO_BOARD}.h をサーチし、存在すればリストPICO_CONFIG_HEADER_FILES
に追加することです (11 ~ 15 行目)。このリストに登録されたヘッダファイルは、ビルド時に生成される ${CMAKE_BINARY_DIR}/generated/pico_base/pico/config_autogen.h からインクルードされるのですが、その説明は次回に譲ります。
この先もまだまだ、重箱の隅をつつくような話が続くので、ここで一旦休憩とします。次回は pico-sdk/src/rp2040.cmake から先をやります。
参考資料
[1] | CQ 出版『インターフェース』2023 年 7 月号 p.51 |
[2] | RP2040 データシート 2.8.1 節 https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf |
[3] | Raspberry Pi Pico データシート https://datasheets.raspberrypi.com/pico/pico-datasheet.pdf |
[4] | CMake Reference Manuals - set https://cmake.org/cmake/help/latest/command/set.html |
[5] | CMake Reference Manuals - List https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#lists |
広告