# 付録: 実機がなくても試せる Arduino UNO R4 WiFi が手元に無くても、**実機と同じファームウェアを PC の中で 動かして**本書を読み進められます。LED マトリクスと内蔵 LED はブラウザ上の 基板写真に重ねて表示され、第5章で作るシェルもそのまま使えます。 ``` pio run -e sim → firmware.elf → QEMU (RA4M1) → ブラウザ (いつもの (実機に書くものと (実機と同じ (基板の上で ビルド) 同じ ELF) Cortex-M4) LED が光る) ``` 図の QEMU は CPU とボードをソフトウェアで再現するエミュレータ、 ELF はビルドでできる実行ファイルの形式です。 サンプルコードは 1 行も変えません。実機向けのビルドとの違いは、Arduino コア 公式のフラグ `-D NO_USB` を足すことだけです(理由は後述)。 :::{note} ブラウザ版はインストール不要です。 後半の「必要なもの」から先の、PC で動かす手順は、 {doc}`platformio` と OS 別のページ({doc}`windows`・{doc}`linux`・{doc}`macos`)で環境を整えてあることが前提です。 そちらを済ませてから戻ってきてください。 ::: :::{tip} **インストールなしで、まずブラウザで試せます。** 各章のファームウェアを ブラウザの中の QEMU(WebAssembly 版)で動かすページを用意しました。 PC に何も入れずに、LED の点滅やシェルを触れます。自分で書き換えてビルドした `firmware.elf` も動かせます({ref}`browser-sim-elf`)。 ブラウザで動かす ブラウザ版の QEMU は命令をインタプリタで実行するため、タイマや点滅は実時間 どおりですが、重い計算は実機より遅くなります。本文の出力と細かく比べるときは、 以下の手順で PC に入れたものか実機を使ってください。 ::: :::{note} 本書のカーネルが使うのは SysTick・PendSV・NVIC・MPU といった Cortex-M の 標準機能です(いずれも本文で順に出てきます)。エミュレータ上でもこれらは実機と同じように動き、第6章の フォールト(ゼロ除算・不正アドレス)も本文どおりに起きます。 ::: (browser-sim)= ## ブラウザで動かす ブラウザで動かす ページを開くと、各章のファームウェア(`pio run -e sim` でビルドしたもの)が ブラウザの中の QEMU で動き出します。章は上の一覧で切り替えます。シェルのある章は、 画面下の入力欄にコマンドを打てばそのまま動きます。 (browser-sim-elf)= ### 自分でビルドした ELF を動かす サンプルを書き換えたら、ビルドしてできた `firmware.elf` をブラウザ版に渡すだけで、 書き込まずに動きを確かめられます。ここでは `00_intro` のメッセージを書き換えてみます。 1. `00_intro/src/main.cpp` の `Environment OK!` を、好きな文に書き換えます (下の GIF では `sed` で `Hello from my build!` に置き換えています)。 2. ビルドします。書き込み(`-t upload`)は要りません。 ```bash pio run -d 00_intro ``` できた ELF は `00_intro/.pio/build/uno_r4_wifi/firmware.elf` にあります。 ```{figure} ../_static/sim_upload_build.gif :width: 100% メッセージを書き換えてビルドし、`firmware.elf` ができたところ ``` 3. ブラウザ版を開き、章の一覧の最後の **「自分でビルドした ELF を動かす…」** を選びます。 4. 出てきた欄に `firmware.elf` をドロップします(欄をクリックしてファイルを選んでも同じです)。 5. Serial monitor に書き換えたメッセージが出て、LED マトリクスに「OS」が出れば成功です。 ```{figure} ../_static/sim_upload_run.gif :width: 80% ビルドした `firmware.elf` をドロップすると、書き換えたメッセージが出る ``` :::{note} - **UNO R4 WiFi 向けのふつうのビルドがそのまま動きます。** Arduino の `boards.txt` は UNO R4 WiFi に `-DNO_USB` を付けていて、`Serial` は元から UART です(`pio run` と `pio run -e sim` の ELF はまったく同じものになります)。 - ファイルはブラウザの中だけで使われ、どこにも送られません。 - 選べるのは `.elf` だけです(`.bin` や `.hex` は使えません)。UNO R4 Minima 向け のように `Serial` を USB で出すビルドは、エミュレータに USB が無いのでシリアルが 出ません(ページに注意が出ます)。 - 章を切り替えるとページが読み込み直されます。動かし直すときは、もう一度 「自分でビルドした ELF を動かす…」を選んでください。 ::: ### ブラウザ版の制限 - **速さは実機の約 1/10 です。** タイマや点滅は実時間どおりに進みますが、重い計算は 遅くなります。第4章の Heavy タスクはほとんど進まず、第9章の `ps` の CPU% は 合計が 100% を少し超えることがあります(ページにも注意が出ます)。 - **起動が終わる前に打った文字は失われます。** プロンプト(`>` など)が出てから 打ってください。 本文の出力と細かく比べるときは、以下の手順で PC に入れたものか実機を使ってください。 ## 必要なもの | | 入手先 | | --- | --- | | **QEMU**(UNO R4 対応版) | | | **Python** 3.10 以降 | 標準ライブラリのみ使用(`pip install` は不要) | | **PlatformIO** | {doc}`platformio` と同じもの | 本家の QEMU には UNO R4 のマイコン(Renesas RA4M1)が入っていないので、 それを足したものを本書用に配布しています。Homebrew や apt で入る `qemu-system-arm` では動きません。 (install-qemu)= ### QEMU のインストール リリースページから OS に合ったファイルを取ってきて、ホームの `qemu-unor4` フォルダに展開するだけです。この場所に置けば、仮想ボードが自動で見つけます。 ::::{tab-set} :::{tab-item} macOS Apple Silicon(macOS 14 以降)と Intel Mac(macOS 15 以降)に対応しています。 ```bash mkdir -p ~/qemu-unor4 && cd ~/qemu-unor4 arch=$(uname -m) # arm64 か x86_64 curl -fLO "https://github.com/iory/qemu-arduino-uno-r4/releases/download/v11.1.1-unor4.5/qemu-arduino-uno-r4-v11.1.1-unor4.5-macos-$arch.tar.gz" tar xzf qemu-arduino-uno-r4-*-macos-*.tar.gz --strip-components=1 ~/qemu-unor4/bin/qemu-system-arm --version ``` 必要なライブラリは同梱しているので、Homebrew は要りません。 ブラウザでダウンロードした場合は、macOS が「開発元を検証できない」として 起動を止めます。展開したフォルダで一度だけ次を実行してください。 ```bash xattr -dr com.apple.quarantine ~/qemu-unor4 ``` macOS 15 に上げられない古い Intel Mac では、後述の Renode を使ってください。 ::: :::{tab-item} Windows x64 版と ARM64 版(Snapdragon X など)があります。**Windows on ARM では 必ず ARM64 版を使ってください。** x64 版はエミュレーション経由では動きません。 PowerShell で: ```powershell $arch = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'arm64' } else { 'x86_64' } $name = "qemu-arduino-uno-r4-v11.1.1-unor4.5-windows-$arch" Invoke-WebRequest "https://github.com/iory/qemu-arduino-uno-r4/releases/download/v11.1.1-unor4.5/$name.zip" -OutFile "$name.zip" Expand-Archive "$name.zip" -DestinationPath "$HOME\qemu-unor4" & "$HOME\qemu-unor4\$name\bin\qemu-system-arm.exe" --version ``` 必要な DLL は `bin` に同梱しています。 ::: :::{tab-item} Linux x86_64 版と arm64 版があります(glibc 2.35 以降、Ubuntu 22.04 相当以降)。 ただし本書のビルドに使う PlatformIO のコンパイラが Linux arm64 向けには 配布されていないため、**章をビルドできるのは x86_64 だけ**です (Raspberry Pi の 64 ビット OS なども同じです)。 ```bash mkdir -p ~/qemu-unor4 && cd ~/qemu-unor4 arch=$(uname -m); [ "$arch" = aarch64 ] && arch=arm64 curl -fLO "https://github.com/iory/qemu-arduino-uno-r4/releases/download/v11.1.1-unor4.5/qemu-arduino-uno-r4-v11.1.1-unor4.5-linux-$arch.tar.gz" tar xzf qemu-arduino-uno-r4-*-linux-*.tar.gz --strip-components=1 ~/qemu-unor4/bin/qemu-system-arm --version ``` 使うライブラリは glib だけで、ほとんどのディストリビューションに最初から 入っています。無い場合は `sudo apt install libglib2.0-0` です。画面は使わない ので、`DISPLAY` の無い環境(SSH 越しなど)でもそのまま動きます。 ::: :::: ## 動かす ```bash cd code/04_scheduler uv run pio run -e sim # シミュレータ用にビルド uv run python ../sim/board.py --chapter . # 仮想ボードを起動 ``` ブラウザで `http://127.0.0.1:8080` を開くと基板が表示されます。終了は Ctrl-C です。 (`localhost` でも開けますが、Windows では IPv6 を先に試すぶん表示が数秒遅れます) ```{figure} ../_static/sim_browser.jpg :name: fig-sim-browser :width: 80% 第7章の改良版 `07_led_matrix_v2` を動かし、シリアルモニタに `ps` と打ったところ。 上が基板、下がシリアルモニタです。マトリクスの横棒と `ps` の CPU% は同じ値を表しています。 ``` Windows では `python3` ではなく `python` と打ってください。 QEMU を `qemu-unor4` 以外の場所に置いた場合は、`--qemu` でその `qemu-system-arm`(Windows は `qemu-system-arm.exe`)を指定するか、 環境変数 `QEMU` に場所を入れておいてください。 ## 画面の見かた - **12×8 LED マトリクス** — `Arduino_LED_Matrix` が実機で走査しているフレーム バッファ(12 バイト)をそのまま読んで光らせています - **L (D13)** — ポートレジスタ `PCNTR1` の出力ビットを読んでいます - **シリアルモニタ** — 双方向です。入力欄にコマンドを打てば、第5章以降の シェルや第11章の TinyPython がそのまま使えます 第1章は実機と同じく起動時に `S` の入力を待ちます。入力欄に `S` と打って ください。 ```{figure} ../_static/sim_d13.gif :name: fig-sim-d13 :width: 80% 第2章のサンプルで、内蔵 LED「L」(D13)が点滅する様子(D13 のまわりを拡大)。 ``` ```{figure} ../_static/sim_matrix_kill.gif :name: fig-sim-matrix :width: 80% 第7章の改良版 `07_led_matrix_v2`。マトリクスの横棒は CPU の使用率で、12 個で 100% です。 いちばん上の 2 行が CPU 全体、1 行あけて下の 5 行がタスクごと(上から LED・Light・Heavy・Shell・Display)です。 `kill 3` で重い計算のタスク(Heavy)を止めると、Heavy の行が消えて全体の棒が縮み、 `exec 3` で元に戻ります。 ``` ## 章ごとの対応 | 章 | シミュレータ | | --- | --- | | 第0章〜第11章、応用編 1〜3 | ✅ 動きます | | 第12章(ハードウェア) | ❌ センサ・アクチュエータの実物が必要です | | 第13章(四脚ロボット) | ❌ サーボとロボット本体が必要です | | 応用編 4(ファイルシステム) | ❌ Renesas FSP のフラッシュドライバが必要です | ## 実機との違い ここだけは実機と違います。**本文の説明と食い違う場面があるので、 気づいたときはこの節を思い出してください。** - **`Serial` が USB ではなく UART になります** エミュレータには USB が実装されていないため、`-D NO_USB` で `Serial` を ハードウェア UART(SCI9)に切り替えています。UNO R4 WiFi の `Serial` が USB CDC(USB をシリアルポートとして見せる標準の方式)であること自体は本書で扱う話題なので、そこは実機で確かめてください。 - **ESP32 / Wi-Fi はありません** UNO R4 WiFi のもう 1 つのチップは載っていません。 - **時間の進み方は近似です** 命令 1 つを 16 ns(≒ 48 MHz)として時間を進めています。タスク切り替えの 周期や CPU 使用率の傾向は追えますが、命令ごとのサイクル数や割り込みの ジッタは本物ではありません。リアルタイム性の最終確認は実機で行ってください。 - **本書で使わない周辺回路はありません** タイマ(AGT)・シリアル(SCI)・GPIO・割り込みコントローラ以外 (ADC、I²C、SPI、PWM 用のタイマなど)は入っていません。 :::{note} 動作確認は **macOS 26 (Apple Silicon)**・**Windows 11 (ARM64)**・ **Ubuntu 24.04 (x86_64)** で行っています。加えて GitHub Actions で、macOS (Apple Silicon / Intel)・Windows(x64 / ARM64)・Ubuntu(x86_64)の 5 環境で、この付録の手順どおりに全章を毎週確かめています。うまくいかない場合は [Issues](https://github.com/iory/build-your-own-arduino-rtos/issues) で教えてください。 ::: ## うまくいかないとき **「ファームウェアが見つかりません」と出る** `pio run -e sim` を実行しましたか。実機向けの `pio run` だけでは、 エミュレータ用のビルド(`.pio/build/sim/firmware.elf`)ができません。 **「arduino-uno-r4 マシン入りの qemu-system-arm が見つかりません」と出る** 探した場所と、それぞれがダメだった理由が一緒に表示されます。QEMU を ホームの `qemu-unor4` に展開したか、`--qemu` が展開した `qemu-system-arm` を 指しているか確認してください。Homebrew や apt で入れた本家の QEMU では 動きません。 macOS で「開発元を検証できない」と出た場合は、上の `xattr` を実行してください。 **シリアルに何も出てこない** 章によっては入力待ちで止まっています(第1章は `S`、第5章以降はシェルの プロンプト)。 **ポートが使用中** `--http-port` / `--monitor-port` / `--uart-port` で番号を変えられます。 **PC が重い・ファンが回り続ける** `board.py` を `kill -9` などで強制終了すると、QEMU だけが残って動き続ける ことがあります(Ctrl-C や端末を閉じた場合は自動で止まります)。 macOS / Linux は `pkill -f qemu-system-arm`、Windows はタスクマネージャーで `qemu-system-arm.exe` を終了してください。 ## 章の確認を自動で走らせる 本書の検証スクリプトは、実機の代わりにエミュレータでも走ります。 期待値は本文の「期待される出力」から起こしたものと同じです。 ```bash cd code uv run python scripts/verify_chapters.py --sim uv run python scripts/verify_chapters.py --sim --only 05_shell ``` `code/sim/record.py` を使うと、基板の光り方をそのまま GIF に録れます。 ## Renode を使う場合 QEMU の代わりに、オープンソースのエミュレータ [Renode](https://renode.io/) (1.17 以降)でも同じ画面が動きます。macOS 15 に上げられない Intel Mac では こちらを使ってください。 Renode は RA4M1 を最初から持っているので、公式のリリースをそのまま使えます。 - macOS: `brew trust renode/tap && brew install renode/tap/renode` (`brew trust` を先に実行しないと `untrusted tap` で止まります) - Windows: `renode-*.setup.exe`(Windows on ARM でも x64 エミュレーションで動きます) - Linux: `renode-*.linux-portable.tar.gz` を展開(.NET 同梱) いずれも から入手できます。 起動するときは `--emulator renode` を付けます。 ```bash uv run python ../sim/board.py --chapter . --emulator renode uv run python ../sim/board.py --chapter . --emulator renode --renode /path/to/renode # PATH に無いとき ``` 検証スクリプトも `--emulator renode` で Renode を使います。 QEMU との違いは、**第6章のフォールトが起きない**ことです。 Renode は `CCR.DIV_0_TRP` を実装していないためゼロ除算で UsageFault に ならず、未マップ領域への書き込みでも BusFault を上げません。「壊れたタスク だけが死んで他は動き続ける」ことは観察できますが、`FAULT DETECTED` の表示と 原因の特定は見られません(検証スクリプトでは `SKIP` になります)。 Renode が残って動き続けたときは `pkill -f Renode`(Windows は `Renode.exe` を終了) で止めてください。 ## 仕組みを知りたい `code/sim/README.md` に、どのアドレスから何を読んでいるか、ブラウザへ どう流しているかを書いています。仮想ボード本体(`board.py` と `board.html`)は 600 行ほどです。QEMU 側の実装(RA4M1 の割り込み コントローラ・SCI・AGT・GPIO)は にあります。