stm32f4-emu 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,14 @@
6
6
  [![CI](https://github.com/danish9661/stm32F4-emulator/actions/workflows/ci.yml/badge.svg)](https://github.com/danish9661/stm32F4-emulator/actions/workflows/ci.yml)
7
7
  [![Pages](https://github.com/danish9661/stm32F4-emulator/actions/workflows/pages.yml/badge.svg)](https://github.com/danish9661/stm32F4-emulator/actions/workflows/pages.yml)
8
8
 
9
+ > **Status: emulation complete.** All 41 peripheral modules are Detailed
10
+ > (0 Partial), all five board maps verify green (37/37/72/73/59 presets),
11
+ > and the Ethernet descriptor layer (EDFE, TCH/TER + RCH/RER walks, RDES4,
12
+ > backoff probe) plus the Wireshark-validated pcap capture (`a1b2c3d4`,
13
+ > tcpdump-verified DHCP→TCP→HTTP) are in. What remains is product work,
14
+ > not emulation: npm publish, https gateway endpoint, VS Code packaging
15
+ > (see [docs/progress-and-future.md](docs/progress-and-future.md)).
16
+
9
17
  An STM32F407 microcontroller emulator that runs real Cortex-M4 firmware. It
10
18
  combines a **Rust CPU core** (a WASM-native Thumb-2 interpreter with exact
11
19
  Cortex-M exception entry/return, including the VFPv4-SP FPU) with a
@@ -15,7 +23,12 @@ browser tab**, with no SDL, no native deps, no hardware.
15
23
 
16
24
  It ships three real networking firmwares (`eth_http`, `eth_dhcp`, `eth_test`)
17
25
  that do DHCP + TCP + HTTP against a simulated (or a real gVisor-backed)
18
- network, a browser demo, and a publishable npm package.
26
+ network, a 66-marker Ethernet feature matrix (`eth_feat_test`: PHY/MDIO,
27
+ checksum offload, hash/perfect/SA/DA filtering, VLAN, PTP, WOL, wire
28
+ pacing, deferral/collisions, pause frames, MMC, RBUS — see
29
+ [NETWORKING.md](NETWORKING.md)) plus real LwIP 2.2.1 (`lwip_demo`) and
30
+ MII/RMII pin mirrors (`eth_pins_test`), a browser demo, and a publishable
31
+ npm package.
19
32
 
20
33
  ## Live demo
21
34
 
@@ -24,7 +37,7 @@ The browser demo deploys to GitHub Pages:
24
37
  **https://danish9661.github.io/stm32F4-emulator/**
25
38
 
26
39
  A single console page that starts **idle** — nothing runs until you pick a
27
- firmware: a preset dropdown with 31 bundled binaries (network demos, a
40
+ firmware: a preset dropdown with 223 bundled binaries (network demos, a
28
41
  bare-metal LED blinker, peripheral/crypto/UART/SPI test binaries), custom
29
42
  firmware upload (`.bin`, Intel `.hex`, `.elf` — with loadable RAM segments
30
43
  and symbols — plus `.map` for a symbol table), Run/Stop/Reset, a **gateway
@@ -35,7 +48,7 @@ characters excluded per HTML spec, see AGENTS.md §11), GPIO pin readout
35
48
  for banks A–E, and key peripheral registers. Interrupt-driven firmware
36
49
  (`rx_interrupt_test`, `rx_crypto_test`) gets inline guest-IRQ delivery;
37
50
  polling firmware (the ETH demos) never uses it. For automation, a
38
- preset can auto-boot via the URL: `?fw=eth_http`, `?fw=blinky`, `?fw=crypto_test`, …
51
+ preset can auto-boot via the URL: `console.html?fw=eth_http`, `console.html?fw=blinky`, `console.html?fw=crypto_test`, …
39
52
 
40
53
  ## DOOM (in the browser)
41
54
 
@@ -60,7 +73,7 @@ reloads.
60
73
  - Controls: move W/S/A/D + arrows · strafe Shift · fire Ctrl · use
61
74
  Space · menu Enter/Esc · save F2/F6 · load F3/F9 · F1/F10/F11/F12.
62
75
  - Boot → menu → gameplay verified end-to-end by
63
- `node site/test_doom.mjs` (boot markers, menu navigation to E1M1,
76
+ `node site/test_doom_wasm.mjs` (boot markers, menu navigation to E1M1,
64
77
  palette + framebuffer, W-move + turn, audio, **save → `SAVE ok slot=0`
65
78
  → load handshake**).
66
79
  - `site/doom1.wad` is the 4.2 MB shareware WAD; the firmware never reads
@@ -78,7 +91,7 @@ npm test # == node site/test_flow.mjs
78
91
 
79
92
  # websocket bridge: headless Node serves the emulator, browser is a thin UI
80
93
  npm run bridge -- blinky/blinky.bin --port 8234
81
- # then open http://127.0.0.1:8123?bridge=ws://127.0.0.1:8234
94
+ # then open http://127.0.0.1:8123/console.html?bridge=ws://127.0.0.1:8234
82
95
 
83
96
  # gateway-backed run: firmware talks to a REAL network stack (gVisor)
84
97
  cd stm32-periph-wasm/pkg
@@ -145,14 +158,14 @@ is a thin UI. Zero impact on the existing local WASM path:
145
158
  node site/ws-bridge.mjs eth_http/eth_http.bin --port 8234
146
159
 
147
160
  # 2. Open the browser console with the bridge URL param
148
- open "http://127.0.0.1:8123/?bridge=ws://127.0.0.1:8234"
161
+ open "http://127.0.0.1:8123/console.html?bridge=ws://127.0.0.1:8234"
149
162
  ```
150
163
 
151
164
  The `RemoteEmu` adapter (`site/remote-emu.js`) is a drop-in replacement
152
165
  for the local `emu` object — same `step()`/`drainUart()`/`read32()` API,
153
166
  all proxied over binary WebSocket. Device stubs (OLED/TFT/etc.) run in
154
167
  Node and are not visible to browser JS. Without `?fw=`, the page boots
155
- whatever firmware the bridge was started with; with `?fw=blinky&bridge=ws://…`,
168
+ whatever firmware the bridge was started with; with `console.html?fw=blinky&bridge=ws://…`,
156
169
  the browser sends the firmware image over the bridge.
157
170
 
158
171
  See AGENTS.md §20 for the full binary protocol reference.
@@ -164,8 +177,11 @@ See AGENTS.md §20 for the full binary protocol reference.
164
177
  | `eth_http/` | DHCP + TCP client + HTTP GET + prints the response | `TCP connected`, `=== HTTP <len>b ===` |
165
178
  | `eth_dhcp/` | Loops DHCP Discover/Offer/Request/Ack | `DHCP SUCCESS` |
166
179
  | `eth_test/` | Raw ETH TX/RX self-test | `ETH Test: done` |
180
+ | `eth_feat_test/` | 66-marker Ethernet feature matrix (netsim, F407 + F429) | `FEAT Test: done` (all 66 markers, `FAIL`/`TIMEOUT` anti-markers) |
181
+ | `lwip_demo/` | Real LwIP 2.2.1 (DHCP→DNS→TCP echo→TCP server→UDP echo) | `LWIP DEMO DONE` |
182
+ | `eth_pins_test/` | MII/RMII pin-level mirrors (TX_EN/CRS_DV/RXD/COL/MDIO/MDC) | `PINS ALL PASS` |
167
183
  | `blinky/` | **No ethernet** — LED blinker on GPIOA PA5 + UART tick counter | `tick N LED=ON/OFF` |
168
- | `doom/` | **DOOM 1 shareware** (doomgeneric F407 port, browser page `site/doom.html`) | `node site/test_doom.mjs` (boot + menu + gameplay + save/load) |
184
+ | `doom/` | **DOOM 1 shareware** (doomgeneric F407 port, browser page `site/doom.html`) | `node site/test_doom_wasm.mjs` (boot + menu + gameplay + save/load) |
169
185
 
170
186
  Plus 17 more test binaries (`crypto_test`, `hal_test`, `timer_test`,
171
187
  `periph_test`, `echo_test`, `blink_serial`, `rx_interrupt_test`,
@@ -199,9 +215,12 @@ firmware .bin ──► Rust WASM CPU (Thumb-2 + NVIC/exceptions)
199
215
  exception stacking — no native deps, no JIT, no hooks.
200
216
  - **Peripherals**: a `wasm-bindgen` crate (`stm32-periph-wasm/`); registers
201
217
  and bit fields come from the vendor SVD (`monox/stm32f407.svd`).
202
- - **Ethernet**: TX is captured from the DMA descriptors; RX frames are
203
- injected into the RX ring and the firmware's `eth_irq_flag` (SRAM) drives
204
- polling — no interrupts required. Optionally, a Go gateway
218
+ - **Ethernet**: TX is captured from the DMA descriptors (OWN/FS/LS poll
219
+ demand); RX frames are delivered head-only with FS+LS status, IPHCE/PCE
220
+ checksum status, and PTP snapshots for event messages. The full MAC
221
+ register map is cross-checked against CMSIS `stm32f407xx.h` (DMASR/
222
+ DMAIER/MACFFR/MACPMTCTL positions — see [NETWORKING.md](NETWORKING.md)
223
+ §6 for the audit). Optionally, a Go gateway
205
224
  (`openhw-local-gateway/`) with a gVisor network stack makes the firmware
206
225
  talk to a real network: `node cli.mjs <fw.bin> <inst> --gateway`.
207
226
  - **Browser build** (`site/`): same modules as ESM; `site/emulator.js` is an
@@ -259,7 +278,10 @@ rebuild — delete it so the vendor assets stay tracked/committed.
259
278
 
260
279
  ## Testing
261
280
 
262
- - `npm test` — flow test (`site/test_flow.mjs`) + blinky test
281
+ - `npm test` — flow test (`site/test_flow.mjs`) + Ethernet mock harnesses
282
+ (`site/test_eth_mock_model.mjs`: 18 checks, fake bindings + real driver;
283
+ `site/test_eth_mock_consumer.mjs`: 44 checks, fake firmware + real model)
284
+ + blinky test
263
285
  (`site/test_blinky.mjs`) + interrupt-UART test (`site/test_rx_interrupt.mjs`)
264
286
  + component-API tests (`site/test_component_{led,button,pwm,i2cregfile}.mjs`,
265
287
  each against real firmware — LED/blinky, Button/exti_test, Pwm/buzzer_test,
@@ -269,7 +291,7 @@ rebuild — delete it so the vendor assets stay tracked/committed.
269
291
  process (see docs/components.md). The same suite runs in CI on
270
292
  `ubuntu-latest`/`windows-latest`/`macos-latest`
271
293
  ([.github/workflows/ci.yml](.github/workflows/ci.yml)) on every push.
272
- - `node site/test_doom.mjs` — DOOM boot → menu → E1M1 gameplay + save/load
294
+ - `node site/test_doom_wasm.mjs` — DOOM boot → menu → E1M1 gameplay + save/load
273
295
  (see the DOOM section above).
274
296
  - `scripts/verify_ethernet.sh [max_inst]` — runs all three firmwares through
275
297
  the gateway, asserts the success markers and 0 `TCP fail`.
@@ -281,7 +303,7 @@ rebuild — delete it so the vendor assets stay tracked/committed.
281
303
  - [site/about.html](site/about.html) — in-repo About page: what it is, architecture, featured firmwares, and how to use it (CLI / browser / Node API / MCP).
282
304
  - [docs/architecture.md](docs/architecture.md) — how the emulator is put
283
305
  together (CPU, peripheral model, drivers, ETH flow, interrupts).
284
- - [docs/peripherals.md](docs/peripherals.md) — all 33 peripherals and the
306
+ - [docs/peripherals.md](docs/peripherals.md) — all 41 peripherals and the
285
307
  level each is implemented to, plus external devices and known gaps.
286
308
  - [docs/usage.md](docs/usage.md) — CLI, browser, and npm-library usage,
287
309
  config files, env vars, building.
package/index.d.ts CHANGED
@@ -46,3 +46,10 @@ export class Potentiometer {
46
46
  export class I2cRegisterDevice {
47
47
  constructor(emu: any, peripheral: string, opts?: any);
48
48
  }
49
+
50
+ // Board LED map + host reset/boot control (see site/boards.js,
51
+ // site/emulator.js, site/stm32f4.js).
52
+ export interface BoardLed { bank: number; pin: number; label: string; }
53
+ export declare const BOARD_LED: Record<string, BoardLed>;
54
+ export declare const BOARD_LED_ALIASES: Record<string, BoardLed>;
55
+ export declare function boardLed(fwName: string, boardKey: string): BoardLed;
package/index.mjs CHANGED
@@ -40,3 +40,4 @@ export async function createSTM32F407(opts = {}) {
40
40
  }
41
41
 
42
42
  export { createEmulator, createNetSim, FIRMWARES, bindings, svdXml, LED, Button, Pwm, I2cRegisterDevice, Potentiometer, STM32F4, GPIOPin, USART, DMAStream };
43
+ export { boardLed, BOARD_LED, BOARD_LED_ALIASES } from './site/boards.js';
package/mcp/server.mjs CHANGED
@@ -252,4 +252,27 @@ server.registerTool('reset', {
252
252
  return text(`closed session (${was})`);
253
253
  });
254
254
 
255
+ server.registerTool('reset_cpu', {
256
+ description: 'Host reset button: CPU back to the vector-table SP/PC, peripherals keep state (like a real NRST pulse without the hold). Prefer over load_firmware when re-running the same image.',
257
+ inputSchema: {},
258
+ }, async () => {
259
+ const { emu } = requireSession();
260
+ if (typeof emu.resetCpu === 'function') emu.resetCpu();
261
+ else emu.reset();
262
+ return text('cpu reset to vector table');
263
+ });
264
+
265
+ server.registerTool('set_nrst', {
266
+ description: 'Hold (assert=true) or release the NRST line. While held, step() only advances the model clock — the CPU executes nothing, like real hardware in reset. Query with level omitted.',
267
+ inputSchema: { level: z.boolean().optional().describe('true = assert (hold in reset), false = release; omit to query') },
268
+ }, async ({ level }) => {
269
+ const { emu } = requireSession();
270
+ if (level === undefined) {
271
+ const s = typeof emu.isNrstAsserted === 'function' ? emu.isNrstAsserted() : false;
272
+ return text({ asserted: !!s });
273
+ }
274
+ const s = typeof emu.setNrst === 'function' ? emu.setNrst(level) : false;
275
+ return text({ asserted: !!s });
276
+ });
277
+
255
278
  await server.connect(new StdioServerTransport());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stm32f4-emu",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "STM32F407 emulator: Rust CPU + peripheral model (WASM). Runs real Cortex-M4 firmware in Node or the browser.",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -31,6 +31,7 @@
31
31
  "site/emulator.js",
32
32
  "site/emulator.d.ts",
33
33
  "site/stm32f4.js",
34
+ "site/boards.js",
34
35
  "site/components.js",
35
36
  "site/netsim.js",
36
37
  "site/firmware.js",
@@ -39,6 +40,7 @@
39
40
  "site/ws-bridge.mjs",
40
41
  "site/app.js",
41
42
  "site/index.html",
43
+ "site/console.html",
42
44
  "site/vendor/",
43
45
  "mcp/server.mjs",
44
46
  "tools/make_firmware.mjs"
@@ -66,8 +68,8 @@
66
68
  "node": ">=20"
67
69
  },
68
70
  "scripts": {
69
- "test": "node site/test_stm32f4_api.mjs && node site/test_stm32f4_periph.mjs && node site/test_ws_bridge.mjs && node site/test_flow.mjs && node site/test_blinky.mjs && node site/test_rx_interrupt.mjs && node site/test_component_led.mjs && node site/test_component_button.mjs && node site/test_component_pwm.mjs && node site/test_component_i2cregfile.mjs && node site/test_component_adc.mjs && node site/test_multi_instance.mjs && node site/test_fsmc_dcmi.mjs && node site/test_fsmc.mjs && node site/test_dcmi.mjs && node site/probe_freertos.mjs && node site/test_candemo.mjs && node site/test_lowpower.mjs && node site/test_edge_cases.mjs && node site/test_can_inject.mjs && node site/test_watchdog.mjs && node site/test_wwdg.mjs && node site/test_wwdg_window.mjs && node site/test_tim_capture.mjs && node site/test_qspi.mjs && node site/test_fpu.mjs && node site/test_fpu_irq.mjs && node site/test_mpu.mjs && node site/test_usb.mjs && node site/test_qspi_cdp.mjs && node site/test_browser.mjs",
70
- "test:unit": "node site/test_stm32f4_api.mjs && node site/test_stm32f4_periph.mjs && node site/test_ws_bridge.mjs && node site/test_flow.mjs && node site/test_blinky.mjs && node site/test_rx_interrupt.mjs && node site/test_component_led.mjs && node site/test_component_button.mjs && node site/test_component_pwm.mjs && node site/test_component_i2cregfile.mjs && node site/test_component_adc.mjs && node site/test_multi_instance.mjs && node site/test_fsmc_dcmi.mjs && node site/test_fsmc.mjs && node site/test_dcmi.mjs && node site/probe_freertos.mjs && node site/test_candemo.mjs && node site/test_lowpower.mjs && node site/test_edge_cases.mjs && node site/test_can_inject.mjs && node site/test_watchdog.mjs && node site/test_wwdg.mjs && node site/test_wwdg_window.mjs && node site/test_tim_capture.mjs && node site/test_qspi.mjs",
71
+ "test": "node site/test_stm32f4_api.mjs && node site/test_stm32f4_periph.mjs && node site/test_ws_bridge.mjs && node site/test_flow.mjs && node site/test_eth_mock_model.mjs && node site/test_eth_mock_consumer.mjs && node site/test_periph_mock_consumer.mjs && node site/test_blinky.mjs && node site/test_blinky_f401.mjs && node site/test_blinky_f411.mjs && node site/test_blinky_f407g.mjs && node site/test_blinky_nucleo_f401.mjs && node site/test_blinky_nucleo_f411.mjs && node site/test_blinky_f429.mjs && node site/test_blinky_f407ve.mjs && node site/test_rx_interrupt.mjs && node site/test_component_led.mjs && node site/test_component_button.mjs && node site/test_component_pwm.mjs && node site/test_component_i2cregfile.mjs && node site/test_component_adc.mjs && node site/test_multi_instance.mjs && node site/test_fsmc_dcmi.mjs && node site/test_fsmc.mjs && node site/test_dcmi.mjs && node site/probe_freertos.mjs && node site/test_candemo.mjs && node site/test_lowpower.mjs && node site/test_edge_cases.mjs && node site/test_can_inject.mjs && node site/test_watchdog.mjs && node site/test_wwdg.mjs && node site/test_wwdg_window.mjs && node site/test_tim_capture.mjs && node site/test_qspi.mjs && node site/test_fpu.mjs && node site/test_fpu_irq.mjs && node site/test_mpu.mjs && node site/test_usb.mjs && node site/test_arduino_boards.mjs && node site/test_exti.mjs && node site/test_flash.mjs && node site/test_spi_flash.mjs && node site/test_board_matrix.mjs && node site/test_qspi_cdp.mjs && node site/test_browser.mjs",
72
+ "test:unit": "node site/test_stm32f4_api.mjs && node site/test_stm32f4_periph.mjs && node site/test_ws_bridge.mjs && node site/test_flow.mjs && node site/test_blinky.mjs && node site/test_blinky_f401.mjs && node site/test_blinky_f411.mjs && node site/test_blinky_f407g.mjs && node site/test_blinky_nucleo_f401.mjs && node site/test_blinky_nucleo_f411.mjs && node site/test_blinky_f429.mjs && node site/test_blinky_f407ve.mjs && node site/test_rx_interrupt.mjs && node site/test_component_led.mjs && node site/test_component_button.mjs && node site/test_component_pwm.mjs && node site/test_component_i2cregfile.mjs && node site/test_component_adc.mjs && node site/test_multi_instance.mjs && node site/test_fsmc_dcmi.mjs && node site/test_fsmc.mjs && node site/test_dcmi.mjs && node site/probe_freertos.mjs && node site/test_candemo.mjs && node site/test_lowpower.mjs && node site/test_edge_cases.mjs && node site/test_can_inject.mjs && node site/test_watchdog.mjs && node site/test_wwdg.mjs && node site/test_wwdg_window.mjs && node site/test_tim_capture.mjs && node site/test_qspi.mjs",
71
73
  "test:bridge": "node site/test_ws_bridge.mjs",
72
74
  "test:bridge:browser": "node site/test_bridge_cdp.mjs",
73
75
  "test:flow": "node site/test_flow.mjs",
@@ -81,7 +83,9 @@
81
83
  "test:wwdgwin": "node site/test_wwdg_window.mjs",
82
84
  "test:timcap": "node site/test_tim_capture.mjs",
83
85
  "test:lowpower": "node site/test_lowpower.mjs",
86
+ "test:standby": "node site/test_standby.mjs",
84
87
  "test:qspi": "node site/test_qspi.mjs",
88
+ "test:eth:mock": "node site/test_eth_mock_model.mjs && node site/test_eth_mock_consumer.mjs && node site/test_periph_mock_consumer.mjs",
85
89
  "test:fpu": "node site/test_fpu.mjs",
86
90
  "test:fpuirq": "node site/test_fpu_irq.mjs",
87
91
  "test:mpu": "node site/test_mpu.mjs",