表題の通り、アプリケーション組み込み型データベースSQLite3をRaspberry Pi Picoに移植しました。4ドルのマイコンで動くRDBMSです。若い頃、「ソフトウェアの移植は創造性のないオッサンの趣味だ」と考えていたのは秘密です。
SQLite3は様々なプラットフォームに移植されています。実はPicoへの移植は既にいくつかあるのですが、恐らくほとんどの実装はOSがない環境への移植をサポートするVFSレイヤーをカスタマイズしたものだと思います。今回はRaspberry Pi PicoにPOSIX互換のファイルシステムを実装し、通常のUnixとして振る舞わせました。表題はSQLite3ですが話のキモはPOSIX互換のファイルシステムAPIです。
Picoのファイルシステム事情
Raspberry Pi Picoはいわゆるマイクロコントローラーなので、OSもファイルシステムもないベアメタルプラットフォームです。一方で、現代の組み込み環境を見ると、公式SDKが比較的リッチな環境を提供しています。リッチな選択肢を提供しつつ、必要最小限のコンポーネントでフットプリントを小さく保つのが現代の傾向です。その点で、PicoのSDKは非常に硬派で、基本的にはオンボードハードウェアのアクセスを簡単にライブラリ化して提供しています。SDカードなどのオンボードに実装されていない要素はライブラリ提供されていません。
当然ファイルシステムもないため、ユーザーは自分でファイルシステムライブラリのドライバを書くか、コミュニティで公開されている実装を利用しています。このような背景から、流通しているファイルシステムライブラリのAPIはUNIXライクですが、それぞれ方言があり、記憶媒体とファイルシステム実装が密接に結びついていて再利用が困難です。
他のプラットフォームを見ると、Arm Mbed OSやMicroPythonは、様々な記憶媒体を一貫したAPIで抽象化し、ファイルシステム実装も抽象化しています。これにより、異なる記憶媒体やファイルシステムを一つの統合されたファイルシステムとして利用できる バーチャルファイルシステム を提供しています。かっこいい。
Thin Virtual File System for Pico
このような現状を打破すべく、Raspberry Pi Picoのpico-sdk環境向けのブロックデバイス抽象化、ファイルシステム抽象化、そしてそれらを単一ネームスペースにマウントできるバーチャルファイルシステムフレームワークであるpico-vfsを開発しました。
サポートするブロックデバイスは以下の通りです:
- オンボードフラッシュメモリー
- SPI接続したSDカード
- ヒープメモリー
ファイルシステムは以下の通りです:
ブロックデバイスとファイルシステムは自由に組み合わせることができますし、ユーザーが独自にデバイスやファイルシステムを追加することも可能です。
これらをまとめるVFSレイヤーはopen
, read
, write
などの基本的な"システムコール"として実装しており、より高レベルのfopen
やfprintf
などはpico-sdkが利用するNewlibがシステムコールを使用して提供します。
この統合により、4ドルのマイコンで以下のプログラムが普通に動作します:
#include <stdio.h>
int main(void) {
FILE *f = fopen("test.txt", "w");
fprintf(f, "Hello World!\n");
fclose(f);
}
個人的にはC言語で抽象化したブロックデバイスとファイルシステムのポリモーフィズムっぷりが最高にカッコいいと思っていますが、それはあまり伝わらないので黙っておきます。
テストコードもそれなりに完備しており、完成度と品質を高めています。APIと実装の互換性の高さを実証できる事例が何か欲しいと考えていたところ、SQLite3を移植したら良いのでは!?と思いつきました。複雑なファイル操作を伴うアプリケーションであるRDBMSなら事例として申し分ありません。
SQLite3の移植
SQLite amalgamation Release 3.46.0 の本体sqlite3.c
とインタラクティブシェルshell.c
をそのままPicoに移植します。やたらと深い場所に不具合箇所があったりしてコードを深掘りするのが酷く大変ではありましたが、いくつかの未実装関数のフォールバックを追加し、不要な部分をコメントアウトするなどして、3度目のトライで動作する状態になりました。変更箇所の少なさは、今回用意したPOSIX互換ファイルAPIが存分に威力を発揮した結果だと自負しています。gitには修正前の状態からチェックインしてあるので、差分を確認できます。
やはりキモはPOSIX互換のファイルシステムAPIです。足回りには記憶媒体やファイルシステムを自由に組み合わせられるバーチャルファイルシステムを採用しているので、littefsだけでなく、SPI接続したSDカードにFATファイルシステムでSQLite3を読み書きさせることもできます。
sqlite3.c
とshell.c
を-Os
でビルドしたバイナリのサイズは約630KB。Picoのフラッシュメモリーの30%ほどを消費します。Picoのフラッシュメモリは2MBあるので、バイナリサイズはまだ余裕があります。
Memory region Used Size Region Size %age Used
FLASH: 648332 B 2 MB 30.91%
RAM: 17940 B 256 KB 6.84%
shell.c
を用いずにSQLite3をライブラリとしてC/C++ APIで利用する場合は約430KB。21%に小さくなり、マイコン用のライブラリとしても、なんとか常識的なサイズに収まっています。
Memory region Used Size Region Size %age Used
FLASH: 446580 B 2 MB 21.29%
RAM: 16048 B 256 KB 6.12%
-DSQLITE_TEMP_STORE=0
で一時データはすべてファイルに保存する設定で、割り当てるメモリについては、-DSQLITE_MAX_MEMORY=131072
で128KBに制限しています。これがないとCREATE TABLE ...
やINSERT ...
SELECT ...
している分には問題ないのですが、DROP TABLE ...
や.dump
でPicoのSRAMメモリ264KBを使い果たしてしまう問題が起こります。SQLITE_MAX_MEMORY
はもう少し増やす余地がありそうです。
Picoでの使い勝手を向上するために.quit
でシェルを終了した後は、Picoを再起動するようにしました。あと、SQL実行時にオンボードLEDが光ります。Lチカは最重要機能です。
今後について
デモの見栄えの観点からもUARTからのシェルの使い勝手が良いことはとても重要です。普通のOSならGNU readlineなどいい感じのラインエディタが組み込めますが、そういうのはないので自前で実装する必要があります。実はここが一番大変かもしれない。
pico-vfsについては公式SDKに統合するPull Requestを提出しました。反応は悪くないのですが、SDKチームが非常に忙しいようで、取り込まれても数年先の話になりそうです。当然、公式SDKに取り込まれることは理想的ですが、pico-vfsは単体でも使いやすく仕上げているので、コミュニティでの利用実績を積み重ねていけたらなと考えています。