@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
@@ -1,124 +1,164 @@
1
- // ---------------------------------------------------------------------------
2
- // SPI lowering — spi_transceive_dt against a spi_dt_spec; CS as GPIO
3
- //
4
- // Zephyr's `spi_transceive_dt(&spec, tx, rx)` is stateless per-call, so there
5
- // is no pending-tx-buffer (unlike I2C). The CS pin is driven as a plain GPIO
6
- // (like ESP32), since the DT spec's CS handling is configured at compile time
7
- // but the HAL's explicit cs_low/cs_high ops want manual control.
8
- //
9
- // The board's SPI node (spi2 on the XIAO) has no pre-declared client device
10
- // node, so the lowering cannot use SPI_DT_SPEC_GET (which needs a client node).
11
- // Instead we build a `struct spi_config` at runtime against
12
- // DEVICE_DT_GET(DT_NODELABEL(spi2)) and call spi_transceive directly.
13
- // ---------------------------------------------------------------------------
14
-
15
- import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
16
- import type { ZephyrChipDescriptor } from '../chips/types.js';
17
- import { parseControllerIndex } from './util.js';
18
- import { controllerNodelabelForPin } from '../chips/controllers.js';
19
-
20
- /** The C variable prefix for a controller's state. */
21
- function prefix(idx: number): string {
22
- return `__tc_spi${idx}`;
23
- }
24
-
25
- /**
26
- * Emit the per-controller SPI state. The bus device resolves at compile time;
27
- * a static spi_config holds the base operation flags, and mutable runtime fields
28
- * hold the mode (CPOL/CPHA bits) + bit order (lsb) so set_mode/set_bit_order can
29
- * rebuild operation at init time (Zephyr's spi_config.operation is the only knob).
30
- */
31
- export function spiInitLines(chip: ZephyrChipDescriptor, controllerIndex: number): string[] {
32
- const ctrl = chip.spi?.controllers[controllerIndex];
33
- if (!ctrl) return [];
34
- const p = prefix(controllerIndex);
35
- return [
36
- '// CUTTLEFISH_SPI_BEGIN',
37
- `static const struct device* ${p}_dev = DEVICE_DT_GET(DT_NODELABEL(${ctrl.nodeLabel}));`,
38
- `static bool ${p}_ready = false;`,
39
- `static uint8_t ${p}_mode = 0; // bit0=CPOL, bit1=CPHA`,
40
- `static bool ${p}_lsb = false; // false=MSB (default), true=LSB`,
41
- `static struct spi_config ${p}_cfg = {`,
42
- ` .frequency = 1000000,`,
43
- `};`,
44
- `static void ${p}_init(void) {`,
45
- ` if (!${p}_ready) {`,
46
- ` ${p}_cfg.bus = ${p}_dev;`,
47
- ` ${p}_cfg.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8)`,
48
- ` | (${p}_lsb ? SPI_TRANSFER_LSB : SPI_TRANSFER_MSB)`,
49
- ` | ((${p}_mode & 0x1) ? SPI_MODE_CPOL : 0)`,
50
- ` | ((${p}_mode & 0x2) ? SPI_MODE_CPHA : 0);`,
51
- ` ${p}_ready = true;`,
52
- ` }`,
53
- `}`,
54
- '// CUTTLEFISH_SPI_END',
55
- ];
56
- }
57
-
58
- /**
59
- * Resolve a HAL spi.* op to Zephyr C++.
60
- * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
61
- */
62
- export function lowerSpi(
63
- op: HALOpIR,
64
- chip: ZephyrChipDescriptor,
65
- ): { code?: string; expression?: string } {
66
- const o = op as any;
67
- const idx = parseControllerIndex(o.bus);
68
- const p = prefix(idx);
69
-
70
- switch (op.operation) {
71
- case 'spi.begin':
72
- return { code: `${p}_init();` };
73
- case 'spi.end':
74
- return { code: `spi_release(${p}_dev, &${p}_cfg);` };
75
- case 'spi.begin_transaction':
76
- // Config is static; transaction begin is a no-op (frequency/mode baked in).
77
- return { code: `${p}_init();` };
78
- case 'spi.end_transaction':
79
- return { code: `(void)0;` };
80
- case 'spi.set_mode':
81
- // Apply CPOL/CPHA: store the mode byte then re-init so the next transfer
82
- // picks up the rebuilt operation flags. The ready flag is cleared so
83
- // _init() rebuilds rather than early-returning.
84
- return { code: `{ ${p}_mode = static_cast<uint8_t>(${o.mode}); ${p}_ready = false; ${p}_init(); }` };
85
- case 'spi.set_bit_order': {
86
- // The HAL payload `order` is a string ("lsb" | "msb"), per SpiSetBitOrderOp.
87
- // Normalize here in TS so we emit a boolean literal, not the raw string
88
- // (which would be an undeclared C++ identifier).
89
- const key = String(o.order).replace(/^["']|["']$/g, '').toLowerCase();
90
- const lsb = (key === 'lsb' || key === 'lsbfirst') ? 'true' : 'false';
91
- return { code: `{ ${p}_lsb = ${lsb}; ${p}_ready = false; ${p}_init(); }` };
92
- }
93
- case 'spi.transfer': {
94
- // Single-byte full-duplex, returns the received byte (GCC stmt-expr).
95
- return {
96
- expression: `({ uint8_t __tx = static_cast<uint8_t>(${o.data}); uint8_t __rx = 0; struct spi_buf __tb = { .buf = &__tx, .len = 1 }; struct spi_buf_set __tbs = { .buffers = &__tb, .count = 1 }; struct spi_buf __rb = { .buf = &__rx, .len = 1 }; struct spi_buf_set __rbs = { .buffers = &__rb, .count = 1 }; ${p}_init(); spi_transceive(${p}_dev, &${p}_cfg, &__tbs, &__rbs); __rx; })`,
97
- };
98
- }
99
- case 'spi.read_buffer': {
100
- // Read count bytes by sending 0xFF dummy bytes (full-duplex read).
101
- const count = o.count;
102
- return {
103
- code: `{ uint8_t __dummy[${count}] = {0}; for (int __i = 0; __i < (int)(${count}); __i++) __dummy[__i] = 0xFF; struct spi_buf __tb = { .buf = __dummy, .len = ${count} }; struct spi_buf_set __tbs = { .buffers = &__tb, .count = 1 }; struct spi_buf __rb = { .buf = reinterpret_cast<void*>(${o.buffer}), .len = ${count} }; struct spi_buf_set __rbs = { .buffers = &__rb, .count = 1 }; ${p}_init(); spi_transceive(${p}_dev, &${p}_cfg, &__tbs, &__rbs); }`,
104
- };
105
- }
106
- case 'spi.cs_low':
107
- case 'spi.cs_high': {
108
- // CS driven as a plain GPIO via the owning controller (the CS pin comes
109
- // from the op's `pin` field; Zephyr uses gpio_pin_set_raw). Resolve the
110
- // controller by pin so a CS on a high-numbered pin (ESP32-S3 gpio1) lands
111
- // on the right node.
112
- const val = op.operation === 'spi.cs_low' ? 0 : 1;
113
- const gpioController = controllerNodelabelForPin(chip, o.pin);
114
- return {
115
- code: `gpio_pin_set_raw(DEVICE_DT_GET(DT_NODELABEL(${gpioController})), ${o.pin}, ${val});`,
116
- };
117
- }
118
- default:
119
- throw new Error(
120
- `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
121
- `Open an issue or use rawCpp() to emit it manually.`,
122
- );
123
- }
124
- }
1
+ // ---------------------------------------------------------------------------
2
+ // SPI lowering — spi_transceive_dt against a spi_dt_spec; CS as GPIO
3
+ //
4
+ // Zephyr's `spi_transceive_dt(&spec, tx, rx)` is stateless per-call, so there
5
+ // is no pending-tx-buffer (unlike I2C). The CS pin is driven as a plain GPIO
6
+ // (like ESP32), since the DT spec's CS handling is configured at compile time
7
+ // but the HAL's explicit cs_low/cs_high ops want manual control.
8
+ //
9
+ // The board's SPI node (spi2 on the XIAO) has no pre-declared client device
10
+ // node, so the lowering cannot use SPI_DT_SPEC_GET (which needs a client node).
11
+ // Instead we build a `struct spi_config` at runtime against
12
+ // DEVICE_DT_GET(DT_NODELABEL(spi2)) and call spi_transceive directly.
13
+ // ---------------------------------------------------------------------------
14
+
15
+ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
16
+ import type { ZephyrChipDescriptor } from '../chips/types.js';
17
+ import { parseControllerIndex } from './util.js';
18
+
19
+ /** The C variable prefix for a controller's state. */
20
+ function prefix(idx: number): string {
21
+ return `__tc_spi${idx}`;
22
+ }
23
+
24
+ // ── Thin SPI device names — the shared-facts discipline ────────────────────
25
+ //
26
+ // The DT nodelabel (tc_spit_spi<N>_cs<cs>), the shim's spi_dt_spec var, and
27
+ // the overlay scanner's regex all derive from bus index + cs pin here.
28
+
29
+ export interface SpiTargetNames {
30
+ dtLabel: string;
31
+ varName: string;
32
+ busIndex: number;
33
+ cs: number;
34
+ }
35
+
36
+ /** Derive a thin SPI target's names from the op facts. */
37
+ export function spiTargetNames(bus: string, cs: number | string): SpiTargetNames {
38
+ const busIndex = parseControllerIndex(typeof bus === 'string' ? bus : String(bus));
39
+ const csNum = typeof cs === 'number' ? cs : parseInt(String(cs), 10);
40
+ const stem = `spit_spi${busIndex}_cs${csNum}`;
41
+ return { dtLabel: `tc_${stem}`, varName: `__tc_${stem}_spec`, busIndex, cs: csNum };
42
+ }
43
+
44
+ /** The per-target state block: one spi_dt_spec against the DT child node the
45
+ * overlay emits, plus the tc-spit-cfg comment the overlay scanner reads (the
46
+ * tc-sensor-cfg channel). Called from shimLines for each distinct target. */
47
+ export function spiTargetStateLines(bus: string, cs: number | string, hz: number | string = 0, mode: number | string = 0): string[] {
48
+ const n = spiTargetNames(bus, cs);
49
+ return [
50
+ '// CUTTLEFISH_SPIT_BEGIN',
51
+ // Zephyr 4.4's SPI_DT_SPEC_GET takes the base operation explicitly (mode
52
+ // bits come from the DT node's spi-cpol/spi-cpha via SPI_CONFIG_DT).
53
+ `static const struct spi_dt_spec ${n.varName} = SPI_DT_SPEC_GET(DT_NODELABEL(${n.dtLabel}), SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0);`,
54
+ `// tc-spit-cfg: ${n.dtLabel} hz=${Number(hz)} mode=${Number(mode)}`,
55
+ '// CUTTLEFISH_SPIT_END',
56
+ ];
57
+ }
58
+
59
+ /** Build the tx buffer declaration + set for a thin SPI op. A literal byte
60
+ * array becomes a local array; a single identifier is the user's own buffer
61
+ * (its storage + sizeof drive length). spi_buf.buf is void*, and the user's
62
+ * buffer may be const-qualified — cast through const void* to stay legal
63
+ * under -Werror without mutating anyone's qualifications. */
64
+ function spiTxBuffers(tx: unknown, tag: string): { decl: string; set: string } {
65
+ const bytes = (Array.isArray(tx) ? tx : []) as unknown[];
66
+ // Only an identifier-shaped string is a real caller buffer — the resolver
67
+ // collapses a single-byte literal array to buffer-kind with NUMERIC text
68
+ // (e.g. "159"), which must take the literal path below.
69
+ const isIdentifier = bytes.length === 1 && typeof bytes[0] === 'string' && /^[A-Za-z_][A-Za-z0-9_]*$/.test(bytes[0] as string);
70
+ if (isIdentifier) {
71
+ const name = bytes[0] as string;
72
+ return {
73
+ decl: `const struct spi_buf __tb${tag} = { .buf = const_cast<void*>(static_cast<const void*>(${name})), .len = sizeof(${name}) }; const struct spi_buf_set __txs${tag} = { .buffers = &__tb${tag}, .count = 1 };`,
74
+ set: `__txs${tag}`,
75
+ };
76
+ }
77
+ return {
78
+ decl: `uint8_t __tx${tag}[] = { ${bytes.join(', ')} }; const struct spi_buf __tb${tag} = { .buf = __tx${tag}, .len = sizeof(__tx${tag}) }; const struct spi_buf_set __txs${tag} = { .buffers = &__tb${tag}, .count = 1 };`,
79
+ set: `__txs${tag}`,
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Emit the per-controller SPI state. The bus device resolves at compile time;
85
+ * a static spi_config holds the base operation flags, and mutable runtime fields
86
+ * hold the mode (CPOL/CPHA bits) + bit order (lsb) so set_mode/set_bit_order can
87
+ * rebuild operation at init time (Zephyr's spi_config.operation is the only knob).
88
+ */
89
+ export function spiInitLines(chip: ZephyrChipDescriptor, controllerIndex: number): string[] {
90
+ const ctrl = chip.spi?.controllers[controllerIndex];
91
+ if (!ctrl) return [];
92
+ const p = prefix(controllerIndex);
93
+ return [
94
+ '// CUTTLEFISH_SPI_BEGIN',
95
+ `static const struct device* ${p}_dev = DEVICE_DT_GET(DT_NODELABEL(${ctrl.nodeLabel}));`,
96
+ `static bool ${p}_ready = false;`,
97
+ `static uint8_t ${p}_mode = 0; // bit0=CPOL, bit1=CPHA`,
98
+ `static bool ${p}_lsb = false; // false=MSB (default), true=LSB`,
99
+ `static struct spi_config ${p}_cfg = {`,
100
+ ` .frequency = 1000000,`,
101
+ `};`,
102
+ `static void ${p}_init(void) {`,
103
+ ` if (!${p}_ready) {`,
104
+ // NOTE: no .bus assignment — struct spi_config lost its `bus` member in
105
+ // Zephyr 4.x (deprecated 3.5, removed 4.0); spi_transceive takes the
106
+ // device alongside the config, which the ops below already do.
107
+ ` ${p}_cfg.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8)`,
108
+ ` | (${p}_lsb ? SPI_TRANSFER_LSB : SPI_TRANSFER_MSB)`,
109
+ ` | ((${p}_mode & 0x1) ? SPI_MODE_CPOL : 0)`,
110
+ ` | ((${p}_mode & 0x2) ? SPI_MODE_CPHA : 0);`,
111
+ ` ${p}_ready = true;`,
112
+ ` }`,
113
+ `}`,
114
+ '// CUTTLEFISH_SPI_END',
115
+ ];
116
+ }
117
+
118
+ /**
119
+ * Resolve a HAL spi.* op to Zephyr C++.
120
+ * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
121
+ */
122
+ export function lowerSpi(
123
+ op: HALOpIR,
124
+ chip: ZephyrChipDescriptor,
125
+ ): { code?: string; expression?: string } {
126
+ const o = op as any;
127
+ const idx = parseControllerIndex(o.bus);
128
+ const p = prefix(idx);
129
+
130
+ switch (op.operation) {
131
+ case 'spi.transceive': {
132
+ const v = spiTargetNames(o.bus, o.cs).varName;
133
+ const tx = spiTxBuffers(o.tx, 't');
134
+ const rxName = String(o.rx ?? '');
135
+ const rx = rxName
136
+ ? `struct spi_buf __rb = { .buf = const_cast<void*>(static_cast<const void*>(${rxName})), .len = sizeof(${rxName}) }; const struct spi_buf_set __rbs = { .buffers = &__rb, .count = 1 };`
137
+ : `const struct spi_buf_set __rbs = { .buffers = NULL, .count = 0 };`;
138
+ return {
139
+ code: `{ ${tx.decl} ${rx} (void)spi_transceive_dt(&${v}, &${tx.set}, &__rbs); }`,
140
+ };
141
+ }
142
+ case 'spi.dev_write': {
143
+ const v = spiTargetNames(o.bus, o.cs).varName;
144
+ const tx = spiTxBuffers(o.tx, 'w');
145
+ return {
146
+ code: `{ ${tx.decl} (void)spi_write_dt(&${v}, &${tx.set}); }`,
147
+ };
148
+ }
149
+ case 'spi.reg_read': {
150
+ // One-byte register read against an INTERNAL buffer — no caller array
151
+ // (sidesteps the file-scope Uint8Array promotion issue; also just the
152
+ // right shape for ID/status registers).
153
+ const v = spiTargetNames(o.bus, o.cs).varName;
154
+ return {
155
+ expression: `({ uint8_t __txr = static_cast<uint8_t>(${o.reg}); uint8_t __rxr = 0; struct spi_buf __tbr = { .buf = &__txr, .len = 1 }; const struct spi_buf_set __txsr = { .buffers = &__tbr, .count = 1 }; struct spi_buf __rbr = { .buf = &__rxr, .len = 1 }; const struct spi_buf_set __rxsr = { .buffers = &__rbr, .count = 1 }; (void)spi_transceive_dt(&${v}, &__txsr, &__rxsr); __rxr; })`,
156
+ };
157
+ }
158
+ default:
159
+ throw new Error(
160
+ `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
161
+ `Open an issue or use rawCpp() to emit it manually.`,
162
+ );
163
+ }
164
+ }
@@ -0,0 +1,63 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Thread lowering — Zephyr kernel threads (k_thread_create / k_thread_join)
3
+ //
4
+ // One state block per thread identity slot the program STARTS: a static
5
+ // K_THREAD_STACK_DEFINE (sized by the construction stackKb), the k_thread
6
+ // struct, and an entry trampoline bridging Zephyr's (void*, void*, void*)
7
+ // entry signature to the no-argument registered callback. start() creates
8
+ // and schedules (K_NO_WAIT); join() blocks (K_FOREVER).
9
+ //
10
+ // No devicetree, no Kconfig — kernel.h (always included) is the whole
11
+ // dependency. State emission is keyed on thread.start ops only, so a
12
+ // join-only program emits nothing (and its join guards on the same slot).
13
+ // ---------------------------------------------------------------------------
14
+
15
+ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
16
+
17
+ /** Per-instance symbol stem. */
18
+ function stem(instance: number): string {
19
+ return `__tc_thrd${instance}`;
20
+ }
21
+
22
+ /**
23
+ * Emit the per-thread state. Called from shimLines for each distinct thread
24
+ * the program starts.
25
+ */
26
+ export function threadStateLines(instance: number, stackBytes: number): string[] {
27
+ const p = stem(instance);
28
+ return [
29
+ '// CUTTLEFISH_THREAD_BEGIN',
30
+ `K_THREAD_STACK_DEFINE(${p}_stack, ${stackBytes});`,
31
+ `static struct k_thread ${p}_thread;`,
32
+ `static void (*${p}_fn)(void) = NULL;`,
33
+ `static void ${p}_tramp(void* a, void* b, void* c) { (void)a; (void)b; (void)c; if (${p}_fn) { ${p}_fn(); } }`,
34
+ '// CUTTLEFISH_THREAD_END',
35
+ ];
36
+ }
37
+
38
+ /**
39
+ * Resolve a HAL thread.* op to Zephyr C++.
40
+ * Returns `{ code }` for statement ops.
41
+ */
42
+ export function lowerThread(op: HALOpIR): { code?: string; expression?: string } {
43
+ const o = op as any;
44
+ const instance = typeof o.instance === 'number' ? o.instance : 0;
45
+ const p = stem(instance);
46
+
47
+ switch (op.operation) {
48
+ case 'thread.start': {
49
+ // Store the entry, create + schedule immediately. The stack size comes
50
+ // from the state block (the construction fact), via K_THREAD_STACK_SIZEOF.
51
+ return {
52
+ code: `${p}_fn = (${o.handler}); (void)k_thread_create(&${p}_thread, ${p}_stack, K_THREAD_STACK_SIZEOF(${p}_stack), ${p}_tramp, NULL, NULL, NULL, ${o.priority ?? 5}, 0, K_NO_WAIT);`,
53
+ };
54
+ }
55
+ case 'thread.join':
56
+ return { code: `(void)k_thread_join(&${p}_thread, K_FOREVER);` };
57
+ default:
58
+ throw new Error(
59
+ `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
60
+ `Open an issue or use rawCpp() to emit it manually.`,
61
+ );
62
+ }
63
+ }
@@ -1,72 +1,46 @@
1
- // ---------------------------------------------------------------------------
2
- // Timing lowering — Zephyr kernel timing
3
- //
4
- // delay/millis/delay_microseconds/micros/free_heap lower directly. The four
5
- // timer ops (set_interval/set_timeout/clear_interval/clear_timeout) call the
6
- // timer_methods polyfill helpers (k_timer + k_work pool), declared as 'polyfill'
7
- // status in the manifest — the validator skips the resolver probe for these
8
- // (they legitimately return polyfill-helper calls, not direct lowering).
9
- // free_heap has no portable Zephyr query without CONFIG_SYS_HEAP_RUNTIME_STATS;
10
- // it returns 0 with a comment (honest limitation).
11
- // ---------------------------------------------------------------------------
12
-
13
- import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
14
-
15
- /**
16
- * Resolve a HAL timing.* op to Zephyr C++.
17
- * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
18
- */
19
- export function lowerTiming(
20
- op: HALOpIR,
21
- ): { code?: string; expression?: string } {
22
- const o = op as any;
23
-
24
- switch (op.operation) {
25
- case 'timing.delay': {
26
- // Milliseconds k_msleep is the blocking Zephyr call. Because
27
- // isRtosTarget() returns true, the transpiler does not warn about
28
- // blocking delays inside loop().
29
- const ms = o.ms;
30
- return { code: `k_msleep(${ms});` };
31
- }
32
- case 'timing.delay_microseconds': {
33
- // k_busy_wait spins (does not yield); for cooperative μs delays.
34
- return { code: `k_busy_wait(${o.us});` };
35
- }
36
- case 'timing.millis':
37
- // k_uptime_get_32() returns int64_t milliseconds since boot. Cast to the
38
- // Arduino-millis() return type (uint32_t) so overflow wraps identically.
39
- return { expression: 'static_cast<uint32_t>(k_uptime_get_32())' };
40
- case 'timing.micros': {
41
- // Convert hardware cycles to microseconds. k_cycle_get_32 + the cycles/sec
42
- // macro (note: sys_clock_hw_cycles_per_sec is a MACRO needs parens).
43
- return {
44
- expression: '(uint32_t)(((uint64_t)k_cycle_get_32() * 1000000ULL) / sys_clock_hw_cycles_per_sec())',
45
- };
46
- }
47
- case 'timing.free_heap':
48
- // No portable free-heap query without CONFIG_SYS_HEAP_RUNTIME_STATS.
49
- // Return 0 with a comment so callers don't get a link error.
50
- return { expression: '(0 /* free_heap: enable CONFIG_SYS_HEAP_RUNTIME_STATS for real value */)' };
51
- case 'timing.set_interval':
52
- // Backed by the timer_methods polyfill (k_timer + k_work). The handler is
53
- // the resolved C++ callback name; timeout is the repeat period (ms).
54
- // NOTE: these ops are declared 'polyfill' in the manifest, so setInterval
55
- // is rewritten to __tc_setInterval before reaching the lowering — these
56
- // cases are a fallback/defense and use the real op fields (handler/timeout).
57
- return { expression: `__tc_setInterval(${o.handler}, ${o.timeout})` };
58
- case 'timing.set_timeout':
59
- // One-shot: k_timer with K_FOREVER period.
60
- return { expression: `__tc_setTimeout(${o.handler}, ${o.timeout})` };
61
- case 'timing.clear_interval':
62
- return { code: `__tc_clearInterval(${o.id});` };
63
- case 'timing.clear_timeout':
64
- return { code: `__tc_clearTimeout(${o.id});` };
65
- default:
66
- throw new Error(
67
- `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
68
- `Open an issue or use rawCpp() to emit it manually.`,
69
- );
70
- }
71
- }
72
-
1
+ // ---------------------------------------------------------------------------
2
+ // Timing lowering — Zephyr kernel timing
3
+ //
4
+ // sleep/now/now_us/busy_wait_us lower directly to kernel calls. Periodic or
5
+ // deferred work is NOT a timing op: it is a Thread (k_thread) or a Counter
6
+ // (hardware timer), each with its own lowering.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
10
+
11
+ /**
12
+ * Resolve a HAL timing.* op to Zephyr C++.
13
+ * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
14
+ */
15
+ export function lowerTiming(
16
+ op: HALOpIR,
17
+ ): { code?: string; expression?: string } {
18
+ const o = op as any;
19
+
20
+ switch (op.operation) {
21
+ case 'timing.sleep':
22
+ // Time.sleep yielding ms sleep (k_msleep), delay()'s replacement.
23
+ return { code: `k_msleep(${o.ms});` };
24
+ case 'timing.now':
25
+ // Time.now — ms since boot as a double (k_uptime_get is int64_t ms).
26
+ return { expression: 'static_cast<double>(k_uptime_get())' };
27
+ case 'timing.now_us':
28
+ // Time.nowUs µs since boot, uptime-derived for EVERY board: the
29
+ // cycle-counter form (k_cyc_to_us_floor64(k_cycle_get_64())) reads a
30
+ // constant on SoCs without a free-running 64-bit counter, so the one
31
+ // uniform expression that is monotonic and advancing everywhere is
32
+ // the kernel uptime scaled to µs. Resolution is therefore the uptime
33
+ // tick (millisecond), uniformly. A double holds µs exactly for ~285
34
+ // years.
35
+ return { expression: 'static_cast<double>(k_uptime_get() * 1000)' };
36
+ case 'timing.busy_wait_us':
37
+ // Time.busyWaitUs spin, no yield; delayMicroseconds's honest name.
38
+ return { code: `k_busy_wait(${o.us});` };
39
+ default:
40
+ throw new Error(
41
+ `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
42
+ `Open an issue or use rawCpp() to emit it manually.`,
43
+ );
44
+ }
45
+ }
46
+