はじめに
Raspberry Pi Pico SDKでのFlashからのブートについて調べたでは、Flashからブートするための仕組みを調査した。
Boot Stage2に相当する部分を自作するのは結構面倒だということがわかったので、コンパイル済み(CRC計算済み)のBoot Stage2である、
bs2_default_padded_checksummed.Sをリンクしてプログラムをつくってみる。
そのためにはBoot Stage2が何をやっているかを調べる必要があったが、既に調べている人がいたので参考にする。(ありがたや!)
また、FLASHやメモリ上にどのように配置しなければならないかを調べるためにPico SDKのリンカスクリプトを眺めた。
下準備
elf2uf2
PicoのFLASHに書き込む場合、ビルドしたelf形式のファイルをuf2形式に変換する必要がある。ここではPico SDKに含まれるelf2uf2というツールを使用する。
そのためには、あらかじめPATHの通った場所にファイルをコピーしておく。
$ cp ${PICO_EXAMPLES_PATH}/build/elf2uf2/elf2uf2 ~/bin
~/binにパスを通すためには下記のように.bashrcなどに設定しておく。
$ echo 'export PATH=${HOME}/bin:${PATH}' >> ~/.bashrc
$ source ~/.bashrc
Boot Stage2
PicoのデフォルトのBoot Stage2である、bs2_default_padded_checksummed.SをPico SDKからコピーする。
$ mkdir boot2
$ cp ${PICO_EXAMPLES_PATH}/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default_padded_checksummed.S ./boot2
ディレクトリ構成
Boot Stage2をboot2ディレクトリに格納してそれ以外はフラットな構造。自分で作るファイルはRAM版とほぼ同じ。
├── Makefile ├── boot2 │ └── bs2_default_padded_checksummed.S ├── main.c ├── memmap.ld ├── pico.gdbinit ├── regs.h └── start.S
違っているのは下記の3つのファイルなので、これ以外のものはRAM版)の記事を参照してほしい。
リンカスクリプト
memmap.ldを下記の内容で作成する。
MEMORY { FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k } ENTRY(reset) SECTIONS { /* boot2 section is for embed the precompiled boot2. this code from memmap_default.ld in pico-sdk */ .boot2 : { __boot2_start__ = .; KEEP(*(.boot2)) __boot2_end__ = .; } > FLASH ASSERT(__boot2_end__ - __boot2_start__ == 256, "ERROR: Pico second stage bootloader must be 256 bytes in size") .text : { KEEP (*(.vectors)) KEEP(*(.text*)) } > FLASH }
スタックポインタもRAM上においてしまうのでSCRATCH_XやSCRATCH_Yは定義しない。
FLASHの先頭256バイトの位置にBoot Stage2を配置する。
Boot Stage2によって、その直後の0x10000100の位置にあるはずのベクターテーブルのリセットベクタを読みに来るので、.vectorsを.boot2の直後に配置されるようにする。
プログラム本体である.textはベクターテーブルの後ろに配置されるようにしておく。
スタートアップ
start.Sを下記の内容で作成する。
.cpu cortex-m0plus .thumb /* vector table */ .section .vectors, "ax" .align 2 .global __vectors __vectors: .word 0x20001000 .word reset /* reset handler */ .thumb_func .global reset reset: ldr r0, =0x20001000 mov sp, r0 bl main b hang .thumb_func hang: b .
ベクターテーブルは先頭の位置とresetベクタの位置が合っていればとりあえずプログラムは起動できる。 ただし割り込みなどを使用する場合は、きちんと定義する必要がある。
リセットベクタとしてreset関数を登録しておく。
resetはスタックポインタを設定してmainにジャンプするだけ。
Makefile
Makefileを下記の名前で作成する。
CROSS_COMPILE ?= arm-none-eabi-
MCPU = -mcpu=cortex-m0plus
ASMOPT = $(MCPU) -g
COPT = $(MCPU) -ffreestanding -g -O0
LOPT = -nostdlib -nostartfiles
all: led.uf2
clean:
rm -f *.o
rm -f *.elf
rm -f *.list
rm -f *.uf2
rm -f *~
start.o: start.S
$(CROSS_COMPILE)as $(ASMOPT) start.S -o start.o
boot2.o: boot2/bs2_default_padded_checksummed.S
$(CROSS_COMPILE)as $(ASMOPT) boot2/bs2_default_padded_checksummed.S -o boot2.o
main.o: main.c
$(CROSS_COMPILE)gcc $(COPT) -fpic -mthumb -c main.c -o main.o
led.elf: start.o boot2.o main.o
$(CROSS_COMPILE)ld $(LOPT) start.o boot2.o main.o -T memmap.ld -o led.elf
led.uf2: led.elf
elf2uf2 led.elf led.uf2
makeコマンドでuf2形式に変換できるようにした。
書き込み
ボード側のBOOTSELボタンを押しながら、USBケーブルでPCと接続すると自動的に/media/${USER}/RPI-RP2にマウントされる。
その状態でled.uf2をボードに書き込む。
$ cp led.uf2 /media/${USER}/RPI-RP2
書き込み終了時点からLEDがチカチカした!

まとめ
軽い気持ちでBoot Stage2を組み込んだらベアメタル簡単につくれるのでは?と作業を始めたが、結構大変だった。
リンカスクリプトについても、最低限起動に必要なルールを踏襲するだけで割と自由に作れることがわかった。
おそらくその2はない。