0.9.0 - MAX7219 Display (max7219.js v1.0.0, engine in max7219-lib.js): - 8-digit LED driver on a firmware bit-banged DIN/CLK/LOAD bus (hardware SPI stays invisible, M0). Level-driven shift engine samples DIN on the CLK rising edge, latches MSB-first 16-bit words on the LOAD rising edge; misaligned frames are dropped and counted in `dropped`, never garbled. Datasheet register model: Code B decode (independent DP), intensity PWM, scan limit, shutdown/display test, power-on defaults. 8x8 matrix and 7-segment Views auto-selected from the decode-mode register; daisy chains share the three pads (sharedBus) and latch their chain position. Lease sizing measured against the tightest real firmware (gun 0.1.0, delay-free pulses ~20 instructions apart): fine at the scheduler floor 20, arm 60. Smoke S12 boots the gun 0.1.0 release (machine-local, skips when absent) with zero drops; examples/esp32s3-gun.json wires it. 0.10.0 - Logic analyzer (logic-analyzer.js v1.0.0, logic in la-lib.js): - Multi-channel GPIO waveform capture: up to 8 pads, channel+edge or immediate trigger, 1-1000 ms guest-time window, auto re-arm, Z shown when the pad is released; rides the M4 scheduler (arm 1000, fine lease per resolution preset 20/100/300, renewed per edge); 20 000-edge buffer freezes with an overflow flag. sharedBus makes it a pure tap on other bus devices' pads (smoke S13 arms it on the P4 soft-I2C bus: SCL 1950 / SDA 352 alternating edges in a 40 ms window, frozen afterwards). - Floating analyzer window: draggable, resizable, non-modal popup (position fixed, no backdrop - the page underneath keeps every pointer event; geometry clamped + persisted). One drawWaveform painter feeds the popup, the details mini-preview and the row icon (AGENT 3.8); channels picked from a GPIO grid, occupied pins greyed. - Saleae-style plot interactions: wheel zoom anchored at the cursor (1 ns floor), left/middle-drag pan, double-click/F fit; hover measurement shades the pulse and reads level + width + period + frequency + PWM duty (rise-to-rise cycle); A/B cursors with delta-t and 1/delta-t; lane selection + </> buttons and arrow keys jump view-centre onto the previous/next edge; lane gutter shows the right-edge level; a fresh capture re-fits the view and drops cursors (capId). - Device API v1 additions (backwards compatible): ctx.pinLevel(pin) snapshot read of the driven level; ViewHost.pinInfo() for multi-pin pickers; View.destroy() teardown on row removal / list rebuild so Views owning DOM outside their row (floating windows) clean it up. - Tests: la-lib units (parsePins, levelAt, axis ticks, fit span, pixel transform, zoom/pan view model, pulseAround, pwmOf, fmtHz/levelChar), the capture state machine via fake ctx (immediate/triggered starts, direction filtering incl. Z-as-falling, window end, stop/clear/auto, buffer overflow, onEmuReady reset, capId), sigbus.pinLevel against a fake emulator; smoke S13 + S1 grows to 12 devices; examples/esp32c3-probe.json wires the analyzer on the I2C pair. - Docs (both languages): CHANGELOG 0.9.0 + 0.10.0 + device version table, ROADMAP shipped sections, DEVICE-API instrument section + new ctx/host/ View members, ARCHITECTURE GUI checklist + floating-window note, README capability table + tree, AGENT rule 3.11 + GUI-smoke bug-table rows; version synced to 0.10.0 in all three places.
9.2 KiB
9.2 KiB
English | 简体中文
esp-emu-peris 系统架构
1. 分层总览
┌───────────────────────────────── 页面(page)─────────────────────────────────┐
│ esp-emu-peris.html 上游样式原块(便于升级 diff)+ <link peris/peris.css> │
│ 三栏同屏:UART0 控制台 │ 外设面板 │ Debug(0.3.0 起不再标签切换) │
│ build/ui/* (TypeScript 编译产物) │
│ app.ts 编排器:机器状态、Run/Pause/固件/网络控件、消息路由 │
│ worker-client Worker 唯一入口(其它模块一律经由它收发消息) │
│ terminal / chart / debug-panels UART 控制台 / 性能图 / Debug 只读面板 │
│ peris-panel + config-editor 行式外设列表(图标/名称/GPIO/V·G 徽章 │
│ + 展开/删除/拖动)与详情配置编辑器 │
└───────────────▲───────────────────────────────────────────────▲────────────┘
postMessage(协议见 core/protocol.ts) │ DOM
┌───────────────┴───────────────────────────────────────────────▼────────────┐
│ 页面侧设备 View(peris/devices/*.js 的 View 类,由 peris-panel 实例化) │
├─────────────────────────────────────────────────────────────────────────────┤
│ ← peris_state / peris_command → │
├─────────────────────────────────────────────────────────────────────────────┤
│ build/core/worker.ts 上游 worker 复刻 + 6 处 peris: 钩子(唯一可碰上游逻辑) │
│ build/core/runtime.ts 设备宿主:配置校验/实例化/命令/onBatch MMIO 节拍+状态节流推送 │
│ build/core/sigbus.ts 引脚信号总线:每批 gpio_state() 差分分发 │
│ build/core/registry.ts 插件注册表(manifest 驱动 + 元数据校验) │
│ build/core/{version,types,protocol,chips,mmio}.ts 版本/契约/消息/芯片表/寄存器表 │
│ worker 侧设备 Device(peris/devices/*.js 的 Device 类;ledc-lib.js 等共享辅助 │
│ 模块在 manifest 之外) │
└───────────────────────────────────────────────────┬─────────────────────────┘
│ WasmEmulator(闭源 wasm,
│ 只此一处触碰)
esp_emu_bg.wasm v0.51.0
2. 关键决策与理由
| 决策 | 理由 |
|---|---|
| 模拟器只存在于 worker | 页面永不触碰 wasm;所有交互走 postMessage,线程模型不可能被破坏 |
| 框架层 TypeScript、设备插件原生 JS | 类型检查覆盖 bug 高发区(本次重构即修掉状态丢弃缺陷);插件保持"丢文件即用",不引入构建依赖 |
| tsc 逐文件编译、不打包 | 产物仍是标准 ESM 目录,部署模型不变(www 只增不改);上游同构,升级 diff 容易 |
| 样式拆 peris.css,上游样式原块留 HTML | 升级时直接 diff HTML 里的上游段 |
| 数据在 TS 常量(chips/mmio)而非 JSON | tsc 原生携带、类型检查、无运行时拷贝问题 |
| registry 运行时校验插件元数据 | 插件作者没有编译期检查,缺 pins()/typeId 在加载时即报错(真实 bug 类) |
3. 数据流与状态归属
- 固件 → 设备:worker 每批
run_batch()后runtime.onBatchEnd()→sigbus.sample()读gpio_state()(4 个 32 位字),按联合变化掩码(out|oe)对每个变化引脚分发一次onPinState。无变化时成本 = 1 次 wasm 调用 + 4 次 XOR。寄存器级设备 (LEDC/RMT)在同一次调用里额外获得一次onBatch()节拍,经ctx.readU32/ctx.mmio轮询 MMIO——它们的信号不会产生引脚变化 (GPIO 矩阵,M0 Q2a/Q3a)。 - 设备 → 页面:设备
markDirty()→flushStates节流 ~30fps;节流窗口内挂尾随 定时器,保证最后一次 dirty 必达(否则静止在窗口内的最终状态会被丢弃)。 - 页面 → 设备:View
sendCmd()→peris_command→runtime.command()→onCommand。 View 可以持有行以外的页面 DOM(逻辑分析仪的浮动窗口挂在document.body上、position: fixed,刻意做成非模态——无遮罩,页面其余部分的指针事件 照常);面板在行移除与列表重建时调用可选的View.destroy(),此类 DOM 不会比设备活得久。 - 输入引脚电平:唯一写者是
ctx.setPinInput(设备)与 Debug 网格的gpio_input。 页面侧gpioInputLevels[]只是 Debug 网格自己的镜像,不是权威状态;权威状态在设备里 (onEmuReady时重驱动)。 - 配置:页面
PerisPanel.config是唯一事实源;每次变更全量peris_config下发 (worker 销毁重建实例)并写 localStorage。引脚冲突由 worker 校验后回报peris_ready.errors,页面黄条展示。
4. 目录职责
src/core/ worker 侧框架(TS 源) → 编译到 build/core/
src/ui/ 页面侧模块(TS 源) → 编译到 build/ui/
build/ tsc 产物(未入 git;HTML/worker/测试都引用这里)
peris/devices/ 设备插件(JS 源,不编译;manifest.json 注册;各自版本号;
ledc-lib.js 等共享辅助模块同目录但不进 manifest)
peris/vendor/ xterm 本地化
peris/peris.css peris 专属样式
tests/unit/ 单元测试(node:test,零依赖)
tests/m1_smoke.mjs 集成冒烟(真实 wasm+固件+runtime+设备)
tests/m0_probe.mjs 模拟器能力探针(升级上游时先跑它)
tests/firmware/ IDF 测试固件
5. 构建与测试
npm install # 一次性(typescript 走 npmmirror 源)
npm run build # tsc → peris/core + peris/ui(改 src 后必须)
npm run check # 仅类型检查
npm test # 单测 + 集成冒烟
npm run serve # python serve.py(合并服务仓库与 www/pkg)
npm run sync # 发布进官方 www(先 build)
测试分层:单元(纯逻辑:位运算/电平语义/校验/行式徽章推导/基于假 MMIO 的
LEDC 寄存器解码,以及 M4 协议层——调度器租约、合成开漏主机下的 I2C 从机
引擎、寄存器模型、NEC 解码——快、定位准)→ 集成(真实 wasm+固件全链路;
S7/S8 以 ###P2 标记驱动,S9 按 ###P3,S10/S11(soft-I2C 从机+NEC 双路
捕获)按 ###P4;所有窗口按标记定界,因为警戒批会把整个睡眠相位快进过去)
→ GUI(手动清单:页面加载、三栏同屏可见、加设备
后行式渲染、行内改名/改 GPIO/翻 V·G、行内图标交互(点灯换色/按压/开关)、展开详情
与配置编辑、拖动排序、载固件、Run 后 LED 翻转、PWM/RGB LED 自动绑定固件
LEDC 通道且 P2 阶段呼吸平滑渐变、P4 阶段 I2C 事务日志滚动且 EEPROM 十六进
制 dump 可从 View 预写、两行 ir-nec 上红外码出现、逻辑分析仪:添加后在浮
动窗口的 GPIO 网格勾选通道(被占用引脚置灰)、Arm 后 P4 软 I2C 总线运行期
间波形渲染、窗口拖动/调大调小且下层页面照常交互、Saleae 风格绘图检查
(滚轮以光标为锚点缩放、拖拽平移、悬停测量显示脉宽/周期/频率/占空比、
两击放置 A/B 游标并读 Δt 与 1/Δt、双击复位适配)、行移除时窗口关闭、
配置导入导出往返;浏览器
自动化待网络环境允许时引入 Playwright,
PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright)。
6. 升级流程(摘要,详见 UPGRADE.zh-cn.md)
- 新 wasm 包就位后先跑
npm run test:m0,对比 FEASIBILITY.zh-cn.md 结论; - diff 上游 worker.js ↔
src/core/worker.ts(改动均标peris:); - diff 上游 app.js/index.html ↔
src/ui/*.ts与 HTML 的上游样式段; npm run build && npm test,GUI 冒测。
7. 扩展点
- 新外设:
peris/devices/放模块 + manifest 一行(见 docs/DEVICE-API.zh-cn.md);Worker/页面 自动加载,元数据即时校验。 - 新芯片:
src/core/mmio/<chip>.ts加地址表 + 注册到mmio.ts;chips.ts补引脚数。 - 新页面面板:ui/ 加模块,在 app.ts 的消息路由注册——worker 协议不动。