|
Download docs/DEVELOPING.md from HelloSun/esp32emu: direct link, hf CLI and curl.
- Browser
- Download file 7.08 kB
-
https://huggingface.co/spaces/HelloSun/esp32emu/resolve/main/docs/DEVELOPING.md
- Command line
-
hf download hf://spaces/HelloSun/esp32emu/docs/DEVELOPING.md
-
curl -L -o DEVELOPING.md https://huggingface.co/spaces/HelloSun/esp32emu/resolve/main/docs/DEVELOPING.md
7.08 kB
ESP32 模擬器 開發 / 部署 / 修改指南 (Developer guide)
給「要部署或改程式的人(含小型 AI 模型)」:照著下面「配方」逐步做即可。所有路徑相對於專案根目錄。
0. 一分鐘總覽
瀏覽器內執行的 ESP32 (Xtensa LX6 雙核) 模擬器。主執行緒 web/app.js 負責 UI;Web Worker web/js/worker.js 跑 web/js/soc.js(SoC + 週邊)。
CPU 有兩套等價實作:WASM(wasm/xtensa.c,預設)與 JS(web/js/xtensa.js,參考/除錯用)。兩者對同一韌體執行的指令數必須逐位相同。
1. 目錄地圖
| 路徑 | 內容 |
|---|---|
web/index.html style.css app.js |
UI:範例選單、載入二進位表、虛擬鍵盤、序列埠、GPIO 面板、畫面 canvas |
web/js/worker.js |
模擬迴圈(對齊真實時間、送 frame/uart/gpio/status 給 UI),處理 UI 訊息 |
web/js/soc.js |
記憶體對映、頁表、中斷矩陣、事件佇列、DPORT、UART、TIMG、FRC、RTC_CNTL、eFuse、ROM hook、主迴圈 run() |
web/js/xtensa.js |
JS 版 CPU(參考實作,可下斷點/trace) |
wasm/xtensa.c → wasm/xtensa.wasm |
WASM 版 CPU(與 xtensa.js 逐行對應) |
web/js/cpu_wasm.js |
WASM CPU 的 JS 包裝(狀態陣列索引表 I,必須與 xtensa.c 的 enum 一致) |
web/js/spi.js |
SPI flash 晶片、PSRAM 晶片、SPI 控制器 |
web/js/gpio.js |
GPIO 矩陣、腳位電氣模型(開集極/外部拉低/上拉)、IO_MUX |
web/js/i2s.js |
I2S DMA 描述子鏈 + VGA 螢幕模型(VGA32_BOARD 為電阻網路接線) |
web/js/ps2.js ulp.js |
PS/2 鍵盤/滑鼠裝置、ULP 協處理器直譯器 + RTC_IO + SENS |
web/js/sha.js |
SHA-1/256/384/512 硬體引擎 |
web/assets.js |
生成檔(gzip+base64 的 ROM、wasm、範例韌體),勿手改 |
web/rom/ |
ESP32 ROM 映像(esp32-v3-rom.bin, esp32-v3-rom-app.bin) |
firmware/*/ |
範例 Arduino 草稿碼(dosvga32=FreeDOS、vgademo、echo);firmware/dist/ 為編好的 flash |
tools/ |
除錯/測試腳本(見 §6) |
scripts/ |
setup.sh build.sh deploy.sh pack_source.sh |
2. 環境(第一次)
需要 Linux、python3、node>=18、curl。
scripts/setup.sh # 安裝 zig(編 wasm)、arduino-cli+ESP32 core 2.0.17+FabGL、下載 ROM
不改韌體/wasm 時可略過,只要有 node 與 python3(+ pip install huggingface_hub pillow playwright)。
3. 建置
scripts/build.sh wasm # 改了 wasm/xtensa.c 後
scripts/build.sh firmware # 改了 firmware/* 草稿碼後(輸出 firmware/dist/*.flash.bin)
scripts/build.sh assets # 任何 ROM/wasm/韌體變動後都要重新生成 web/assets.js
scripts/build.sh # 全部
本機預覽:python3 -m http.server 8000 --directory web → 開 http://localhost:8000
4. 部署到 Hugging Face(Token 只用環境變數,絕不寫進檔案)
export HF_TOKEN=hf_xxxxxxxx # 需 write 權限
export HF_SPACE=使用者/空間名 # 可省略,預設 HelloSun/esp32emu
scripts/deploy.sh
它會:重生 assets → 壓縮整個專案成 dist/esp32emu-source.tar.gz → 上傳網站、原始碼、文件、腳本、壓縮包(Space 路徑 downloads/esp32emu-source.tar.gz)。
注意:HF 對二進位檔的下載會 302 轉址且無 CORS,所以網站用的 ROM/wasm/韌體一律以 base64 內嵌在 assets.js,不要改成 fetch 二進位檔。
5. 修改配方(改什麼 → 改哪裡 → 怎麼驗證)
- 新增內建範例:把草稿碼放
firmware/<name>/<name>.ino→scripts/build.sh firmware(需在 build.sh 的 for 清單加名字)→ 在tools/build_assets.py的examples加{'id','title','psram','flash'}→scripts/build.sh assets。 - 新增/修正週邊暫存器:在
web/js/soc.js找對應類別(Uart/TimerGroup/RtcCntl/Efuse/Frc…)或新建類別,於建構子reg(基底位址, 裝置)註冊;裝置要有read(off)/write(off,v)(off 為頁內位元組偏移,32 位元存取)。未實作位址讀 0。中斷用soc.setIrq(來源編號, 0/1)(編號見 ESP-IDFsoc/soc.h的ETS_*_SOURCE)。延遲事件用soc.scheduleIn(奈秒, fn)。 - 改 CPU 指令行為:同時改
web/js/xtensa.js與wasm/xtensa.c(兩邊結構相同),重建 wasm,然後跑 §6 的「JS/WASM 一致性」。 - **ROM 函式加速/替身 (HLE)**:
soc.addRomHook(位址, function(a2..a7){return 回傳值}),見installRomHooks()(目前用於 APLL 的rom_i2c_*)。 - 新增板子接線(VGA 腳位):
web/js/i2s.js的VGA32_BOARD;PS/2 腳位在soc.js建構子new Ps2Device(this, clk, dat, kind);UI 示意圖在app.js的buildBoard()。 - UI 調整:只動
web/index.html style.css app.js;與 worker 的訊息格式見worker.js的self.onmessage與app.js的worker.onmessage。 - 時序/速度:
soc.run()的cpi(每指令週期,預設 1,VGA 逐行 ISR 需要 ≤1)、slice(切片指令數,預設 256)。雙核同時進中斷時lockstep會細切片模擬真實競爭。
6. 測試與除錯工具(node,皆在 tools/)
| 目的 | 指令 |
|---|---|
| 跑韌體看序列埠 | node tools/runw.mjs firmware/dist/echo.flash.bin 4(加 --js 用 JS 核心、--psram) |
| JS/WASM 一致性 | 分別 node tools/runw.mjs X 5 --psram 與 ... --js,最後印的 instr 數必須相同 |
| 擷取 VGA 畫面成 ppm | node tools/vgaw.mjs X 秒 間隔 [--psram] [--keys=3.5:KeyH,...] → /tmp/opencode/frame_N.ppm |
| 例外/崩潰定位 | node tools/exc.mjs X 秒 --syms=<nm 輸出>(用 xtensa-esp32-elf-nm -n elf 產生符號檔) |
| 找函式呼叫序列 | node tools/bp.mjs X 函式名 --syms=... |
| 取樣剖析 | node tools/prof.mjs X 秒 --syms=... --warm=8 |
| 瀏覽器整合測試 | python3 tools/browser_test.py 秒 截圖.png(playwright;需 playwright install chromium) |
| 效能微基準 | node tools/bench.mjs |
除錯技巧:用 --js 核心才能 trace/斷點(cpu.enableTrace()、cpu.bpAddr);WASM 核心看不到。 |
7. 常見陷阱(已踩過)
- 改
wasm/xtensa.c的狀態索引 enum 時,必須同步web/js/cpu_wasm.js的I表。 - 未對齊的 L32I/S32I 必須真的支援(硬體支援),不能遮罩位址。
- ULP 的
MOVR取 sreg、MOVI取立即值;ULP 觸發的 GPIO 事件時間用soc.nowNs(已處理ulpNow)。 I2S OUT_EOF_DES_ADDR要回傳完整 32 位元位址,否則 FabGL 的畫面幀同步錯亂。- 雙核同時收到同一中斷時要同時進入 ISR(
deliverNow),否則 FabGL 的 RTC 喚醒 ISR 會輸給 core0 的rtc_isr。 - WASM 記憶體固定 64MB(與 wasm 匯入記憶體一致),heap 佈局常數在
soc.js開頭H_*。 - 效能上限約 80–110 MIPS/核心(單核直譯),FreeDOS 約真機 25–100%。
8. 授權
GPL-3.0-or-later(範例韌體含 FabGL)。ROM 取自 Espressif QEMU 發行版;FreeDOS 為 GPL。