PlatformIO Arduino-Picoに付属するpioasmをビルドして使う（Windows）

## はじめに

Raspberry Pi Picoのファームウェアを開発する際の有力な選択肢として、[PlatformIO](https://platformio.org/)で[Arduino-Pico](https://github.com/earlephilhower/arduino-pico)を使用する方法が挙げられます。

https://arduino-pico.readthedocs.io/en/latest/platformio.html

Arduino-PicoをPlatformIO公式のRaspberry Pi Pico対応と合流させる[PR](https://github.com/platformio/platform-raspberrypi/pull/36)は物別れに終わったようですが、依然、手軽かつ多機能な開発環境であることに変わりはありません。

Raspberry Pi Picoに搭載されているRP2040は、PIOという特徴的な機能を備えています。ここでは活用方法は紹介しませんが、大まかに言うと`*.pio`のアセンブリ言語を記述し、[pico-sdk](https://github.com/raspberrypi/pico-sdk)に付属する`pioasm`で`*.pio.h`を生成して利用します。pico-sdkではCMake用の関数を提供していますが、PlatformIOでもビルド時に自動でPIOのアセンブリを行ってほしいものです。

Arduino-Picoはpico-sdkを丸ごと含んでおり、`toos/pioasm`以下にpioasmのソースも含まれています。Windowsでこれらをビルドし、PlatformIOから利用する手順を紹介します。

## MSYS2のセットアップ

pioasmには厄介な依存関係はありません。CMakeとお好みのコンパイラがあればビルドには苦労しませんが、特に用意していない場合はMSYS2を導入するのが最も手軽です。

https://www.msys2.org/

`C:\`直下にインストールすることを嫌うなら、自己解凍形式でダウンロードして環境一式を一つのフォルダに収めることもできます。私の好みはこちらです。

https://github.com/msys2/msys2-installer

<!-- consoleはPowerShellの対話をきれいに色付けしてくれないのでplaintext -->

```plaintext
> Invoke-WebRequest -Uri https://github.com/msys2/msys2-installer/releases/download/2023-10-26/msys2-base-x86_64-20231026.sfx.exe -OutFile msys2-base-x86_64-20231026.sfx.exe
> .\msys2-base-x86_64-20231026.sfx.exe -y "-o$PWD\"
> cd .\msys64\
> .\msys2_shell.cmd -defterm -here -no-start -ucrt64
```

以降はMSYS2 UCRT64シェル上で作業します。まずはCMakeとMake、GCC等をインストールします。

- [`mingw-w64-ucrt-x86_64-toolchain`](https://packages.msys2.org/groups/mingw-w64-ucrt-x86_64-toolchain)
- [`mingw-w64-ucrt-x86_64-cmake`](https://packages.msys2.org/package/mingw-w64-ucrt-x86_64-cmake)

```console
$ pacman -Suy # コアシステムの更新が行われた場合はMSYS2シェルが終了するので、再度起動してもう一度`pacman -Suy`を実行する
$ pacman -S mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-cmake
```

前者は多くのパッケージを含んでおり明らかに過剰ですが、ディスク容量が切羽詰まっていなければデフォルト=allのままで問題ないでしょう。Makeの実行ファイル名が`mingw32-make.exe`であることに注意してください。

```console
$ which cmake && which mingw32-make.exe && which gcc
/ucrt64/bin/cmake
/ucrt64/bin/mingw32-make.exe
/ucrt64/bin/gcc
```

## pioasmのビルド

PlatformIOでArduino-Picoを使用したことがあるなら、`$USERPROFILE/.platformio/packages/framework-arduinopico/pico-sdk/tools/pioasm`にソースコード一式がダウンロードされています。

ビルド手順自体は通常通りですが、MSYS2を削除しても動くように、CMakeLists.txtに静的リンクの設定を加えています。

```console
$ cd $USERPROFILE/.platformio/packages/framework-arduinopico/pico-sdk/tools/pioasm

$ cp CMakeLists.txt _CMakeLists.txt
$ echo 'set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")' >> CMakeLists.txt
$ echo 'set(BUILD_SHARED_LIBS OFF)' >> CMakeLists.txt
$ echo 'set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ -static")' >> CMakeLists.txt

$ cmake -S . -B build -G "MinGW Makefiles"
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.5 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


-- The CXX compiler identification is GNU 13.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Users/mukai/msys64/ucrt64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (4.1s)
-- Generating done (0.0s)
-- Build files have been written to: C:/Users/mukai/.platformio/packages/framework-arduinopico/pico-sdk/tools/pioasm/build

$ cd build
$ mingw32-make all
[ 10%] Building CXX object CMakeFiles/pioasm.dir/main.cpp.obj
[ 20%] Building CXX object CMakeFiles/pioasm.dir/pio_assembler.cpp.obj
[ 30%] Building CXX object CMakeFiles/pioasm.dir/pio_disassembler.cpp.obj
[ 40%] Building CXX object CMakeFiles/pioasm.dir/gen/lexer.cpp.obj
[ 50%] Building CXX object CMakeFiles/pioasm.dir/gen/parser.cpp.obj
[ 60%] Building CXX object CMakeFiles/pioasm.dir/c_sdk_output.cpp.obj
[ 70%] Building CXX object CMakeFiles/pioasm.dir/python_output.cpp.obj
[ 80%] Building CXX object CMakeFiles/pioasm.dir/hex_output.cpp.obj
[ 90%] Building CXX object CMakeFiles/pioasm.dir/ada_output.cpp.obj
[100%] Linking CXX executable pioasm.exe
[100%] Built target pioasm

$ ./pioasm.exe -?
usage: pioasm <options> <input> (<output>)

Assemble file of PIO program(s) for use in applications.
   <input>             the input filename
   <output>            the output filename (or filename prefix if the output format produces multiple outputs).
                       if not specified, a single output will be written to stdout

options:
  -o <output_format>   select output_format (default 'c-sdk'); available options are:
                           ada
                               Ada specification
                           hex
                               Raw hex output (only valid for single program inputs)
                           python
                               Python file suitable for use with MicroPython
                           c-sdk
                               C header suitable for use with the Raspberry Pi Pico SDK
  -p <output_param>    add a parameter to be passed to the output format generator
  -?, --help           print this help and exit
```

## PlatformIOのカスタムスクリプトを用意する

PlatformIOでは、ビルドの前後にPython（SCons）スクリプトを実行できるようになっています。

https://docs.platformio.org/en/latest/scripting/actions.html

このスクリプト内ではインストール済みのPlatformIOパッケージのパスを取得する方法が提供されているため、`framework-arduinopico`からたどって先ほどビルドしたpioasmを利用します。

```ini:platformio.ini
[env:pico]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = pico
framework = arduino
board_build.core = earlephilhower
extra_scripts = pre:scripts/pioasm.py
```

```python:scripts/pioasm.py
import glob
import os.path
import pathlib
import sys
import warnings


Import("env")

PIO_FILES = glob.glob(os.path.join(env["PROJECT_SRC_DIR"], "*.pio"), recursive=True)
if len(PIO_FILES) != 0:
    PIOASM_EXECUTABLE = (
        pathlib.Path(env.PioPlatform().get_package_dir("framework-arduinopico"))
        .joinpath("pico-sdk/tools/pioasm/build/")
        .joinpath(f"pioasm{'.exe' if sys.platform.startswith('win') else ''}")
        .resolve()
    )
    if PIOASM_EXECUTABLE.exists():
        for filename in PIO_FILES:
            env.Execute(f'"{PIOASM_EXECUTABLE}" -o c-sdk "{filename}" "{filename}.h"')
    else:
        warnings.warn(f'pioasm executable not found in "{PIOASM_EXECUTABLE}"', Warning)
```

ファームウェアをビルドすると、`src`以下の`*.pio`が処理され、対応する`*.pio.h`が生成されるようになります。

## 参考

- [PlatformIO で Raspberry Pi Pico の PIO の開発をする](https://qiita.com/74th/items/3863129561a752d8bb44)
- [Using pioasm in platformio projects](https://gist.github.com/hexeguitar/f4533bc697c956ac1245b6843e2ef438#file-pioasm_platformio-md)