Sipeed Lichee Pi 4A 導入記

Sipeed Lichee Pi 4A を購入しました…! プレオーダーして発売を楽しみにしていたハイスペック RISC-V シングルボードコンピュータです。

https://sipeed.com/licheepi4a

Lichee Pi 4A
Powerful RISC-V SBC ever!

TH1520, 12nm, RISC-V 2.0G C910 x4
4 / 8 / 16 GB 64bit LPDDR4X-3733
TF Card, or 16 / 32 / 64 / 128 GB eMMC

今回購入したのは最初期バージョン(販売サイトではβの文字がついています)の 8GB memory + 8GB storage (日本円で 1.7万円ほど)のもとのなり、しばらくすると各バリエーションが増えていくと思います。(そういえばこちらのスペック表に eMMC 8GB 版の記載がされてないですね… Wiki によると16GB 版はなくて 8GB 版になったようです)

https://wiki.sipeed.com/hardware/en/lichee/th1520/lpi4a/1_intro.html

· eMMC: 可选 空贴、 8G、 32G、 128G

メインボード(?) と、LM4A と呼ばれる RISC-V SoC と eMMC・メモリーとが 260P SODIMM で接続されているのが特徴で、おそらく今後 LM4A の各バリエーションの単体売りや、LM4A を使った機器の販売が予定されているのだと思います。楽しみです…!


ドキュメント Wiki(英語翻訳中とのこと) :

https://wiki.sipeed.com/hardware/en/lichee/th1520/lpi4a/1_intro.html

LicheePi 4A 是基于 Lichee Module 4A 核心板的 高性能 RISC-V Linux 开发板,以 TH1520 为主控核心(4xC910@1.85G, RV64GCV,4TOPS@int8 NPU, 50GFLOP GPU),板载最大 16GB 64bit LPDDR4X,128GB eMMC,支持 HDMI+MIPI 双4K 显示输出,支持 4K 摄像头接入,双千兆网口(其中一个支持POE供电)和 4 个 USB3.0 接口,多种音频输入输出(由专用 C906 核心处理)。

Github リポジトリー(まだ 2023-05 時点では空です)

https://github.com/sipeed/LicheePi4A

LicheePi4A info&sdk

Linux カーネルと u-boot

https://github.com/revyos/thead-kernel

https://github.com/revyos/thead-u-boot

本記事(2023-05)は発売直後のものです。OS アップデート等で今後変わる可能性が高い情報ですが、動かしてみた部分をいくつか書いておきたいと思います。

  • HDMI とオーディオコネクタはしっかり奥まで差し込みましょう。特に HDMI は割と硬いので奥まで刺さないと写りません。
  • 空冷用のファンとサーマルグリースシートが付属します。プログラムのビルドなどで RISC-V 4コアを 1.85GHz で連続的に回し続けるとファン付きで 60度くらいまで上がりますので付けましょう。
  • 温度を観測していると SoC sensors が 40度以降でファンが回転始めるようです。部屋が寒いと OS 起動直後は動かない時がありますのでご安心を。そのうち回ります。
  • 電源は USB-C の 2A で SoC と HDMI、USB-SSD、Ethernet の動作は問題ありませんでした。Anker のスマホ充電用 USB Type C アダプタと(オーバースペックですが) 60W 通せる USB Type C to C ケーブルを使いました。
  • 12V DC Power は MISI LCDパネル(基板背面にコネクタあり)などを利用するときに使うとよいとのこと。

以下は OS に初期提供されている Debian イメージを使ったときの話題です。

執筆時点(2023-05) – LPi4A_20230511_gnome.zip

  • 消費電力にも関係しますがメインボード側(LM4A の外側)に付いている WiFi/BT モジュールは(技適の関係もありますので)disable に設定していて未検証です。
  • GPU については現在ドライバーがフルサポートではないようです。自分もいくつかの GPU アクセラレーションを使うソフトが真っ黒になるなどを確認しました。
  • HDMI からの音声出力は現在未サポートです。付属のラインアウトは I2S に接続されており、こちらから音が出ます。(パッチが入っているのを見ましたので、次のリリースで対応されそうです)

eMMC への OS イメージの焼き方

ドキュメント記載の通りですが、執筆時点(2023-05)で最新だった Debian – GNOME(LPi4A_20230511_gnome.zip)イメージを使っています。

ダウンロード:

https://wiki.sipeed.com/hardware/en/lichee/th1520/lpi4a/3_images.html

Mega 云盘:点我 (←ここにあります)

焼き方:

https://wiki.sipeed.com/hardware/en/lichee/th1520/lpi4a/4_burn_image.html

電源の USB-C の上にある BOOT ボタンを押しながら、USB-C を PC の USB-C に接続すれば T-HEAD USB download gadget として認識されるはずです。

自分は PC に Ubuntu/ThinkPad を使っているので dmesg lsusb は次のようになります。

$ sudo dmesg | tail -5
[ 2827.718195] usb 6-1: new high-speed USB device number 4 using xhci_hcd
[ 2827.882776] usb 6-1: New USB device found, idVendor=2345, idProduct=7654, bcdDevice= 2.23
[ 2827.882787] usb 6-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[ 2827.882791] usb 6-1: Product: USB download gadget
[ 2827.882794] usb 6-1: Manufacturer: T-HEAD
$ lsusb | grep 2345
Bus 006 Device 004: ID 2345:7654 T-HEAD USB download gadget

Windows の方は先程のダウンロード先にドライバーと fastboot バイナリーがあります。

認識できたらアーカイブ内の burn_gnome_gpu.sh を実行すれば OK です。イメージ同梱の fastboot コマンドは Linux バイナリで、burn_gnome_gpu.sh もシェルスクリプトなので、Windows の方も WSL2 の Ubuntu でやったほうが簡単かもしれません。ドライバーも入れる必要がありません(認識のさせ方を記事の最後の「関連」に記載しておきます)

$ pwd
/home/hiromasa/devel/riscv/LPi4A/images/0511_gnome
$ ls -laF
合計 4259020
drwxrwxr-x 2 hiromasa hiromasa       4096  5月 12 12:04 ./
drwxrwxr-x 3 hiromasa hiromasa       4096  5月 17 16:05 ../
-rw-rw-r-- 1 hiromasa hiromasa   62914560  5月 12 11:35 boot-20230510-230240.ext4
-rwxrwxr-x 1 hiromasa hiromasa        368  5月 12 11:29 burn_gnome_gpu.sh*
-rwxrwxr-x 1 hiromasa hiromasa    2230360  5月  5 23:47 fastboot*
-rw-rw-r-- 1 hiromasa hiromasa       2022  5月 12 11:38 image_intro.txt
-rw-r--r-- 1 hiromasa hiromasa      68443  5月 12 11:36 light-lpi4a_1.85GHz.dtb
-rw-r--r-- 1 hiromasa hiromasa      68464  5月 12 11:36 light-lpi4a_2Ghz.dtb
-rw-rw-r-- 1 hiromasa hiromasa        259  5月 12 11:21 md5sum.txt
-rw-rw-r-- 1 hiromasa hiromasa 4294967296  5月 12 11:32 rootfs-20230511-183752-gnome.ext4
-rw-rw-r-- 1 hiromasa hiromasa     957728  5月 12 11:21 u-boot-with-spl-lpi4a-20230510.bin
$ cat burn_gnome_gpu.sh
#! /bin/sh
# Script to flash images via fastboot, edit image path first
#


sudo ./fastboot flash ram ./u-boot-with-spl-lpi4a-20230510.bin
sudo ./fastboot reboot
sleep 10
sudo ./fastboot flash uboot ./u-boot-with-spl-lpi4a-20230510.bin
sudo ./fastboot flash boot ./boot-20230510-230240.ext4
sudo ./fastboot flash root ./rootfs-20230511-183752-gnome.ext4

ちなみに、最初は慎重にやるために burn_gnome_gpu.sh を使わずに 1行づつ手で実行していたのですが flash uboot が waiting device になってしまいうまくいきませんでした。シェルスクリプトでやるとうまくいったので、reboot 後の sleep 10 後のタイミングが重要なのかもしれません。

$ ./fastboot --version
fastboot version 31.0.3-7562133
Installed as /home/hiromasa/devel/riscv/LPi4A/images/0511_gnome/fastboot
$ file fastboot
fastboot: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, not stripped
$ ./burn_gnome_gpu.sh
[sudo] hiromasa のパスワード:
Sending 'ram' (935 KB)                             OKAY [  0.248s]
Writing 'ram'                                      OKAY [  0.002s]
Finished. Total time: 0.262s
Rebooting                                          OKAY [  0.001s]
Finished. Total time: 0.202s
Sending 'uboot' (935 KB)                           OKAY [  0.057s]
Writing 'uboot'                                    OKAY [  0.030s]
Finished. Total time: 0.110s
Sending 'boot' (61440 KB)                          OKAY [  1.860s]
Writing 'boot'                                     OKAY [  1.439s]
Finished. Total time: 3.417s
Invalid sparse file format at header magic
Sending sparse 'root' 1/33 (113316 KB)             OKAY [  3.530s]
Writing 'root'                                     OKAY [  3.099s]
Sending sparse 'root' 2/33 (110992 KB)             OKAY [  3.236s]
Writing 'root'                                     OKAY [  2.869s]
...(snip)...
Sending sparse 'root' 32/33 (114684 KB)            OKAY [  3.522s]
Writing 'root'                                     OKAY [  2.620s]
Sending sparse 'root' 33/33 (9492 KB)              OKAY [  0.342s]
Writing 'root'                                     OKAY [  0.225s]
Finished. Total time: 213.113s
$ sudo dmesg | tail -20
[23502.628598] usb 6-1: new high-speed USB device number 4 using xhci_hcd
[23502.793534] usb 6-1: New USB device found, idVendor=2345, idProduct=7654, bcdDevice= 2.23
[23502.793545] usb 6-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[23502.793549] usb 6-1: Product: USB download gadget
[23502.793552] usb 6-1: Manufacturer: T-HEAD
[23546.038600] usb 6-1: USB disconnect, device number 4
# ここで書き込み終了
[23547.584600] usb 6-1: new high-speed USB device number 5 using xhci_hcd
[23547.749402] usb 6-1: New USB device found, idVendor=1234, idProduct=8888, bcdDevice= 2.23
[23547.749415] usb 6-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[23547.749419] usb 6-1: Product: USB download gadget
[23547.749422] usb 6-1: Manufacturer: U-Boot-THEAD
$ lsusb | grep 1234
Bus 006 Device 005: ID 1234:8888 Brain Actuated Technologies USB download gadget

起動

イメージが焼けたら HDMI をしっかり接続して USB-C 電源に接続すれば、ファンが始動し始め GUI が表示されると思います。ちなみにうちの 2560 x 1440 HDMI モニターだとログイン GDM から 2560 x 480 みたいな解像度で起動しました。w

$ cat /etc/debian_version
12.0
$ uname -a
Linux lpi4a 5.10.113-gbb4691fe5572 #1 SMP PREEMPT Wed May 10 14:16:50 UTC 2023 riscv64 GNU/Linux
$ arch
riscv64
$ cat /proc/cpuinfo | grep -e 'isa' -e 'freq'
isa             : rv64imafdcvsu
cpu-freq        : 1.848Ghz
isa             : rv64imafdcvsu
cpu-freq        : 1.848Ghz
isa             : rv64imafdcvsu
cpu-freq        : 1.848Ghz
isa             : rv64imafdcvsu
cpu-freq        : 1.848Ghz
$ lsmod
Module                  Size  Used by
pvrsrvkm             8585216  0
dwc3                 2584576  0
galcore              3170304  0
roles                  40960  1 dwc3
dwc3_thead             28672  0
$ sudo modinfo dwc3
filename:       /lib/modules/5.10.113-gbb4691fe5572/kernel/drivers/usb/dwc3/dwc3.ko
description:    DesignWare USB3 DRD Controller Driver
license:        GPL v2
author:         Felipe Balbi <balbi@ti.com>
alias:          platform:dwc3
alias:          of:N*T*Csynopsys,dwc3C*
alias:          of:N*T*Csynopsys,dwc3
alias:          of:N*T*Csnps,dwc3C*
alias:          of:N*T*Csnps,dwc3
depends:        roles
intree:         Y
name:           dwc3
vermagic:       5.10.113-gbb4691fe5572 SMP preempt mod_unload riscv
parm:           usb_mode:USB mode (int)
parm:           usb_speed:USB speed (int)

ログインユーザは debian と sipeed ユーザの 2つがデフォルトで用意されています。最初は sipeed/licheepi でログインすると良いでしょう。sudo することができます。

もし正しい解像度で Wayland セッションが起動しなかった場合は、GNOME 右上から Settings にたどり着いて Displays 項目から正しい解像度を選択してください。GDM ログイン画面の解像度については、本設定後、以下のコマンドで同じ解像度にできます。

sudo cp ~/.config/monitors.xml /var/lib/gdm3/.config/

eMMC の rootfs は 8GB 弱です。自分は開発ツールチェインを入れる関係で /mnt/ssd に ext4 フォーマットをした外付けの USB 3.1 SSD を接続してマウントしています。

開発専用にして fstab で /home ごとオートマウントしてもいいかもしれません。

$ sudo mkdir /mnt/ssd
$ sudo mount /dev/sda2 /mnt/ssd/
$ mount | grep ssd
/dev/sda2 on /mnt/ssd type ext4 (rw,relatime)
$ pwd
/home/sipeed
$ ls -laF | grep /mnt
lrwxrwxrwx  1 sipeed sipeed   28 May 18 15:51 .cargo -> /mnt/ssd/home/sipeed/.cargo//
lrwxrwxrwx  1 sipeed sipeed   29 May 18 16:10 .rustup -> /mnt/ssd/home/sipeed/.rustup//
lrwxrwxrwx  1 sipeed sipeed   28 May 18 15:51 .wasmer -> /mnt/ssd/home/sipeed/.wasmer/
lrwxrwxrwx  1 sipeed sipeed   30 May 18 15:51 .wasmtime -> /mnt/ssd/home/sipeed/.wasmtime/
lrwxrwxrwx  1 sipeed sipeed   27 May 18 15:51 devel -> /mnt/ssd/home/sipeed/devel//
lrwxrwxrwx  1 sipeed sipeed   25 May 19 03:02 vgm -> /mnt/ssd/home/sipeed/vgm//

gcc/clang や Rust 等を導入しています。また、リソース監視系のコマンドと openssh-server も入れました。

$ sudo apt install curl wget
$ sudo apt install build-essential cmake git
$ sudo apt install libssl-dev pkg-config
$ sudo apt install clang llvm-dev
$ sudo apt install lm-sensors nmon htop
$ sudo apt install ssh openssh-server # ssh リモート接続したい場合
$ sudo apt install ffmpeg

openssh-server で導入した SSH でのリモート接続はデフォルトでパスワード認証になっています。接続先は ip address コマンドで IP アドレスを取得すると良いでしょう。

また、執筆時点では DHCP では起動のたびにアドレスが変わってしまうようなので(MAC アドレスが変わってしまう)、GNOME のネットワーク設定から固定にしたほうがいいかもしれません。(既にパッチが入っているのを確認しましたので次のリリースで修正されるはずです)

Every boot, a random MAC address. #7

導入後のファイルシステム:

$ df -k | grep /
/dev/root        6923792  4687340   1893296  72% /
devtmpfs         3822156        0   3822156   0% /dev
tmpfs            3986160        0   3986160   0% /dev/shm
tmpfs            1594464     2272   1592192   1% /run
tmpfs               5120        8      5112   1% /run/lock
/dev/mmcblk0p2     52197    24842     23055  52% /boot
tmpfs             797232       28    797204   1% /run/user/1001
/dev/sda2      115012332 38564448  71671768  35% /mnt/ssd

なお、Rust toolchaine は Debian の rust-all ではなく rustup で導入しています。上記の通り Rust 系の導入場所はシンボリックリンクで USB-SSD 側に飛ばしていますので rootfs の容量には含まれていません。

$ # rustup
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
$ # wasmer
$ curl https://get.wasmer.io -sSfL | sh
$ # wasmtime
curl https://wasmtime.dev/install.sh -sSf | bash
$ pwd
/home/sipeed/.cargo
$ cat config.toml
[env]
TMPDIR = { value = "/mnt/ssd/home/sipeed/tmp2", force = true }
$ # cargo install する場合は rootfs /tmp を使われないように --target-dir を指定
$ cargo install --target-dir /mnt/ssd/home/sipeed/tmp alacritty

以下、LicheePi 4A 上で Rust アプリケーションのビルドをしている様子。RISC-V 4コア全てが正しく使われ 100% となり、SoC の温度は 60度未満くらいに。ファンコントロールもうまく動作しています。

$ watch df -k
$ watch sensors
$ sudo cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq

以下、観測できた CPU クロック周波数です。コメントは自分が書いたものです。

# /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq の結果例
800000  # Silence
1500000 # Normal
1848000 # Peek
300000  # Sleep?

一日中 rustc などでビルドを回していたら、一度だけサーマルスロットリング機能が働いて速度が低下するのを確認しました。断続的に休んだり働いたりする動きとなります。

セルフホスト開発などでヘビーに使う場合は、ターミナルマルチプレクサなどで画面分割して、次のように CPU クロック等を watch して使うと良いでしょう。なお、Debian イメージは最大 1.85GHz で動作するように設定されています。(Wiki にありますが、個体によっては設定を変更して 2GHz でも動作するかもとのことです)

sudo watch -n1 cat /sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_fre

これらのリソース情報をターミナルマルチプレクサ Zellij を使い、リモート ssh 接続時に一気に表示するレイアウトファイル(.config/zellij/layouts/licheepi.kdl)をつくってみました。

後述していますが現在 LPi 上では Zellij が動作しないので、ssh 接続元ホスト側用です。Windows の方は WSL2/Ubuntu を使うと便利だと思います。

zellij -l licheepi

上記のコマンドひとつで以下のような画面となります。

Ubuntu/Alactirry での接続の様子(これでいつでも開発できる…!):

Windows Terminal/WSL2 Ubuntu での様子。

サイズやユーザ・パスワード、IP アドレス、ターミナルサイズに応じて適宜変更してください。

layout {
    tab name="LPi4A" focus=true {
        pane split_direction="vertical" {
            pane size="60%" close_on_exit=true {
                command "sshpass"
                args "-p" "licheepi" "ssh" "sipeed@172.16.15.201"
            }
            pane size="40%" {
                pane size="25%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "-n5" "df" "-k"
                }
                pane size="16%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "sudo" "cat" "/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_cur_freq"
                }
                pane size="16%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "watch" "sensors"
                }
                pane size="43%" close_on_exit=true {
                    command "sshpass"
                    args "-p" "licheepi" "ssh" "sipeed@172.16.15.201" "-t" "htop"
                }
            }
        }
    }
    tab name="Host"
    default_tab_template {
        pane size=1 borderless=true {
            plugin location="zellij:tab-bar"
        }
        children
        pane size=2 borderless=true {
            plugin location="zellij:status-bar"
        }
    }
}

ssh 接続にはパスワード認証を使っています。また LPi Debian ユーザには sudo に NOPASSWORD していますので、ご理解の上、必要ならアレンジしてください。

ホスト側 Linux

sudo apt install sshpass

LPi 側 Debian

echo "sipeed ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/users

あとこれは趣味ですが、デフォルトエディタを vim にしています。

$ sudo update-alternatives --config editor
There are 3 choices for the alternative editor (providing /usr/bin/editor).

  Selection    Path                Priority   Status
------------------------------------------------------------
  0            /bin/nano            40        auto mode
  1            /bin/nano            40        manual mode
* 2            /usr/bin/vim.basic   30        manual mode
  3            /usr/bin/vim.tiny    15        manual mode

Press <enter> to keep the current choice[*], or type selection number:

Alacritty

試しに、GPU アクセラレーションを使う Rust 製のターミナルエミュレータである Alacritty のビルドを LPi 上でしてみました。

$ sudo apt install pkg-config libfontconfig-dev
$ time cargo install --target-dir /mnt/ssd/home/sipeed/tmp alacritty
...(snip)..
   Installed package `alacritty v0.12.0` (executable `alacritty`)
real    16m22.428s
user    60m41.044s
sys     1m26.451s

起動ですが(予想はしていましたが)恐らく GPU ドライバーが未完成のため真っ黒画面でした。コマンドを打つとカーソルは動いているのが見えるので動作はしていそうです。(スクリーンショット右下で CPU が MAX になっているのは偶然です)

Zellij

ターミナルマルチプレクサ・ワークスペースである Zellij のビルド。こちらも Rust 製。

$ time cargo install --target-dir /mnt/ssd/home/sipeed/tmp zellij
real    51m20.308s
user    127m50.878s
sys     2m56.126s
$ zellij
Error occurred in server:

  × Thread 'async-std/runtime' panicked.
  ├─▶ At /home/sipeed/.cargo/registry/src/github.com-1ecc6299db9ec823/wasmer-compiler-cranelift-2.3.0/src/config.rs:73:45
  ╰─▶ construct Cranelift ISA for triple: Unsupported

実はこれも予想通りの結果なのですが、Zellij がプラグインシステムのために内蔵する WebAssembly ランタイム(Wasmer 2.3)が riscv64gc-unknown-linux-gnu に対応していないため Unsuppoted でダウンしました。OK です。

上述の通り、本記事のスクリーンショットで使われている Zellij は、ssh 接続元のホスト側で動作しているものです。 LPi 4A 側でターミナルマルチプレクサを使いたい場合は tmux を使うと良いでしょう。

sudo apt install tmux

Wasmer

WebAssembly ランタイムである Wasmer(3.2 以降で RISC-V を initial support)を導入して、自分がつくっている VGM parser を動作させてみました。FM 音源ライブラリである ymfm を使って波形合成を行っています。

Wasmer の速度が思ったとおりにでなくてリアルタイム演算できていないのですが、メインボード側の I2S デバイスで発音しています。(詳しくは動画概要欄をご覧ください)

Rust LLVM ビルドしたプログラムの実行速度については現在いろいろ検証中です。どうも生成するバイナリーが機械の性能に対して想定より遅いような気がしていますが、はっきりとまだ特定できていません。

target=riscv64gc-unknown-linux-gnu だけでなく、LLVM に RISC-V C910 に合わせた optimize option を指定する必要があるかもしれません。

https://github.com/h1romas4/libymfm.wasm

This repository is an experimental WebAssembly build of the [ymfm](https://github.com/aaronsgiles/ymfm) Yamaha FM sound cores library.

自分がビルドをミスしており最適化オプションがうまくコンパイラに渡っていないだけでした…。修正後はシングルスレッド性能になりますが、非常にパワフルな速度で動作しています。

実行メモ:

$ # 54.vzg: Music Duration: 00:01:41.94
$ time wasmer run --cranelift ~/.local/share/libymfm/libymfm-cli.wasm --mapdir /:"$(pwd)" -- -r 44100 54.vgz > /dev/null

real    0m44.462s
user    0m42.783s
sys     0m1.856s
$ wasmer run --cranelift ~/.local/share/libymfm/libymfm-cli.wasm --mapdir /:"$(pwd)" \
-- -r 44100 54.vgz | ffplay -infbuf -nodisp -autoexit -f f32le -ar 44100 -ac 2 -i -

顛末をツイッターでいくつかつぶやいていますのでご参考程度で…

https://twitter.com/h1romas4/status/1660172653704146944

サウンド系メモ

Debian sid なので PipeWire と WirePlumber でオーディオデバイスが制御されています。Wayland セッション「未ログイン時」は auto_null というフォールバックデバイスになって I2S から発音しないようです。

LPi 関係ありませんが、、

pw-top

$ pw-top # Wayland session login 時
alsa_output.platform-lightsound_1.stereo-fallback
$ pw-top # Wayland session no login 時
auto_null
$ # wireplumber.service がこのあたりで動的に制御しているようだ
$ ls -alF /usr/share/wireplumber/scripts
$ ls -alF /usr/share/wireplumber/main.lua.d

ssh 経由で GUI を使わずに I2S を発音させたい場合は、(この方法が最良か分かりませんが)とりあえず、ユーザーを audio グループに入れておくと /dev/snd 配下の ALSA sound device が見えて認識され、Wayland セッションに入らなくても cli コマンドから PipeWire 経由で音声が再生できるようになります。

$ sudo usermod -aG audio sipeed
$ sudo reboot
$ sudo apt install alsa-utils
$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: LightSoundCard [Light-Sound-Card], device 0: light-i2s-dai-ES8156 HiFi es8156.5-0008-0 [light-i2s-dai-ES8156 HiFi es8156.5-0008-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

ALSA 系メモ:

$ sudo dmesg | grep -A1 ALSA
[    5.765048] ALSA device list:
[    5.772741]   #0: Light-Sound-Card
$ cat /proc/devices | grep alsa
116 alsa
$ cat /proc/asound/devices
  0: [ 0]   : control
 16: [ 0- 0]: digital audio playback
 25: [ 0- 1]: digital audio capture
 33:        : timer
$ cat /proc/asound/cards
 0 [LightSoundCard ]: Light-Sound-Car - Light-Sound-Card
                      Light-Sound-Card
$ cat /proc/asound/pcm
00-00: light-i2s-dai-ES8156 HiFi es8156.5-0008-0 : light-i2s-dai-ES8156 HiFi es8156.5-0008-0 : playback 1
00-01: light-i2s-dai-ES7210 ADC 0 es7210.5-0040-1 : light-i2s-dai-ES7210 ADC 0 es7210.5-0040-1 : capture 1
$ ls -laF /dev/snd/
total 0
drwxr-xr-x   3 root root      140 Feb 28 11:15 ./
drwxr-xr-x  17 root root    14000 May 22 21:36 ../
drwxr-xr-x   2 root root       60 Feb 28 11:15 by-path/
crw-rw----+  1 root audio 116,  0 Feb 28 11:15 controlC0
crw-rw----+  1 root audio 116, 16 Feb 28 11:15 pcmC0D0p
crw-rw----+  1 root audio 116, 25 Feb 28 11:15 pcmC0D1c
crw-rw----+  1 root audio 116, 33 Feb 28 11:15 timer
$ # もし ALSA 直で音を鳴らす場合
$ aplay -D hw:0,0 test.wav

pipewire 経由で音がノイズだけになったり、サンプリングレートがおかしくなった時(何かのタイミングで発生):

$ systemctl --user restart pipewire.service pipewire-pulse.service

PipeWire 経由の cli からのボリューム制御はこちらの pw-volume が便利でした。Rust 製なのでビルドして使うと良いでしょう。(標準の wpctrl コマンドでも良いかもしれません)

https://github.com/smasher164/pw-volume

Basic interface to PipeWire volume controls

$ git colne https://github.com/smasher164/pw-volume.git
$ cd pw-volume
$ cargo build --release
$ target/release/pw-volume --help
pw-volume
Basic interface to PipeWire volume controls

USAGE:
    pw-volume <SUBCOMMAND>

OPTIONS:
    -h, --help    Prints help information

SUBCOMMANDS:
    change    adjusts volume by decimal percentage, e.g. '+1%', '-0.5%'
    mute      mutes audio [possible values: on, off, toggle]
    status    get volume and mute information
$ cp -p target/release/pw-volume ~/.local/bin/
$ pw-volume status
{"percentage":6, "tooltip":"6.3997%"}

なお、メインボード側に配置されているラインアウト I2S の音質ですが、オーディオブロックのハイパスフィルター値が高く設定されており低音が若干出にくくなっています。次の rev では修正されるとのこと。

MAME

Debian sid に MAME 0.251 が入っています。

$ sudo apt install mame

残念ながら SDL2 が opengl を見つけられないというメッセージで起動せず。

$ ./mame -window -video soft -window -videodriver wayland -audiodriver pipewire
OpenGL not supported on this driver: Couldn't find matching EGL config (call to eglChooseConfig failed, reporting an error of EGL_SUCCESS)
video_init: Initialization failed!

gles なら?vulkan なら?等

$ ./mame -window -video bgfx -bgfx_backend gles -window
../../../../../3rdparty/bgfx/src/glcontext_glx.cpp (198): BGFX FATAL 0x00000002: Failed to create GL 2.1 context.
Aborted
$ ./mame -window -video bgfx -bgfx_backend vulkan -window
../../../../../3rdparty/bgfx/src/glcontext_glx.cpp (198): BGFX FATAL 0x00000002: Failed to create GL 2.1 context.
Aborted
$ sudo apt install vulkan-tools
$ vulkaninfo
ERROR: [Loader Message] Code 0 : vkCreateInstance: Found no drivers!
Cannot create Vulkan instance.
This problem is often caused by a faulty installation of the Vulkan driver or attempting to use a GPU that does not support Vulkan.
ERROR at ./vulkaninfo/vulkaninfo.h:674:vkCreateInstance failed with ERROR_INCOMPATIBLE_DRIVER
$ sudo systemctl status pvrsrvkm.service
× pvrsrvkm.service - Imagination GPU BXM-4-64 driver init Service.
     Loaded: loaded (/etc/systemd/system/pvrsrvkm.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Fri 2023-05-26 14:50:32 UTC; 1 day 23h ago
   Duration: 192ms
    Process: 385 ExecStart=sh usr/share/gpu/insmod.sh (code=exited, status=1/FAILURE)
   Main PID: 385 (code=exited, status=1/FAILURE)
        CPU: 171ms

May 26 14:50:32 lpi4a systemd[1]: Started pvrsrvkm.service - Imagination GPU BXM-4-64 driver init Service..
May 26 14:50:32 lpi4a sh[387]: insmod: ERROR: could not insert module /lib/modules/5.10.113-gbb4691fe5572/extra/pvrsrvkm.ko: File exists
May 26 14:50:32 lpi4a systemd[1]: pvrsrvkm.service: Main process exited, code=exited, status=1/FAILURE
May 26 14:50:32 lpi4a systemd[1]: pvrsrvkm.service: Failed with result 'exit-code'.
$ ls -alF /lib/firmware/rgx*
-rwxr-xr-x 1 debian debian 122880 May 26 14:14 /lib/firmware/rgx.fw.36.52.104.182*
-rwxr-xr-x 1 debian debian 306660 May 26 14:14 /lib/firmware/rgx.sh.36.52.104.182*
$ sudo modinfo pvrsrvkm
filename:       /lib/modules/5.10.113-gbb4691fe5572/extra/pvrsrvkm.ko
license:        Dual MIT/GPL
author:         Imagination Technologies Ltd. <gpl-support@imgtec.com>
alias:          platform:rgxthead
alias:          of:N*T*Cimg,gpuC*
alias:          of:N*T*Cimg,gpu
depends:
name:           pvrsrvkm
vermagic:       5.10.113-gbb4691fe5572 SMP preempt mod_unload riscv
parm:           gPVRDebugLevel:Sets the level of debug output (default 0x7) (uint)

自分がなにか勘違いしている可能性もありますが、いったん GPU ドライバーのアップデートを楽しみに待ちたいと思います。

なお MAME のビルドについてですが、Debian sid 版の 0.251 より先の執筆時最新の MAME 0.254 との間で、ビルドスクリプトから RISC-V へ渡るデフォルトコンパイルオプションが変わっていて、コンパイルとリンクがエラーで落ちてしまうようなので、最新 MAME ソースからビルドする場合は以下の patch を当ててみてください。

riscv64 build patch

diff --git a/scripts/genie.lua b/scripts/genie.lua
index 9a1f3140..310ec2f2 100644
--- a/scripts/genie.lua
+++ b/scripts/genie.lua
@@ -1134,6 +1134,10 @@ if (_OPTIONS["PLATFORM"]=="arm64") then
 end

 if (_OPTIONS["PLATFORM"]=="riscv64") then
+       buildoptions {
+               "-Wno-cast-align",
+               "-fpic",
+       }
        defines {
                "PTR64=1",
        }

LPi 4A GPU ドライバーがリリースされれば、MAME 0.253 から入った新 m68000 エミュコアを RISC-V で試すことができるはずです。

ちなみにコンパイル時間ですが、MAME のビルドをしている方だとおなじみだと思いますが make -j4 などで並行コンパイルすると、src/emu 配下の emumem* あたりでメモリーを 8GB 近くまでとってしまい I/O wait スローダウンしてしまうパターンがありますので、make -j3 くらいがいいかと思います。

$ sudo apt-get install libsdl2-dev libsdl2-ttf-dev libasound2-dev libxinerama-dev libxi-dev # qtbase5-dev qtbase5-dev-tools
$ git clone https://github.com/mamedev/mame.git
$ cd mame
$ # apply riscv64 build patch - scripts/genie.lua
$ time make -j3 SOURCES=src/mame/neogeo/neogeo.cpp USE_QTDEBUG=0

neogeo ドライバーだけ指定して 120分といったところでした。今回はベンチマーク的にセルフコンパイル(USB3 -SSD を使用 )していますが、さすがにこの規模になるとクロスコンパイル環境をつくったほうがいいかもしれません。

その他

Node.js は unofficial build で RISC-V 版があります。動くのかな?(未検証)

https://unofficial-builds.nodejs.org/download/release/v18.16.0/

node-v18.16.0-linux-riscv64.tar.gz

Chromium はメインラインに RISC-V ビルドがないようで対応版を以下で作業されているようです。まだ動作しないとドキュメントにはありました(未検証)

https://github.com/revyos/chromium-109.0.5414.119

Android が RISC-V 対応するとどこかで見たのでそのうちメインラインにも入るかもしれません。

ちなみにまだ詳しく見ていないですが、Firefox の WebAssembly モジュールは RISC-V では disable になっているような気がします(軽く動かしたら WebAssembly Object がないとエラーになりました)

おわりに

というわけで、非常に高性能な実機の RISC-V Linux を堪能できて楽しいボードだと感じています。各アプリケーションの RISC-V 対応も進化が見られてホットですね。

LM4A モジュールが単体発売されたら、自作でシンセサイザーになるボードをつくってみたいと夢見ていろいろ検証中です。モジュールを使ったプロダクトの発売もいろいろ予定されているようなので、そちらも楽しみにしています…!


関連

M5Stack RCA Module の I2S PCM5102A と Rust ESP32 xtensa-esp32-espidf ビルド

M5Stack RCA Module に搭載されている I2S (PCM5102APWR) を、Rust で生成した PCM 波形で発音させたメモです。

波形の生成は libymfm.wasm として WebAssembly 向けに作成している Rust 製のシーケンサーとサウンドチップエミュレーションをそのまま esp-idf に持ってきて xtensa-esp32-espidf ビルドしています。また、libymfm.wasm は C++ でつくられた FM 音源エミュレータの ymfm もリンクしています。

ESP32 Xtensa の Rust は初挑戦でしたが、確認した範囲で問題なくすんなり動作しました。ESP32 Xtensa Rust ツールチェインの準備やビルドは大変な印象がありましたが、昨今は esp-rs の各プロジェクトを組み合わせることで簡単に設定できるようになっています。

ちなみに ESP32-S3 開発ボードでも軽く動かしてみましたが問題なさそうです。ESP-S3 では PSRAM 80MHz Octa 設定にしているのが効いたか、波形生成は 1.4 倍程度高速でした。(Rust モジュールの細かなメモリ配置については未調査で課題としています)

ということで、この記事には以下の内容が含まれます。

  • I2S PCM5102APWR のイニシャライズ方法
  • ESP32 Xtensa Rust のビルド
実は M5Stack のスタックするモジュールを初めて買ったので、ボトムがないと寂しいことになると知らなかったのは内緒です。でも手軽に接続できてかっこいいです!

ソースコードは以下のリポジトリから見ることができます。詳しくはソースを見ていただくのが早いかもしれません。

https://github.com/h1romas4/m5stack-chipstream

This is a test to port C++’s ymfm and Rust’s vgmplay to ESP32(Xtensa).

Rust でつくられた VGM パーサーと SEGAPCM エミュレーションで I2S を発音させてるデモ:(残念ながら今回のビルドでは、ymfm の FM 音源エミュレーションは ESP32 で処理速度が間に合いませんでした

I2S PCM5102APWR のイニシャライズ

M5Stack Core2 に接続した RCA Module の PCM5102A の i2s_config と pin_config は以下のように設定すると良いようです。

// i2s_driver_install
i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = sample_rate,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = dma_buf_count,
    .dma_buf_len = dma_buf_len,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = I2S_PIN_NO_CHANGE
};
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM_1, &i2s_config, 0, NULL));

// i2s_set_pin
i2s_pin_config_t i2s_pin_config = {
    .mck_io_num = GPIO_NUM_0,
    .bck_io_num = GPIO_NUM_19,
    .ws_io_num = GPIO_NUM_0,
    .data_out_num = GPIO_NUM_2,
    .data_in_num = I2S_PIN_NO_CHANGE
};
ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM_1, &i2s_pin_config));

communication_format は必ず I2S_COMM_FORMAT_STAND_I2S を設定のこと。

うっかり I2S_COMM_FORMAT_STAND_MSB を設定するとなんとなく発音するものの、送信する PCM の先頭 1bit が無視されるような動きになってややはまり。(-32768, 32767 のフルスイング矩形波が正しく発音しなくて気が付きました…)

bits_per_sampleI2S_BITS_PER_SAMPLE_16BITchannel_formatI2S_CHANNEL_FMT_RIGHT_LEFT することで、int16_t のステレオ PCM 形式になります。

DMA バッファは、波形生成側のプリレンダのバッファリングに合わせて dma_buf_len を 1024 で dma_buf_count を 32個設定して動作させています。一度に扱うサンプル数としては int16_t ステレオにて 256 です。

今回のオーディオ系の実装ですが、波形生成を ESP32 の core 0 で、I2S への PCM の送信を core 1 と FreeRTOS タスクを分割して実行しています。なお、i2s_write にはほとんど時間がかからないようですので、波形生成は core 1 に持ってきても影響ないかもしれません。

タスク間の通信には、esp-idf の FreeRTOS Additions になっている RingBuffer の Byte buffers を介して PCM データの送受信を行っています。

FreeRTOS AdditionsRing Buffers

Byte buffers do not store data as separate items. All data is stored as a sequence of bytes, and any number of bytes can be sent or retrieved each time. Use byte buffers when separate items do not need to be maintained (e.g. a byte stream).

ESP32 Xtensa Rust のビルド方法

Xtensa の Rust ツールチェインは espup で導入するのが簡単でした。Windows の場合は WSL2 の Ubuntu 22.04 を使うと便利かもです。(sysroot は esp-idf 4.4.3 を使っています)

詳しくはリポジトリに GitHub Actions のビルドを入れていますの参考にしてください。

https://github.com/esp-rs/espup

Tool for installing and maintaining Espressif Rust ecosystem.

espup で Rust を導入すると ~/.rustup/toolchains/esp に Xtensa の Rust が導入されます。また .espressif/tools/xtensa-esp32-elf-clang に clang が入ります。

$ ls -laF ~/.rustup/toolchains/esp/bin/
合計 58120
drwxr-xr-x 2 hiromasa hiromasa     4096  2月 19 15:56 ./
drwxr-xr-x 7 hiromasa hiromasa     4096  2月 19 15:56 ../
-rwxr-xr-x 1 hiromasa hiromasa 21897920  2月 19 15:56 cargo*
-rwxr-xr-x 1 hiromasa hiromasa   954888  2月 19 15:56 cargo-clippy*
-rwxr-xr-x 1 hiromasa hiromasa  1796976  2月 19 15:56 cargo-fmt*
-rwxr-xr-x 1 hiromasa hiromasa 11781088  2月 19 15:56 clippy-driver*
-rwxr-xr-x 1 hiromasa hiromasa      759  2月 19 15:56 rust-gdb*
-rwxr-xr-x 1 hiromasa hiromasa     1933  2月 19 15:56 rust-gdbgui*
-rwxr-xr-x 1 hiromasa hiromasa     1072  2月 19 15:56 rust-lldb*
-rwxr-xr-x 1 hiromasa hiromasa    17264  2月 19 15:56 rustc*
-rwxr-xr-x 1 hiromasa hiromasa 11124040  2月 19 15:56 rustdoc*
-rwxr-xr-x 1 hiromasa hiromasa 11906968  2月 19 15:56 rustfmt*
$ ls -laF ~/.espressif/tools/xtensa-esp32-elf-clang/esp-15.0.0-20221201-x86_64-unknown-linux-gnu/esp-clang
合計 44
drwxr-xr-x 11 hiromasa hiromasa 4096  2月 19 15:57 ./
drwxr-xr-x  3 hiromasa hiromasa 4096  2月 19 15:57 ../
drwxr-xr-x  2 hiromasa hiromasa 4096  2月 19 15:57 bin/
drwxr-xr-x  4 hiromasa hiromasa 4096  2月 19 15:57 include/
drwxr-xr-x  8 hiromasa hiromasa 4096  2月 19 15:57 lib/
drwxr-xr-x  2 hiromasa hiromasa 4096  2月 19 15:57 libexec/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 riscv32-esp-elf/
drwxr-xr-x  9 hiromasa hiromasa 4096  2月 19 15:57 share/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 xtensa-esp32-elf/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 xtensa-esp32s2-elf/
drwxr-xr-x  5 hiromasa hiromasa 4096  2月 19 15:57 xtensa-esp32s3-elf/

ちなみに espup でツールチェインを入れると、esp-idf とは別のディレクトリ名に同バージョンの gcc が入るようです。(?)

さて、今回は Rust は波形生成をするライブラリの形で、C/C++ の Arduino Loop をメインとしたかったので、”esp-idf first” という構成としています。components 配下に Rust のプロジェクトをおいて、いつも通り idf.py build すると Rust ごとビルドしてくれます。

cargo generate で以下のテンプレートを cmake 指定してビルドスクリプトやディレクトリストラクチャーをつくっています。(引数を cargo にすると Rust 中心の構成になります)

https://github.com/esp-rs/esp-idf-template

cargo generate https://github.com/esp-rs/esp-idf-template cmake

esp-idf first のビルドでは components の下にいつも通りコンポーネントを配置し、CMakeLists.txt から external project として Rust を追加してビルドする動作するようにつくってくれます。

ビルドさえできてしまえば、あとは std 環境の Rust で、今回は外部ライブラリとしてバイナリーパーサ nom や JSON シリアライズ serde などを入れていますが、そのまま動作しました。

一点、Rust から返した *const i16 (16bit) の RAW ポインターを C 側から読むとメモリーの状態によって 1 byte (?) ズレるような動作がありました。いったん C からポインターを渡して Rust 側から書き込むことで修正していますが、Xtensa 特有なのか自分のミスなのかまだ詳しく原因を調査できていません。(Rust 構造体内の配列ポインターなのですがアライメント関連?)

というわけで、ESP32 で Rust std が呼べる。とても便利す。

Rust の特定モジュールを IRAM に載せたり、ヒープアロケータの SRAM/PSRAM コントロール(できる?)など未調査の部分もありますので、引き続きやってみたいと思います。

関連

Windows WSL2 の Ubuntu 22.04 上から USB-UART 経由で M5Stack に書き込みする

Windows WSL2 の 1.0 版以降のカーネルでは、usbipd を経由することで Windows 側の USB 機器を IP 経由で参照することができます。

この動きを利用して、USB に接続した マイコン(M5Stack/USB-UART等々) に WSL2 Ubuntu 上のツールチェインから書き込みする手順を記載してみます。数コマンド叩くだけでセットアップは完了します。便利。

書き込み処理(libusb) は Linux 側で動作しますので、Windows 側にUSB-UART ドライバーなどのセットアップは不要です。

本記事では WSL2 を使った書き込み実用例として以下の手順を記載しています。

  • esp-idf の idf.py コマンドを使った M5Stack Core2 への書き込み
  • PlatformIO を使った書き込み例
  • openocd を使った M5Stamp C3 のデバッグ構成
  • LicheePi 4A への fastboot コマンドを使ったファームウェア書き込み

環境

Windows WSL2 (Windows 10 の場合 1.0 以降)

> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
MSRDC バージョン: 1.2.3575
Direct3D バージョン: 1.606.4
DXCore バージョン: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windowsバージョン: 10.0.22621.1105

WSL2 Ubuntu 22.04

$ uname -a
Linux minis-um690 5.15.79.1-microsoft-standard-WSL2 #1 SMP Wed Nov 23 01:01:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Windows に usbipd-win を導入

基本的に以下の Microsoft のガイドに従うと OK ですが、一部コマンドがカーネルバージョンに依存しているので、usbipd-win の手順も参照しています。

USB デバイスを接続する

このガイドでは、USB/IP オープンソース プロジェクト usbipd-win を使用して、WSL 2 で実行されている Linux ディストリビューションに USB デバイスを接続するために必要な手順について説明します。

まずは Windows 側に usbipd-win を導入します。 PowerShell 窓より以下のコマンドにて。

> winget install --interactive --exact dorssel.usbipd-win

次に WSL2 Ubuntu 22.04 に apt で usbipd を導入します。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install linux-tools-virtual hwdata

usbipd を登録。

$ sudo update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20

なお、Ubuntu カーネル(tools の)バージョンが上がった場合に、後述の Windows 側からの操作が以下のエラーが出力された場合は再度 sudo update-alternatives --install を実行してください。

❯ usbipd.exe wsl attach --busid 9-3
usbipd: error: WSL 'usbip' client not correctly installed. See https://github.com/dorssel/usbipd-win/wiki/WSL-support for the latest instructions.

linux-tools がアップデートされた例(ここでは Kernel 5.15.0-70 から 5.15.0.-71)された後の update-alternatives 設定ログ:

$ sudo update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20
update-alternatives: 警告: /usr/lib/linux-tools/5.15.0-70-generic/usbip の alternative (リンクグループ usbip のパート) が存在しません 。alternatives のリストから削除しています
update-alternatives: 警告: /etc/alternatives/usbip is dangling; it will be updated with best choice
update-alternatives: /usr/local/bin/usbip (usbip) を提供するために自動モードで /usr/lib/linux-tools/5.15.0-71-generic/usbip を使います

さて、この流れでついでに Ubuntu の udev を構成して、マイコンの USB 接続をユーザ権限から見れるようにしておきます。 PlatformIO の udev 定義を使うとほとんどのマイコンで使えて便利です。

$ curl -fsSL https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/system/99-platformio-udev.rules | sudo tee /etc/udev/rules.d/99-platformio-udev.rules

これで WSL2 側から接続された USB を認識させる準備は完了です。手順上、いったん状態をクリアするため Windows ごと再起動します。

USB 機器のバインド

Windows 起動後、先に Ubuntu の usbipd を起動するために、WSL2 Ubuntu 窓を上げておきます。

WSL2 では udev サービスが自動起動しませんので、WSL2 起動後に以下のコマンドで起動します。(wsl.conf などで自動起動設定するのもいいかもしれません)

$ sudo service udev restart

次に Windows の PowerShell 窓を「管理者で起動」し、使いたい USB 機器を usbipdにアタッチします。

usbipd wsl list コマンドで USB 機器を表示。

> usbipd wsl list
BUSID  VID:PID    DEVICE                                                        STATE
3-1    17ef:6047  Lenovo USB Interface Device(HID), USB 入力デバイス            Not attached
3-3    0e8d:0608  RZ608 Bluetooth(R) Adapter                                    Not attached
7-1    0499:170d  AG06/AG03, Line (AG06/AG03)                                   Not attached
9-1    10c4:ea60  CP2104 USB to UART Bridge Controller                          Not attached

ここでは、CP2104 USB to UART が M5Stack なので 9-1 をアタッチ。なお、初回のアタッチの操作のみ管理者権限が必要で次回からはユーザ権限でいけるようです。

> usbipd wsl attach --busid 9-1

なお、ユーザ権限がついたあとは、Ubuntu 側 WSL2 窓から次のコマンドでも操作可能です。(.exe を付けて明示的に Windows 側のコマンドを呼びます)

$ usbipd.exe wsl attach --busid 9-1

うまくいけば、upsipd wsl list コマンドのデバイス部分が Ubuntu になります。

> usbipd wsl list
BUSID  VID:PID    DEVICE                                                        STATE
9-1    10c4:ea60  CP2104 USB to UART Bridge Controller                          Attached - Ubuntu-22.04

できたら、WSL2 Ubuntu で lsusb してみて機器(CP210)が見えればOKです。

$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

PlatformIO からお借りした udev 定義が効いていれば、USB-UART では /dev/ttyUSB0 あたりにユーザ権限の書き込み付きでブロックデバイスが見えます。rw-rw-rw なら成功です。

$ ls -laF /dev/ttyUSB*
crw-rw-rw- 1 root dialout 188, 0  1月 28 17:17 /dev/ttyUSB0

マイコンが暴走して USB を抜き差しした後などで、usbipd.exe で再アタッチするも udev が効かないケースがあるようです。その場合は面倒なので手動で chmod するのもありかと思います。

$ sudo chmod 666 /dev/ttyUSB0

esp-idf の idf.py コマンドを使った M5Stack Core2 への書き込み

Ubuntu 22.04 上に esp-idf を構成して idf.py コマンドで書き込む例です。なお、esp-idf ツールチェインが必要とする Ubuntu の依存関係はこちらに記載されています。

$ get_idf
$ echo ${IDF_PATH}
/home/hiromasa/devel/toolchain/esp-idf
$ idf.py --version
ESP-IDF v4.4.3

esp-idf (esp-tools) は自動的にシリアルポートを探してくれますので、以下のいつもの idf.py flash monitor コマンドで書き込みからログ入力まで動作します。

$ idf.py build flash monitor
Executing action: all (aliases: build)
Running ninja in directory /home/hiromasa/devel/esp32/m5stack-core2-wasm3-as/build
Executing "ninja all"...
[1/4] cd /home/hiromasa/devel/esp32/m5stack-core2-wasm3-as/b...asa/devel/esp32/m5stack-core2-wasm3-as/build/hello_world.bin
hello_world.bin binary size 0x124cb0 bytes. Smallest app partition is 0x200000 bytes. 0xdb350 bytes (43%) free.
[2/4] Performing build step for 'bootloader'
[1/1] cd /home/hiromasa/devel/esp32/m5stack-core2-wasm3-as/build/bootloader/esp-idf/esptool_py && /home/hiromasa/.espressif/python_env/idf4.4_py3.10_env/bin/python /home/hiromasa/devel/toolchain/esp-idf/components/partition_table/check_sizes.py --offset 0x8000 bootloader 0x1000 /home/hiromasa/devel/esp32/m5stack-core2-wasm3-as/build/bootloader/bootloader.bin
Bootloader binary size 0x6330 bytes. 0xcd0 bytes (11%) free.
Executing action: flash
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting.....
Detecting chip type... ESP32
...以下略...

本環境設定と合わせて、プロジェクトディレクトリを VSCode の WSL Remote 拡張で開けば、ソースコードの編集からマイコンへの書き込みまで Linux 周りのツールで揃えることができるので便利なのではないかと思います。

WSL2 からのマイコンへの書き込みの操作は以上です。usbipd 経由で接続された USB が WSL2 側から認識できれば、他は特殊な操作は不要で書き込みすることができます。

PlatformIO を使った書き込み例

Longan Nano (GD32V) + VSCode + PlatformIO の例です。

Longan Nano dfu 用の udev 構成:

$ cat /etc/udev/rules.d/70-ttyusb.rules #ファイル追加
# Longan Nano
SUBSYSTEM=="usb", ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", MODE="0666"
SUBSYSTEM=="usb_device", ATTRS{idVendor}=="28e9", ATTRS{idProduct}=="0189", MODE="0666"
$ sudo service udev restart # udev restart

Longan Nano を BOOT + RESET 起動で dfu モードに入れて attach。

> usbipd wsl list
BUSID  VID:PID    DEVICE                                                        STATE
3-1    17ef:6047  Lenovo USB Interface Device(HID), USB 入力デバイス            Not attached
3-3    0e8d:0608  RZ608 Bluetooth(R) Adapter                                    Not attached
7-1    0499:170d  AG06/AG03, Line (AG06/AG03)                                   Not attached
9-1    28e9:0189                                                                Not attached

先に管理者権限 PowerShell でアタッチした後、リセットするとアタッチが切れるので、再アタッチ。 WSL2 Ubuntu 側からも Windows の usbipd.exe を呼べる動作を活用してアップロード操作前にアタッチコマンドを実行できるようにタスクを組むと便利。

$ usbipd.exe wsl attach --busid 9-1
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 28e9:0189 GDMicroelectronics GD32 DFU Bootloader (Longan Nano)
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

あとは WSL2 側に入れた VSCode と PlatformIO 拡張から、いつも通り Upload を起動すれば書き込みできます。

openocd デバッグを行う場合

M5Stamp C3 の USB JTAG デバッグを行う設定例です。

M5Stamp C3 は 「M5Stamp C3 用の開発向けボードを製作」のように USB JTAG を引き出すことができます。また、M5Stamp C3U では付属の USB ポートでいけると思います。

openocd の依存となる libusb の導入(esp-idf の依存で導入されているかもですが念の為。どちらかでだけでもいいかものと、-dev を入れているのは癖なので外しても OK です)

$ sudo apt install libusb-dev libusb-1.0-0-dev

openocd の依存となる libpython2.7 導入

$ sudo apt install libpython2.7

USB ポートに M5Stamp C3 の JTAG USB 側(?) を引き出して接続、Windows 側でアタッチ後 Linux で USB 認識確認

$ lsusb | grep JTAG
Bus 001 Device 007: ID 303a:1001 Espressif USB JTAG/serial debug unit

udev 構成追加( 303a の 追加)

$ cat /etc/udev/rules.d/99-platformio-udev.rules | grep 303a
# 以下を追加
ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"

udev restart

$ sudo service udev restart

接続確認

いったん USB を抜き差しして、Windows 側で再アタッチなどして、接続確認します。

$ ls -laF /dev/ttyACM*
crw-rw-rw- 1 root dialout 166, 0  1月 28 19:46 /dev/ttyACM0

openocd 起動

$ which openocd
/home/hiromasa/.espressif/tools/openocd-esp32/v0.11.0-esp32-20221026/openocd-esp32/bin/openocd
$ openocd -f board/esp32c3-builtin.cfg
Open On-Chip Debugger  v0.11.0-esp32-20220706 (2022-07-06-15:48)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'jtag'
Info : esp_usb_jtag: VID set to 0x303a and PID to 0x1001
Info : esp_usb_jtag: capabilities descriptor set to 0x2000
Warn : Transport "jtag" was already selected
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : esp_usb_jtag: serial (34:B4:72:12:94:14)
Info : esp_usb_jtag: Device found. Base speed 40000KHz, div range 1 to 255
Info : clock speed 40000 kHz
Info : JTAG tap: esp32c3.cpu tap/device found: 0x00005c25 (mfg: 0x612 (Espressif Systems), part: 0x0005, ver: 0x0)
Info : datacount=2 progbufsize=16
Info : Examined RISC-V core; found 1 harts
Info :  hart 0: XLEN=32, misa=0x40101104
Info : starting gdb server for esp32c3 on 3333
Info : Listening on port 3333 for gdb connections

VSCode などから接続(設定例

おそらく openocd 接続後初回の Debug Launch は(マイコンの状態により)失敗するので、2回 Debug Launch するとブレイクすると思います。(WSL2 ではない Ubuntu でも再現するので自分の setupCommandsが悪いのかもしれません)

Lichee Pi 4A の fastboot による Flash

Lichee Pi 4A の boot ボタンを押しながら PC の USB-C に接続。

> usbipd.exe wsl list
BUSID  VID:PID    DEVICE                                                        STATE
6-1    2345:7654  USB download gadget                                           

初回の場合は PowerShell 管理者で実行。

> usbipd.exe wsl attach --busid 6-1

WSL2 側で認識確認。

$ sudo dmesg
[   31.713030] usb 1-1: new high-speed USB device number 2 using vhci_hcd
[   31.873057] usb 1-1: SetAddress Request (2) to port 0
[   31.920340] usb 1-1: New USB device found, idVendor=2345, idProduct=7654, bcdDevice= 2.23
[   31.921254] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[   31.921580] usb 1-1: Product: USB download gadget
[   31.921790] usb 1-1: Manufacturer: T-HEAD
[   49.129386] hv_balloon: Max. dynamic memory size: 14204 MB

あとは fastboot コマンドで書き込めば OK です。

$ pwd
/home/hiromasa/devel/riscv/lip4a/images/0511_gnome
$ ls -laF
合計 4259020
drwxrwxr-x 2 hiromasa hiromasa       4096  5月 12 12:04 ./
drwxr-xr-x 3 hiromasa hiromasa       4096  6月  2 17:24 ../
-rw-rw-r-- 1 hiromasa hiromasa   62914560  5月 12 11:35 boot-20230510-230240.ext4
-rwxrwxrwx 1 hiromasa hiromasa        368  5月 12 11:29 burn_gnome_gpu.sh*
-rwxrwxrwx 1 hiromasa hiromasa    2230360  5月  5 23:47 fastboot*
-rw-rw-r-- 1 hiromasa hiromasa       2022  5月 12 11:38 image_intro.txt
-rw-r--r-- 1 hiromasa hiromasa      68443  5月 12 11:36 light-lpi4a_1.85GHz.dtb
-rw-r--r-- 1 hiromasa hiromasa      68464  5月 12 11:36 light-lpi4a_2Ghz.dtb
-rw-rw-r-- 1 hiromasa hiromasa        259  5月 12 11:21 md5sum.txt
-rw-rw-r-- 1 hiromasa hiromasa 4294967296  5月 12 11:32 rootfs-20230511-183752-gnome.ext4
-rw-rw-r-- 1 hiromasa hiromasa     957728  5月 12 11:21 u-boot-with-spl-lpi4a-20230510.bin
$ file fastboot
fastboot: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, not stripped

なのですが、一度 uboot を書く前のファームウェア書き込み後に一度リセットがかかり <waiting device..> となりますので、ここのタイミングで再度 PowerShell 側で usbipd.exe wsl attach --busid 6-1 すれば完走できます。

$ ./burn_gnome_gpu.sh
Sending 'ram' (935 KB)                             OKAY [  0.248s]
Writing 'ram'                                      OKAY [  0.002s]
Finished. Total time: 0.256s
Rebooting                                          OKAY [  0.001s]
Finished. Total time: 0.202s
< waiting for any device > # ←ここで再度 wsl attach する
Sending 'uboot' (935 KB)                           OKAY [  0.058s]
Writing 'uboot'                                    OKAY [  0.031s]
Finished. Total time: 0.113s
Sending 'boot' (61440 KB)                          OKAY [  2.568s]
Writing 'boot'                                     OKAY [  1.535s]
Finished. Total time: 4.241s
Invalid sparse file format at header magic
Sending sparse 'root' 1/33 (113316 KB)             OKAY [  4.680s]
Writing 'root'                                     OKAY [  2.791s]
Sending sparse 'root' 2/33 (110992 KB)             OKAY [  4.660s]
Writing 'root'                                     OKAY [  2.568s]
...(snip)...
Writing 'root'                                     OKAY [  2.891s]
Sending sparse 'root' 32/33 (114684 KB)            OKAY [  4.709s]
Writing 'root'                                     OKAY [  2.617s]
Sending sparse 'root' 33/33 (9492 KB)              OKAY [  0.417s]
Writing 'root'                                     OKAY [  0.236s]
Finished. Total time: 264.331s

関連