5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

M5Stamp P4(ESP32-P4)でMMUなしのLinux 7.2を動かし、Wi-FiとSSHまで通した話【AIと250コミット】

5
Last updated at Posted at 2026-10-19

image.png

M5 Stamp P4の価格と購入先

今回使用する M5Stack Stamp-P4(SKU: S013) は、ESP32-P4NRW32を搭載した小型マイコンモジュールです。RISC-Vデュアルコア360MHz、16MB Flash、32MB PSRAMを備えており、国内でも約2,500円で購入できます。

購入先 価格 購入リンク
スイッチサイエンス(国内) 2,453円(税込) 商品ページ
M5Stack公式ストア US$12.95 商品ページ

※価格は2026年10月20日確認時点。送料・為替・在庫状況によって変動します。

日本国内ならスイッチサイエンスで購入するのが手軽です。

なお、Stamp-P4単体にはWi-Fi機能がないため、無線通信を利用する場合は別途C6拡張モジュールなどが必要です。

M5Stamp P4(ESP32-P4)向けに、MMUのないRISC-V上で動くLinuxディストリビューションを作っています。v0.3.2 でカーネルを Linux 7.2.11 に上げ、Wi-・MicroPython まで普通に使えるようになりました。

ここまでのコミットは約250件で、ほぼすべてAI(Claude Code / Devin)と一緒に進めています。この記事では、何ができるようになったか、どう作っているか、どこでハマったかを、AIへの任せ方も含めてまとめます。

M5Stamp ESP32-P4 + C6 でSSHログインし、/proc/cpuinfo で2コアが見えている画面

リポジトリ: https://github.com/tsuyoshi-otake/m5stamp-p4-c6-linux

できること

項目 内容
SoC ESP32-P4(RV32IMAC デュアルコア、360 MHz、MMUなし)
メモリ PSRAM 32 MiB、SPI Flash 16 MiB
カーネル Linux 7.2.11(SMPでビルド。出荷イメージは1コアで起動)
Wi-Fi Stamp-AddOn C6(ESP32-C6)を4ビットSDIOでつなぎ、ESP-Hosted NG で使う
ユーザーランド Buildroot、BusyBox、uClibc-ng、Dropbear SSH、MicroPython v1.22.2
ファイルシステム ルートは読み取り専用の SquashFS。/home、/root、/var/lib はRAM上の OverlayFS

電源を入れると10秒ほどで SoftAP(SSIDは m5-XXXX)が立ち上がり、つないだPCから ssh m5@192.168.4.1 でログインできます。SSIDとパスワードは初回起動時に端末ごとに生成されるので、共通のパスワードはありません。

ログインすれば、vi でファイルを編集して python3 で実行する、という普通のLinuxの使い方ができます。

$ ssh m5@192.168.4.1
$ vi hello.py
$ python3 hello.py

逆に、sudo、nano、curl、file などは入っていません。MMUが必要だったり、メモリが足りなかったりするためです。

全体の構成

起動の流れは次のとおりです。ESP-IDFのブートローダーのあとに自作の boot-shim を挟み、PSRAMとSDIOの初期化、CPU1の解放をしてからLinuxに飛びます。

Wi-FiはP4自身ではなく、C6をSDIO経由のWi-Fiコプロセッサとして使っています。Linux側のドライバーは ESP-Hosted NG の esp32_sdio.ko です。

試し方

Releases にある16MBのイメージを、esptool でアドレス 0x0 に書き込むだけです。

gzip -d m5stamp-p4-c6-linux-smp-v0.3.2-16mb.bin.gz
esptool.py --chip esp32p4 --port COM10 --baud 460800 write_flash 0x0 m5stamp-p4-c6-linux-smp-v0.3.2-16mb.bin

C6側は ESP-Hosted NG 1.0.6 が動いている必要があります。起動ログに ESP-Hosted Version: NG-1.0.6.0.1 が出ていれば正しく認識されています。

AIとの進め方

段階に分けて、Issueで管理する

いきなり全部を目指さず、次の順に1つずつ動かしました。

  1. カーネルが起動する
  2. シリアルコンソールでログインできる
  3. C6をSDIO経由のWi-Fiとして認識する
  4. SSHでログインできる

段階ごとにIssueを立て、AIにはそのIssueの範囲だけを任せます。参考実装(why2025-linux、ESP-Hosted)を読ませて、P4向けに必要な差分を洗い出すところから、Buildroot+Dropbearの構成づくりまでAIに任せました。

実機の確認結果は、日付付きのmdに残す

実機で確かめた結果は、linux/tests/ の下に v0.3.0-feature-checks-2026-10-18.md のような日付付きのMarkdownで残しています。AIは前回の作業内容を覚えていないので、毎回これを読ませてから作業させます。

これを始めてから、同じ失敗を繰り返すことが目に見えて減りました。

モデルは難しさで使い分ける

作業の種類 使うモデル
単純作業 Opus 5.5 High、SWE-2 Medium
難しく複雑な問題 Opus 5.5 Max、SWE-2 Max

ハマりどころ

1. SSHでコマンドを実行すると固まる(SSH wedge)

一番時間がかかったのがこれです。SSHの対話シェルには入れるのに、ssh m5@host id のようにコマンドを1つ実行したり、セッションを閉じたりした瞬間に、P4が完全に固まります。pingも返らずシリアル出力も止まり、120〜160秒後にウォッチドッグでリセットされる、という症状でした。

SSHを使ったときに起きるので、最初はSDIOやWi-Fi側を疑いました。C6側のバッファあふれ、SDIOホストコントローラーのタイムアウト、Dropbearが送るパケットの連続などです。SDIOの転送方式を変えたり、Dropbearの送信間隔を空けたりしましたが、どれも直りませんでした。SSHを安定させるまでに、2週間と膨大なトークンを使っています。

転機になったのは、Wi-Fiを使わずにP4の中だけで再現させたことです。

ifconfig lo 127.0.0.1 up
dbclient <ユーザー名>@127.0.0.1 id

ループバックだけでも同じように固まったので、SDIOとWi-Fiは原因から外れました。

固まるとシリアルに何も出ないので、リセットされても消えないRTCメモリに、例外発生時のレジスターを残す仕組み(クラッシュカプセル)を入れました。再起動後に読み出すと Load access fault が起きていて、addr2line で追うと、子プロセスの終了時に呼ばれる timerqueue_del() から rb_erase() の中でNULLを読んでいました。

ただ、rb_erase() 自体にバグがあったわけではありません。メモリ上のデータが壊れていたのが原因で、ESP32-P4向けの次の3つの修正で直りました。

パッチ 内容
0014 シグナルハンドラーから戻るときに cause レジスターに -1 を書くと、P4では割り込みの状態が壊れる。P4では 0 を書くようにした
0015 命令キャッシュのフラッシュ処理が、命令とデータで共有のL2キャッシュを書き戻さずに無効化していた。そのため、まだPSRAMに書いていないデータが消えていた
0016 52ビットのタイマー値を32ビットずつ読むときに、途中で下位が桁上がりすると値が飛んでいた。上位を2回読んで一致するまで読み直すようにした

「ネットワーク操作のあとに固まる=ネットワークの問題」と決めつけないこと、がこの件の一番の教訓です。

2. MMUがないので、メモリが断片化する

MMUがないので、プログラムは連続した物理メモリに読み込む必要があります。動かしているうちにメモリが断片化して、SSHログインやコマンドの実行が nommu: Allocation of length … failed でよく失敗していました。

そこで、プログラムを読み込むための10MBの専用領域(6MB+4MBの2か所)を起動時に確保する方式に変えました(パッチ 0070)。普段使いではほぼ失敗しなくなっています。領域の使用状況は /sys/kernel/easystick/nommu_pool で見られるようにしました。

もう1つ効いたのがBusyBoxの分割です。MMUがない環境の実行形式(bFLT)はプログラムのコード部分を共有できないため、BusyBoxのプロセスは1つあたり約500KBを使います。常駐するデーモン(init、inetd、syslogd、udhcpd)だけを持つ小さなBusyBox(1つ192KB)を別に用意したところ、待機中の空きが約1MB増え、同時に張れるSSHセッションが1つ増えました。

3. Linux 7.2への移植

v0.3.0 でカーネルを 6.18.35 から 7.2.11 に上げました。ボード用のパッチはほぼそのまま移せましたが、MMUなしの環境は上流でテストされていないため、追加で4か所の修正が必要でした。

  • プログラム用のメモリ領域の確保を、新しい予約メモリのAPIに対応させた(最初の7.2ビルドは起動途中で止まった)
  • MMUなしでの /dev/mem の mmap が動かなくなっていたので直した
  • プリエンプションの設定を 6.18 と同じ PREEMPT_NONE に戻した
  • Wi-Fiドライバーのワークキューに WQ_PERCPU を指定した

4. デュアルコア(SMP)はまだ実験中

カーネルはSMPでビルドしていますが、出荷イメージは maxcpus=1 で1コアだけで起動しています。2コアで起動すると、2つ目のCPUがタイマーやプロセッサー間の割り込みを受け付けなくなり、システム全体がそれを待って止まることがあるためです。7.2.11 では10回中3回しかまともに起動しませんでした。

カーネルのコマンドラインに isolcpus=1 を付けると、17回中17回起動でき、568秒の連続動作も問題ありませんでした。ただしこの方法では、2つ目のCPUがほぼ使われません。ここが今の一番の課題です。

性能の目安

C6とのSDIOクロックを変えて、家のWi-Fi経由で測った転送速度です。

SDIOクロック 生のTCP SSH
5 MHz(初期値) 300〜330 KB/s 210〜220 KB/s
40 MHz 410〜440 KB/s 約250 KB/s

40 MHzでは、SSHの速度は360 MHzの1コアで動くDropbearの暗号処理で頭打ちになります。40 MHzは長時間の連続試験をまだしていないので、初期値は5 MHzのままにしています。

学んだこと

  • AIは「次に何を確かめればいいか」を出すのがとにかく速い。仮説を出させて、実機で確かめて、結果を記録する、という流れが一番うまく回りました。
  • 逆に、実機で何が起きたかは人が正確に伝えないと迷走します。ログや計測値をそのまま渡すのが一番確実でした。
  • 確認結果を日付付きのmdに残し、毎回AIに読ませる運用は、長いプロジェクトほど効きます。

今後

次はSMPを進めて、2コアをちゃんと使えるようにします。RISC-VのSMPやMMUなしLinuxの経験がある方、ヒントをいただけるとうれしいです。

この記事のもとになったXのスレッドはこちらです。

参考

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?