@typecad/framework-zephyr 1.0.0-alpha.16 → 1.0.0-alpha.19

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 (210) 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/audit.d.ts +111 -0
  6. package/dist/audit.js +416 -0
  7. package/dist/boardgen.d.ts +1 -9
  8. package/dist/boardgen.js +288 -47
  9. package/dist/chips/resolve.js +20 -0
  10. package/dist/chips/types.d.ts +22 -1
  11. package/dist/debug-codegen.js +1 -1
  12. package/dist/display/bindings.d.ts +55 -0
  13. package/dist/display/bindings.js +316 -0
  14. package/dist/display/gfx.d.ts +2 -3
  15. package/dist/display/gfx.js +166 -154
  16. package/dist/display/index.js +20 -1
  17. package/dist/display/mipi-dbi-host.d.ts +9 -0
  18. package/dist/display/mipi-dbi-host.js +174 -0
  19. package/dist/display/profiles.d.ts +109 -4
  20. package/dist/display/profiles.js +270 -7
  21. package/dist/display/touch-adapter.js +119 -49
  22. package/dist/display/ui-adapter-eink.d.ts +2 -0
  23. package/dist/display/ui-adapter-eink.js +4 -0
  24. package/dist/display/ui-adapter-gray.d.ts +8 -0
  25. package/dist/display/ui-adapter-gray.js +170 -0
  26. package/dist/display/ui-adapter-mono.d.ts +13 -0
  27. package/dist/display/ui-adapter-mono.js +230 -0
  28. package/dist/display/ui-adapter-native.d.ts +10 -0
  29. package/dist/display/ui-adapter-native.js +295 -0
  30. package/dist/display/ui-adapter-shared.d.ts +11 -0
  31. package/dist/display/ui-adapter-shared.js +122 -0
  32. package/dist/display/ui-adapter.js +510 -558
  33. package/dist/doctor.js +4 -4
  34. package/dist/dt-config/custom-board.js +2 -2
  35. package/dist/dt-config/kconfig.d.ts +62 -1
  36. package/dist/dt-config/kconfig.js +141 -38
  37. package/dist/dt-config/overlay.d.ts +15 -2
  38. package/dist/dt-config/overlay.js +433 -18
  39. package/dist/framework.manifest.d.ts +10 -4
  40. package/dist/framework.manifest.js +133 -17
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +14 -6
  43. package/dist/licenses.d.ts +2 -2
  44. package/dist/licenses.js +13 -92
  45. package/dist/lowering/can.d.ts +25 -0
  46. package/dist/lowering/can.js +97 -0
  47. package/dist/lowering/clock.d.ts +17 -0
  48. package/dist/lowering/clock.js +58 -0
  49. package/dist/lowering/fs.js +1 -1
  50. package/dist/lowering/gpio.js +0 -32
  51. package/dist/lowering/hid.d.ts +27 -0
  52. package/dist/lowering/hid.js +244 -0
  53. package/dist/lowering/http.js +264 -32
  54. package/dist/lowering/i2c.d.ts +8 -0
  55. package/dist/lowering/i2c.js +137 -5
  56. package/dist/lowering/i2s.d.ts +27 -0
  57. package/dist/lowering/i2s.js +98 -0
  58. package/dist/lowering/index.d.ts +9 -1
  59. package/dist/lowering/index.js +25 -1
  60. package/dist/lowering/interrupts.js +6 -0
  61. package/dist/lowering/matrix.d.ts +15 -0
  62. package/dist/lowering/matrix.js +63 -0
  63. package/dist/lowering/mqtt.js +110 -8
  64. package/dist/lowering/power.d.ts +3 -2
  65. package/dist/lowering/power.js +20 -45
  66. package/dist/lowering/pwm.js +25 -0
  67. package/dist/lowering/sensor.d.ts +2 -2
  68. package/dist/lowering/sensor.js +8 -4
  69. package/dist/lowering/strip.d.ts +16 -0
  70. package/dist/lowering/strip.js +70 -0
  71. package/dist/lowering/thread.js +5 -1
  72. package/dist/lowering/trace.d.ts +44 -0
  73. package/dist/lowering/trace.js +239 -0
  74. package/dist/lowering/uart.js +6 -1
  75. package/dist/lowering/usb.d.ts +3 -1
  76. package/dist/lowering/usb.js +16 -13
  77. package/dist/lowering/wdt.js +2 -29
  78. package/dist/sbom.d.ts +181 -0
  79. package/dist/sbom.js +901 -0
  80. package/dist/sdk/board-catalog-sync.d.ts +1 -3
  81. package/dist/sdk/board-catalog-sync.js +4 -10
  82. package/dist/strategy.d.ts +82 -46
  83. package/dist/strategy.js +639 -182
  84. package/dist/tmp-probe.d.ts +2 -0
  85. package/dist/tmp-probe.js +9 -0
  86. package/dist/toolchain/debug-config.d.ts +50 -90
  87. package/dist/toolchain/debug-config.js +239 -510
  88. package/dist/toolchain/env-check.d.ts +1 -3
  89. package/dist/toolchain/env-check.js +2 -7
  90. package/dist/toolchain/index.d.ts +28 -3
  91. package/dist/toolchain/index.js +498 -62
  92. package/dist/toolchain/runners.d.ts +16 -0
  93. package/dist/toolchain/runners.js +75 -0
  94. package/dist/toolchain/scaffold.d.ts +5 -2
  95. package/dist/toolchain/scaffold.js +79 -15
  96. package/dist/toolchain/west-discover.d.ts +6 -0
  97. package/dist/toolchain/west-discover.js +36 -13
  98. package/dist/toolchain/west-spawn.js +8 -2
  99. package/dist/west-inventory.d.ts +25 -0
  100. package/dist/west-inventory.js +97 -0
  101. package/installer/README.md +328 -328
  102. package/installer/install.sh +2 -2
  103. package/installer/templates/project/.typecad/activate-zephyr.ps1 +1 -1
  104. package/installer/templates/project/.typecad/activate-zephyr.sh +1 -1
  105. package/installer/templates/project/.vscode/settings.json +1 -1
  106. package/installer/templates/project/README.md +2 -2
  107. package/package.json +5 -5
  108. package/src/as-built.ts +206 -206
  109. package/src/audit.ts +529 -0
  110. package/src/boardgen.ts +265 -50
  111. package/src/chips/resolve.ts +21 -0
  112. package/src/chips/types.ts +10 -1
  113. package/src/display/bindings.ts +347 -0
  114. package/src/display/gfx.ts +318 -306
  115. package/src/display/index.ts +87 -70
  116. package/src/display/mipi-dbi-host.ts +183 -0
  117. package/src/display/profiles.ts +458 -139
  118. package/src/display/touch-adapter.ts +119 -49
  119. package/src/display/ui-adapter-eink.ts +13 -0
  120. package/src/display/ui-adapter-gray.ts +178 -0
  121. package/src/display/ui-adapter-mono.ts +238 -0
  122. package/src/display/ui-adapter-native.ts +304 -0
  123. package/src/display/ui-adapter-shared.ts +125 -0
  124. package/src/display/ui-adapter.ts +732 -781
  125. package/src/doctor.ts +4 -4
  126. package/src/dt-config/custom-board.ts +2 -2
  127. package/src/dt-config/kconfig.ts +647 -505
  128. package/src/dt-config/overlay.ts +1475 -1058
  129. package/src/framework.manifest.ts +659 -535
  130. package/src/index.ts +16 -6
  131. package/src/licenses.ts +346 -425
  132. package/src/lowering/can.ts +140 -0
  133. package/src/lowering/clock.ts +91 -0
  134. package/src/lowering/fs.ts +135 -135
  135. package/src/lowering/gpio.ts +0 -33
  136. package/src/lowering/hid.ts +261 -0
  137. package/src/lowering/http.ts +264 -32
  138. package/src/lowering/i2c.ts +142 -5
  139. package/src/lowering/i2s.ts +143 -0
  140. package/src/lowering/index.ts +18 -1
  141. package/src/lowering/interrupts.ts +6 -0
  142. package/src/lowering/matrix.ts +70 -0
  143. package/src/lowering/mqtt.ts +109 -8
  144. package/src/lowering/power.ts +41 -0
  145. package/src/lowering/pwm.ts +192 -167
  146. package/src/lowering/sensor.ts +159 -155
  147. package/src/lowering/strip.ts +81 -0
  148. package/src/lowering/thread.ts +5 -1
  149. package/src/lowering/trace.ts +270 -0
  150. package/src/lowering/uart.ts +6 -1
  151. package/src/lowering/usb.ts +16 -13
  152. package/src/lowering/wdt.ts +2 -25
  153. package/src/sbom.ts +1117 -0
  154. package/src/sdk/board-catalog-sync.ts +4 -25
  155. package/src/strategy.ts +2680 -2309
  156. package/src/toolchain/debug-config.ts +262 -522
  157. package/src/toolchain/env-check.ts +279 -285
  158. package/src/toolchain/index.ts +1792 -1359
  159. package/src/toolchain/runners.ts +80 -0
  160. package/src/toolchain/scaffold.ts +355 -296
  161. package/src/toolchain/west-discover.ts +35 -13
  162. package/src/toolchain/west-spawn.ts +174 -168
  163. package/src/west-inventory.ts +102 -0
  164. package/dist/async/timer-polyfill.d.ts +0 -10
  165. package/dist/async/timer-polyfill.js +0 -95
  166. package/dist/chips/board-overrides.d.ts +0 -7
  167. package/dist/chips/board-overrides.js +0 -11
  168. package/dist/chips/esp32.d.ts +0 -2
  169. package/dist/chips/esp32.js +0 -71
  170. package/dist/chips/esp32s3.d.ts +0 -2
  171. package/dist/chips/esp32s3.js +0 -103
  172. package/dist/chips/soc/.d.ts +0 -2
  173. package/dist/chips/soc/.js +0 -129
  174. package/dist/chips/soc/esp32.d.ts +0 -2
  175. package/dist/chips/soc/esp32.js +0 -120
  176. package/dist/chips/soc/esp32c3.d.ts +0 -2
  177. package/dist/chips/soc/esp32c3.js +0 -90
  178. package/dist/chips/soc/esp32c6.d.ts +0 -2
  179. package/dist/chips/soc/esp32c6.js +0 -109
  180. package/dist/chips/soc/esp32s3.d.ts +0 -2
  181. package/dist/chips/soc/esp32s3.js +0 -189
  182. package/dist/chips/soc/index.d.ts +0 -2
  183. package/dist/chips/soc/index.js +0 -23
  184. package/dist/chips/soc/nrf52840.d.ts +0 -2
  185. package/dist/chips/soc/nrf52840.js +0 -130
  186. package/dist/chips/soc/rp2040.d.ts +0 -2
  187. package/dist/chips/soc/rp2040.js +0 -141
  188. package/dist/chips/soc/rp2350a.d.ts +0 -2
  189. package/dist/chips/soc/rp2350a.js +0 -145
  190. package/dist/chips/soc/samd21g18a.d.ts +0 -2
  191. package/dist/chips/soc/samd21g18a.js +0 -143
  192. package/dist/chips/soc/stm32f411xe.d.ts +0 -2
  193. package/dist/chips/soc/stm32f411xe.js +0 -251
  194. package/dist/chips/xiao-ble.d.ts +0 -2
  195. package/dist/chips/xiao-ble.js +0 -100
  196. package/dist/lowering/pulse.d.ts +0 -7
  197. package/dist/lowering/pulse.js +0 -51
  198. package/dist/lowering/tone.d.ts +0 -10
  199. package/dist/lowering/tone.js +0 -63
  200. package/dist/lowering/worker-backing.d.ts +0 -14
  201. package/dist/lowering/worker-backing.js +0 -79
  202. package/dist/lowering/worker.d.ts +0 -6
  203. package/dist/lowering/worker.js +0 -14
  204. package/dist/sdk/board-data.generated.d.ts +0 -2
  205. package/dist/sdk/board-data.generated.js +0 -4
  206. package/dist/sdk/catalog-walker.d.ts +0 -90
  207. package/dist/sdk/catalog-walker.js +0 -682
  208. package/dist/sdk/dts-reader.d.ts +0 -83
  209. package/dist/sdk/dts-reader.js +0 -596
  210. package/src/debug-codegen.ts +0 -207
@@ -1,535 +1,659 @@
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
+ // Servo (hal/servo.ts): calibrated 50 Hz sugar on the same channel —
108
+ // clamping and the angle→pulse mapping run in the lowered C++.
109
+ 'pwm.servo_us': 'supported',
110
+ 'pwm.servo_angle': 'supported',
111
+ 'pwm.servo_idle': 'supported',
112
+ },
113
+ },
114
+
115
+ // ── Supported: LED strip (ws2812-spi over led_strip API) ───────────────
116
+ strip: {
117
+ // DAC discipline: lowers on every board with a wired SPI controller;
118
+ // a board with none lowers to a comment (profileDiagnostics flags it).
119
+ supported: true,
120
+ partialCoverage: true,
121
+ ops: {
122
+ // Strip (hal/strip.ts): buffer edits + one-wire-transaction show.
123
+ 'strip.set_pixel': 'supported',
124
+ 'strip.fill': 'supported',
125
+ 'strip.show': 'supported',
126
+ },
127
+ },
128
+
129
+ // ── Board-gated: USB HID keyboard/mouse (zephyr,hid-device) ───────────
130
+ hid: {
131
+ // Same BOARD-GATED shape as usb below: the lowering only fires when
132
+ // the board's manifest carries zephyr.usb.* — the manifest probe runs
133
+ // with no board, so the honest declaration is unsupported here. The
134
+ // board module exports Keyboard/Mouse only on USB-capable boards
135
+ // (the module-resolution gate); one HID interface per program (the v1
136
+ // ceiling — mixing the two classes is a compile-time #error).
137
+ supported: false,
138
+ unsupportedReason: 'Board-gated: HID lowers only on boards whose DTS enables the USB device controller (boardgen emits zephyr.usb.* from the catalog).',
139
+ partialCoverage: false,
140
+ ops: unsupportedOps('hid.'),
141
+ },
142
+
143
+ // ── Supported: CAN bus (harvested can@ controllers) ────────────────────
144
+ can: {
145
+ supported: true,
146
+ partialCoverage: true,
147
+ ops: {
148
+ // Classic CAN (11/29-bit ids, ≤8-byte payloads). Partial: CAN-FD,
149
+ // bitrate data phase, and RTR frames are not surfaced in v1.
150
+ 'can.begin': 'supported',
151
+ 'can.send': 'supported',
152
+ 'can.on_receive': 'supported',
153
+ },
154
+ },
155
+
156
+ // ── Supported: wall-clock time (rtc alias / counter shim) ─────────────
157
+ clock: {
158
+ supported: true,
159
+ partialCoverage: true,
160
+ ops: {
161
+ // set/now in Unix epoch seconds. Partial: v1 semantics are
162
+ // session-scoped on counter-backed boards — only hardware-calendar
163
+ // aliases persist across power loss.
164
+ 'clock.set': 'supported',
165
+ 'clock.now': 'supported',
166
+ },
167
+ },
168
+
169
+ // ── Supported: explicit power-state entry (sys_poweroff) ───────────────
170
+ power: {
171
+ supported: true,
172
+ partialCoverage: true,
173
+ ops: {
174
+ // Soft-off entry — never returns; wake by reset or a board wake
175
+ // source. Partial: the light states belong to the idle policy, not
176
+ // the app (no op surface for them in v1).
177
+ 'power.off': 'supported',
178
+ // off_for arms the RTC wake timer before soft-off — ESP32 family
179
+ // (the harvested rtc_timer fact); boards without the fact lower
180
+ // to a comment naming the limitation (the DAC discipline).
181
+ 'power.off_for': 'supported',
182
+ },
183
+ },
184
+
185
+ // ── Supported: GPIO key matrix (gpio-kbd-matrix, input subsystem) ─────
186
+ matrix: {
187
+ supported: true,
188
+ partialCoverage: false,
189
+ ops: {
190
+ // Matrix (hal/matrix.ts): (row, col, pressed) trampoline.
191
+ 'matrix.on_key': 'supported',
192
+ },
193
+ },
194
+
195
+ // ── Supported: ADC (SAADC via adc_read + channel setup) ────────────────
196
+ adc: {
197
+ supported: true,
198
+ partialCoverage: false,
199
+ ops: {
200
+ // Thin ADC (hal/adc-pin.ts): construction gain/reference tokens,
201
+ // lazy inline channel setup.
202
+ 'adc.read_raw': 'supported',
203
+ 'adc.read_mv': 'supported',
204
+ },
205
+ },
206
+ dac: {
207
+ // ESP32 DAC (2× 8-bit channels on GPIO25/26) via the Zephyr DAC driver
208
+ // (dac_channel_setup + dac_write_value). nRF52840 / ESP32-S3 have no DAC;
209
+ // usage there lowers to a comment and profileDiagnostics flags it
210
+ // (zephyr-dac-pin-unavailable).
211
+ supported: true,
212
+ partialCoverage: true,
213
+ ops: {
214
+ // Thin DAC (hal/dac-pin.ts): raw code, construction resolution.
215
+ 'dac.write_value': 'supported',
216
+ },
217
+ },
218
+ interrupts: {
219
+ supported: true,
220
+ partialCoverage: true,
221
+ unsupportedReason: undefined,
222
+ ops: {
223
+ 'interrupt.detach': 'supported',
224
+ // Thin GPIO interrupts (hal/gpio-pin.ts onInterrupt): INT_* tokens,
225
+ // covering the level modes the legacy mode strings could not express.
226
+ 'interrupt.attach_flags': 'supported',
227
+ },
228
+ },
229
+ i2c: {
230
+ supported: true,
231
+ partialCoverage: false,
232
+ ops: {
233
+ // Thin I2C device (hal/i2c-target.ts): Zephyr register verbs.
234
+ 'i2c.reg_write': 'supported',
235
+ 'i2c.reg_read': 'supported',
236
+ 'i2c.reg_update': 'supported',
237
+ 'i2c.dev_write': 'supported',
238
+ // I2C responder (hal/i2c-responder.ts): this board as an I2C target.
239
+ 'i2c.resp_on_receive': 'supported',
240
+ 'i2c.resp_on_request': 'supported',
241
+ 'i2c.resp_available': 'supported',
242
+ 'i2c.resp_read': 'supported',
243
+ 'i2c.resp_write': 'supported',
244
+ },
245
+ },
246
+ spi: {
247
+ supported: true,
248
+ partialCoverage: false,
249
+ ops: {
250
+ // Thin SPI device (hal/spi-target.ts): spi_dt_spec against the DT
251
+ // child node the overlay emits per constructed target.
252
+ 'spi.transceive': 'supported',
253
+ 'spi.dev_write': 'supported',
254
+ 'spi.reg_read': 'supported',
255
+ },
256
+ },
257
+ uart: {
258
+ supported: true,
259
+ partialCoverage: true,
260
+ ops: {
261
+ // Thin UART (hal/uart-port.ts): poll API with construction baud.
262
+ 'uart.poll_write': 'supported',
263
+ // Interrupt-drained RX ring (hal/uart-port.ts).
264
+ 'uart.rx_arm': 'supported',
265
+ 'uart.rx_available': 'supported',
266
+ 'uart.rx_peek': 'supported',
267
+ 'uart.rx_read': 'supported',
268
+ },
269
+ },
270
+ board: {
271
+ // Board constant resolution. Board.definition.<path> /
272
+ // Pins.definition.<path> property accesses are folded by
273
+ // ZephyrStrategy.renderBoardDefinitionAccess against the loaded
274
+ // board/MCU constants — the same mechanism framework-arduino uses. The
275
+ // lone HAL op (board.resolve) is constant-folded at IR-build time
276
+ // (expression-to-ir.ts / hal-emitter.ts), so it only reaches the
277
+ // resolver as a dead-letter; 'probe-inconclusive' reflects that the
278
+ // minimal validator probe carries no path/board constants to resolve.
279
+ supported: true,
280
+ partialCoverage: false,
281
+ ops: { 'board.resolve': 'probe-inconclusive' },
282
+ },
283
+ wdt: {
284
+ supported: true,
285
+ partialCoverage: false,
286
+ ops: {
287
+ 'wdt.disable': 'supported',
288
+ // Thin Watchdog (hal/watchdog.ts): construction timeout in ms.
289
+ 'wdt.setup': 'supported',
290
+ 'wdt.feed': 'supported',
291
+ },
292
+ },
293
+ // Thin Counter (hal/counter.ts) — Zephyr's counter driver with Zephyr's
294
+ // verbs, over the same per-instance state as hwtimer.*. Chips without a
295
+ // declared free counter lower to a comment + profileDiagnostics flag.
296
+ counter: {
297
+ supported: true,
298
+ partialCoverage: true,
299
+ ops: {
300
+ 'counter.on_alarm': 'supported',
301
+ 'counter.start': 'supported',
302
+ 'counter.stop': 'supported',
303
+ },
304
+ },
305
+ // Thin Thread (hal/thread.ts) — kernel threads. start(fn) is
306
+ // k_thread_create (K_NO_WAIT) over a per-slot stack sized by the
307
+ // construction stackKb; join is k_thread_join (K_FOREVER).
308
+ thread: {
309
+ supported: true,
310
+ partialCoverage: false,
311
+ ops: {
312
+ 'thread.start': 'supported',
313
+ 'thread.join': 'supported',
314
+ },
315
+ },
316
+ // ── Partial: WiFi (STA connect + scan + config via conn_mgr/net_mgmt) ────
317
+ // ESP32 family only (esp32, esp32s3, esp32c3, esp32c6) — profileDiagnostics
318
+ // flags wifi usage on radioless chips (nRF52840, RP2040/RP2350).
319
+ // AP mode, credential persistence, static IP, and event callbacks deferred.
320
+ wifi: {
321
+ supported: true,
322
+ partialCoverage: true,
323
+ // Unsupported surface: AP client enumeration/IP/per-station-config have no
324
+ // driver hook; credentials need a custom settings-subsystem layer; static IP
325
+ // / auto-reconnect / tx-power aren't wifi-shaped or aren't exposed by the
326
+ // esp32 Zephyr driver. See per-op reasons.
327
+ unsupportedReason: 'Per-station AP enumeration and credential persistence have no Zephyr lowering (no driver/Kconfig hook).',
328
+ ops: {
329
+ // Station (8) — join carries the construction facts (credentials,
330
+ // security, band/channel, timeout, static IPv4, power-save);
331
+ // net_mgmt connect/disconnect + L4 connectivity state underneath.
332
+ 'wifi.join': 'supported', 'wifi.connect_start': 'supported',
333
+ 'wifi.disconnect': 'supported', 'wifi.is_connected': 'supported',
334
+ 'wifi.local_ip': 'supported', 'wifi.rssi': 'supported',
335
+ 'wifi.mac': 'supported',
336
+ // Scan (8) — net_mgmt NET_REQUEST_WIFI_SCAN + result pool.
337
+ // scan_start is the async split of scan (kick + poll scan_done);
338
+ // emitted synthetically by the async tier, no TS-facing method.
339
+ 'wifi.scan': 'supported',
340
+ 'wifi.scan_start': 'supported',
341
+ 'wifi.scan_done': 'supported', 'wifi.scan_count': 'supported',
342
+ 'wifi.scan_ssid': 'supported', 'wifi.scan_rssi': 'supported',
343
+ 'wifi.scan_encryption': 'supported', 'wifi.scan_channel': 'supported',
344
+ // AP mode (2) — esp32 driver wires ap_enable/ap_disable. Only
345
+ // ssid/psk/channel are honored (the thin WiFiAP facts carry exactly those).
346
+ 'wifi.ap_start': 'supported', 'wifi.ap_stop': 'supported',
347
+ // on_event: 'disconnect' (NET_EVENT_L4_DISCONNECTED) + 'connect'
348
+ // (NET_EVENT_IPV4_ADDR_ADD) are lowered.
349
+ 'wifi.on_event': 'supported',
350
+ // ── Genuinely unsupported (no driver hook) ─────────────────────────
351
+ // ap_client_count: no API to enumerate connected AP stations.
352
+ 'wifi.ap_client_count': 'unsupported',
353
+ },
354
+
355
+ },
356
+ http: {
357
+ supported: true,
358
+ partialCoverage: false,
359
+ // HTTP/S client over Zephyr BSD sockets + http_client_req (TLS via
360
+ // mbedTLS / NET_SOCKETS_SOCKOPT_TLS). The __tc_http shim owns url parse,
361
+ // DNS (getaddrinfo), socket/TLS connect, and body accumulation. Requires
362
+ // a networked target (ESP32 WiFi); profileDiagnostics flags usage on a
363
+ // radioless chip as 'zephyr-http-unavailable-on-target'.
364
+ ops: {
365
+ 'http.begin': 'supported',
366
+ 'http.set_header': 'supported', 'http.set_timeout': 'supported',
367
+ 'http.set_max_body': 'supported', 'http.set_body': 'supported',
368
+ 'http.set_insecure': 'supported', 'http.set_ca_cert': 'supported',
369
+ 'http.send': 'supported', 'http.send_start': 'supported',
370
+ 'http.done': 'supported', 'http.status': 'supported',
371
+ 'http.ok': 'supported', 'http.body': 'supported',
372
+ 'http.content_length': 'supported', 'http.response_header': 'supported',
373
+ },
374
+ },
375
+ display: {
376
+ supported: true,
377
+ partialCoverage: true,
378
+ // Partial: mono profiles (ssd1306-zephyr) drive display.* ops via the
379
+ // direct GFX runtime only — no CuttlefishGFX UI rendering path. The
380
+ // direct-spi UI adapters (st7796-zephyr, ili9341-zephyr) drive the
381
+ // panel over spi_write with per-controller Adafruit init tables;
382
+ // st7796-zephyr is the full-fidelity path on the clone rig
383
+ // (18-bit mode, hardware-verified incl. colors + touch interaction;
384
+ // the demo needs psram:'opi' for full rendering and resetPin for the
385
+ // touch module's power enable). The zephyr-display transport rides
386
+ // in-tree panel drivers through Zephyr's display API: st7796s via an
387
+ // adapter-emitted CS-holding mipi-dbi host — colors hardware-confirmed
388
+ // correct on the rig with the byte-swap + CS-hold knobs (the stock
389
+ // bridge corrupts bursts on this clone; a ladder test observed slight
390
+ // uniform dimness vs the direct path's 18-bit mode, so direct remains
391
+ // the max-fidelity option there); ili9341 via the stock mipi-dbi-spi
392
+ // bridge is compile/link-verified only. Drop-in configs (display.driver
393
+ // = any bound compatible + geometry + quirk flags) ride the same path.
394
+ // Touch: FT6336U through the input subsystem (in-tree driver,
395
+ // polling mode, hardware-verified); XPT2046 stays on the raw adapter
396
+ // (the in-tree driver's DT scaling would double-apply calibration).
397
+ unsupportedReason: 'Mono panels (ssd1306) are direct-op only (no UI rendering); the direct-spi ili9341 UI path is not yet hardware-verified; the zephyr-display transport is hue-verified but 16-bit-brightness-limited on the clone st7796s (use direct-spi there) and compile-verified on ili9341; e-ink is out of scope at this time.',
398
+ drivers: ['ili9341-zephyr', 'ili9341-zephyr-display', 'st7796-zephyr', 'st7796-zephyr-display', 'ssd1306-zephyr'],
399
+ colorFormat: 'rgb565',
400
+ ops: {
401
+ 'display.init': 'supported',
402
+ 'display.fill_rect': 'supported',
403
+ 'display.draw_text': 'supported',
404
+ 'display.draw_rect': 'supported',
405
+ 'display.flush': 'supported',
406
+ },
407
+ },
408
+ // ── Supported: BLE (NimBLE GATT peripheral via runtime service register) ──
409
+ ble: {
410
+ supported: true,
411
+ partialCoverage: true,
412
+ ops: Object.fromEntries(
413
+ HAL_OPERATION_KINDS.filter((k) => k.startsWith('ble.')).map((k) => [k, 'supported']),
414
+ ),
415
+ },
416
+
417
+ // ── Supported: Worker offload (k_work system workqueue + k_sem) ──────────
418
+
419
+ // ── Honestly unsupported extended categories ─────────────────────────────
420
+ // These have op-kinds in HAL_OPERATION_KINDS but no Zephyr lowering. Each
421
+ // is declared unsupported (with a reason) so the coverage matrix is uniform
422
+ // and the resolver's `return undefined` for these prefixes is honest. The
423
+ // catchall schema (HalCoverageSchema) validates any declared extended
424
+ // category; declaring them keeps the manifest a complete coverage record.
425
+
426
+ snprintf: {
427
+ supported: false,
428
+ unsupportedReason: 'snprintf.emit is a raw escape hatch; the Zephyr resolver returns undefined (use rawCpp()).',
429
+ partialCoverage: false,
430
+ ops: { 'snprintf.emit': 'unsupported' },
431
+ },
432
+ preferences: {
433
+ // ZMS-backed Zephyr settings. The HAL ESP32-NVS session model (begin/end
434
+ // + typed put/get) is modeled on top of Zephyr's flat settings key-space:
435
+ // begin(ns) records a "tc/<ns>/" prefix; put/get operate on an in-RAM
436
+ // cache populated once at boot by settings_load()'s h_set callback;
437
+ // writes mirror to flash via settings_save_one/settings_delete. The ZMS
438
+ // backend auto-locates the storage_partition fixed-partition (or the
439
+ // /chosen zephyr,settings-partition node — see dt-config/overlay.ts).
440
+ // begin/end are no-ops beyond prefix bookkeeping: Zephyr settings has no
441
+ // session/namespace, but keeping the ops preserves portability with the
442
+ // ESP32 NVS model and leaves a hook for a future session-needing backend.
443
+ supported: true,
444
+ partialCoverage: false,
445
+ ops: {
446
+ 'preferences.clear': 'supported', 'preferences.remove': 'supported',
447
+ 'preferences.put_int': 'supported', 'preferences.get_int': 'supported',
448
+ 'preferences.put_bool': 'supported', 'preferences.get_bool': 'supported',
449
+ 'preferences.put_float': 'supported', 'preferences.get_float': 'supported',
450
+ 'preferences.put_string': 'supported', 'preferences.get_string': 'supported',
451
+ },
452
+ },
453
+ random: {
454
+ // <zephyr/random/random.h> sys_rand_get seeds a userspace xorshift32
455
+ // PRNG (__tc_rand_*); random.seed re-seeds it deterministically (matching
456
+ // Arduino randomSeed). random.int → [0, 2^31-1], random.range → [min,max-1].
457
+ supported: true,
458
+ partialCoverage: false,
459
+ ops: {
460
+ 'random.int': 'supported',
461
+ 'random.range': 'supported',
462
+ 'random.seed': 'supported',
463
+ },
464
+ },
465
+ fs: {
466
+ // littlefs on the board's storage_partition, via <zephyr/fs/fs.h>. The
467
+ // shim mounts at /lfs lazily (formats on first use) and the HAL paths are
468
+ // treated as paths within the filesystem. Requires CONFIG_FILE_SYSTEM +
469
+ // CONFIG_FILE_SYSTEM_LITTLEFS (emitted by the scaffold when fs.* is used)
470
+ // and the storage_partition node.
471
+ supported: true,
472
+ partialCoverage: false,
473
+ ops: {
474
+ 'fs.read_text': 'supported', 'fs.write_text': 'supported',
475
+ 'fs.exists': 'supported', 'fs.remove': 'supported',
476
+ },
477
+ },
478
+ mqtt: {
479
+ supported: true,
480
+ partialCoverage: false,
481
+ // MQTT 3.1.1 client over Zephyr <zephyr/net/mqtt.h> (mqtts:// TLS via
482
+ // MQTT_TRANSPORT_SECURE + the shared mbedTLS matrix). caCert pins the
483
+ // broker's CA (DER-decoded at emit, tls_credential_add sec tag,
484
+ // TLS_PEER_VERIFY_REQUIRED); mqtts:// without a CA stays
485
+ // encrypted-but-unverified (TLS_PEER_VERIFY_NONE). The __tc_mqtt
486
+ // shim resolves the broker, runs the mqtt_input/mqtt_live poll loop on
487
+ // a background k_thread, and dispatches incoming PUBLISHes to the user's
488
+ // onMessage callback. Requires a networked target (ESP32 WiFi);
489
+ // profileDiagnostics flags usage on a radioless chip.
490
+ ops: {
491
+ 'mqtt.connect': 'supported', 'mqtt.set_ca_cert': 'supported',
492
+ 'mqtt.on_message': 'supported',
493
+ 'mqtt.subscribe': 'supported', 'mqtt.publish': 'supported',
494
+ 'mqtt.connected': 'supported', 'mqtt.disconnect': 'supported',
495
+ },
496
+ },
497
+ sensor: {
498
+ // DT-bound sensor parts — the generic catalog (hal/sensor.ts +
499
+ // sensor-catalog.generated.ts). fetch → sensor_sample_fetch,
500
+ // get → sensor_channel_get on a per-sensor device handle; the overlay
501
+ // generator emits the DT child node, and the driver's own Kconfig
502
+ // `default y` lights it up (only CONFIG_SENSOR is set, usage-gated).
503
+ supported: true,
504
+ partialCoverage: false,
505
+ ops: {
506
+ 'sensor.fetch': 'supported',
507
+ 'sensor.get': 'supported',
508
+ },
509
+ },
510
+ i2s: {
511
+ supported: true,
512
+ partialCoverage: true,
513
+ ops: {
514
+ 'i2s.write': 'supported',
515
+ 'i2s.read': 'supported',
516
+ 'i2s.read_at': 'supported',
517
+ },
518
+ },
519
+ twai: {
520
+ supported: false,
521
+ unsupportedReason: 'No CAN / TWAI lowering on Zephyr (Zephyr CAN driver not wired).',
522
+ partialCoverage: false,
523
+ ops: unsupportedOps('twai.'),
524
+ },
525
+ usb: {
526
+ // CDC-ACM serial over the board's USB connector. BOARD-GATED: the
527
+ // lowering only fires when the board's manifest carries zephyr.usb.*
528
+ // (boardgen emits it from the board's own DTS) — the manifest probe
529
+ // runs with no board, so the honest declaration is unsupported here.
530
+ supported: false,
531
+ unsupportedReason: 'Board-gated: USB lowers only on boards whose DTS enables the USB device controller (boardgen emits zephyr.usb.* from the catalog).',
532
+ partialCoverage: false,
533
+ ops: unsupportedOps('usb.'),
534
+ },
535
+ eth: {
536
+ supported: false,
537
+ unsupportedReason: 'No Ethernet MAC lowering on Zephyr.',
538
+ partialCoverage: false,
539
+ ops: unsupportedOps('eth.'),
540
+ },
541
+ espnow: {
542
+ supported: false,
543
+ unsupportedReason: 'ESP-NOW is an ESP-exclusive wireless protocol; no Zephyr lowering.',
544
+ partialCoverage: false,
545
+ ops: unsupportedOps('espnow.'),
546
+ },
547
+ crypto: {
548
+ supported: false,
549
+ unsupportedReason: 'No hardware crypto (AES/SHA/HMAC) lowering on Zephyr.',
550
+ partialCoverage: false,
551
+ ops: unsupportedOps('crypto.'),
552
+ },
553
+ pcnt: {
554
+ supported: false,
555
+ unsupportedReason: 'No pulse-counter (PCNT) lowering on Zephyr.',
556
+ partialCoverage: false,
557
+ ops: unsupportedOps('pcnt.'),
558
+ },
559
+ mcpwm: {
560
+ supported: false,
561
+ unsupportedReason: 'No motor-control PWM (MCPWM) lowering on Zephyr.',
562
+ partialCoverage: false,
563
+ ops: unsupportedOps('mcpwm.'),
564
+ },
565
+ raw: { supported: true },
566
+ },
567
+
568
+ polyfills: {
569
+ emitted: [
570
+ { id: 'cuttlefish_halt', domain: 'standard', notes: 'Mapped to a k_msleep halt loop (exceptions disabled)' },
571
+ { 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' },
572
+ { id: 'string_methods', domain: 'embedded', notes: 'STL-free __tc_* string helpers (const char*, inline ASCII case conv, <cstring> only)' },
573
+ { id: 'static_array', domain: 'embedded', notes: 'STL-free __tc_StaticArray<T,N> wrapper for no-<vector> mutated/struct array literals' },
574
+ { id: 'async_runtime', domain: 'embedded', notes: 'Heap-free static Promise/microtask runtime (generateStaticAsyncRuntime), pumped in loop()' },
575
+ ],
576
+ suppressed: [],
577
+ },
578
+
579
+ toolchain: {
580
+ backend: 'west',
581
+ operations: { prepare: true, compile: true, upload: true, monitor: true, debug: true },
582
+ },
583
+
584
+ libraryResolution: {
585
+ isFrameworkLibraryImport: false,
586
+ getFrameworkLibraryHeaderName: false,
587
+ buildClassNameMap: false,
588
+ tryGenerateLibDecl: false,
589
+ },
590
+
591
+ typeEmission: {
592
+ normalizeCppType: true,
593
+ mathHeader: '<cmath>',
594
+ needsStdString: false,
595
+ needsStdVector: false,
596
+ needsIostream: false,
597
+ needsStdFunction: false,
598
+ stdlibSupport: {
599
+ hasVector: false,
600
+ hasString: false,
601
+ hasIostream: false,
602
+ hasExceptions: false,
603
+ hasRTTI: false,
604
+ recommendedArrayImpl: 'static_array',
605
+ recommendedStringImpl: 'static_string',
606
+ },
607
+ },
608
+
609
+ ambientTypes: ['Preferences'],
610
+
611
+ conformance: {
612
+ // Hardware-test groups moved to the shared HAL suite
613
+ // (packages/hal/tests — common/ + board/ via board test-pins.json), which
614
+ // runs on Zephyr targets through the hal board configs. The
615
+ // hal-resolution snapshots below stay.
616
+ hardwareTestGroups: [],
617
+ // Per-op HAL-resolution suite — one tests/packages/framework-zephyr/
618
+ // hal-resolution/<cat>.test.ts per category, snapshotting the exact C++
619
+ // each op lowers to. Mirrors the framework-esp32 convention. These are
620
+ // pure string-snapshot tests (no hardware); they are the safety net that
621
+ // catches regressions like silent pull-resistor / interrupt no-ops.
622
+ halResolutionTests: [
623
+ 'adc', 'ble', 'board', 'dac', 'fs', 'gpio', 'http', 'hwtimer', 'i2c',
624
+ 'interrupts', 'mqtt', 'preferences', 'pwm', 'random',
625
+ 'spi', 'thin-buses', 'thin-classes', 'thread', 'timing',
626
+ 'uart', 'usb', 'wdt', 'wifi',
627
+ ],
628
+ },
629
+
630
+ // Declared compatibility range for the installed Zephyr RTOS. The framework's
631
+ // build-time version check (toolchain/compat.ts) reads this and fails fast
632
+ // with a clear message when the user's Zephyr is outside the range, instead
633
+ // of letting west/CMake surface a cryptic board-target error (the class of
634
+ // breakage behind the HWMv2 qualifier requirement in Zephyr 4.3+).
635
+ compat: {
636
+ zephyr: '>=4.3 <5.0',
637
+ },
638
+
639
+ // `typecad-hal doctor` prints the detected Zephyr version + compat result and
640
+ // previews how the configured board target resolves for that version.
641
+ doctor: { available: true },
642
+
643
+ // `typecad-hal licenses` enumerates the Zephyr kernel + west manifest projects
644
+ // and resolves each one's SPDX license.
645
+ licenses: { available: true },
646
+
647
+ // `typecad-hal sbom` emits a build-true SBOM (CycloneDX 1.6 / SPDX 2.3):
648
+ // linked west modules + kernel with commit SHAs and licenses, the hashed
649
+ // firmware artifact, the board, and the toolchain in `formulation`;
650
+ // --check/--diff gate the record against the build.
651
+ sbom: { available: true },
652
+
653
+ // `typecad-hal audit` evaluates the last build's merged Kconfig against a
654
+ // security baseline (shell/console exposure, entropy, watchdog, hard-coded
655
+ // credentials...), with committed waivers (.typecad-hal/audit-waivers.json)
656
+ // recorded as deviations and a machine-readable sidecar
657
+ // (<buildDir>/security-audit.json).
658
+ audit: { available: true },
659
+ });