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

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 (185) hide show
  1. package/README.md +22 -47
  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/esp32s3.js +49 -0
  10. package/dist/chips/index.d.ts +6 -12
  11. package/dist/chips/index.js +18 -34
  12. package/dist/chips/resolve.d.ts +16 -2
  13. package/dist/chips/resolve.js +267 -10
  14. package/dist/chips/soc/.d.ts +2 -0
  15. package/dist/chips/soc/.js +129 -0
  16. package/dist/chips/soc/esp32.d.ts +2 -0
  17. package/dist/chips/soc/esp32.js +120 -0
  18. package/dist/chips/soc/esp32c3.d.ts +2 -0
  19. package/dist/chips/soc/esp32c3.js +90 -0
  20. package/dist/chips/soc/esp32c6.d.ts +2 -0
  21. package/dist/chips/soc/esp32c6.js +109 -0
  22. package/dist/chips/soc/esp32s3.d.ts +2 -0
  23. package/dist/chips/soc/esp32s3.js +189 -0
  24. package/dist/chips/soc/index.d.ts +2 -0
  25. package/dist/chips/soc/index.js +23 -0
  26. package/dist/chips/soc/nrf52840.d.ts +2 -0
  27. package/dist/chips/soc/nrf52840.js +130 -0
  28. package/dist/chips/soc/rp2040.d.ts +2 -0
  29. package/dist/chips/soc/rp2040.js +141 -0
  30. package/dist/chips/soc/rp2350a.d.ts +2 -0
  31. package/dist/chips/soc/rp2350a.js +145 -0
  32. package/dist/chips/soc/samd21g18a.d.ts +2 -0
  33. package/dist/chips/soc/samd21g18a.js +143 -0
  34. package/dist/chips/soc/stm32f411xe.d.ts +2 -0
  35. package/dist/chips/soc/stm32f411xe.js +251 -0
  36. package/dist/chips/types.d.ts +319 -0
  37. package/dist/chips/xiao-ble.js +21 -0
  38. package/dist/display/profiles.js +1 -1
  39. package/dist/doctor.js +38 -4
  40. package/dist/dt-config/custom-board.d.ts +23 -0
  41. package/dist/dt-config/custom-board.js +227 -0
  42. package/dist/dt-config/kconfig.d.ts +72 -1
  43. package/dist/dt-config/kconfig.js +115 -26
  44. package/dist/dt-config/overlay.js +548 -28
  45. package/dist/framework.manifest.d.ts +3 -3
  46. package/dist/framework.manifest.js +122 -185
  47. package/dist/index.d.ts +4 -1
  48. package/dist/index.js +8 -2
  49. package/dist/lowering/adc.d.ts +20 -2
  50. package/dist/lowering/adc.js +121 -29
  51. package/dist/lowering/ble.js +31 -28
  52. package/dist/lowering/dac.js +9 -9
  53. package/dist/lowering/fs.js +18 -10
  54. package/dist/lowering/gpio.d.ts +2 -0
  55. package/dist/lowering/gpio.js +82 -5
  56. package/dist/lowering/http.js +39 -14
  57. package/dist/lowering/hwtimer.d.ts +4 -0
  58. package/dist/lowering/hwtimer.js +35 -10
  59. package/dist/lowering/i2c.js +46 -54
  60. package/dist/lowering/index.d.ts +5 -5
  61. package/dist/lowering/index.js +15 -16
  62. package/dist/lowering/interrupts.d.ts +22 -5
  63. package/dist/lowering/interrupts.js +104 -22
  64. package/dist/lowering/mqtt.js +3 -5
  65. package/dist/lowering/power.js +0 -4
  66. package/dist/lowering/preferences.js +49 -77
  67. package/dist/lowering/pulse.js +0 -31
  68. package/dist/lowering/pwm.d.ts +12 -1
  69. package/dist/lowering/pwm.js +78 -14
  70. package/dist/lowering/random.js +3 -3
  71. package/dist/lowering/sensor.d.ts +37 -0
  72. package/dist/lowering/sensor.js +119 -0
  73. package/dist/lowering/spi.d.ts +12 -0
  74. package/dist/lowering/spi.js +65 -40
  75. package/dist/lowering/thread.d.ts +14 -0
  76. package/dist/lowering/thread.js +55 -0
  77. package/dist/lowering/timing.js +20 -46
  78. package/dist/lowering/tone.js +16 -4
  79. package/dist/lowering/uart.d.ts +13 -0
  80. package/dist/lowering/uart.js +72 -51
  81. package/dist/lowering/usb.d.ts +34 -0
  82. package/dist/lowering/usb.js +206 -0
  83. package/dist/lowering/wdt.d.ts +1 -1
  84. package/dist/lowering/wdt.js +17 -7
  85. package/dist/lowering/wifi.d.ts +3 -2
  86. package/dist/lowering/wifi.js +97 -111
  87. package/dist/sdk/board-catalog-sync.d.ts +16 -0
  88. package/dist/sdk/board-catalog-sync.js +41 -0
  89. package/dist/sdk/board-data.generated.d.ts +2 -0
  90. package/dist/sdk/board-data.generated.js +4 -0
  91. package/dist/sdk/catalog-walker.d.ts +90 -0
  92. package/dist/sdk/catalog-walker.js +682 -0
  93. package/dist/sdk/dts-reader.d.ts +83 -0
  94. package/dist/sdk/dts-reader.js +596 -0
  95. package/dist/strategy.d.ts +136 -17
  96. package/dist/strategy.js +839 -222
  97. package/dist/toolchain/bossac-touch.d.ts +18 -0
  98. package/dist/toolchain/bossac-touch.js +88 -0
  99. package/dist/toolchain/compat.d.ts +2 -2
  100. package/dist/toolchain/compat.js +8 -3
  101. package/dist/toolchain/debug-config.d.ts +21 -26
  102. package/dist/toolchain/debug-config.js +226 -76
  103. package/dist/toolchain/env-check.d.ts +4 -4
  104. package/dist/toolchain/env-check.js +5 -5
  105. package/dist/toolchain/index.d.ts +115 -1
  106. package/dist/toolchain/index.js +680 -71
  107. package/dist/toolchain/scaffold.js +39 -7
  108. package/dist/toolchain/west-discover.d.ts +4 -0
  109. package/dist/toolchain/west-discover.js +17 -6
  110. package/installer/CHANGELOG.md +473 -0
  111. package/installer/README.md +328 -0
  112. package/installer/environment.yml +46 -0
  113. package/installer/etc/conda/activate.d/zephyr.bat +7 -0
  114. package/installer/etc/conda/activate.d/zephyr.ps1 +12 -0
  115. package/installer/etc/conda/activate.d/zephyr.sh +20 -0
  116. package/installer/etc/conda/deactivate.d/zephyr.bat +6 -0
  117. package/installer/etc/conda/deactivate.d/zephyr.ps1 +6 -0
  118. package/installer/etc/conda/deactivate.d/zephyr.sh +4 -0
  119. package/installer/install.mjs +580 -0
  120. package/installer/install.ps1 +619 -0
  121. package/installer/install.sh +310 -0
  122. package/installer/lib/detect-platform.sh +62 -0
  123. package/installer/lib/fetch-sdk.sh +304 -0
  124. package/installer/lib/init-workspace.sh +153 -0
  125. package/installer/lib/write-activation.sh +76 -0
  126. package/installer/patches/tfm-c-include-separators.patch +19 -0
  127. package/installer/patches/zephyr-tfm-nordic-zephyr-include.patch +24 -0
  128. package/installer/templates/project/.typecad/activate-zephyr.ps1 +31 -0
  129. package/installer/templates/project/.typecad/activate-zephyr.sh +31 -0
  130. package/installer/templates/project/.vscode/settings.json +30 -0
  131. package/installer/templates/project/README.md +58 -0
  132. package/installer/versions.env +110 -0
  133. package/package.json +15 -15
  134. package/src/as-built.ts +206 -0
  135. package/src/boardgen.ts +1428 -0
  136. package/src/chips/index.ts +35 -54
  137. package/src/chips/resolve.ts +285 -10
  138. package/src/chips/types.ts +325 -1
  139. package/src/display/profiles.ts +1 -1
  140. package/src/doctor.ts +110 -77
  141. package/src/dt-config/custom-board.ts +325 -0
  142. package/src/dt-config/kconfig.ts +165 -18
  143. package/src/dt-config/overlay.ts +537 -25
  144. package/src/framework.manifest.ts +123 -185
  145. package/src/index.ts +57 -41
  146. package/src/lowering/adc.ts +222 -118
  147. package/src/lowering/ble.ts +32 -28
  148. package/src/lowering/dac.ts +82 -82
  149. package/src/lowering/fs.ts +18 -10
  150. package/src/lowering/gpio.ts +242 -155
  151. package/src/lowering/http.ts +512 -488
  152. package/src/lowering/hwtimer.ts +139 -101
  153. package/src/lowering/i2c.ts +116 -126
  154. package/src/lowering/index.ts +91 -93
  155. package/src/lowering/interrupts.ts +113 -21
  156. package/src/lowering/mqtt.ts +3 -5
  157. package/src/lowering/preferences.ts +327 -354
  158. package/src/lowering/pwm.ts +167 -99
  159. package/src/lowering/random.ts +3 -3
  160. package/src/lowering/sensor.ts +155 -0
  161. package/src/lowering/spi.ts +164 -124
  162. package/src/lowering/thread.ts +63 -0
  163. package/src/lowering/timing.ts +46 -72
  164. package/src/lowering/uart.ts +73 -51
  165. package/src/lowering/usb.ts +221 -0
  166. package/src/lowering/wdt.ts +95 -84
  167. package/src/lowering/wifi.ts +97 -112
  168. package/src/sdk/board-catalog-sync.ts +62 -0
  169. package/src/strategy.ts +848 -237
  170. package/src/toolchain/bossac-touch.ts +112 -0
  171. package/src/toolchain/compat.ts +8 -3
  172. package/src/toolchain/debug-config.ts +676 -522
  173. package/src/toolchain/env-check.ts +9 -9
  174. package/src/toolchain/index.ts +1359 -645
  175. package/src/toolchain/scaffold.ts +38 -7
  176. package/src/toolchain/west-discover.ts +19 -6
  177. package/src/async/timer-polyfill.ts +0 -107
  178. package/src/chips/esp32.ts +0 -74
  179. package/src/chips/esp32s3.ts +0 -57
  180. package/src/chips/xiao-ble.ts +0 -82
  181. package/src/lowering/power.ts +0 -70
  182. package/src/lowering/pulse.ts +0 -93
  183. package/src/lowering/tone.ts +0 -60
  184. package/src/lowering/worker-backing.ts +0 -94
  185. package/src/lowering/worker.ts +0 -17
@@ -10,7 +10,9 @@
10
10
  import { writeFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
11
11
  import { join } from 'node:path';
12
12
  import { resolveKconfigFragments, type KconfigUsage } from '../dt-config/kconfig.js';
13
+ import { scanSensorParts } from './index.js';
13
14
  import { readCuttlefishLibrarySidecar } from '@typecad/cuttlefish/library-packages';
15
+ import { SENSOR_PART_INFO } from '@typecad/hal';
14
16
 
15
17
  /** Write a file only if the content differs from the existing file.
16
18
  * Returns true when the file was written (content changed or file was new). */
@@ -123,17 +125,28 @@ export function scaffoldZephyrProject(projectRoot: string, debug = false, userKc
123
125
  usesDac: uses('dac_') || uses('__tc_dac'),
124
126
  usesFS: uses('__tc_fs'),
125
127
  usesHwtimer: uses('counter_') || uses('__tc_hw'),
126
- usesI2c: uses('i2c_'),
127
- usesSpi: uses('spi_'),
128
- usesUart: uses('uart_'),
128
+ // The __tc_<bus> alternative matches the shim's state block — a program
129
+ // that only calls begin() emits `(void)__tc_i2c1_dev;` (no driver API
130
+ // call yet), but the shim still declared DEVICE_DT_GET(DT_NODELABEL(...))
131
+ // so the overlay must enable the node or the device symbol is missing.
132
+ usesI2c: uses('i2c_') || uses('__tc_i2c'),
133
+ // DT-bound sensor parts: the __tc_sensor_* state references and the
134
+ // sensor_sample_fetch/sensor_channel_get calls both carry the token.
135
+ usesSensor: uses('sensor_') || uses('__tc_sensor'),
136
+ usesFloatFormat: /%[-0-9.]*[eEfFgG]/.test(src),
137
+ usesSpi: uses('spi_') || uses('__tc_spi'),
138
+ usesUart: uses('uart_') || uses('__tc_uart'),
139
+ // USB CDC serial: every usb.* lowering calls into the __tc_usb<N>_* shim
140
+ // (device + init helper emitted under usesUsb).
141
+ usesUsb: uses('__tc_usb'),
142
+ // STM32F4 DBGMCU keep-SWD-alive init present (emitted for stm32f4 socs).
143
+ usesStm32DebugSleep: uses('__tc_stm32_dbgmcu'),
129
144
  usesWdt: uses('wdt_'),
130
145
  usesBle: uses('bt_') || uses('bt_gatt') || uses('bt_le_'),
131
146
  usesDisplay: uses('display_write') || uses('display_init') || uses('display_fill_rect') || uses('__tc_display_dev') || uses('CuttlefishDisplayTarget'),
132
147
  usesTouch: uses('ft6336u') || uses('touch_'),
133
- // Power tokens: k_sleep + pm_state_force / PM_STATE_* (what power.ts emits).
134
- // The old `power_` token matched nothing the power HAL emits and collides
135
- // with tx_power_dbm — removed.
136
- usesPower: uses('pm_') || uses('k_sleep') || uses('PM_STATE_'),
148
+ // (The power HAL is removed; its former pm_/k_sleep token scan is gone —
149
+ // nothing here must match tx_power_dbm's `power` substring.)
137
150
  usesWifi: uses('wifi_') || uses('net_mgmt') || uses('conn_mgr'),
138
151
  // HTTP: the __tc_http_* shim + http_client_req + getaddrinfo. The '_'-anchored
139
152
  // 'http_' token matches __tc_http_* and http_client_req (the core HTTP lib
@@ -166,6 +179,12 @@ export function scaffoldZephyrProject(projectRoot: string, debug = false, userKc
166
179
  '# Auto-generated by @typecad/framework-zephyr from cuttlefish.config.ts.',
167
180
  'cmake_minimum_required(VERSION 3.20.0)',
168
181
  '',
182
+ '# The app is its own board root: MCU-only targets generate an out-of-tree',
183
+ '# custom board under boards/typecad/<name>/ that Zephyr must discover.',
184
+ '# Harmless when boards/ holds only .overlay files (BOARD_ROOT just needs',
185
+ '# the directory to exist).',
186
+ 'list(APPEND BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR})',
187
+ '',
169
188
  'find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})',
170
189
  '',
171
190
  'project(zephyr_app)',
@@ -248,6 +267,18 @@ export function scaffoldZephyrProject(projectRoot: string, debug = false, userKc
248
267
  }
249
268
  }
250
269
  }
270
+ // Per-part Kconfig exceptions: catalog parts whose driver is NOT default-y
271
+ // on its DT node carry extra lines here (empty today — every in-tree sensor
272
+ // driver lights up from the node; the path exists so the first exception
273
+ // found by the generator has somewhere to go).
274
+ const sensorParts = scanSensorParts(src);
275
+ if (sensorParts.length > 0) {
276
+ const extra = [...new Set(sensorParts.flatMap((sp) => SENSOR_PART_INFO[sp.part]?.kconfig ?? []))];
277
+ if (extra.length > 0) {
278
+ prjConf.push('', '# Sensor part Kconfig (catalog exceptions).');
279
+ for (const line of extra) prjConf.push(line);
280
+ }
281
+ }
251
282
  // Emit user-specified Kconfig from cuttlefish.config.ts zephyr.kconfig.
252
283
  // These override any matching auto-detected symbol (skipped above).
253
284
  if (userKconfig) {
@@ -46,13 +46,14 @@ function pythonRunsWest(exe: string): boolean {
46
46
  }
47
47
  }
48
48
 
49
- /** True if `cmd` runs `west --version` successfully. */
49
+ /** True if `cmd` runs `west --version` successfully. `cmd` comes from
50
+ * `where`/`which` output — a venv launcher (west.exe) or script path, spawned
51
+ * without a shell like every other command in this module. */
50
52
  function westOnPath(cmd: string): boolean {
51
53
  try {
52
54
  const r = spawnSync(cmd, ['--version'], {
53
55
  encoding: 'utf8',
54
56
  timeout: 15_000,
55
- shell: IS_WIN,
56
57
  windowsHide: true,
57
58
  });
58
59
  return r.status === 0;
@@ -100,12 +101,11 @@ export function isZephyrBase(dir: string): boolean {
100
101
  // ── Strategy 1: `west` on PATH ──────────────────────────────────────────────
101
102
 
102
103
  export function discoverFromPath(): WestInstall | null {
103
- // shell only on Windows (where.exe resolution through cmd) an args array
104
- // with shell: true triggers Node's DEP0190 deprecation warning on Linux/
105
- // macOS, where `which` is a plain executable that needs no shell.
104
+ // No shell: `where`/`which` are plain executables Node resolves from PATH
105
+ // (where.exe is a real PE, not a cmd builtin), and an args array with a
106
+ // truthy shell triggers Node's DEP0190 deprecation warning.
106
107
  const which = spawnSync(IS_WIN ? 'where' : 'which', ['west'], {
107
108
  encoding: 'utf8',
108
- shell: IS_WIN,
109
109
  windowsHide: true,
110
110
  });
111
111
  if (which.status !== 0) return null;
@@ -190,6 +190,19 @@ function readMicromambaEnvVar(envDir: string, varName: string): string | undefin
190
190
  return undefined;
191
191
  }
192
192
 
193
+ /** The installer-written ZEPHYR_BASE of the micromamba env, fs-only (no
194
+ * spawn) — the cheap probe shared by board-catalog overlay discovery.
195
+ * Undefined when the installer env or its env-vars file is absent. */
196
+ export function micromambaZephyrBase(
197
+ envName: string = process.env.TYPECAD_ZEPHYR_ENV || 'zephyr',
198
+ ): string | undefined {
199
+ const mm = findMicromamba();
200
+ if (!mm) return undefined;
201
+ const envDir = join(mm.rootPrefix, 'envs', envName);
202
+ const zb = readMicromambaEnvVar(envDir, 'TYPECAD_ZEPHYR_BASE');
203
+ return zb && isZephyrBase(zb) ? zb : undefined;
204
+ }
205
+
193
206
  export function discoverFromMicromamba(
194
207
  envName: string = process.env.TYPECAD_ZEPHYR_ENV || 'zephyr',
195
208
  ): WestInstall | null {
@@ -1,107 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Timer methods polyfill — Zephyr k_timer + k_work (heap-free)
3
- //
4
- // setInterval / setTimeout / clearInterval / clearTimeout backed by a fixed
5
- // pool of k_timer + k_work pairs. The timer's expiry function (ISR context)
6
- // submits a k_work item to the system workqueue; the work handler runs the
7
- // user callback in thread context. This split keeps the ISR cheap and lets the
8
- // callback run on a real thread (so it can do real work — but must not block,
9
- // since the system workqueue is shared).
10
- //
11
- // EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
12
- // Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
13
- // ---------------------------------------------------------------------------
14
-
15
- import type { RuntimePolyfillIR } from '@typecad/cuttlefish/api/shared';
16
-
17
- const MIN_TIMERS = 1;
18
- const MAX_TIMERS_CAP = 16;
19
-
20
- export interface TimerPolyfillIR extends RuntimePolyfillIR {
21
- /** The clamped pool size the C++ was generated with. */
22
- maxTimers: number;
23
- }
24
-
25
- /**
26
- * Build the timer_methods polyfill IR. `requestedTimers` is sized from
27
- * ctx.analysis.timerCallCount; it is clamped to [1, 16].
28
- */
29
- export function buildTimerPolyfill(requestedTimers: number): TimerPolyfillIR {
30
- const maxTimers = Math.min(MAX_TIMERS_CAP, Math.max(MIN_TIMERS, requestedTimers));
31
-
32
- const helperStructs = [`
33
- // cuttlefish timer runtime — k_timer + k_work pool (heap-free).
34
- struct __tc_TimerSlot {
35
- struct k_timer timer;
36
- struct k_work work;
37
- void (*callback)(void);
38
- bool active;
39
- };
40
-
41
- static struct __tc_TimerSlot __tc_timer_slots[${maxTimers}];
42
-
43
- static void __tc_timer_work_handler(struct k_work* w) {
44
- // Runs on the system workqueue thread (NOT ISR). Find the owning slot by
45
- // address and invoke the callback. Must not block.
46
- for (int32_t i = 0; i < ${maxTimers}; i++) {
47
- if (&__tc_timer_slots[i].work == w) {
48
- if (__tc_timer_slots[i].callback != nullptr) { __tc_timer_slots[i].callback(); }
49
- return;
50
- }
51
- }
52
- }
53
-
54
- static void __tc_timer_expiry_fn(struct k_timer* t) {
55
- // ISR context: submit the work item, do NOT run the callback here.
56
- for (int32_t i = 0; i < ${maxTimers}; i++) {
57
- if (&__tc_timer_slots[i].timer == t) {
58
- (void)k_work_submit(&__tc_timer_slots[i].work);
59
- return;
60
- }
61
- }
62
- }
63
-
64
- static int32_t __tc_timer_add(void (*cb)(void), int32_t ms, bool repeat) {
65
- for (int32_t i = 0; i < ${maxTimers}; i++) {
66
- if (!__tc_timer_slots[i].active) {
67
- __tc_timer_slots[i].callback = cb;
68
- __tc_timer_slots[i].active = true;
69
- k_timer_init(&__tc_timer_slots[i].timer, __tc_timer_expiry_fn, nullptr);
70
- k_work_init(&__tc_timer_slots[i].work, __tc_timer_work_handler);
71
- k_timer_start(&__tc_timer_slots[i].timer, K_MSEC(ms), repeat ? K_MSEC(ms) : K_FOREVER);
72
- return i + 1; // 1-based id (Arduino parity)
73
- }
74
- }
75
- return 0; // pool full
76
- }
77
-
78
- static void __tc_timer_clear(int32_t id) {
79
- if (id > 0 && id <= ${maxTimers}) {
80
- int32_t i = id - 1;
81
- k_timer_stop(&__tc_timer_slots[i].timer);
82
- __tc_timer_slots[i].active = false;
83
- __tc_timer_slots[i].callback = nullptr;
84
- }
85
- }
86
- `];
87
-
88
- const helperFunctions = [`
89
- int32_t __tc_setInterval(void (*cb)(), int32_t ms) { return __tc_timer_add(cb, ms, true); }
90
- int32_t __tc_setTimeout(void (*cb)(), int32_t ms) { return __tc_timer_add(cb, ms, false); }
91
- void __tc_clearInterval(int32_t id) { __tc_timer_clear(id); }
92
- void __tc_clearTimeout(int32_t id) { __tc_timer_clear(id); }
93
- `];
94
-
95
- return {
96
- kind: 'polyfill',
97
- id: 'timer_methods',
98
- domain: 'embedded',
99
- requiredIncludes: [],
100
- forwardDeclarations: [],
101
- helperStructs,
102
- helperFunctions,
103
- shimMacros: [],
104
- dependencies: [],
105
- maxTimers,
106
- };
107
- }
@@ -1,74 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Espressif ESP32 (esp32_devkitc) — Zephyr board descriptor
3
- //
4
- // Board target: `esp32_devkitc` (mainline Zephyr,
5
- // boards/espressif/esp32_devkitc). Programmed over USB via the esptool runner
6
- // (see toolchain/index.ts), the same flash path as the ESP32-S3.
7
- //
8
- // GPIO is split across TWO devicetree controllers — `gpio0` (pins 0–31) and
9
- // `gpio1` (pins 32–39) — so this descriptor lists both in `gpioControllers`.
10
- // The lowering routes each HAL pin to its owning controller at runtime; see
11
- // chips/controllers.ts. (Same shape as the S3, but the ESP32's highest GPIO is
12
- // 39, not 48 — `gpio1` has ngpios=8, not 17.)
13
- //
14
- // Minimal-by-design: the only DT facts carried here are the ones a compile-time
15
- // DT macro cannot reach — the runtime pin→controller split, plus the `sw0`
16
- // alias for the BOOT button (used by the devicetree-spec GPIO path). Every other
17
- // DT fact (UART/I2C/SPI/`wdt` nodelabels) is resolved by Zephyr's own
18
- // devicetree via emitted DT_NODELABEL macros, not hand-copied here.
19
- //
20
- // Verified against the Zephyr board DTS:
21
- // boards/espressif/esp32_devkitc/esp32_devkitc_procpu.dts
22
- // aliases { sw0 = &button0; } → button_0: pin 0 on gpio0, active-low + pull-up
23
- // &gpio0/&gpio1 { status = "okay" } (esp32_devkitc_procpu.dts:64-70)
24
- // &wifi { status = "okay" } (esp32_devkitc_procpu.dts:143-145)
25
- // GPIO controller coverage:
26
- // dts/xtensa/espressif/esp32/esp32_common.dtsi:314-337
27
- // gpio0: ngpios = <32> (pins 0–31)
28
- // gpio1: ngpios = <8> (pins 32–39)
29
- //
30
- // Note: GPIO 34–39 are input-only pads on ESP32 silicon (not modeled here —
31
- // the DT does not encode output restrictions per pin; an output config on those
32
- // pins fails at runtime against the raw controller, which is the expected
33
- // silicon-accurate behavior).
34
- // ---------------------------------------------------------------------------
35
-
36
- import type { ZephyrChipDescriptor } from './types.js';
37
-
38
- export const ESP32_DEVKITC: ZephyrChipDescriptor = {
39
- id: 'esp32_devkitc',
40
- soc: 'esp32',
41
- gpioController: 'gpio0',
42
- gpioControllers: [
43
- { nodelabel: 'gpio0', minPin: 0, maxPin: 31 },
44
- { nodelabel: 'gpio1', minPin: 32, maxPin: 39 },
45
- ],
46
- gpio: {
47
- // The BOOT button (GPIO0) is the board's only DT-aliased GPIO. Listed so a
48
- // program reading/interrupting pin 0 goes through the polarity-correct
49
- // devicetree-spec path (GPIO_ACTIVE_LOW honored by the DT flags).
50
- dtSpecs: [
51
- { pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
52
- ],
53
- interruptPins: [
54
- { pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
55
- ],
56
- },
57
- // WiFi: the ESP32 has a 2.4GHz radio; conn_mgr + the esp32 wifi driver
58
- // (CONFIG_WIFI_ESP32) provide connectivity. WIFI_ESP32 depends on !SMP, and
59
- // the ESP32 is AMP (dual-image procpu/appcpu), not SMP, by default — so the
60
- // dependency is satisfied. Omitted on radioless targets.
61
- wifi: { supported: true },
62
- // DAC: the ESP32 has two 8-bit DAC channels on GPIO25 (channel 1) and GPIO26
63
- // (channel 2). The Zephyr esp32 DAC driver (drivers/dac/dac_esp32.c) exposes
64
- // them via the `dac0` node; the lowering emits dac_channel_setup +
65
- // dac_write_value against DEVICE_DT_GET(DT_NODELABEL(dac0)). The overlay
66
- // enables the node when the program uses dac.*. ESP32-S3 has no DAC.
67
- dac: {
68
- device: 'dac0',
69
- channels: [
70
- { pin: 25, channel: 1, resolution: 8 },
71
- { pin: 26, channel: 2, resolution: 8 },
72
- ],
73
- },
74
- };
@@ -1,57 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Espressif ESP32-S3 (esp32s3_devkitc) — Zephyr board descriptor
3
- //
4
- // Board target: `esp32s3_devkitc` (mainline Zephyr,
5
- // boards/espressif/esp32s3_devkitc). Programmed over USB via the esptool
6
- // runner (see toolchain/index.ts), unlike the J-Link/nrfjprog path used for
7
- // the XIAO nRF52840.
8
- //
9
- // GPIO is split across TWO devicetree controllers — `gpio0` (pins 0–31) and
10
- // `gpio1` (pins 32–48) — so this descriptor lists both in `gpioControllers`.
11
- // The lowering routes each HAL pin to its owning controller at runtime; see
12
- // chips/controllers.ts.
13
- //
14
- // Minimal-by-design: the only DT facts carried here are the ones a
15
- // compile-time DT macro cannot reach — the runtime pin→controller split, plus
16
- // the `sw0` alias for the BOOT button (used by the devicetree-spec GPIO path).
17
- // Every other DT fact (UART/I2C/SPI/`wdt` nodelabels) is resolved by Zephyr's
18
- // own devicetree via emitted DT_NODELABEL macros, not hand-copied here.
19
- //
20
- // Verified against the Zephyr board DTS:
21
- // boards/espressif/esp32s3_devkitc/esp32s3_devkitc_procpu.dts
22
- // aliases { sw0 = &button0; } → button_0: pin 0 on gpio0, active-low + pull-up
23
- // (the DevKitC board DTS defines no led0 alias — the onboard RGB is a WS2812
24
- // on GPIO38, not a plain GPIO LED, so it is intentionally NOT listed here.)
25
- // ---------------------------------------------------------------------------
26
-
27
- import type { ZephyrChipDescriptor } from './types.js';
28
-
29
- export const ESP32S3_DEVKITC: ZephyrChipDescriptor = {
30
- id: 'esp32s3_devkitc',
31
- soc: 'esp32s3',
32
- gpioController: 'gpio0',
33
- gpioControllers: [
34
- { nodelabel: 'gpio0', minPin: 0, maxPin: 31 },
35
- { nodelabel: 'gpio1', minPin: 32, maxPin: 48 },
36
- ],
37
- gpio: {
38
- // The BOOT button (GPIO0) is the board's only DT-aliased GPIO. Listed so a
39
- // program reading/interrupting pin 0 goes through the polarity-correct
40
- // devicetree-spec path (GPIO_ACTIVE_LOW honored by the DT flags). Every
41
- // other GPIO pin uses the raw-controller path against its owning controller.
42
- dtSpecs: [
43
- { pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
44
- ],
45
- interruptPins: [
46
- { pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
47
- ],
48
- },
49
- // UART/I2C/SPI/`wdt` are intentionally omitted: their devicetree nodelabels
50
- // (uart0/uart1/uart2, i2c0/i2c1, spi2/spi3, wdt0) are resolved by Zephyr's
51
- // devicetree at compile time and don't need to be carried as data here. ADC is
52
- // omitted as well — added when a demo needs analog reads, with the verified
53
- // ESP32-S3 ADC1/ADC2 pin→channel map.
54
- // WiFi: the ESP32-S3 has a 2.4GHz radio; conn_mgr + the esp32 wifi driver
55
- // (CONFIG_WIFI_ESP32) provide connectivity. Omitted on radioless targets.
56
- wifi: { supported: true },
57
- };
@@ -1,82 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Seeed Studio XIAO nRF52840 — Zephyr board descriptor
3
- //
4
- // Board target: `xiao_ble` (mainline Zephyr, boards/seeed/xiao_ble).
5
- // The onboard user LED is active-low and exposed as the DT alias `led0`.
6
- // ---------------------------------------------------------------------------
7
-
8
- import type { ZephyrChipDescriptor } from './types.js';
9
-
10
- export const XIAO_BLE: ZephyrChipDescriptor = {
11
- id: 'xiao_ble',
12
- soc: 'nrf52840',
13
- gpioController: 'gpio0',
14
- // GPIO is split across two devicetree controllers: gpio0 (P0.00–P0.31)
15
- // and gpio1 (P1.00–P1.15, HAL pins 32–47). Declaring the split makes the
16
- // raw path emit the port-relative index against gpio1 (P1.11 = raw 11),
17
- // which NRF_GPIO_PIN_MAP(1, 11) resolves to absolute pin 43 — the same
18
- // physical pin the old single-controller form reached only by accident:
19
- // gpio0 + global 43 also maps to 43 (MAP(0, 43) = 43), but that form
20
- // trips the generic layer's port_pin_mask __ASSERT ("Unsupported pin",
21
- // gpio0's mask covers 0–31) on any assert-enabled build.
22
- gpioControllers: [
23
- { nodelabel: 'gpio0', minPin: 0, maxPin: 31 },
24
- { nodelabel: 'gpio1', minPin: 32, maxPin: 47 },
25
- ],
26
- gpio: {
27
- dtSpecs: [
28
- // Onboard RGB LEDs — active-low (GPIO_ACTIVE_LOW in xiao_ble_common.dtsi).
29
- // Verified against the board's `aliases { led0 = &led0; led1 = &led1; led2 = &led2; }`.
30
- { pin: 26, dtSpec: 'led0' }, // Red (P0.26)
31
- { pin: 30, dtSpec: 'led1' }, // Green (P0.30)
32
- { pin: 6, dtSpec: 'led2' }, // Blue (P0.06)
33
- ],
34
- // The XIAO nRF52840 has a user button on P0.04, but mainline Zephyr's
35
- // xiao_ble board (verified against Zephyr 4.3.99) does NOT expose it as a
36
- // DT `sw0` alias — there is no gpio-keys node in xiao_ble.dts. Emitting
37
- // GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios) therefore fails to compile
38
- // ('DT_N_ALIAS_sw0_... was not declared'). Until a button node + alias is
39
- // added (via an overlay or an upstream board update), interruptPins stays
40
- // empty: attachInterrupt on this board lowers to the no-DT-spec comment
41
- // fallback rather than a hard compile error.
42
- interruptPins: [],
43
- },
44
- // The XIAO connector wiring (from seeed_xiao_connector.dtsi + xiao_ble-pinctrl.dtsi):
45
- // xiao_i2c → i2c1 (SDA P0.04/D4, SCL P0.05/D5)
46
- // xiao_spi → spi2 (SCK P1.13/D8, MOSI P1.15/D10, MISO P1.14/D9)
47
- // xiao_serial → uart0 (TX P1.11/D6, RX P1.12/D7)
48
- // The board DTS leaves i2c0/spi0/spi1 disabled (shared instances).
49
- i2c: {
50
- controllers: [{ nodeLabel: 'i2c1' }],
51
- },
52
- spi: {
53
- controllers: [{ nodeLabel: 'spi2' }],
54
- },
55
- uart: {
56
- controllers: [{ nodeLabel: 'uart0' }],
57
- },
58
- pwm: {
59
- // pwm-led0 drives the board PWM LED (PWM_OUT0 on P0.17, inverted).
60
- specs: [{ pin: 17, dtSpec: 'pwm-led0' }],
61
- },
62
- adc: {
63
- // SAADC node is `adc`; no pre-declared channels. XIAO D0–D3 = AIN0–AIN3
64
- // (P0.02/P0.03/P0.28/P0.29). Internal VREF ~0.6V with VDD/4 gain ⇒ 3000mV.
65
- nodeLabel: 'adc',
66
- resolution: 12,
67
- vrefMv: 3000,
68
- channels: [
69
- { pin: 2, channel: 0 }, // P0.02 / D0 / AIN0
70
- { pin: 3, channel: 1 }, // P0.03 / D1 / AIN1
71
- { pin: 28, channel: 2 }, // P0.28 / D2 / AIN2
72
- { pin: 29, channel: 3 }, // P0.29 / D3 / AIN3
73
- ],
74
- },
75
- wdt: { nodeLabel: 'wdt0' },
76
- // Hardware timer: nRF RTC1 is the free counter (RTC0 is kernel-owned by the
77
- // softdevice/clock driver). The hwtimer lowering drives it as a Zephyr
78
- // counter device (counter_start/stop + a top-value alarm for set_frequency).
79
- // Verified against the nRF52840 SoC dtsi (rtc0/rtc1 nodes). The kernel uses
80
- // RTC0 for the system tick; RTC1 is available for application use.
81
- hwtimer: { controllers: [{ nodeLabel: 'rtc1' }] },
82
- };
@@ -1,70 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Power lowering — Zephyr power management
3
- //
4
- // Zephyr enters low-power states from the idle thread per policy. The app-facing
5
- // controls are pm_state_force (override policy to force a state on next idle)
6
- // and k_sleep (block the calling thread, allowing the system to idle into a
7
- // low-power state). "Deep sleep" maps to k_sleep (the SoC picks its deepest
8
- // allowed state); "light sleep" best-effort forces SUSPEND_TO_IDLE.
9
- //
10
- // deep_sleep_pin configures the wake GPIO as an interrupt source then k_sleeps
11
- // (the interrupt wakes the SoC). CPU frequency is SoC-specific (nRF52 has a
12
- // fixed 64 MHz HFXO with no portable dynamic scaling) and stays a documented
13
- // no-op. deep_sleep_pin targets gpio0 (single-controller nRF52840 case);
14
- // multi-controller SoC routing is a documented follow-on.
15
- // ---------------------------------------------------------------------------
16
-
17
- import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
18
-
19
- /**
20
- * Resolve a HAL power.* op to Zephyr C++.
21
- * Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
22
- */
23
- export function lowerPower(op: HALOpIR): { code?: string; expression?: string } {
24
- const o = op as any;
25
-
26
- switch (op.operation) {
27
- case 'power.deep_sleep':
28
- // Block for the requested duration; the idle thread + PM policy will enter
29
- // the deepest state the SoC allows during the sleep.
30
- return { code: `k_sleep(K_MSEC(${o.ms}));` };
31
- case 'power.light_sleep':
32
- // Best-effort: force a shallow suspend on the next idle. pm_state_force
33
- // requires a pm_state_info; SUSPEND_TO_IDLE is a common shallow state.
34
- return { code: `pm_state_force(0, &(const struct pm_state_info){ .state = PM_STATE_SUSPEND_TO_IDLE, .substate_id = 0 });` };
35
- case 'power.set_cpu_frequency':
36
- // nRF52 has a fixed 64 MHz HFXO; no portable dynamic scaling from C++.
37
- // Documented no-op (best-effort), matching Arduino's approach.
38
- return { code: `/* power.set_cpu_frequency(${o.mhz}): nRF52 fixed 64 MHz HFXO — no portable scaling */` };
39
- case 'power.deep_sleep_pin': {
40
- // Configure the pin as a GPIO wake source (level interrupt), then k_sleep
41
- // until the interrupt fires. A static gpio_callback + a no-op handler (the
42
- // wake just needs the interrupt enabled; the handler does nothing). level:
43
- // 1=high, 0=low. Targets gpio0 (single-controller nRF52840); multi-
44
- // controller SoC routing is a documented follow-on.
45
- const pin = o.pin;
46
- const level = o.level;
47
- const flags = level ? 'GPIO_INT_LEVEL_HIGH' : 'GPIO_INT_LEVEL_LOW';
48
- return {
49
- code: [
50
- '{',
51
- ` static struct gpio_callback __tc_wake_cb;`,
52
- ` const struct device* __dev = DEVICE_DT_GET(DT_NODELABEL(gpio0));`,
53
- ` gpio_pin_configure(__dev, ${pin}, GPIO_INPUT);`,
54
- ` gpio_init_callback(&__tc_wake_cb, [](const struct device*, struct gpio_callback*, gpio_port_pins_t) { (void)0; }, BIT(${pin}));`,
55
- ` gpio_add_callback(__dev, &__tc_wake_cb);`,
56
- ` gpio_pin_interrupt_configure(__dev, ${pin}, ${flags});`,
57
- ` k_sleep(K_FOREVER);`,
58
- ` gpio_pin_interrupt_configure(__dev, ${pin}, GPIO_INT_DISABLE);`,
59
- ` gpio_remove_callback(__dev, &__tc_wake_cb);`,
60
- '}',
61
- ].join(' '),
62
- };
63
- }
64
- default:
65
- throw new Error(
66
- `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
67
- `Open an issue or use rawCpp() to emit it manually.`,
68
- );
69
- }
70
- }
@@ -1,93 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // Pulse + shift lowering — bit-bang via GPIO + timing
3
- //
4
- // pulse.in measures the duration of a pulse (HIGH or LOW) on a GPIO pin by
5
- // edge-polling with k_uptime_get. shift.out/in bit-bang a byte through a data
6
- // + clock pin pair using gpio_pin_set + k_busy_wait. These are software
7
- // implementations (no hardware pulse capture on nRF for the HAL surface); they
8
- // are correct but not high-precision.
9
- // ---------------------------------------------------------------------------
10
-
11
- import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
12
- import type { ZephyrChipDescriptor } from '../chips/types.js';
13
- import { controllerNodelabelForPin, controllerRawPinForPin } from '../chips/controllers.js';
14
-
15
- /** `DEVICE_DT_GET(DT_NODELABEL(<owning-controller>))` for a HAL pin. */
16
- function devForPin(chip: ZephyrChipDescriptor, pin: number): string {
17
- return `DEVICE_DT_GET(DT_NODELABEL(${controllerNodelabelForPin(chip, pin)}))`;
18
- }
19
-
20
- /** Resolve a HAL pulse.* or shift.* op to Zephyr C++ (bit-bang). */
21
- export function lowerPulseOrShift(
22
- op: HALOpIR,
23
- chip: ZephyrChipDescriptor,
24
- ): { code?: string; expression?: string } {
25
- const o = op as any;
26
-
27
- switch (op.operation) {
28
- case 'pulse.in': {
29
- // Wait for the pin to reach `value`, then measure until it changes.
30
- // Edge-poll with k_uptime_get() (ms granularity — coarse but correct,
31
- // and 64-bit so it cannot wrap mid-measurement over ~49 days). The wait
32
- // for the start edge is bounded by the timeout; the measurement of the
33
- // pulse itself is intentionally unbounded (that IS the pulse length).
34
- // Returns -1 (0) if the start edge never arrives within the timeout.
35
- const pin = controllerRawPinForPin(chip, o.pin);
36
- const want = o.value;
37
- const dev = devForPin(chip, o.pin);
38
- const timeout = o.timeout ?? 1_000_000; // default 1s in us
39
- return {
40
- expression: `({ int64_t __max = static_cast<int64_t>(${timeout} / 1000); int64_t __t0 = k_uptime_get(); bool __ok = true; while (gpio_pin_get_raw(${dev}, ${pin}) != ${want}) { if ((k_uptime_get() - __t0) > __max) { __ok = false; break; } } int32_t __ret = 0; if (__ok) { int64_t __start = k_uptime_get(); while (gpio_pin_get_raw(${dev}, ${pin}) == ${want}) { } __ret = static_cast<int32_t>((k_uptime_get() - __start) * 1000); } __ret; })`,
41
- };
42
- }
43
- case 'pulse.in_long': {
44
- // Same as pulse.in but for long pulses. Previously this spun forever
45
- // with NO timeout (the comment claimed "no overflow concern" but the
46
- // real risk was hanging the thread on a stuck pin). Apply the same
47
- // timeout-bounded start-edge wait as pulse.in (bug Q5).
48
- const pin = controllerRawPinForPin(chip, o.pin);
49
- const want = o.value;
50
- const dev = devForPin(chip, o.pin);
51
- const timeout = o.timeout ?? 3_000_000; // default 3s in us (long pulses)
52
- return {
53
- expression: `({ int64_t __max = static_cast<int64_t>(${timeout} / 1000); int64_t __t0 = k_uptime_get(); bool __ok = true; while (gpio_pin_get_raw(${dev}, ${pin}) != ${want}) { if ((k_uptime_get() - __t0) > __max) { __ok = false; break; } } int32_t __ret = 0; if (__ok) { int64_t __start = k_uptime_get(); while (gpio_pin_get_raw(${dev}, ${pin}) == ${want}) { } __ret = static_cast<int32_t>((k_uptime_get() - __start) * 1000); } __ret; })`,
54
- };
55
- }
56
- case 'shift.out': {
57
- // Bit-bang `value` (MSB or LSB first) via dataPin + clockPin. The two
58
- // pins may sit on different GPIO controllers on multi-controller SoCs
59
- // (ESP32-S3), so each is resolved to its own owning controller.
60
- const dataPin = o.dataPin;
61
- const clockPin = o.clockPin;
62
- const dataDev = devForPin(chip, dataPin);
63
- const clockDev = devForPin(chip, clockPin);
64
- const msbFirst = o.bitOrder === 'msb';
65
- const init = msbFirst ? '7' : '0';
66
- const test = msbFirst ? '(__i >= 0)' : '(__i < 8)';
67
- const step = msbFirst ? '__i--' : '__i++';
68
- return {
69
- code: `for (int __i = ${init}; ${test}; ${step}) { gpio_pin_set_raw(${dataDev}, ${controllerRawPinForPin(chip, dataPin)}, (${o.value} >> __i) & 1); gpio_pin_set_raw(${clockDev}, ${controllerRawPinForPin(chip, clockPin)}, 1); k_busy_wait(1); gpio_pin_set_raw(${clockDev}, ${controllerRawPinForPin(chip, clockPin)}, 0); }`,
70
- };
71
- }
72
- case 'shift.in': {
73
- // Read 8 bits (MSB or LSB first) from dataPin, clocking on clockPin.
74
- const dataPin = o.dataPin;
75
- const clockPin = o.clockPin;
76
- const dataDev = devForPin(chip, dataPin);
77
- const clockDev = devForPin(chip, clockPin);
78
- const msbFirst = o.bitOrder === 'msb';
79
- const init = msbFirst ? '7' : '0';
80
- const test = msbFirst ? '(__i >= 0)' : '(__i < 8)';
81
- const step = msbFirst ? '__i--' : '__i++';
82
- const accum = msbFirst ? '__v = (__v << 1)' : '__v |= (bit << __i)';
83
- return {
84
- expression: `({ uint8_t __v = 0; for (int __i = ${init}; ${test}; ${step}) { gpio_pin_set_raw(${clockDev}, ${controllerRawPinForPin(chip, clockPin)}, 1); k_busy_wait(1); int bit = gpio_pin_get_raw(${dataDev}, ${controllerRawPinForPin(chip, dataPin)}); gpio_pin_set_raw(${clockDev}, ${controllerRawPinForPin(chip, clockPin)}, 0); ${accum}; } __v; })`,
85
- };
86
- }
87
- default:
88
- throw new Error(
89
- `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
90
- `Open an issue or use rawCpp() to emit it manually.`,
91
- );
92
- }
93
- }