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

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 (191) hide show
  1. package/README.md +22 -35
  2. package/dist/as-built.d.ts +33 -0
  3. package/dist/as-built.js +188 -0
  4. package/dist/async/timer-polyfill.js +1 -1
  5. package/dist/boardgen.d.ts +128 -0
  6. package/dist/boardgen.js +1366 -0
  7. package/dist/chips/board-overrides.d.ts +7 -0
  8. package/dist/chips/board-overrides.js +11 -0
  9. package/dist/chips/controllers.d.ts +28 -8
  10. package/dist/chips/controllers.js +49 -12
  11. package/dist/chips/esp32s3.js +49 -0
  12. package/dist/chips/index.d.ts +6 -12
  13. package/dist/chips/index.js +18 -34
  14. package/dist/chips/resolve.d.ts +16 -2
  15. package/dist/chips/resolve.js +298 -15
  16. package/dist/chips/soc/.d.ts +2 -0
  17. package/dist/chips/soc/.js +129 -0
  18. package/dist/chips/soc/esp32.d.ts +2 -0
  19. package/dist/chips/soc/esp32.js +120 -0
  20. package/dist/chips/soc/esp32c3.d.ts +2 -0
  21. package/dist/chips/soc/esp32c3.js +90 -0
  22. package/dist/chips/soc/esp32c6.d.ts +2 -0
  23. package/dist/chips/soc/esp32c6.js +109 -0
  24. package/dist/chips/soc/esp32s3.d.ts +2 -0
  25. package/dist/chips/soc/esp32s3.js +189 -0
  26. package/dist/chips/soc/index.d.ts +2 -0
  27. package/dist/chips/soc/index.js +23 -0
  28. package/dist/chips/soc/nrf52840.d.ts +2 -0
  29. package/dist/chips/soc/nrf52840.js +130 -0
  30. package/dist/chips/soc/rp2040.d.ts +2 -0
  31. package/dist/chips/soc/rp2040.js +141 -0
  32. package/dist/chips/soc/rp2350a.d.ts +2 -0
  33. package/dist/chips/soc/rp2350a.js +145 -0
  34. package/dist/chips/soc/samd21g18a.d.ts +2 -0
  35. package/dist/chips/soc/samd21g18a.js +143 -0
  36. package/dist/chips/soc/stm32f411xe.d.ts +2 -0
  37. package/dist/chips/soc/stm32f411xe.js +251 -0
  38. package/dist/chips/types.d.ts +382 -12
  39. package/dist/chips/xiao-ble.js +33 -0
  40. package/dist/display/profiles.js +1 -1
  41. package/dist/doctor.js +38 -4
  42. package/dist/dt-config/custom-board.d.ts +23 -0
  43. package/dist/dt-config/custom-board.js +227 -0
  44. package/dist/dt-config/kconfig.d.ts +83 -1
  45. package/dist/dt-config/kconfig.js +116 -27
  46. package/dist/dt-config/overlay.js +623 -18
  47. package/dist/framework.manifest.d.ts +20 -30
  48. package/dist/framework.manifest.js +124 -178
  49. package/dist/index.d.ts +4 -1
  50. package/dist/index.js +8 -2
  51. package/dist/lowering/adc.d.ts +26 -5
  52. package/dist/lowering/adc.js +137 -31
  53. package/dist/lowering/ble.js +31 -28
  54. package/dist/lowering/dac.js +9 -9
  55. package/dist/lowering/fs.js +18 -10
  56. package/dist/lowering/gpio.d.ts +2 -0
  57. package/dist/lowering/gpio.js +90 -10
  58. package/dist/lowering/http.js +39 -14
  59. package/dist/lowering/hwtimer.d.ts +4 -0
  60. package/dist/lowering/hwtimer.js +35 -10
  61. package/dist/lowering/i2c.js +46 -54
  62. package/dist/lowering/index.d.ts +5 -5
  63. package/dist/lowering/index.js +15 -16
  64. package/dist/lowering/interrupts.d.ts +22 -5
  65. package/dist/lowering/interrupts.js +104 -22
  66. package/dist/lowering/mqtt.js +12 -6
  67. package/dist/lowering/power.js +0 -4
  68. package/dist/lowering/preferences.js +49 -77
  69. package/dist/lowering/pulse.js +5 -36
  70. package/dist/lowering/pwm.d.ts +32 -3
  71. package/dist/lowering/pwm.js +105 -17
  72. package/dist/lowering/random.js +3 -3
  73. package/dist/lowering/sensor.d.ts +37 -0
  74. package/dist/lowering/sensor.js +119 -0
  75. package/dist/lowering/spi.d.ts +12 -0
  76. package/dist/lowering/spi.js +65 -40
  77. package/dist/lowering/thread.d.ts +14 -0
  78. package/dist/lowering/thread.js +55 -0
  79. package/dist/lowering/timing.js +20 -46
  80. package/dist/lowering/tone.js +18 -5
  81. package/dist/lowering/uart.d.ts +13 -0
  82. package/dist/lowering/uart.js +72 -51
  83. package/dist/lowering/usb.d.ts +34 -0
  84. package/dist/lowering/usb.js +206 -0
  85. package/dist/lowering/wdt.d.ts +1 -1
  86. package/dist/lowering/wdt.js +17 -7
  87. package/dist/lowering/wifi.d.ts +3 -2
  88. package/dist/lowering/wifi.js +120 -111
  89. package/dist/sdk/board-catalog-sync.d.ts +16 -0
  90. package/dist/sdk/board-catalog-sync.js +41 -0
  91. package/dist/sdk/board-data.generated.d.ts +2 -0
  92. package/dist/sdk/board-data.generated.js +4 -0
  93. package/dist/sdk/catalog-walker.d.ts +90 -0
  94. package/dist/sdk/catalog-walker.js +682 -0
  95. package/dist/sdk/dts-reader.d.ts +83 -0
  96. package/dist/sdk/dts-reader.js +596 -0
  97. package/dist/strategy.d.ts +136 -17
  98. package/dist/strategy.js +881 -212
  99. package/dist/toolchain/bossac-touch.d.ts +18 -0
  100. package/dist/toolchain/bossac-touch.js +88 -0
  101. package/dist/toolchain/compat.d.ts +2 -2
  102. package/dist/toolchain/compat.js +8 -3
  103. package/dist/toolchain/debug-config.d.ts +21 -26
  104. package/dist/toolchain/debug-config.js +212 -72
  105. package/dist/toolchain/env-check.d.ts +4 -4
  106. package/dist/toolchain/env-check.js +5 -5
  107. package/dist/toolchain/index.d.ts +66 -1
  108. package/dist/toolchain/index.js +713 -58
  109. package/dist/toolchain/scaffold.d.ts +9 -0
  110. package/dist/toolchain/scaffold.js +84 -7
  111. package/dist/toolchain/west-discover.d.ts +8 -1
  112. package/dist/toolchain/west-discover.js +19 -6
  113. package/dist/toolchain/west-spawn.js +17 -5
  114. package/installer/CHANGELOG.md +473 -0
  115. package/installer/README.md +328 -0
  116. package/installer/environment.yml +46 -0
  117. package/installer/etc/conda/activate.d/zephyr.bat +7 -0
  118. package/installer/etc/conda/activate.d/zephyr.ps1 +12 -0
  119. package/installer/etc/conda/activate.d/zephyr.sh +20 -0
  120. package/installer/etc/conda/deactivate.d/zephyr.bat +6 -0
  121. package/installer/etc/conda/deactivate.d/zephyr.ps1 +6 -0
  122. package/installer/etc/conda/deactivate.d/zephyr.sh +4 -0
  123. package/installer/install.mjs +580 -0
  124. package/installer/install.ps1 +619 -0
  125. package/installer/install.sh +310 -0
  126. package/installer/lib/detect-platform.sh +62 -0
  127. package/installer/lib/fetch-sdk.sh +304 -0
  128. package/installer/lib/init-workspace.sh +153 -0
  129. package/installer/lib/write-activation.sh +76 -0
  130. package/installer/patches/tfm-c-include-separators.patch +19 -0
  131. package/installer/patches/zephyr-tfm-nordic-zephyr-include.patch +24 -0
  132. package/installer/templates/project/.typecad/activate-zephyr.ps1 +31 -0
  133. package/installer/templates/project/.typecad/activate-zephyr.sh +31 -0
  134. package/installer/templates/project/.vscode/settings.json +30 -0
  135. package/installer/templates/project/README.md +58 -0
  136. package/installer/versions.env +110 -0
  137. package/package.json +16 -16
  138. package/src/as-built.ts +206 -0
  139. package/src/boardgen.ts +1428 -0
  140. package/src/chips/controllers.ts +61 -12
  141. package/src/chips/index.ts +35 -54
  142. package/src/chips/resolve.ts +316 -14
  143. package/src/chips/types.ts +388 -13
  144. package/src/display/profiles.ts +1 -1
  145. package/src/doctor.ts +110 -77
  146. package/src/dt-config/custom-board.ts +325 -0
  147. package/src/dt-config/kconfig.ts +177 -19
  148. package/src/dt-config/overlay.ts +1058 -450
  149. package/src/framework.manifest.ts +125 -178
  150. package/src/index.ts +57 -41
  151. package/src/lowering/adc.ts +222 -102
  152. package/src/lowering/ble.ts +32 -28
  153. package/src/lowering/dac.ts +82 -82
  154. package/src/lowering/fs.ts +18 -10
  155. package/src/lowering/gpio.ts +242 -152
  156. package/src/lowering/http.ts +512 -488
  157. package/src/lowering/hwtimer.ts +139 -101
  158. package/src/lowering/i2c.ts +116 -126
  159. package/src/lowering/index.ts +91 -93
  160. package/src/lowering/interrupts.ts +113 -21
  161. package/src/lowering/mqtt.ts +12 -6
  162. package/src/lowering/preferences.ts +327 -354
  163. package/src/lowering/pwm.ts +167 -74
  164. package/src/lowering/random.ts +3 -3
  165. package/src/lowering/sensor.ts +155 -0
  166. package/src/lowering/spi.ts +164 -124
  167. package/src/lowering/thread.ts +63 -0
  168. package/src/lowering/timing.ts +46 -72
  169. package/src/lowering/uart.ts +73 -51
  170. package/src/lowering/usb.ts +221 -0
  171. package/src/lowering/wdt.ts +95 -84
  172. package/src/lowering/wifi.ts +120 -111
  173. package/src/sdk/board-catalog-sync.ts +62 -0
  174. package/src/strategy.ts +888 -229
  175. package/src/toolchain/bossac-touch.ts +112 -0
  176. package/src/toolchain/compat.ts +8 -3
  177. package/src/toolchain/debug-config.ts +666 -522
  178. package/src/toolchain/env-check.ts +9 -9
  179. package/src/toolchain/index.ts +765 -44
  180. package/src/toolchain/scaffold.ts +81 -7
  181. package/src/toolchain/west-discover.ts +334 -316
  182. package/src/toolchain/west-spawn.ts +17 -5
  183. package/src/async/timer-polyfill.ts +0 -107
  184. package/src/chips/esp32.ts +0 -74
  185. package/src/chips/esp32s3.ts +0 -57
  186. package/src/chips/xiao-ble.ts +0 -70
  187. package/src/lowering/power.ts +0 -70
  188. package/src/lowering/pulse.ts +0 -93
  189. package/src/lowering/tone.ts +0 -60
  190. package/src/lowering/worker-backing.ts +0 -94
  191. package/src/lowering/worker.ts +0 -17
@@ -62,7 +62,8 @@ export function httpInitLines() {
62
62
  ` const char* body; // request body (user-owned)`,
63
63
  ` int32_t body_len;`,
64
64
  ` bool insecure;`,
65
- ` const char* ca_cert; // PEM, user-owned; nullptr = no pinned CA`,
65
+ ` const uint8_t* ca_der; // DER, user-owned; nullptr = no pinned CA`,
66
+ ` size_t ca_der_len;`,
66
67
  ` const char* hdr_name[__TC_HTTP_MAX_HEADERS];`,
67
68
  ` const char* hdr_value[__TC_HTTP_MAX_HEADERS];`,
68
69
  ` int32_t hdr_count;`,
@@ -199,17 +200,23 @@ export function httpInitLines() {
199
200
  ` // Register the pinned CA as the active sec tag. tls_credential_add`,
200
201
  ` // is idempotent-enough for the single-slot model: a duplicate tag`,
201
202
  ` // is an error we ignore, and the most recently added cert wins.`,
202
- ` if (__tc_http.ca_cert != nullptr) {`,
203
- ` (void)tls_credential_add(__TC_HTTP_SEC_TAG,`,
203
+ ` if (__tc_http.ca_der != nullptr) {`,
204
+ ` int cred_rc = tls_credential_add(__TC_HTTP_SEC_TAG,`,
204
205
  ` TLS_CREDENTIAL_CA_CERTIFICATE,`,
205
- ` __tc_http.ca_cert, strlen(__tc_http.ca_cert) + 1U);`,
206
+ ` __tc_http.ca_der, __tc_http.ca_der_len);`,
206
207
  ` sec_tag_t tags[1] = { __TC_HTTP_SEC_TAG };`,
207
208
  ` (void)zsock_setsockopt(sock_tls, SOL_TLS, TLS_SEC_TAG_LIST, tags, sizeof(tags));`,
209
+ ` (void)cred_rc;`,
208
210
  ` }`,
209
211
  ` // Hostname SNI + verification. insecure() disables verification;`,
210
212
  ` // the default (no caCert, no insecure) leaves verification required`,
211
213
  ` // with no sec tag, so the handshake fails safely until a CA is set.`,
212
- ` (void)zsock_setsockopt(sock_tls, SOL_TLS, TLS_HOSTNAME, __tc_http.host, strlen(__tc_http.host) + 1U);`,
214
+ ` // mbedtls does not match IP literals against SAN entries —`,
215
+ ` // skip the hostname check for IP hosts (chain-only verify).`,
216
+ ` bool host_is_ip = true; for (const char* p3 = __tc_http.host; *p3 != 0; p3++) { if (!((*p3 >= '0' && *p3 <= '9') || *p3 == '.')) { host_is_ip = false; break; } }`,
217
+ ` if (!host_is_ip) {`,
218
+ ` (void)zsock_setsockopt(sock_tls, SOL_TLS, TLS_HOSTNAME, __tc_http.host, strlen(__tc_http.host) + 1U);`,
219
+ ` }`,
213
220
  ` int32_t verify = __tc_http.insecure ? TLS_PEER_VERIFY_NONE : TLS_PEER_VERIFY_REQUIRED;`,
214
221
  ` (void)zsock_setsockopt(sock_tls, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));`,
215
222
  ` }`,
@@ -245,7 +252,8 @@ export function httpInitLines() {
245
252
  ` __tc_http.body = nullptr;`,
246
253
  ` __tc_http.body_len = 0;`,
247
254
  ` __tc_http.insecure = false;`,
248
- ` __tc_http.ca_cert = nullptr;`,
255
+ ` __tc_http.ca_der = nullptr;`,
256
+ ` __tc_http.ca_der_len = 0;`,
249
257
  ` __tc_http.hdr_count = 0;`,
250
258
  ` __tc_http.status = 0;`,
251
259
  ` __tc_http.done = false;`,
@@ -293,7 +301,7 @@ export function httpInitLines() {
293
301
  ``,
294
302
  `static inline void __tc_http_set_insecure(void) { __tc_http.insecure = true; }`,
295
303
  ``,
296
- `static inline void __tc_http_set_ca_cert(const char* pem) { __tc_http.ca_cert = pem; }`,
304
+ `static inline void __tc_http_set_ca_cert_der(const uint8_t* der, size_t len) { __tc_http.ca_der = der; __tc_http.ca_der_len = len; }`,
297
305
  ``,
298
306
  `// ── perform (the shared blocking core) ──────────────────────────────────`,
299
307
  `// Parse url → open socket → build http_request → http_client_req → close.`,
@@ -441,12 +449,12 @@ const METHOD_MAP = {
441
449
  export function lowerHttp(op) {
442
450
  const o = op;
443
451
  switch (op.operation) {
444
- case 'http.reset':
445
- return { code: `__tc_http_reset();` };
446
452
  case 'http.begin': {
447
453
  const methodName = String(o.method).replace(/^["']|["']$/g, '').toUpperCase();
448
454
  const method = METHOD_MAP[methodName] ?? 'HTTP_GET';
449
- return { code: `__tc_http_begin(${method}, ${s(o.url)});` };
455
+ // Fresh shim state per request — the reset used to ride the deleted
456
+ // factory op (http.reset); construction now owns it.
457
+ return { code: `__tc_http_reset(); __tc_http_begin(${method}, ${s(o.url)});` };
450
458
  }
451
459
  case 'http.set_header':
452
460
  return { code: `__tc_http_set_header(${s(o.name)}, ${s(o.value)});` };
@@ -455,11 +463,28 @@ export function lowerHttp(op) {
455
463
  case 'http.set_max_body':
456
464
  return { code: `__tc_http_set_max_body(${s(o.bytes)});` };
457
465
  case 'http.set_body':
458
- return { code: `__tc_http_set_body(${s(o.data)}, ${o.json ? 'true' : 'false'});` };
466
+ // '' is the no-body sentinel the op always emits from send(); elide.
467
+ return { code: o.data === '""' || o.data === '' ? '' : `__tc_http_set_body(${s(o.data)}, ${o.json ? 'true' : 'false'});` };
459
468
  case 'http.set_insecure':
460
- return { code: `__tc_http_set_insecure();` };
461
- case 'http.set_ca_cert':
462
- return { code: `__tc_http_set_ca_cert(${s(o.pem)});` };
469
+ return { code: o.insecure ? `__tc_http_set_insecure();` : '' };
470
+ case 'http.set_ca_cert': {
471
+ // This tree's tf-psa-crypto mbedTLS has no PEM parser (MBEDTLS_PEM_C is
472
+ // not settable) — decode the PEM literal to DER here and emit a static
473
+ // byte array. tls_credential_add takes the DER directly (crt_is_pem is
474
+ // false → mbedtls_x509_crt_parse_der_nocopy on the verify path).
475
+ const pem = String(o.pem).replace(/^"|"$/g, '').replace(/\\n/g, '\n');
476
+ if (pem.replace(/\s+/g, '') === '') {
477
+ // No-CA sentinel (Request always emits the op; opts.caCert defaults
478
+ // to '') — elide silently so plain-HTTP requests carry no comment.
479
+ return { code: '' };
480
+ }
481
+ const b64 = pem.replace(/-----[A-Z ]+-----/g, '').replace(/\s+/g, '');
482
+ const der = Buffer.from(b64, 'base64');
483
+ if (der.length < 100)
484
+ return { code: `// tc-http: caCert PEM failed to decode` };
485
+ const hex = [...der].map((b) => `0x${b.toString(16).padStart(2, '0')}`).join(', ');
486
+ return { code: `{ static const uint8_t __tc_ca_der[] = { ${hex} }; __tc_http_set_ca_cert_der(__tc_ca_der, sizeof(__tc_ca_der)); }` };
487
+ }
463
488
  case 'http.send':
464
489
  return { expression: `__tc_http_send()` };
465
490
  case 'http.send_start':
@@ -13,3 +13,7 @@ export declare function lowerHwtimer(op: HALOpIR, chip: ZephyrChipDescriptor): {
13
13
  code?: string;
14
14
  expression?: string;
15
15
  };
16
+ export declare function lowerCounter(op: HALOpIR, chip: ZephyrChipDescriptor): {
17
+ code?: string;
18
+ expression?: string;
19
+ };
@@ -26,6 +26,10 @@ function hzVar(instance) {
26
26
  function cbVar(instance) {
27
27
  return `__tc_hw_cb_${instance}`;
28
28
  }
29
+ /** The trampoline matching counter_top_callback_t: (dev, user_data). */
30
+ function trampVar(instance) {
31
+ return `__tc_hw_tramp_${instance}`;
32
+ }
29
33
  /**
30
34
  * Emit the per-instance counter device handles + frequency/callback state.
31
35
  * Called from shimLines when the program uses hardware timers.
@@ -36,7 +40,7 @@ export function hwtimerInitLines(chip) {
36
40
  return [];
37
41
  const lines = ['// CUTTLEFISH_HWTIMER_BEGIN'];
38
42
  controllers.forEach((c, i) => {
39
- lines.push(`static const struct device* ${devVar(i)} = DEVICE_DT_GET(DT_NODELABEL(${c.nodeLabel}));`, `static uint32_t ${hzVar(i)} = 1;`, `static counter_top_callback_t ${cbVar(i)} = NULL;`);
43
+ lines.push(`static const struct device* ${devVar(i)} = DEVICE_DT_GET(DT_NODELABEL(${c.nodeLabel}));`, `static uint32_t ${hzVar(i)} = 1;`, `static void (*${cbVar(i)})(void) = NULL;`, `static void ${trampVar(i)}(const struct device* dev, void* user_data) {`, ` (void)dev; (void)user_data;`, ` if (${cbVar(i)}) { ${cbVar(i)}(); }`, `}`);
40
44
  });
41
45
  lines.push('// CUTTLEFISH_HWTIMER_END');
42
46
  return lines;
@@ -57,25 +61,46 @@ export function lowerHwtimer(op, chip) {
57
61
  return { code: `/* hwtimer instance ${o.instance}: out of range on ${chip.id} */` };
58
62
  }
59
63
  switch (op.operation) {
60
- case 'hwtimer.set_frequency':
61
- return { code: `${hzVar(instance)} = ${o.hz};` };
62
- case 'hwtimer.on_overflow':
64
+ default:
65
+ throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
66
+ `Open an issue or use rawCpp() to emit it manually.`);
67
+ }
68
+ }
69
+ export function lowerCounter(op, chip) {
70
+ const o = op;
71
+ const controllers = chip.hwtimer?.controllers ?? [];
72
+ if (controllers.length === 0) {
73
+ return { code: `/* ${op.operation}: no counter device on ${chip.id} */` };
74
+ }
75
+ const instance = typeof o.instance === 'number' ? o.instance : parseInt(String(o.instance), 10);
76
+ if (isNaN(instance) || instance < 0 || instance >= controllers.length) {
77
+ return { code: `/* counter instance ${o.instance}: out of range on ${chip.id} (${controllers.length} declared) */` };
78
+ }
79
+ switch (op.operation) {
80
+ case 'counter.on_alarm':
63
81
  return { code: `${cbVar(instance)} = (${o.handler});` };
64
- case 'hwtimer.start':
65
- // Arm the top value (counter_freq / desired_hz) + the overflow callback,
66
- // then start the counter. Doing both here handles any call order the
67
- // HAL typical sequence (setFrequency onOverflow → start) and permutations.
82
+ case 'counter.start': {
83
+ // Apply the construction hz as the top value, arm the alarm callback,
84
+ // start. hz rides the op, so no ordering constraint exists. The API
85
+ // is counter_set_top_value(dev, const counter_top_cfg*) with a
86
+ // (dev, user_data) callback — the trampoline in hwtimerInitLines
87
+ // adapts the user's void() handler.
68
88
  return {
69
89
  code: [
70
90
  `{`,
91
+ ` ${hzVar(instance)} = ${o.hz};`,
71
92
  ` uint32_t __f = counter_get_frequency(${devVar(instance)});`,
72
93
  ` uint32_t __top = __f ? (__f / ${hzVar(instance)}) : 0U;`,
73
- ` if (__top > 0U) { (void)counter_set_top_value(${devVar(instance)}, __top, ${cbVar(instance)}, NULL); }`,
94
+ ` if (__top > 0U) {`,
95
+ ` struct counter_top_cfg __cfg = { .ticks = __top, .callback = ${trampVar(instance)}, .user_data = NULL, .flags = 0U };`,
96
+ ` (void)counter_set_top_value(${devVar(instance)}, &__cfg);`,
97
+ ` }`,
74
98
  ` (void)counter_start(${devVar(instance)});`,
75
99
  `}`,
76
100
  ].join(' '),
77
101
  };
78
- case 'hwtimer.stop':
102
+ }
103
+ case 'counter.stop':
79
104
  return { code: `(void)counter_stop(${devVar(instance)});` };
80
105
  default:
81
106
  throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
@@ -11,8 +11,6 @@
11
11
  // The device handle resolves at compile time via DEVICE_DT_GET(DT_NODELABEL(...)).
12
12
  // ---------------------------------------------------------------------------
13
13
  import { parseControllerIndex } from './util.js';
14
- const TXBUF_SIZE = 32;
15
- const RXBUF_SIZE = 32;
16
14
  /** The C variable prefix for a controller's state. */
17
15
  function prefix(idx) {
18
16
  return `__tc_i2c${idx}`;
@@ -29,12 +27,6 @@ export function i2cInitLines(chip, controllerIndex) {
29
27
  return [
30
28
  '// CUTTLEFISH_I2C_BEGIN',
31
29
  `static const struct device* ${p}_dev = DEVICE_DT_GET(DT_NODELABEL(${ctrl.nodeLabel}));`,
32
- `static uint16_t ${p}_addr = 0xFFFF;`,
33
- `static uint8_t ${p}_txbuf[${TXBUF_SIZE}];`,
34
- `static size_t ${p}_txlen = 0;`,
35
- `static uint8_t ${p}_rxbuf[${RXBUF_SIZE}];`,
36
- `static size_t ${p}_rxlen = 0;`,
37
- `static size_t ${p}_rxpos = 0;`,
38
30
  '// CUTTLEFISH_I2C_END',
39
31
  ];
40
32
  }
@@ -48,6 +40,26 @@ function speedForHz(hz) {
48
40
  return 'I2C_SPEED_FAST_PLUS';
49
41
  return 'I2C_SPEED_HIGH';
50
42
  }
43
+ /**
44
+ * The (void) comma expression begin/end lower to — a no-op that references
45
+ * the controller's whole state block (device + transaction buffers) so a
46
+ * program calling only begin() stays -Werror clean (-Wunused-variable).
47
+ */
48
+ function i2cKeepAlive(p) {
49
+ return `(void)${p}_dev;`;
50
+ }
51
+ /** The thin-device bus-speed preamble: apply the construction hz once
52
+ * (i2c_configure with Zephyr's I2C_SPEED_SET tier mapping), or '' when the
53
+ * target constructed without hz. Guarded per controller, so the cost after
54
+ * the first op is one bool test. */
55
+ function i2cSpeedGuard(p, hz) {
56
+ const n = typeof hz === 'number' ? hz : parseInt(String(hz ?? 0), 10);
57
+ if (!n || isNaN(n))
58
+ return '';
59
+ const speed = speedForHz(n);
60
+ const done = `${p}_spd_done`;
61
+ return `{ static bool ${done} = false; if (!${done}) { (void)i2c_configure(${p}_dev, I2C_SPEED_SET(${speed})); ${done} = true; } } `;
62
+ }
51
63
  /**
52
64
  * Resolve a HAL i2c.* op to Zephyr C++.
53
65
  * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
@@ -57,56 +69,36 @@ export function lowerI2c(op, chip) {
57
69
  const idx = parseControllerIndex(o.bus);
58
70
  const p = prefix(idx);
59
71
  switch (op.operation) {
60
- case 'i2c.begin':
61
- // Zephyr resolves the device at compile time; begin is a no-op (device
62
- // ready check is folded into the driver calls).
63
- return { code: `(void)${p}_dev;` };
64
- case 'i2c.end':
65
- return { code: `(void)${p}_dev;` };
66
- case 'i2c.set_clock': {
67
- const hz = typeof o.hz === 'number' ? o.hz : parseInt(String(o.hz), 10);
68
- const speed = isNaN(hz) ? 'I2C_SPEED_STANDARD' : speedForHz(hz || 100000);
69
- return { code: `i2c_configure(${p}_dev, I2C_SPEED_SET(${speed}));` };
72
+ // ── Thin I2C device (hal/i2c-target.ts) — Zephyr register verbs ──────
73
+ // One call per op against the controller device handle; the optional
74
+ // construction hz applies once via a guarded i2c_configure (the same
75
+ // call i2c.set_clock lowers to).
76
+ case 'i2c.reg_write': {
77
+ const pre = i2cSpeedGuard(p, o.hz);
78
+ return { code: `${pre} i2c_reg_write_byte(${p}_dev, static_cast<uint16_t>(${o.address}), static_cast<uint8_t>(${o.reg}), static_cast<uint8_t>(${o.value}));` };
70
79
  }
71
- case 'i2c.begin_transmission':
72
- // Record the target address + reset the pending write buffer.
73
- return { code: `${p}_addr = static_cast<uint16_t>(${o.address}); ${p}_txlen = 0;` };
74
- case 'i2c.write':
75
- // Append one byte (clamped to capacity).
76
- return { code: `if (${p}_txlen < ${TXBUF_SIZE}) { ${p}_txbuf[${p}_txlen++] = static_cast<uint8_t>(${o.data}); }` };
77
- case 'i2c.write_bytes': {
78
- const bytes = o.bytes ?? [];
79
- const stmts = bytes.map((b) => `if (${p}_txlen < ${TXBUF_SIZE}) { ${p}_txbuf[${p}_txlen++] = static_cast<uint8_t>(${b}); }`);
80
- return { code: stmts.join(' ') };
81
- }
82
- case 'i2c.write_buffer': {
83
- // Copy from a user buffer (its name is o.data), clamped to capacity.
84
- return {
85
- code: `for (size_t __i = 0; __i < sizeof(${o.data}) && ${p}_txlen < ${TXBUF_SIZE}; __i++) { ${p}_txbuf[${p}_txlen++] = reinterpret_cast<const uint8_t*>(${o.data})[__i]; }`,
86
- };
80
+ case 'i2c.reg_read': {
81
+ const pre = i2cSpeedGuard(p, o.hz);
82
+ return { expression: `({ ${pre} uint8_t __v = 0; (void)i2c_reg_read_byte(${p}_dev, static_cast<uint16_t>(${o.address}), static_cast<uint8_t>(${o.reg}), &__v); __v; })` };
87
83
  }
88
- case 'i2c.end_transmission':
89
- // Flush the accumulated txbuf to the cached address.
90
- return { code: `i2c_write(${p}_dev, ${p}_txbuf, ${p}_txlen, ${p}_addr);` };
91
- case 'i2c.request_from': {
92
- const qty = o.quantity;
93
- return {
94
- code: `i2c_read(${p}_dev, ${p}_rxbuf, static_cast<uint32_t>(${qty}), static_cast<uint16_t>(${o.address})); ${p}_rxlen = ${qty}; ${p}_rxpos = 0;`,
95
- };
84
+ case 'i2c.reg_update': {
85
+ const pre = i2cSpeedGuard(p, o.hz);
86
+ return { code: `${pre} (void)i2c_reg_update_byte(${p}_dev, static_cast<uint16_t>(${o.address}), static_cast<uint8_t>(${o.reg}), static_cast<uint8_t>(${o.mask}), static_cast<uint8_t>(${o.value}));` };
96
87
  }
97
- case 'i2c.available':
98
- return { expression: `(${p}_rxlen - ${p}_rxpos)` };
99
- case 'i2c.read':
100
- return { expression: `(${p}_rxpos < ${p}_rxlen ? ${p}_rxbuf[${p}_rxpos++] : -1)` };
101
- case 'i2c.read_buffer': {
102
- // Read straight into the user's buffer (o.buffer), count bytes.
103
- const count = o.count;
104
- return {
105
- code: `i2c_read(${p}_dev, reinterpret_cast<uint8_t*>(${o.buffer}), static_cast<uint32_t>(${count}), ${p}_addr);`,
106
- };
88
+ case 'i2c.dev_write': {
89
+ const pre = i2cSpeedGuard(p, o.hz);
90
+ const bytes = o.bytes ?? [];
91
+ // Identifier-shaped only a single-byte literal array arrives as
92
+ // numeric text ("159") and must take the literal path.
93
+ const isBuffer = bytes.length === 1 && typeof bytes[0] === 'string' && /^[A-Za-z_][A-Za-z0-9_]*$/.test(bytes[0]);
94
+ if (isBuffer) {
95
+ const name = bytes[0];
96
+ // A named user buffer: i2c_write against its storage directly.
97
+ return { code: `${pre} (void)i2c_write(${p}_dev, reinterpret_cast<const uint8_t*>(${name}), sizeof(${name}), static_cast<uint16_t>(${o.address}));` };
98
+ }
99
+ const arr = `static const uint8_t __tc_i2cw[] = { ${bytes.join(', ')} };`;
100
+ return { code: `${pre} ${arr} (void)i2c_write(${p}_dev, __tc_i2cw, sizeof(__tc_i2cw), static_cast<uint16_t>(${o.address}));` };
107
101
  }
108
- case 'i2c.recover':
109
- return { code: `i2c_recover_bus(${p}_dev);` };
110
102
  default:
111
103
  throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
112
104
  `Open an issue or use rawCpp() to emit it manually.`);
@@ -6,11 +6,9 @@ import { lowerPwm } from './pwm.js';
6
6
  import { lowerI2c } from './i2c.js';
7
7
  import { lowerSpi } from './spi.js';
8
8
  import { lowerUart } from './uart.js';
9
+ import { lowerUsb } from './usb.js';
9
10
  import { lowerInterrupt } from './interrupts.js';
10
11
  import { lowerWdt } from './wdt.js';
11
- import { lowerPower } from './power.js';
12
- import { lowerTone } from './tone.js';
13
- import { lowerPulseOrShift } from './pulse.js';
14
12
  import { lowerBle } from './ble.js';
15
13
  import { lowerWifi } from './wifi.js';
16
14
  import { lowerHttp } from './http.js';
@@ -20,8 +18,10 @@ import { lowerBoard } from './board.js';
20
18
  import { lowerRandom } from './random.js';
21
19
  import { lowerDac } from './dac.js';
22
20
  import { lowerFs } from './fs.js';
23
- import { lowerHwtimer } from './hwtimer.js';
24
- export { lowerGpio, lowerTiming, lowerAdc, lowerPwm, lowerI2c, lowerSpi, lowerUart, lowerInterrupt, lowerWdt, lowerPower, lowerTone, lowerPulseOrShift, lowerBle, lowerWifi, lowerHttp, lowerMqtt, lowerPreferences, lowerBoard, lowerRandom, lowerDac, lowerFs, lowerHwtimer, };
21
+ import { lowerSensor } from './sensor.js';
22
+ import { lowerHwtimer, lowerCounter } from './hwtimer.js';
23
+ import { lowerThread } from './thread.js';
24
+ export { lowerGpio, lowerTiming, lowerAdc, lowerPwm, lowerI2c, lowerSpi, lowerUart, lowerUsb, lowerInterrupt, lowerWdt, lowerBle, lowerWifi, lowerHttp, lowerMqtt, lowerPreferences, lowerBoard, lowerRandom, lowerDac, lowerFs, lowerHwtimer, lowerCounter, lowerSensor, lowerThread, };
25
25
  /**
26
26
  * Lower a HAL op to Zephyr C++. Returns undefined for unsupported categories
27
27
  * (mirrors lowerHalOp in framework-esp32/src/lowering/index.ts).
@@ -23,13 +23,10 @@ import { lowerPwm } from './pwm.js';
23
23
  import { lowerI2c } from './i2c.js';
24
24
  import { lowerSpi } from './spi.js';
25
25
  import { lowerUart } from './uart.js';
26
+ import { lowerUsb } from './usb.js';
26
27
  import { lowerInterrupt } from './interrupts.js';
27
28
  import { lowerWdt } from './wdt.js';
28
- import { lowerPower } from './power.js';
29
- import { lowerTone } from './tone.js';
30
- import { lowerPulseOrShift } from './pulse.js';
31
29
  import { lowerBle } from './ble.js';
32
- import { lowerWorker } from './worker.js';
33
30
  import { lowerWifi } from './wifi.js';
34
31
  import { lowerHttp } from './http.js';
35
32
  import { lowerMqtt } from './mqtt.js';
@@ -38,8 +35,10 @@ import { lowerBoard } from './board.js';
38
35
  import { lowerRandom } from './random.js';
39
36
  import { lowerDac } from './dac.js';
40
37
  import { lowerFs } from './fs.js';
41
- import { lowerHwtimer } from './hwtimer.js';
42
- export { lowerGpio, lowerTiming, lowerAdc, lowerPwm, lowerI2c, lowerSpi, lowerUart, lowerInterrupt, lowerWdt, lowerPower, lowerTone, lowerPulseOrShift, lowerBle, lowerWifi, lowerHttp, lowerMqtt, lowerPreferences, lowerBoard, lowerRandom, lowerDac, lowerFs, lowerHwtimer, };
38
+ import { lowerSensor } from './sensor.js';
39
+ import { lowerHwtimer, lowerCounter } from './hwtimer.js';
40
+ import { lowerThread } from './thread.js';
41
+ export { lowerGpio, lowerTiming, lowerAdc, lowerPwm, lowerI2c, lowerSpi, lowerUart, lowerUsb, lowerInterrupt, lowerWdt, lowerBle, lowerWifi, lowerHttp, lowerMqtt, lowerPreferences, lowerBoard, lowerRandom, lowerDac, lowerFs, lowerHwtimer, lowerCounter, lowerSensor, lowerThread, };
43
42
  /**
44
43
  * Lower a HAL op to Zephyr C++. Returns undefined for unsupported categories
45
44
  * (mirrors lowerHalOp in framework-esp32/src/lowering/index.ts).
@@ -62,23 +61,19 @@ export function lowerHalOp(op) {
62
61
  return lowerSpi(op, chip);
63
62
  if (op.operation.startsWith('uart.'))
64
63
  return lowerUart(op);
64
+ if (op.operation.startsWith('usb.'))
65
+ return lowerUsb(op, chip);
65
66
  if (op.operation.startsWith('interrupt.'))
66
67
  return lowerInterrupt(op, chip);
67
68
  if (op.operation.startsWith('wdt.'))
68
- return lowerWdt(op);
69
+ return lowerWdt(op, chip);
69
70
  if (op.operation.startsWith('hwtimer.'))
70
71
  return lowerHwtimer(op, chip);
71
- if (op.operation.startsWith('power.'))
72
- return lowerPower(op);
73
- if (op.operation.startsWith('tone.'))
74
- return lowerTone(op, chip);
75
- // pulse.* and shift.* share a bit-bang lowering module.
76
- if (op.operation.startsWith('pulse.') || op.operation.startsWith('shift.'))
77
- return lowerPulseOrShift(op, chip);
72
+ if (op.operation.startsWith('counter.'))
73
+ return lowerCounter(op, chip);
74
+ // (legacy pulse/shift bit-bang module removed)
78
75
  if (op.operation.startsWith('ble.'))
79
76
  return lowerBle(op);
80
- if (op.operation.startsWith('worker.'))
81
- return lowerWorker(op);
82
77
  if (op.operation.startsWith('wifi.'))
83
78
  return lowerWifi(op);
84
79
  if (op.operation.startsWith('http.'))
@@ -95,6 +90,10 @@ export function lowerHalOp(op) {
95
90
  return lowerBoard(op);
96
91
  if (op.operation.startsWith('random.'))
97
92
  return lowerRandom(op);
93
+ if (op.operation.startsWith('sensor.'))
94
+ return lowerSensor(op);
95
+ if (op.operation.startsWith('thread.'))
96
+ return lowerThread(op);
98
97
  // raw / snprintf.emit / display.* / ... — not lowered by this
99
98
  // framework. Return undefined so the transpiler falls back and the manifest
100
99
  // validator confirms the unsupported declaration.
@@ -1,19 +1,36 @@
1
1
  import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
2
2
  import type { ZephyrChipDescriptor } from '../chips/types.js';
3
+ /** Map thin-GPIO INT token text to the GPIO_INT_* macro. */
4
+ export declare function gpioIntTokenToMacro(intFlags: string): string;
3
5
  /**
4
6
  * Emit the per-pin interrupt callback state + trampolines. Called from shimLines
5
7
  * when the program uses interrupts. The trampoline calls a user function via a
6
8
  * function-pointer static, set at attach time.
9
+ *
10
+ * Two paths:
11
+ * - descriptor `gpio.interruptPins` (buttons with DT specs): callback state
12
+ * against the DT spec, polarity-correct via gpio_*_dt.
13
+ * - `usedPins` (any other pin the program attaches to): raw-controller state —
14
+ * GPIO.onInterrupt() works on EVERY GPIO, not just DT-aliased buttons. Each
15
+ * such pin gets its own callback struct + trampoline addressed by the owning
16
+ * controller (STM32 splits gpioa/gpiob/gpioc) and port-relative bit.
17
+ */
18
+ export declare function interruptInitLines(chip: ZephyrChipDescriptor, usedPins?: ReadonlySet<number>): string[];
19
+ /**
20
+ * Collect the pin numbers the program attaches interrupts to (any GPIO — not
21
+ * just descriptor-listed buttons). Feeds interruptInitLines' raw-path state.
7
22
  */
8
- export declare function interruptInitLines(chip: ZephyrChipDescriptor): string[];
23
+ export declare function collectInterruptPins(program: unknown): Set<number>;
9
24
  /**
10
25
  * Resolve a HAL interrupt.* op to Zephyr C++.
11
26
  * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
12
27
  *
13
- * Note: attach requires the pin to be in the descriptor's interruptPins. If a
14
- * program attaches to an unlisted pin, we fall back to a diagnostic comment
15
- * (the probe uses an unknown pin, so the manifest marks these 'supported' via
16
- * the comment lowering — the probe sees a non-undefined return).
28
+ * Two paths: pins listed in the descriptor's gpio.interruptPins (DT-aliased
29
+ * buttons) go through the polarity-correct gpio_*_dt chain; every other GPIO
30
+ * attaches through the raw-controller chain (any GPIO is interrupt-capable on
31
+ * the supported SoCs — the descriptor list exists for DT specs, not as a
32
+ * capability gate). The manifest probe's synthetic op keeps the comment
33
+ * fallback (no program ⇒ no shim state was emitted for the raw path).
17
34
  */
18
35
  export declare function lowerInterrupt(op: HALOpIR, chip: ZephyrChipDescriptor): {
19
36
  code?: string;