@typecad/framework-zephyr 1.0.0-alpha.15 → 1.0.0-alpha.18

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.
Files changed (132) hide show
  1. package/LICENSE +202 -21
  2. package/README.md +49 -87
  3. package/dist/as-built.d.ts +2 -2
  4. package/dist/as-built.js +2 -2
  5. package/dist/boardgen.d.ts +1 -9
  6. package/dist/boardgen.js +228 -45
  7. package/dist/chips/types.d.ts +1 -1
  8. package/dist/debug-codegen.js +1 -1
  9. package/dist/display/touch-adapter.js +1 -1
  10. package/dist/display/ui-adapter.js +549 -549
  11. package/dist/doctor.js +4 -4
  12. package/dist/dt-config/custom-board.js +2 -2
  13. package/dist/dt-config/kconfig.js +18 -12
  14. package/dist/dt-config/overlay.d.ts +2 -2
  15. package/dist/dt-config/overlay.js +2 -2
  16. package/dist/framework.manifest.d.ts +3 -3
  17. package/dist/framework.manifest.js +10 -7
  18. package/dist/index.js +5 -5
  19. package/dist/licenses.d.ts +2 -2
  20. package/dist/licenses.js +8 -8
  21. package/dist/lowering/fs.js +1 -1
  22. package/dist/lowering/gpio.js +0 -32
  23. package/dist/lowering/http.js +264 -32
  24. package/dist/lowering/i2c.js +0 -8
  25. package/dist/lowering/interrupts.js +6 -0
  26. package/dist/lowering/mqtt.js +110 -8
  27. package/dist/lowering/usb.js +11 -11
  28. package/dist/lowering/wdt.js +2 -29
  29. package/dist/sdk/board-catalog-sync.d.ts +1 -3
  30. package/dist/sdk/board-catalog-sync.js +4 -10
  31. package/dist/strategy.d.ts +22 -44
  32. package/dist/strategy.js +152 -155
  33. package/dist/tmp-probe.d.ts +2 -0
  34. package/dist/tmp-probe.js +9 -0
  35. package/dist/toolchain/debug-config.d.ts +50 -90
  36. package/dist/toolchain/debug-config.js +241 -502
  37. package/dist/toolchain/env-check.d.ts +1 -3
  38. package/dist/toolchain/env-check.js +2 -7
  39. package/dist/toolchain/index.d.ts +65 -2
  40. package/dist/toolchain/index.js +432 -55
  41. package/dist/toolchain/runners.d.ts +16 -0
  42. package/dist/toolchain/runners.js +75 -0
  43. package/dist/toolchain/scaffold.d.ts +1 -1
  44. package/dist/toolchain/scaffold.js +12 -12
  45. package/dist/toolchain/west-discover.d.ts +6 -0
  46. package/dist/toolchain/west-discover.js +36 -13
  47. package/dist/toolchain/west-spawn.js +8 -2
  48. package/installer/README.md +328 -328
  49. package/installer/install.sh +2 -2
  50. package/installer/templates/project/.typecad/activate-zephyr.ps1 +1 -1
  51. package/installer/templates/project/.typecad/activate-zephyr.sh +1 -1
  52. package/installer/templates/project/.vscode/settings.json +1 -1
  53. package/installer/templates/project/README.md +2 -2
  54. package/package.json +5 -5
  55. package/src/as-built.ts +206 -206
  56. package/src/boardgen.ts +214 -48
  57. package/src/chips/types.ts +567 -567
  58. package/src/display/touch-adapter.ts +204 -204
  59. package/src/display/ui-adapter.ts +781 -781
  60. package/src/doctor.ts +4 -4
  61. package/src/dt-config/custom-board.ts +2 -2
  62. package/src/dt-config/kconfig.ts +18 -12
  63. package/src/dt-config/overlay.ts +1058 -1058
  64. package/src/framework.manifest.ts +538 -535
  65. package/src/index.ts +5 -5
  66. package/src/licenses.ts +425 -425
  67. package/src/lowering/fs.ts +135 -135
  68. package/src/lowering/gpio.ts +0 -33
  69. package/src/lowering/http.ts +264 -32
  70. package/src/lowering/i2c.ts +0 -9
  71. package/src/lowering/interrupts.ts +6 -0
  72. package/src/lowering/mqtt.ts +109 -8
  73. package/src/lowering/usb.ts +221 -221
  74. package/src/lowering/wdt.ts +2 -25
  75. package/src/sdk/board-catalog-sync.ts +4 -25
  76. package/src/strategy.ts +2265 -2309
  77. package/src/toolchain/debug-config.ts +264 -514
  78. package/src/toolchain/env-check.ts +279 -285
  79. package/src/toolchain/index.ts +1703 -1286
  80. package/src/toolchain/runners.ts +80 -0
  81. package/src/toolchain/scaffold.ts +296 -296
  82. package/src/toolchain/west-discover.ts +35 -13
  83. package/src/toolchain/west-spawn.ts +174 -168
  84. package/dist/async/timer-polyfill.d.ts +0 -10
  85. package/dist/async/timer-polyfill.js +0 -95
  86. package/dist/chips/board-overrides.d.ts +0 -7
  87. package/dist/chips/board-overrides.js +0 -11
  88. package/dist/chips/esp32.d.ts +0 -2
  89. package/dist/chips/esp32.js +0 -71
  90. package/dist/chips/esp32s3.d.ts +0 -2
  91. package/dist/chips/esp32s3.js +0 -103
  92. package/dist/chips/soc/.d.ts +0 -2
  93. package/dist/chips/soc/.js +0 -129
  94. package/dist/chips/soc/esp32.d.ts +0 -2
  95. package/dist/chips/soc/esp32.js +0 -120
  96. package/dist/chips/soc/esp32c3.d.ts +0 -2
  97. package/dist/chips/soc/esp32c3.js +0 -90
  98. package/dist/chips/soc/esp32c6.d.ts +0 -2
  99. package/dist/chips/soc/esp32c6.js +0 -109
  100. package/dist/chips/soc/esp32s3.d.ts +0 -2
  101. package/dist/chips/soc/esp32s3.js +0 -189
  102. package/dist/chips/soc/index.d.ts +0 -2
  103. package/dist/chips/soc/index.js +0 -23
  104. package/dist/chips/soc/nrf52840.d.ts +0 -2
  105. package/dist/chips/soc/nrf52840.js +0 -130
  106. package/dist/chips/soc/rp2040.d.ts +0 -2
  107. package/dist/chips/soc/rp2040.js +0 -141
  108. package/dist/chips/soc/rp2350a.d.ts +0 -2
  109. package/dist/chips/soc/rp2350a.js +0 -145
  110. package/dist/chips/soc/samd21g18a.d.ts +0 -2
  111. package/dist/chips/soc/samd21g18a.js +0 -143
  112. package/dist/chips/soc/stm32f411xe.d.ts +0 -2
  113. package/dist/chips/soc/stm32f411xe.js +0 -251
  114. package/dist/chips/xiao-ble.d.ts +0 -2
  115. package/dist/chips/xiao-ble.js +0 -100
  116. package/dist/lowering/power.d.ts +0 -9
  117. package/dist/lowering/power.js +0 -60
  118. package/dist/lowering/pulse.d.ts +0 -7
  119. package/dist/lowering/pulse.js +0 -51
  120. package/dist/lowering/tone.d.ts +0 -10
  121. package/dist/lowering/tone.js +0 -63
  122. package/dist/lowering/worker-backing.d.ts +0 -14
  123. package/dist/lowering/worker-backing.js +0 -79
  124. package/dist/lowering/worker.d.ts +0 -6
  125. package/dist/lowering/worker.js +0 -14
  126. package/dist/sdk/board-data.generated.d.ts +0 -2
  127. package/dist/sdk/board-data.generated.js +0 -4
  128. package/dist/sdk/catalog-walker.d.ts +0 -90
  129. package/dist/sdk/catalog-walker.js +0 -682
  130. package/dist/sdk/dts-reader.d.ts +0 -83
  131. package/dist/sdk/dts-reader.js +0 -596
  132. package/src/debug-codegen.ts +0 -207
@@ -1,535 +1,538 @@
1
- // ---------------------------------------------------------------------------
2
- // Zephyr framework manifest
3
- //
4
- // Coverage reflects actual resolveHALOperation / lowerHalOp + resolveDisplayOp
5
- // behavior. GPIO, PWM, ADC, I2C, SPI, UART, interrupts, pulse,
6
- // shift, WDT, BLE, timing, WiFi, HTTP/S, MQTT, board constants, and random
7
- // are lowered; display is lowered via the generic <zephyr/drivers/display.h>
8
- // GFX runtime. WiFi/HTTP/MQTT require an ESP32 target (nRF52840 has no radio).
9
- // The manifest validator probes every declared op against the resolver: a
10
- // 'supported' op must lower, an 'unsupported' op must return undefined.
11
- // ---------------------------------------------------------------------------
12
-
13
- import { defineFrameworkManifest, HAL_OPERATION_KINDS } from '@typecad/cuttlefish/api/shared';
14
-
15
- /**
16
- * Build an `{ opKind: 'unsupported' }` record for every op kind under a given
17
- * category prefix. Keeps unsupported categories honest without hand-listing
18
- * every op (the validator requires every op kind be declared, even as
19
- * unsupported). Mirrors the discipline esp32 applies by hand-listing.
20
- */
21
- function unsupportedOps(prefix: string): Record<string, 'unsupported'> {
22
- const out: Record<string, 'unsupported'> = {};
23
- for (const kind of HAL_OPERATION_KINDS) {
24
- if (kind.startsWith(prefix)) out[kind] = 'unsupported';
25
- }
26
- return out;
27
- }
28
-
29
- export default defineFrameworkManifest({
30
- schemaVersion: 1,
31
- frameworkId: 'zephyr',
32
- packageName: '@typecad/framework-zephyr',
33
- canonical: false,
34
- displayName: 'Zephyr RTOS',
35
- description:
36
- 'Native Zephyr framework targeting the Zephyr RTOS via west/CMake. ' +
37
- 'GPIO is lowered through devicetree specs (gpio_pin_*_dt).',
38
- implementationMode: 'from-scratch',
39
-
40
- entrypoint: {
41
- // Zephyr is a main()-based RTOS: top-level statements lower straight into
42
- // main() (no setup()/loop() pair, no bridge shim). Event-driven programs
43
- // get their scheduler loop appended inside main() by the driver machinery.
44
- entrypointFunctionName: 'main',
45
- requiresLoopFunction: false,
46
- sourceExtension: 'cpp',
47
- overrideBaseName: 'main',
48
- outputSubdirectory: 'src',
49
- generateHeaderFile: true,
50
- },
51
-
52
- profile: {
53
- // Informational list of supported board targets. The manifest validator
54
- // never iterates this. Every board in the catalog resolves the same way
55
- // (resolveChipFromBoard over the generated manifest) — there is no
56
- // curated target list.
57
- targets: [],
58
- forcedIncludes: ['<zephyr/kernel.h>', '<zephyr/drivers/gpio.h>', '<cstdint>'],
59
- symbolAliases: {},
60
- },
61
-
62
- hal: {
63
- // ── Supported: GPIO (devicetree-spec bridge) ──────────────────────────
64
- gpio: {
65
- supported: true,
66
- partialCoverage: false,
67
- ops: {
68
- 'gpio.write': 'supported',
69
- 'gpio.read': 'supported',
70
- 'gpio.toggle': 'supported',
71
- // Thin GPIO (hal/gpio-pin.ts): construction flags as tokens, applied
72
- // once per pin ahead of first use.
73
- 'gpio.configure': 'supported',
74
- 'gpio.read_cfg': 'supported', // fused guarded configure + read
75
- 'gpio.shift_out': 'supported',
76
- 'gpio.shift_in': 'supported',
77
- },
78
- },
79
-
80
- // ── Supported: Timing (delay/millis/delay_us/micros; timers via polyfill) ─
81
- timing: {
82
- supported: true,
83
- partialCoverage: false,
84
- ops: {
85
- // Timer ops are POLYFILL_BACKED_OPS → timer_methods. The validator skips
86
- // the resolver probe (these legitimately return polyfill-helper calls,
87
- // not direct lowering) and requires the polyfill be in polyfills.emitted.
88
- // Time.* — the TS-flavored surface (hal/time.ts), preferred over the
89
- // Arduino-named forms above for new code.
90
- 'timing.sleep': 'supported', // → k_msleep(ms) — Time.sleep
91
- 'timing.now': 'supported', // → k_uptime_get() — Time.now
92
- 'timing.now_us': 'supported', // → k_cyc_to_us_floor64 — Time.nowUs
93
- 'timing.busy_wait_us': 'supported', // → k_busy_wait(us) — Time.busyWaitUs
94
- },
95
- },
96
-
97
- // ── Supported: PWM (pwm_dt_spec via pwm-led0) ──────────────────────────
98
- pwm: {
99
- supported: true,
100
- partialCoverage: false,
101
- ops: {
102
- // Thin PWM (hal/pwm-pin.ts): ns-true verbs; construction period
103
- // applies once, setDuty is 0.0–1.0 sugar over one set_pulse call.
104
- 'pwm.set_pulse': 'supported',
105
- 'pwm.set_duty': 'supported',
106
- 'pwm.set_period': 'supported',
107
- },
108
- },
109
-
110
- // ── Supported: ADC (SAADC via adc_read + channel setup) ────────────────
111
- adc: {
112
- supported: true,
113
- partialCoverage: false,
114
- ops: {
115
- // Thin ADC (hal/adc-pin.ts): construction gain/reference tokens,
116
- // lazy inline channel setup.
117
- 'adc.read_raw': 'supported',
118
- 'adc.read_mv': 'supported',
119
- },
120
- },
121
- dac: {
122
- // ESP32 DAC (2× 8-bit channels on GPIO25/26) via the Zephyr DAC driver
123
- // (dac_channel_setup + dac_write_value). nRF52840 / ESP32-S3 have no DAC;
124
- // usage there lowers to a comment and profileDiagnostics flags it
125
- // (zephyr-dac-pin-unavailable).
126
- supported: true,
127
- partialCoverage: true,
128
- ops: {
129
- // Thin DAC (hal/dac-pin.ts): raw code, construction resolution.
130
- 'dac.write_value': 'supported',
131
- },
132
- },
133
- interrupts: {
134
- supported: true,
135
- partialCoverage: true,
136
- unsupportedReason: undefined,
137
- ops: {
138
- 'interrupt.detach': 'supported',
139
- // Thin GPIO interrupts (hal/gpio-pin.ts onInterrupt): INT_* tokens,
140
- // covering the level modes the legacy mode strings could not express.
141
- 'interrupt.attach_flags': 'supported',
142
- },
143
- },
144
- i2c: {
145
- supported: true,
146
- partialCoverage: false,
147
- ops: {
148
- // Thin I2C device (hal/i2c-target.ts): Zephyr register verbs.
149
- 'i2c.reg_write': 'supported',
150
- 'i2c.reg_read': 'supported',
151
- 'i2c.reg_update': 'supported',
152
- 'i2c.dev_write': 'supported',
153
- },
154
- },
155
- spi: {
156
- supported: true,
157
- partialCoverage: false,
158
- ops: {
159
- // Thin SPI device (hal/spi-target.ts): spi_dt_spec against the DT
160
- // child node the overlay emits per constructed target.
161
- 'spi.transceive': 'supported',
162
- 'spi.dev_write': 'supported',
163
- 'spi.reg_read': 'supported',
164
- },
165
- },
166
- uart: {
167
- supported: true,
168
- partialCoverage: true,
169
- ops: {
170
- // Thin UART (hal/uart-port.ts): poll API with construction baud.
171
- 'uart.poll_write': 'supported',
172
- // Interrupt-drained RX ring (hal/uart-port.ts).
173
- 'uart.rx_arm': 'supported',
174
- 'uart.rx_available': 'supported',
175
- 'uart.rx_peek': 'supported',
176
- 'uart.rx_read': 'supported',
177
- },
178
- },
179
- board: {
180
- // Board constant resolution. Board.definition.<path> /
181
- // Pins.definition.<path> property accesses are folded by
182
- // ZephyrStrategy.renderBoardDefinitionAccess against the loaded
183
- // board/MCU constants — the same mechanism framework-arduino uses. The
184
- // lone HAL op (board.resolve) is constant-folded at IR-build time
185
- // (expression-to-ir.ts / hal-emitter.ts), so it only reaches the
186
- // resolver as a dead-letter; 'probe-inconclusive' reflects that the
187
- // minimal validator probe carries no path/board constants to resolve.
188
- supported: true,
189
- partialCoverage: false,
190
- ops: { 'board.resolve': 'probe-inconclusive' },
191
- },
192
- wdt: {
193
- supported: true,
194
- partialCoverage: false,
195
- ops: {
196
- 'wdt.disable': 'supported',
197
- // Thin Watchdog (hal/watchdog.ts): construction timeout in ms.
198
- 'wdt.setup': 'supported',
199
- 'wdt.feed': 'supported',
200
- },
201
- },
202
- // Thin Counter (hal/counter.ts) — Zephyr's counter driver with Zephyr's
203
- // verbs, over the same per-instance state as hwtimer.*. Chips without a
204
- // declared free counter lower to a comment + profileDiagnostics flag.
205
- counter: {
206
- supported: true,
207
- partialCoverage: true,
208
- ops: {
209
- 'counter.on_alarm': 'supported',
210
- 'counter.start': 'supported',
211
- 'counter.stop': 'supported',
212
- },
213
- },
214
- // Thin Thread (hal/thread.ts) — kernel threads. start(fn) is
215
- // k_thread_create (K_NO_WAIT) over a per-slot stack sized by the
216
- // construction stackKb; join is k_thread_join (K_FOREVER).
217
- thread: {
218
- supported: true,
219
- partialCoverage: false,
220
- ops: {
221
- 'thread.start': 'supported',
222
- 'thread.join': 'supported',
223
- },
224
- },
225
- // ── Partial: WiFi (STA connect + scan + config via conn_mgr/net_mgmt) ────
226
- // ESP32 family only (esp32, esp32s3, esp32c3, esp32c6) — profileDiagnostics
227
- // flags wifi usage on radioless chips (nRF52840, RP2040/RP2350).
228
- // AP mode, credential persistence, static IP, and event callbacks deferred.
229
- wifi: {
230
- supported: true,
231
- partialCoverage: true,
232
- // Unsupported surface: AP client enumeration/IP/per-station-config have no
233
- // driver hook; credentials need a custom settings-subsystem layer; static IP
234
- // / auto-reconnect / tx-power aren't wifi-shaped or aren't exposed by the
235
- // esp32 Zephyr driver. See per-op reasons.
236
- unsupportedReason: 'Per-station AP enumeration and credential persistence have no Zephyr lowering (no driver/Kconfig hook).',
237
- ops: {
238
- // Station (8) — join carries the construction facts (credentials,
239
- // security, band/channel, timeout, static IPv4, power-save);
240
- // net_mgmt connect/disconnect + L4 connectivity state underneath.
241
- 'wifi.join': 'supported', 'wifi.connect_start': 'supported',
242
- 'wifi.disconnect': 'supported', 'wifi.is_connected': 'supported',
243
- 'wifi.local_ip': 'supported', 'wifi.rssi': 'supported',
244
- 'wifi.mac': 'supported',
245
- // Scan (8) — net_mgmt NET_REQUEST_WIFI_SCAN + result pool.
246
- // scan_start is the async split of scan (kick + poll scan_done);
247
- // emitted synthetically by the async tier, no TS-facing method.
248
- 'wifi.scan': 'supported',
249
- 'wifi.scan_start': 'supported',
250
- 'wifi.scan_done': 'supported', 'wifi.scan_count': 'supported',
251
- 'wifi.scan_ssid': 'supported', 'wifi.scan_rssi': 'supported',
252
- 'wifi.scan_encryption': 'supported', 'wifi.scan_channel': 'supported',
253
- // AP mode (2) — esp32 driver wires ap_enable/ap_disable. Only
254
- // ssid/psk/channel are honored (the thin WiFiAP facts carry exactly those).
255
- 'wifi.ap_start': 'supported', 'wifi.ap_stop': 'supported',
256
- // on_event: 'disconnect' (NET_EVENT_L4_DISCONNECTED) + 'connect'
257
- // (NET_EVENT_IPV4_ADDR_ADD) are lowered.
258
- 'wifi.on_event': 'supported',
259
- // ── Genuinely unsupported (no driver hook) ─────────────────────────
260
- // ap_client_count: no API to enumerate connected AP stations.
261
- 'wifi.ap_client_count': 'unsupported',
262
- },
263
-
264
- },
265
- http: {
266
- supported: true,
267
- partialCoverage: false,
268
- // HTTP/S client over Zephyr BSD sockets + http_client_req (TLS via
269
- // mbedTLS / NET_SOCKETS_SOCKOPT_TLS). The __tc_http shim owns url parse,
270
- // DNS (getaddrinfo), socket/TLS connect, and body accumulation. Requires
271
- // a networked target (ESP32 WiFi); profileDiagnostics flags usage on a
272
- // radioless chip as 'zephyr-http-unavailable-on-target'.
273
- ops: {
274
- 'http.begin': 'supported',
275
- 'http.set_header': 'supported', 'http.set_timeout': 'supported',
276
- 'http.set_max_body': 'supported', 'http.set_body': 'supported',
277
- 'http.set_insecure': 'supported', 'http.set_ca_cert': 'supported',
278
- 'http.send': 'supported', 'http.send_start': 'supported',
279
- 'http.done': 'supported', 'http.status': 'supported',
280
- 'http.ok': 'supported', 'http.body': 'supported',
281
- 'http.content_length': 'supported', 'http.response_header': 'supported',
282
- },
283
- },
284
- display: {
285
- supported: true,
286
- partialCoverage: true,
287
- // Partial: mono profiles (ssd1306-zephyr) drive display.* ops via the
288
- // direct GFX runtime only — no CuttlefishGFX UI rendering path. The
289
- // ILI9341 UI adapter shares the ST7796S direct-drive transport with a
290
- // per-controller init table (16-bit RGB565 wire format); hardware-tuned
291
- // on ST7796S only. E-ink panels are out of scope at this time.
292
- unsupportedReason: 'Mono panels (ssd1306) are direct-op only (no UI rendering); ili9341 UI path is ported but not yet hardware-verified; e-ink is out of scope at this time.',
293
- drivers: ['ili9341-zephyr', 'st7796-zephyr', 'ssd1306-zephyr'],
294
- colorFormat: 'rgb565',
295
- ops: {
296
- 'display.init': 'supported',
297
- 'display.fill_rect': 'supported',
298
- 'display.draw_text': 'supported',
299
- 'display.draw_rect': 'supported',
300
- 'display.flush': 'supported',
301
- },
302
- },
303
- // ── Supported: BLE (NimBLE GATT peripheral via runtime service register) ──
304
- ble: {
305
- supported: true,
306
- partialCoverage: true,
307
- ops: Object.fromEntries(
308
- HAL_OPERATION_KINDS.filter((k) => k.startsWith('ble.')).map((k) => [k, 'supported']),
309
- ),
310
- },
311
-
312
- // ── Supported: Worker offload (k_work system workqueue + k_sem) ──────────
313
-
314
- // ── Honestly unsupported extended categories ─────────────────────────────
315
- // These have op-kinds in HAL_OPERATION_KINDS but no Zephyr lowering. Each
316
- // is declared unsupported (with a reason) so the coverage matrix is uniform
317
- // and the resolver's `return undefined` for these prefixes is honest. The
318
- // catchall schema (HalCoverageSchema) validates any declared extended
319
- // category; declaring them keeps the manifest a complete coverage record.
320
-
321
- snprintf: {
322
- supported: false,
323
- unsupportedReason: 'snprintf.emit is a raw escape hatch; the Zephyr resolver returns undefined (use rawCpp()).',
324
- partialCoverage: false,
325
- ops: { 'snprintf.emit': 'unsupported' },
326
- },
327
- preferences: {
328
- // ZMS-backed Zephyr settings. The HAL ESP32-NVS session model (begin/end
329
- // + typed put/get) is modeled on top of Zephyr's flat settings key-space:
330
- // begin(ns) records a "tc/<ns>/" prefix; put/get operate on an in-RAM
331
- // cache populated once at boot by settings_load()'s h_set callback;
332
- // writes mirror to flash via settings_save_one/settings_delete. The ZMS
333
- // backend auto-locates the storage_partition fixed-partition (or the
334
- // /chosen zephyr,settings-partition node — see dt-config/overlay.ts).
335
- // begin/end are no-ops beyond prefix bookkeeping: Zephyr settings has no
336
- // session/namespace, but keeping the ops preserves portability with the
337
- // ESP32 NVS model and leaves a hook for a future session-needing backend.
338
- supported: true,
339
- partialCoverage: false,
340
- ops: {
341
- 'preferences.clear': 'supported', 'preferences.remove': 'supported',
342
- 'preferences.put_int': 'supported', 'preferences.get_int': 'supported',
343
- 'preferences.put_bool': 'supported', 'preferences.get_bool': 'supported',
344
- 'preferences.put_float': 'supported', 'preferences.get_float': 'supported',
345
- 'preferences.put_string': 'supported', 'preferences.get_string': 'supported',
346
- },
347
- },
348
- random: {
349
- // <zephyr/random/random.h> sys_rand_get seeds a userspace xorshift32
350
- // PRNG (__tc_rand_*); random.seed re-seeds it deterministically (matching
351
- // Arduino randomSeed). random.int → [0, 2^31-1], random.range → [min,max-1].
352
- supported: true,
353
- partialCoverage: false,
354
- ops: {
355
- 'random.int': 'supported',
356
- 'random.range': 'supported',
357
- 'random.seed': 'supported',
358
- },
359
- },
360
- fs: {
361
- // littlefs on the board's storage_partition, via <zephyr/fs/fs.h>. The
362
- // shim mounts at /lfs lazily (formats on first use) and the HAL paths are
363
- // treated as paths within the filesystem. Requires CONFIG_FILE_SYSTEM +
364
- // CONFIG_FILE_SYSTEM_LITTLEFS (emitted by the scaffold when fs.* is used)
365
- // and the storage_partition node.
366
- supported: true,
367
- partialCoverage: false,
368
- ops: {
369
- 'fs.read_text': 'supported', 'fs.write_text': 'supported',
370
- 'fs.exists': 'supported', 'fs.remove': 'supported',
371
- },
372
- },
373
- mqtt: {
374
- supported: true,
375
- partialCoverage: false,
376
- // MQTT 3.1.1 client over Zephyr <zephyr/net/mqtt.h> (mqtts:// TLS via
377
- // MQTT_TRANSPORT_SECURE + the shared mbedTLS matrix, encryption only — the
378
- // HAL surface has no CA-pinning op, so peer verify is NONE). The __tc_mqtt
379
- // shim resolves the broker, runs the mqtt_input/mqtt_live poll loop on a
380
- // background k_thread, and dispatches incoming PUBLISHes to the user's
381
- // onMessage callback. Requires a networked target (ESP32 WiFi);
382
- // profileDiagnostics flags usage on a radioless chip.
383
- ops: {
384
- 'mqtt.connect': 'supported', 'mqtt.on_message': 'supported',
385
- 'mqtt.subscribe': 'supported', 'mqtt.publish': 'supported',
386
- 'mqtt.connected': 'supported', 'mqtt.disconnect': 'supported',
387
- },
388
- },
389
- sensor: {
390
- // DT-bound sensor parts — the generic catalog (hal/sensor.ts +
391
- // sensor-catalog.generated.ts). fetch → sensor_sample_fetch,
392
- // get → sensor_channel_get on a per-sensor device handle; the overlay
393
- // generator emits the DT child node, and the driver's own Kconfig
394
- // `default y` lights it up (only CONFIG_SENSOR is set, usage-gated).
395
- supported: true,
396
- partialCoverage: false,
397
- ops: {
398
- 'sensor.fetch': 'supported',
399
- 'sensor.get': 'supported',
400
- },
401
- },
402
- i2s: {
403
- supported: false,
404
- unsupportedReason: 'No I2S / digital audio lowering on Zephyr.',
405
- partialCoverage: false,
406
- ops: unsupportedOps('i2s.'),
407
- },
408
- twai: {
409
- supported: false,
410
- unsupportedReason: 'No CAN / TWAI lowering on Zephyr (Zephyr CAN driver not wired).',
411
- partialCoverage: false,
412
- ops: unsupportedOps('twai.'),
413
- },
414
- usb: {
415
- // CDC-ACM serial over the board's USB connector. BOARD-GATED: the
416
- // lowering only fires when the board's manifest carries zephyr.usb.*
417
- // (boardgen emits it from the board's own DTS) — the manifest probe
418
- // runs with no board, so the honest declaration is unsupported here.
419
- supported: false,
420
- unsupportedReason: 'Board-gated: USB lowers only on boards whose DTS enables the USB device controller (boardgen emits zephyr.usb.* from the catalog).',
421
- partialCoverage: false,
422
- ops: unsupportedOps('usb.'),
423
- },
424
- eth: {
425
- supported: false,
426
- unsupportedReason: 'No Ethernet MAC lowering on Zephyr.',
427
- partialCoverage: false,
428
- ops: unsupportedOps('eth.'),
429
- },
430
- espnow: {
431
- supported: false,
432
- unsupportedReason: 'ESP-NOW is an ESP-exclusive wireless protocol; no Zephyr lowering.',
433
- partialCoverage: false,
434
- ops: unsupportedOps('espnow.'),
435
- },
436
- crypto: {
437
- supported: false,
438
- unsupportedReason: 'No hardware crypto (AES/SHA/HMAC) lowering on Zephyr.',
439
- partialCoverage: false,
440
- ops: unsupportedOps('crypto.'),
441
- },
442
- pcnt: {
443
- supported: false,
444
- unsupportedReason: 'No pulse-counter (PCNT) lowering on Zephyr.',
445
- partialCoverage: false,
446
- ops: unsupportedOps('pcnt.'),
447
- },
448
- mcpwm: {
449
- supported: false,
450
- unsupportedReason: 'No motor-control PWM (MCPWM) lowering on Zephyr.',
451
- partialCoverage: false,
452
- ops: unsupportedOps('mcpwm.'),
453
- },
454
- raw: { supported: true },
455
- },
456
-
457
- polyfills: {
458
- emitted: [
459
- { id: 'cuttlefish_halt', domain: 'standard', notes: 'Mapped to a k_msleep halt loop (exceptions disabled)' },
460
- { id: 'wiring_compat', domain: 'standard', notes: 'HIGH/LOW/digitalRead/etc. macros routing Wiring tokens (referenced unconditionally by the UI runtime header) to the __tc_gpio_* helpers' },
461
- { id: 'string_methods', domain: 'embedded', notes: 'STL-free __tc_* string helpers (const char*, inline ASCII case conv, <cstring> only)' },
462
- { id: 'static_array', domain: 'embedded', notes: 'STL-free __tc_StaticArray<T,N> wrapper for no-<vector> mutated/struct array literals' },
463
- { id: 'async_runtime', domain: 'embedded', notes: 'Heap-free static Promise/microtask runtime (generateStaticAsyncRuntime), pumped in loop()' },
464
- ],
465
- suppressed: [],
466
- },
467
-
468
- toolchain: {
469
- backend: 'west',
470
- operations: { prepare: true, compile: true, upload: true, monitor: true, debug: true },
471
- },
472
-
473
- libraryResolution: {
474
- isFrameworkLibraryImport: false,
475
- getFrameworkLibraryHeaderName: false,
476
- buildClassNameMap: false,
477
- tryGenerateLibDecl: false,
478
- },
479
-
480
- typeEmission: {
481
- normalizeCppType: true,
482
- mathHeader: '<cmath>',
483
- needsStdString: false,
484
- needsStdVector: false,
485
- needsIostream: false,
486
- needsStdFunction: false,
487
- stdlibSupport: {
488
- hasVector: false,
489
- hasString: false,
490
- hasIostream: false,
491
- hasExceptions: false,
492
- hasRTTI: false,
493
- recommendedArrayImpl: 'static_array',
494
- recommendedStringImpl: 'static_string',
495
- },
496
- },
497
-
498
- ambientTypes: ['Preferences'],
499
-
500
- conformance: {
501
- // Hardware-test groups moved to the shared HAL suite
502
- // (packages/hal/tests — common/ + board/ via board test-pins.json), which
503
- // runs on Zephyr targets through the hal board configs. The
504
- // hal-resolution snapshots below stay.
505
- hardwareTestGroups: [],
506
- // Per-op HAL-resolution suite — one tests/packages/framework-zephyr/
507
- // hal-resolution/<cat>.test.ts per category, snapshotting the exact C++
508
- // each op lowers to. Mirrors the framework-esp32 convention. These are
509
- // pure string-snapshot tests (no hardware); they are the safety net that
510
- // catches regressions like silent pull-resistor / interrupt no-ops.
511
- halResolutionTests: [
512
- 'adc', 'ble', 'board', 'dac', 'fs', 'gpio', 'http', 'hwtimer', 'i2c',
513
- 'interrupts', 'mqtt', 'preferences', 'pwm', 'random',
514
- 'spi', 'thin-buses', 'thin-classes', 'thread', 'timing',
515
- 'uart', 'usb', 'wdt', 'wifi',
516
- ],
517
- },
518
-
519
- // Declared compatibility range for the installed Zephyr RTOS. The framework's
520
- // build-time version check (toolchain/compat.ts) reads this and fails fast
521
- // with a clear message when the user's Zephyr is outside the range, instead
522
- // of letting west/CMake surface a cryptic board-target error (the class of
523
- // breakage behind the HWMv2 qualifier requirement in Zephyr 4.3+).
524
- compat: {
525
- zephyr: '>=4.3 <5.0',
526
- },
527
-
528
- // `cuttlefish doctor` prints the detected Zephyr version + compat result and
529
- // previews how the configured board target resolves for that version.
530
- doctor: { available: true },
531
-
532
- // `cuttlefish licenses` enumerates the Zephyr kernel + west manifest projects
533
- // and resolves each one's SPDX license (mirrors framework-arduino).
534
- licenses: { available: true },
535
- });
1
+ // ---------------------------------------------------------------------------
2
+ // Zephyr framework manifest
3
+ //
4
+ // Coverage reflects actual resolveHALOperation / lowerHalOp + resolveDisplayOp
5
+ // behavior. GPIO, PWM, ADC, I2C, SPI, UART, interrupts, pulse,
6
+ // shift, WDT, BLE, timing, WiFi, HTTP/S, MQTT, board constants, and random
7
+ // are lowered; display is lowered via the generic <zephyr/drivers/display.h>
8
+ // GFX runtime. WiFi/HTTP/MQTT require an ESP32 target (nRF52840 has no radio).
9
+ // The manifest validator probes every declared op against the resolver: a
10
+ // 'supported' op must lower, an 'unsupported' op must return undefined.
11
+ // ---------------------------------------------------------------------------
12
+
13
+ import { defineFrameworkManifest, HAL_OPERATION_KINDS } from '@typecad/cuttlefish/api/shared';
14
+
15
+ /**
16
+ * Build an `{ opKind: 'unsupported' }` record for every op kind under a given
17
+ * category prefix. Keeps unsupported categories honest without hand-listing
18
+ * every op (the validator requires every op kind be declared, even as
19
+ * unsupported). Mirrors the discipline esp32 applies by hand-listing.
20
+ */
21
+ function unsupportedOps(prefix: string): Record<string, 'unsupported'> {
22
+ const out: Record<string, 'unsupported'> = {};
23
+ for (const kind of HAL_OPERATION_KINDS) {
24
+ if (kind.startsWith(prefix)) out[kind] = 'unsupported';
25
+ }
26
+ return out;
27
+ }
28
+
29
+ export default defineFrameworkManifest({
30
+ schemaVersion: 1,
31
+ frameworkId: 'zephyr',
32
+ packageName: '@typecad/framework-zephyr',
33
+ canonical: false,
34
+ displayName: 'Zephyr RTOS',
35
+ description:
36
+ 'Native Zephyr framework targeting the Zephyr RTOS via west/CMake. ' +
37
+ 'GPIO is lowered through devicetree specs (gpio_pin_*_dt).',
38
+ implementationMode: 'from-scratch',
39
+
40
+ entrypoint: {
41
+ // Zephyr is a main()-based RTOS: top-level statements lower straight into
42
+ // main() (no setup()/loop() pair, no bridge shim). Event-driven programs
43
+ // get their scheduler loop appended inside main() by the driver machinery.
44
+ entrypointFunctionName: 'main',
45
+ requiresLoopFunction: false,
46
+ sourceExtension: 'cpp',
47
+ overrideBaseName: 'main',
48
+ outputSubdirectory: 'src',
49
+ generateHeaderFile: true,
50
+ },
51
+
52
+ profile: {
53
+ // Informational list of supported board targets. The manifest validator
54
+ // never iterates this. Every board in the catalog resolves the same way
55
+ // (resolveChipFromBoard over the generated manifest) — there is no
56
+ // curated target list.
57
+ targets: [],
58
+ forcedIncludes: ['<zephyr/kernel.h>', '<zephyr/drivers/gpio.h>', '<cstdint>'],
59
+ symbolAliases: {},
60
+ },
61
+
62
+ hal: {
63
+ // ── Supported: GPIO (devicetree-spec bridge) ──────────────────────────
64
+ gpio: {
65
+ supported: true,
66
+ partialCoverage: false,
67
+ ops: {
68
+ 'gpio.write': 'supported',
69
+ 'gpio.read': 'supported',
70
+ 'gpio.toggle': 'supported',
71
+ // Thin GPIO (hal/gpio-pin.ts): construction flags as tokens, applied
72
+ // once per pin ahead of first use.
73
+ 'gpio.configure': 'supported',
74
+ 'gpio.read_cfg': 'supported', // fused guarded configure + read
75
+ 'gpio.shift_out': 'supported',
76
+ 'gpio.shift_in': 'supported',
77
+ },
78
+ },
79
+
80
+ // ── Supported: Timing (delay/millis/delay_us/micros; timers via polyfill) ─
81
+ timing: {
82
+ supported: true,
83
+ partialCoverage: false,
84
+ ops: {
85
+ // Timer ops are POLYFILL_BACKED_OPS → timer_methods. The validator skips
86
+ // the resolver probe (these legitimately return polyfill-helper calls,
87
+ // not direct lowering) and requires the polyfill be in polyfills.emitted.
88
+ // Time.* — the TS-flavored surface (hal/time.ts), preferred over the
89
+ // Arduino-named forms above for new code.
90
+ 'timing.sleep': 'supported', // → k_msleep(ms) — Time.sleep
91
+ 'timing.now': 'supported', // → k_uptime_get() — Time.now
92
+ 'timing.now_us': 'supported', // → k_cyc_to_us_floor64 — Time.nowUs
93
+ 'timing.busy_wait_us': 'supported', // → k_busy_wait(us) — Time.busyWaitUs
94
+ },
95
+ },
96
+
97
+ // ── Supported: PWM (pwm_dt_spec via pwm-led0) ──────────────────────────
98
+ pwm: {
99
+ supported: true,
100
+ partialCoverage: false,
101
+ ops: {
102
+ // Thin PWM (hal/pwm-pin.ts): ns-true verbs; construction period
103
+ // applies once, setDuty is 0.0–1.0 sugar over one set_pulse call.
104
+ 'pwm.set_pulse': 'supported',
105
+ 'pwm.set_duty': 'supported',
106
+ 'pwm.set_period': 'supported',
107
+ },
108
+ },
109
+
110
+ // ── Supported: ADC (SAADC via adc_read + channel setup) ────────────────
111
+ adc: {
112
+ supported: true,
113
+ partialCoverage: false,
114
+ ops: {
115
+ // Thin ADC (hal/adc-pin.ts): construction gain/reference tokens,
116
+ // lazy inline channel setup.
117
+ 'adc.read_raw': 'supported',
118
+ 'adc.read_mv': 'supported',
119
+ },
120
+ },
121
+ dac: {
122
+ // ESP32 DAC (2× 8-bit channels on GPIO25/26) via the Zephyr DAC driver
123
+ // (dac_channel_setup + dac_write_value). nRF52840 / ESP32-S3 have no DAC;
124
+ // usage there lowers to a comment and profileDiagnostics flags it
125
+ // (zephyr-dac-pin-unavailable).
126
+ supported: true,
127
+ partialCoverage: true,
128
+ ops: {
129
+ // Thin DAC (hal/dac-pin.ts): raw code, construction resolution.
130
+ 'dac.write_value': 'supported',
131
+ },
132
+ },
133
+ interrupts: {
134
+ supported: true,
135
+ partialCoverage: true,
136
+ unsupportedReason: undefined,
137
+ ops: {
138
+ 'interrupt.detach': 'supported',
139
+ // Thin GPIO interrupts (hal/gpio-pin.ts onInterrupt): INT_* tokens,
140
+ // covering the level modes the legacy mode strings could not express.
141
+ 'interrupt.attach_flags': 'supported',
142
+ },
143
+ },
144
+ i2c: {
145
+ supported: true,
146
+ partialCoverage: false,
147
+ ops: {
148
+ // Thin I2C device (hal/i2c-target.ts): Zephyr register verbs.
149
+ 'i2c.reg_write': 'supported',
150
+ 'i2c.reg_read': 'supported',
151
+ 'i2c.reg_update': 'supported',
152
+ 'i2c.dev_write': 'supported',
153
+ },
154
+ },
155
+ spi: {
156
+ supported: true,
157
+ partialCoverage: false,
158
+ ops: {
159
+ // Thin SPI device (hal/spi-target.ts): spi_dt_spec against the DT
160
+ // child node the overlay emits per constructed target.
161
+ 'spi.transceive': 'supported',
162
+ 'spi.dev_write': 'supported',
163
+ 'spi.reg_read': 'supported',
164
+ },
165
+ },
166
+ uart: {
167
+ supported: true,
168
+ partialCoverage: true,
169
+ ops: {
170
+ // Thin UART (hal/uart-port.ts): poll API with construction baud.
171
+ 'uart.poll_write': 'supported',
172
+ // Interrupt-drained RX ring (hal/uart-port.ts).
173
+ 'uart.rx_arm': 'supported',
174
+ 'uart.rx_available': 'supported',
175
+ 'uart.rx_peek': 'supported',
176
+ 'uart.rx_read': 'supported',
177
+ },
178
+ },
179
+ board: {
180
+ // Board constant resolution. Board.definition.<path> /
181
+ // Pins.definition.<path> property accesses are folded by
182
+ // ZephyrStrategy.renderBoardDefinitionAccess against the loaded
183
+ // board/MCU constants — the same mechanism framework-arduino uses. The
184
+ // lone HAL op (board.resolve) is constant-folded at IR-build time
185
+ // (expression-to-ir.ts / hal-emitter.ts), so it only reaches the
186
+ // resolver as a dead-letter; 'probe-inconclusive' reflects that the
187
+ // minimal validator probe carries no path/board constants to resolve.
188
+ supported: true,
189
+ partialCoverage: false,
190
+ ops: { 'board.resolve': 'probe-inconclusive' },
191
+ },
192
+ wdt: {
193
+ supported: true,
194
+ partialCoverage: false,
195
+ ops: {
196
+ 'wdt.disable': 'supported',
197
+ // Thin Watchdog (hal/watchdog.ts): construction timeout in ms.
198
+ 'wdt.setup': 'supported',
199
+ 'wdt.feed': 'supported',
200
+ },
201
+ },
202
+ // Thin Counter (hal/counter.ts) — Zephyr's counter driver with Zephyr's
203
+ // verbs, over the same per-instance state as hwtimer.*. Chips without a
204
+ // declared free counter lower to a comment + profileDiagnostics flag.
205
+ counter: {
206
+ supported: true,
207
+ partialCoverage: true,
208
+ ops: {
209
+ 'counter.on_alarm': 'supported',
210
+ 'counter.start': 'supported',
211
+ 'counter.stop': 'supported',
212
+ },
213
+ },
214
+ // Thin Thread (hal/thread.ts) — kernel threads. start(fn) is
215
+ // k_thread_create (K_NO_WAIT) over a per-slot stack sized by the
216
+ // construction stackKb; join is k_thread_join (K_FOREVER).
217
+ thread: {
218
+ supported: true,
219
+ partialCoverage: false,
220
+ ops: {
221
+ 'thread.start': 'supported',
222
+ 'thread.join': 'supported',
223
+ },
224
+ },
225
+ // ── Partial: WiFi (STA connect + scan + config via conn_mgr/net_mgmt) ────
226
+ // ESP32 family only (esp32, esp32s3, esp32c3, esp32c6) — profileDiagnostics
227
+ // flags wifi usage on radioless chips (nRF52840, RP2040/RP2350).
228
+ // AP mode, credential persistence, static IP, and event callbacks deferred.
229
+ wifi: {
230
+ supported: true,
231
+ partialCoverage: true,
232
+ // Unsupported surface: AP client enumeration/IP/per-station-config have no
233
+ // driver hook; credentials need a custom settings-subsystem layer; static IP
234
+ // / auto-reconnect / tx-power aren't wifi-shaped or aren't exposed by the
235
+ // esp32 Zephyr driver. See per-op reasons.
236
+ unsupportedReason: 'Per-station AP enumeration and credential persistence have no Zephyr lowering (no driver/Kconfig hook).',
237
+ ops: {
238
+ // Station (8) — join carries the construction facts (credentials,
239
+ // security, band/channel, timeout, static IPv4, power-save);
240
+ // net_mgmt connect/disconnect + L4 connectivity state underneath.
241
+ 'wifi.join': 'supported', 'wifi.connect_start': 'supported',
242
+ 'wifi.disconnect': 'supported', 'wifi.is_connected': 'supported',
243
+ 'wifi.local_ip': 'supported', 'wifi.rssi': 'supported',
244
+ 'wifi.mac': 'supported',
245
+ // Scan (8) — net_mgmt NET_REQUEST_WIFI_SCAN + result pool.
246
+ // scan_start is the async split of scan (kick + poll scan_done);
247
+ // emitted synthetically by the async tier, no TS-facing method.
248
+ 'wifi.scan': 'supported',
249
+ 'wifi.scan_start': 'supported',
250
+ 'wifi.scan_done': 'supported', 'wifi.scan_count': 'supported',
251
+ 'wifi.scan_ssid': 'supported', 'wifi.scan_rssi': 'supported',
252
+ 'wifi.scan_encryption': 'supported', 'wifi.scan_channel': 'supported',
253
+ // AP mode (2) — esp32 driver wires ap_enable/ap_disable. Only
254
+ // ssid/psk/channel are honored (the thin WiFiAP facts carry exactly those).
255
+ 'wifi.ap_start': 'supported', 'wifi.ap_stop': 'supported',
256
+ // on_event: 'disconnect' (NET_EVENT_L4_DISCONNECTED) + 'connect'
257
+ // (NET_EVENT_IPV4_ADDR_ADD) are lowered.
258
+ 'wifi.on_event': 'supported',
259
+ // ── Genuinely unsupported (no driver hook) ─────────────────────────
260
+ // ap_client_count: no API to enumerate connected AP stations.
261
+ 'wifi.ap_client_count': 'unsupported',
262
+ },
263
+
264
+ },
265
+ http: {
266
+ supported: true,
267
+ partialCoverage: false,
268
+ // HTTP/S client over Zephyr BSD sockets + http_client_req (TLS via
269
+ // mbedTLS / NET_SOCKETS_SOCKOPT_TLS). The __tc_http shim owns url parse,
270
+ // DNS (getaddrinfo), socket/TLS connect, and body accumulation. Requires
271
+ // a networked target (ESP32 WiFi); profileDiagnostics flags usage on a
272
+ // radioless chip as 'zephyr-http-unavailable-on-target'.
273
+ ops: {
274
+ 'http.begin': 'supported',
275
+ 'http.set_header': 'supported', 'http.set_timeout': 'supported',
276
+ 'http.set_max_body': 'supported', 'http.set_body': 'supported',
277
+ 'http.set_insecure': 'supported', 'http.set_ca_cert': 'supported',
278
+ 'http.send': 'supported', 'http.send_start': 'supported',
279
+ 'http.done': 'supported', 'http.status': 'supported',
280
+ 'http.ok': 'supported', 'http.body': 'supported',
281
+ 'http.content_length': 'supported', 'http.response_header': 'supported',
282
+ },
283
+ },
284
+ display: {
285
+ supported: true,
286
+ partialCoverage: true,
287
+ // Partial: mono profiles (ssd1306-zephyr) drive display.* ops via the
288
+ // direct GFX runtime only — no CuttlefishGFX UI rendering path. The
289
+ // ILI9341 UI adapter shares the ST7796S direct-drive transport with a
290
+ // per-controller init table (16-bit RGB565 wire format); hardware-tuned
291
+ // on ST7796S only. E-ink panels are out of scope at this time.
292
+ unsupportedReason: 'Mono panels (ssd1306) are direct-op only (no UI rendering); ili9341 UI path is ported but not yet hardware-verified; e-ink is out of scope at this time.',
293
+ drivers: ['ili9341-zephyr', 'st7796-zephyr', 'ssd1306-zephyr'],
294
+ colorFormat: 'rgb565',
295
+ ops: {
296
+ 'display.init': 'supported',
297
+ 'display.fill_rect': 'supported',
298
+ 'display.draw_text': 'supported',
299
+ 'display.draw_rect': 'supported',
300
+ 'display.flush': 'supported',
301
+ },
302
+ },
303
+ // ── Supported: BLE (NimBLE GATT peripheral via runtime service register) ──
304
+ ble: {
305
+ supported: true,
306
+ partialCoverage: true,
307
+ ops: Object.fromEntries(
308
+ HAL_OPERATION_KINDS.filter((k) => k.startsWith('ble.')).map((k) => [k, 'supported']),
309
+ ),
310
+ },
311
+
312
+ // ── Supported: Worker offload (k_work system workqueue + k_sem) ──────────
313
+
314
+ // ── Honestly unsupported extended categories ─────────────────────────────
315
+ // These have op-kinds in HAL_OPERATION_KINDS but no Zephyr lowering. Each
316
+ // is declared unsupported (with a reason) so the coverage matrix is uniform
317
+ // and the resolver's `return undefined` for these prefixes is honest. The
318
+ // catchall schema (HalCoverageSchema) validates any declared extended
319
+ // category; declaring them keeps the manifest a complete coverage record.
320
+
321
+ snprintf: {
322
+ supported: false,
323
+ unsupportedReason: 'snprintf.emit is a raw escape hatch; the Zephyr resolver returns undefined (use rawCpp()).',
324
+ partialCoverage: false,
325
+ ops: { 'snprintf.emit': 'unsupported' },
326
+ },
327
+ preferences: {
328
+ // ZMS-backed Zephyr settings. The HAL ESP32-NVS session model (begin/end
329
+ // + typed put/get) is modeled on top of Zephyr's flat settings key-space:
330
+ // begin(ns) records a "tc/<ns>/" prefix; put/get operate on an in-RAM
331
+ // cache populated once at boot by settings_load()'s h_set callback;
332
+ // writes mirror to flash via settings_save_one/settings_delete. The ZMS
333
+ // backend auto-locates the storage_partition fixed-partition (or the
334
+ // /chosen zephyr,settings-partition node — see dt-config/overlay.ts).
335
+ // begin/end are no-ops beyond prefix bookkeeping: Zephyr settings has no
336
+ // session/namespace, but keeping the ops preserves portability with the
337
+ // ESP32 NVS model and leaves a hook for a future session-needing backend.
338
+ supported: true,
339
+ partialCoverage: false,
340
+ ops: {
341
+ 'preferences.clear': 'supported', 'preferences.remove': 'supported',
342
+ 'preferences.put_int': 'supported', 'preferences.get_int': 'supported',
343
+ 'preferences.put_bool': 'supported', 'preferences.get_bool': 'supported',
344
+ 'preferences.put_float': 'supported', 'preferences.get_float': 'supported',
345
+ 'preferences.put_string': 'supported', 'preferences.get_string': 'supported',
346
+ },
347
+ },
348
+ random: {
349
+ // <zephyr/random/random.h> sys_rand_get seeds a userspace xorshift32
350
+ // PRNG (__tc_rand_*); random.seed re-seeds it deterministically (matching
351
+ // Arduino randomSeed). random.int → [0, 2^31-1], random.range → [min,max-1].
352
+ supported: true,
353
+ partialCoverage: false,
354
+ ops: {
355
+ 'random.int': 'supported',
356
+ 'random.range': 'supported',
357
+ 'random.seed': 'supported',
358
+ },
359
+ },
360
+ fs: {
361
+ // littlefs on the board's storage_partition, via <zephyr/fs/fs.h>. The
362
+ // shim mounts at /lfs lazily (formats on first use) and the HAL paths are
363
+ // treated as paths within the filesystem. Requires CONFIG_FILE_SYSTEM +
364
+ // CONFIG_FILE_SYSTEM_LITTLEFS (emitted by the scaffold when fs.* is used)
365
+ // and the storage_partition node.
366
+ supported: true,
367
+ partialCoverage: false,
368
+ ops: {
369
+ 'fs.read_text': 'supported', 'fs.write_text': 'supported',
370
+ 'fs.exists': 'supported', 'fs.remove': 'supported',
371
+ },
372
+ },
373
+ mqtt: {
374
+ supported: true,
375
+ partialCoverage: false,
376
+ // MQTT 3.1.1 client over Zephyr <zephyr/net/mqtt.h> (mqtts:// TLS via
377
+ // MQTT_TRANSPORT_SECURE + the shared mbedTLS matrix). caCert pins the
378
+ // broker's CA (DER-decoded at emit, tls_credential_add sec tag,
379
+ // TLS_PEER_VERIFY_REQUIRED); mqtts:// without a CA stays
380
+ // encrypted-but-unverified (TLS_PEER_VERIFY_NONE). The __tc_mqtt
381
+ // shim resolves the broker, runs the mqtt_input/mqtt_live poll loop on
382
+ // a background k_thread, and dispatches incoming PUBLISHes to the user's
383
+ // onMessage callback. Requires a networked target (ESP32 WiFi);
384
+ // profileDiagnostics flags usage on a radioless chip.
385
+ ops: {
386
+ 'mqtt.connect': 'supported', 'mqtt.set_ca_cert': 'supported',
387
+ 'mqtt.on_message': 'supported',
388
+ 'mqtt.subscribe': 'supported', 'mqtt.publish': 'supported',
389
+ 'mqtt.connected': 'supported', 'mqtt.disconnect': 'supported',
390
+ },
391
+ },
392
+ sensor: {
393
+ // DT-bound sensor parts — the generic catalog (hal/sensor.ts +
394
+ // sensor-catalog.generated.ts). fetch → sensor_sample_fetch,
395
+ // get → sensor_channel_get on a per-sensor device handle; the overlay
396
+ // generator emits the DT child node, and the driver's own Kconfig
397
+ // `default y` lights it up (only CONFIG_SENSOR is set, usage-gated).
398
+ supported: true,
399
+ partialCoverage: false,
400
+ ops: {
401
+ 'sensor.fetch': 'supported',
402
+ 'sensor.get': 'supported',
403
+ },
404
+ },
405
+ i2s: {
406
+ supported: false,
407
+ unsupportedReason: 'No I2S / digital audio lowering on Zephyr.',
408
+ partialCoverage: false,
409
+ ops: unsupportedOps('i2s.'),
410
+ },
411
+ twai: {
412
+ supported: false,
413
+ unsupportedReason: 'No CAN / TWAI lowering on Zephyr (Zephyr CAN driver not wired).',
414
+ partialCoverage: false,
415
+ ops: unsupportedOps('twai.'),
416
+ },
417
+ usb: {
418
+ // CDC-ACM serial over the board's USB connector. BOARD-GATED: the
419
+ // lowering only fires when the board's manifest carries zephyr.usb.*
420
+ // (boardgen emits it from the board's own DTS) — the manifest probe
421
+ // runs with no board, so the honest declaration is unsupported here.
422
+ supported: false,
423
+ unsupportedReason: 'Board-gated: USB lowers only on boards whose DTS enables the USB device controller (boardgen emits zephyr.usb.* from the catalog).',
424
+ partialCoverage: false,
425
+ ops: unsupportedOps('usb.'),
426
+ },
427
+ eth: {
428
+ supported: false,
429
+ unsupportedReason: 'No Ethernet MAC lowering on Zephyr.',
430
+ partialCoverage: false,
431
+ ops: unsupportedOps('eth.'),
432
+ },
433
+ espnow: {
434
+ supported: false,
435
+ unsupportedReason: 'ESP-NOW is an ESP-exclusive wireless protocol; no Zephyr lowering.',
436
+ partialCoverage: false,
437
+ ops: unsupportedOps('espnow.'),
438
+ },
439
+ crypto: {
440
+ supported: false,
441
+ unsupportedReason: 'No hardware crypto (AES/SHA/HMAC) lowering on Zephyr.',
442
+ partialCoverage: false,
443
+ ops: unsupportedOps('crypto.'),
444
+ },
445
+ pcnt: {
446
+ supported: false,
447
+ unsupportedReason: 'No pulse-counter (PCNT) lowering on Zephyr.',
448
+ partialCoverage: false,
449
+ ops: unsupportedOps('pcnt.'),
450
+ },
451
+ mcpwm: {
452
+ supported: false,
453
+ unsupportedReason: 'No motor-control PWM (MCPWM) lowering on Zephyr.',
454
+ partialCoverage: false,
455
+ ops: unsupportedOps('mcpwm.'),
456
+ },
457
+ raw: { supported: true },
458
+ },
459
+
460
+ polyfills: {
461
+ emitted: [
462
+ { id: 'cuttlefish_halt', domain: 'standard', notes: 'Mapped to a k_msleep halt loop (exceptions disabled)' },
463
+ { id: 'wiring_compat', domain: 'standard', notes: 'HIGH/LOW/digitalRead/etc. macros routing Wiring tokens (referenced unconditionally by the UI runtime header) to the __tc_gpio_* helpers' },
464
+ { id: 'string_methods', domain: 'embedded', notes: 'STL-free __tc_* string helpers (const char*, inline ASCII case conv, <cstring> only)' },
465
+ { id: 'static_array', domain: 'embedded', notes: 'STL-free __tc_StaticArray<T,N> wrapper for no-<vector> mutated/struct array literals' },
466
+ { id: 'async_runtime', domain: 'embedded', notes: 'Heap-free static Promise/microtask runtime (generateStaticAsyncRuntime), pumped in loop()' },
467
+ ],
468
+ suppressed: [],
469
+ },
470
+
471
+ toolchain: {
472
+ backend: 'west',
473
+ operations: { prepare: true, compile: true, upload: true, monitor: true, debug: true },
474
+ },
475
+
476
+ libraryResolution: {
477
+ isFrameworkLibraryImport: false,
478
+ getFrameworkLibraryHeaderName: false,
479
+ buildClassNameMap: false,
480
+ tryGenerateLibDecl: false,
481
+ },
482
+
483
+ typeEmission: {
484
+ normalizeCppType: true,
485
+ mathHeader: '<cmath>',
486
+ needsStdString: false,
487
+ needsStdVector: false,
488
+ needsIostream: false,
489
+ needsStdFunction: false,
490
+ stdlibSupport: {
491
+ hasVector: false,
492
+ hasString: false,
493
+ hasIostream: false,
494
+ hasExceptions: false,
495
+ hasRTTI: false,
496
+ recommendedArrayImpl: 'static_array',
497
+ recommendedStringImpl: 'static_string',
498
+ },
499
+ },
500
+
501
+ ambientTypes: ['Preferences'],
502
+
503
+ conformance: {
504
+ // Hardware-test groups moved to the shared HAL suite
505
+ // (packages/hal/tests — common/ + board/ via board test-pins.json), which
506
+ // runs on Zephyr targets through the hal board configs. The
507
+ // hal-resolution snapshots below stay.
508
+ hardwareTestGroups: [],
509
+ // Per-op HAL-resolution suite — one tests/packages/framework-zephyr/
510
+ // hal-resolution/<cat>.test.ts per category, snapshotting the exact C++
511
+ // each op lowers to. Mirrors the framework-esp32 convention. These are
512
+ // pure string-snapshot tests (no hardware); they are the safety net that
513
+ // catches regressions like silent pull-resistor / interrupt no-ops.
514
+ halResolutionTests: [
515
+ 'adc', 'ble', 'board', 'dac', 'fs', 'gpio', 'http', 'hwtimer', 'i2c',
516
+ 'interrupts', 'mqtt', 'preferences', 'pwm', 'random',
517
+ 'spi', 'thin-buses', 'thin-classes', 'thread', 'timing',
518
+ 'uart', 'usb', 'wdt', 'wifi',
519
+ ],
520
+ },
521
+
522
+ // Declared compatibility range for the installed Zephyr RTOS. The framework's
523
+ // build-time version check (toolchain/compat.ts) reads this and fails fast
524
+ // with a clear message when the user's Zephyr is outside the range, instead
525
+ // of letting west/CMake surface a cryptic board-target error (the class of
526
+ // breakage behind the HWMv2 qualifier requirement in Zephyr 4.3+).
527
+ compat: {
528
+ zephyr: '>=4.3 <5.0',
529
+ },
530
+
531
+ // `typecad-hal doctor` prints the detected Zephyr version + compat result and
532
+ // previews how the configured board target resolves for that version.
533
+ doctor: { available: true },
534
+
535
+ // `typecad-hal licenses` enumerates the Zephyr kernel + west manifest projects
536
+ // and resolves each one's SPDX license (mirrors framework-arduino).
537
+ licenses: { available: true },
538
+ });