Files
esp-emu-peris/docs/ARCHITECTURE.zh-cn.md
T
darceye 9cd5863d36 MAX7219 display + logic analyzer with Saleae-style floating window (0.9.0 + 0.10.0)
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.
2026-11-02 00:05:48 +08:00

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)

  1. 新 wasm 包就位后先跑 npm run test:m0,对比 FEASIBILITY.zh-cn.md 结论;
  2. diff 上游 worker.js ↔ src/core/worker.ts(改动均标 peris:);
  3. diff 上游 app.js/index.html ↔ src/ui/*.ts 与 HTML 的上游样式段;
  4. 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 协议不动。