@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
@@ -0,0 +1,7 @@
1
+ export interface BoardOverride {
2
+ /** LED pin name for boards whose user LED is not a gpio-leds node (the
3
+ * ESP32-S3 DevKitC's addressable RGB on GPIO48 — driven as plain GPIO,
4
+ * same as the deleted board package did). */
5
+ readonly led?: string;
6
+ }
7
+ export declare const BOARD_OVERRIDES: Readonly<Record<string, BoardOverride>>;
@@ -0,0 +1,11 @@
1
+ // ---------------------------------------------------------------------------
2
+ // board-overrides.ts — curated board-level facts the devicetree cannot
3
+ // express. The honest residue of the board-package deletion: things a board
4
+ // vendor documents in a datasheet but never encodes in DTS.
5
+ //
6
+ // Keyed by the bare board id (the first segment of the qualified target, so
7
+ // every variant/qualifier of the board inherits the override).
8
+ // ----------------------------------------------------------------------------
9
+ export const BOARD_OVERRIDES = {
10
+ esp32s3_devkitc: { led: 'GPIO48' },
11
+ };
@@ -51,4 +51,53 @@ export const ESP32S3_DEVKITC = {
51
51
  // WiFi: the ESP32-S3 has a 2.4GHz radio; conn_mgr + the esp32 wifi driver
52
52
  // (CONFIG_WIFI_ESP32) provide connectivity. Omitted on radioless targets.
53
53
  wifi: { supported: true },
54
+ // User buses, mirroring the devkit's verified facts (the
55
+ // board-constants path is authoritative in real builds; this registry
56
+ // entry is the fallback when board constants are absent — e.g. the
57
+ // transpile test harness). uart1 (not uart0 — the console) with the
58
+ // pinctrl group + current-speed the esp32-uart binding requires; i2c0;
59
+ // spi2/spi3.
60
+ i2c: { controllers: [{ nodeLabel: 'i2c0' }] },
61
+ spi: { controllers: [{ nodeLabel: 'spi2' }, { nodeLabel: 'spi3' }] },
62
+ uart: {
63
+ controllers: [
64
+ {
65
+ nodeLabel: 'uart1',
66
+ pinctrlRef: 'uart1_default',
67
+ props: ['current-speed = <115200>;'],
68
+ },
69
+ ],
70
+ },
71
+ // USB CDC-ACM over the S3's native USB-OTG (D-/D+ on the dedicated GPIO19/20
72
+ // pads, no GPIO matrix). The board DTS already aliases the DWC2 controller
73
+ // node and enables it (`zephyr_udc0: &usb_otg { status = "okay"; }` —
74
+ // esp32s3_devkitc_procpu.dts), and the UDC DWC2 driver is DT-default-on, so
75
+ // declaring it here is all usb.* needs: the overlay composes one CDC-ACM
76
+ // instance child and the kconfig resolver enables the "next" USB stack.
77
+ // Console stays on uart0 (the devkit's USB-serial bridge) unless
78
+ // `console.output: 'usb'` rebinds zephyr,console to the CDC port.
79
+ usb: { controller: 'zephyr_udc0', cdcInstances: 1, vid: '0x2FE3', pid: '0x0006' },
80
+ // PWM: the LEDC controller (ledc0 in esp32s3_common.dtsi) — 8 channels, each
81
+ // routable to nearly any pad through the GPIO matrix, so this is a matrix
82
+ // (channels assigned to the driven pins at build time), not a static spec
83
+ // list. Pin selection excludes: GPIO0 (boot strap / BOOT button), GPIO19/20
84
+ // (USB D-/D+), GPIO26–32 (SPI flash/PSRAM), GPIO33–37 (octal PSRAM on OPI
85
+ // modules), GPIO43/44 (uart0 console pads). The driver
86
+ // (CONFIG_PWM_LED_ESP32, DT-default-on) requires a pinctrl group routing
87
+ // each used channel (LEDC_CH<ch>_GPIO<pin> pinmux tokens, all 8×46 pairs in
88
+ // esp32s3-pinctrl.h) plus per-channel child nodes (reg + timer) — both
89
+ // emitted by the overlay generator (dt-config/overlay.ts emitPwmNodes).
90
+ pwm: {
91
+ specs: [],
92
+ matrix: {
93
+ controller: 'ledc0',
94
+ channelCount: 8,
95
+ pins: [
96
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
97
+ 21,
98
+ 38, 39, 40, 41, 42,
99
+ 45, 46, 47, 48,
100
+ ],
101
+ },
102
+ },
54
103
  };
@@ -1,15 +1,9 @@
1
1
  import type { ZephyrChipDescriptor } from './types.js';
2
- import { XIAO_BLE } from './xiao-ble.js';
3
- import { ESP32S3_DEVKITC } from './esp32s3.js';
4
- import { ESP32_DEVKITC } from './esp32.js';
5
- export { XIAO_BLE, ESP32S3_DEVKITC, ESP32_DEVKITC };
6
- export type { ZephyrChipDescriptor, ZephyrGpioDtSpec } from './types.js';
7
- export declare function setActiveChip(d: ZephyrChipDescriptor): void;
8
- export declare function getActiveChip(): ZephyrChipDescriptor;
9
2
  /**
10
- * Resolve a chip descriptor from the Zephyr board target string
11
- * (frameworkData.buildTarget / frameworkData.target). Accepts the bare board
12
- * id ('xiao_ble', 'esp32s3_devkitc', 'esp32_devkitc') or a board/qualifier
13
- * path ('esp32s3_devkitc/esp32s3/procpu', 'esp32_devkitc/esp32/procpu').
3
+ * The no-board fallback: no controllers, no buses, no silicon facts. The
4
+ * lowering treats it as "unsupported operation" per subsystem, which is the
5
+ * honest answer when no board resolved.
14
6
  */
15
- export declare function chipForTarget(target?: string): ZephyrChipDescriptor;
7
+ export declare const NO_BOARD_CHIP: ZephyrChipDescriptor;
8
+ export declare function setActiveChip(d: ZephyrChipDescriptor): void;
9
+ export declare function getActiveChip(): ZephyrChipDescriptor;
@@ -1,45 +1,29 @@
1
1
  // ---------------------------------------------------------------------------
2
- // Chip descriptor registrymaps Zephyr board target ZephyrChipDescriptor
2
+ // Chip descriptor cachethe mechanism the strategy's lowering reads.
3
3
  //
4
- // Mirrors framework-esp32/src/chips/index.ts: a module-level activeChip,
5
- // setActiveChip/getActiveChip, and chipForTarget resolving from
6
- // frameworkData.buildTarget. The strategy calls setActiveChip during profile
7
- // resolution so the lowering reads a single cached descriptor.
4
+ // There is no curated descriptor registry: every board's chip view is
5
+ // reconstructed from its generated board manifest via resolveChipFromBoard
6
+ // (src/chips/resolve.ts). This module only caches that resolved descriptor
7
+ // for the process lifetime (setActiveChip during profile resolution, the
8
+ // lowering reads getActiveChip) and provides the empty fallback for
9
+ // contexts with no board at all.
8
10
  // ---------------------------------------------------------------------------
9
- import { XIAO_BLE } from './xiao-ble.js';
10
- import { ESP32S3_DEVKITC } from './esp32s3.js';
11
- import { ESP32_DEVKITC } from './esp32.js';
12
- export { XIAO_BLE, ESP32S3_DEVKITC, ESP32_DEVKITC };
13
11
  /**
14
- * Default chip used when no buildTarget is supplied. The XIAO nRF52840 is the
15
- * canonical MVP target; subsequent board additions extend the switch below.
12
+ * The no-board fallback: no controllers, no buses, no silicon facts. The
13
+ * lowering treats it as "unsupported operation" per subsystem, which is the
14
+ * honest answer when no board resolved.
16
15
  */
17
- const DEFAULT_CHIP = XIAO_BLE;
18
- let activeChip = DEFAULT_CHIP;
16
+ export const NO_BOARD_CHIP = {
17
+ id: 'no-board',
18
+ soc: '',
19
+ gpioController: 'gpio0',
20
+ gpio: { dtSpecs: [] },
21
+ probeMethods: [],
22
+ };
23
+ let activeChip = NO_BOARD_CHIP;
19
24
  export function setActiveChip(d) {
20
25
  activeChip = d;
21
26
  }
22
27
  export function getActiveChip() {
23
28
  return activeChip;
24
29
  }
25
- /**
26
- * Resolve a chip descriptor from the Zephyr board target string
27
- * (frameworkData.buildTarget / frameworkData.target). Accepts the bare board
28
- * id ('xiao_ble', 'esp32s3_devkitc', 'esp32_devkitc') or a board/qualifier
29
- * path ('esp32s3_devkitc/esp32s3/procpu', 'esp32_devkitc/esp32/procpu').
30
- */
31
- export function chipForTarget(target) {
32
- const t = (target ?? '').trim().toLowerCase();
33
- const boardId = t.split('/')[0];
34
- switch (boardId) {
35
- case 'xiao_ble':
36
- return XIAO_BLE;
37
- case 'esp32s3_devkitc':
38
- return ESP32S3_DEVKITC;
39
- case 'esp32_devkitc':
40
- return ESP32_DEVKITC;
41
- case '':
42
- default:
43
- return DEFAULT_CHIP;
44
- }
45
- }
@@ -1,9 +1,23 @@
1
1
  import type { BoardConstants } from '@typecad/cuttlefish/api/shared';
2
- import type { ZephyrChipDescriptor } from './types.js';
2
+ import type { ZephyrChipDescriptor, ZephyrPwmSpec } from './types.js';
3
3
  /**
4
4
  * Try to derive a ZephyrChipDescriptor from board/MCU package constants.
5
5
  *
6
6
  * Returns null when no zephyr info is available in the board constants
7
- * (the caller should fall back to the hardcoded chipForTarget registry).
7
+ * (the caller should treat the board as unresolved NO_BOARD_CHIP).
8
8
  */
9
9
  export declare function resolveChipFromBoard(bc: BoardConstants | undefined): ZephyrChipDescriptor | null;
10
+ /**
11
+ * Board-level PWM specs from the generated board manifest (zephyr.pwm.specs.*
12
+ * — the pwm-leds facts boardgen emits on virtual pins 8192+). The active
13
+ * chip's spec table is silicon-curated; these JOIN it (see mergeBoardPwmSpecs)
14
+ * so a board's own devicetree-aliased PWM LED is addressable without curation.
15
+ */
16
+ export declare function boardPwmSpecsFromConstants(bc: BoardConstants | undefined): ZephyrPwmSpec[];
17
+ /**
18
+ * Merge board-level PWM specs into a chip descriptor. Collision rule: a
19
+ * board spec whose pin OR dtSpec already exists in the chip's table is
20
+ * dropped (the curated entry wins — e.g. the XIAO's hand-tuned pwm-led0
21
+ * spec). Returns the chip unchanged when nothing joins.
22
+ */
23
+ export declare function mergeBoardPwmSpecs(chip: ZephyrChipDescriptor, boardSpecs: readonly ZephyrPwmSpec[]): ZephyrChipDescriptor;
@@ -7,9 +7,9 @@
7
7
  // (from the MCU package's zephyr field) with board-level overrides (from the
8
8
  // board package's zephyr field).
9
9
  //
10
- // When a board package carries Zephyr config, this path replaces the
11
- // hardcoded chip descriptor registry. When it doesn't (legacy), chipForTarget
12
- // still works as the fallback.
10
+ // This is the ONE resolution path: every board's chip view reconstructs
11
+ // from its generated manifest there is no curated registry to fall back
12
+ // to.
13
13
  // ---------------------------------------------------------------------------
14
14
  /** Collect an indexed array of objects reconstructed from flat dot-path keys. */
15
15
  function collectIndexed(bc, prefix, build) {
@@ -34,23 +34,64 @@ function collectIndexed(bc, prefix, build) {
34
34
  function collectBusControllers(bc, prefix) {
35
35
  return collectIndexed(bc, prefix, (m, i) => {
36
36
  const nodeLabel = m.get(`${prefix}.${i}.nodeLabel`);
37
- return nodeLabel ? { nodeLabel } : null;
37
+ if (!nodeLabel)
38
+ return null;
39
+ // Optional pinctrl synthesis data (nested object; string arrays arrive
40
+ // comma-joined from the board-constants flattener).
41
+ const splitCsv = (v) => typeof v === 'string' && v.length > 0
42
+ ? v.split(',').map((s) => s.trim()).filter(Boolean)
43
+ : undefined;
44
+ const include = m.get(`${prefix}.${i}.pinctrl.include`);
45
+ const pinmux = splitCsv(m.get(`${prefix}.${i}.pinctrl.pinmux`));
46
+ const inputPinmux = splitCsv(m.get(`${prefix}.${i}.pinctrl.inputPinmux`));
47
+ const defines = splitCsv(m.get(`${prefix}.${i}.pinctrl.defines`));
48
+ const pinctrlRef = m.get(`${prefix}.${i}.pinctrlRef`);
49
+ const props = splitCsv(m.get(`${prefix}.${i}.props`));
50
+ return {
51
+ nodeLabel,
52
+ ...(include && pinmux
53
+ ? {
54
+ pinctrl: {
55
+ include,
56
+ pinmux,
57
+ ...(inputPinmux ? { inputPinmux } : {}),
58
+ ...(defines ? { defines } : {}),
59
+ },
60
+ }
61
+ : {}),
62
+ ...(pinctrlRef ? { pinctrlRef } : {}),
63
+ ...(props ? { props } : {}),
64
+ };
38
65
  });
39
66
  }
40
67
  /**
41
68
  * Try to derive a ZephyrChipDescriptor from board/MCU package constants.
42
69
  *
43
70
  * Returns null when no zephyr info is available in the board constants
44
- * (the caller should fall back to the hardcoded chipForTarget registry).
71
+ * (the caller should treat the board as unresolved NO_BOARD_CHIP).
45
72
  */
46
73
  export function resolveChipFromBoard(bc) {
47
74
  if (!bc)
48
75
  return null;
49
76
  const boardTarget = bc.get('build.frameworks.zephyr');
50
- if (!boardTarget)
77
+ // MCU-silicon entry: an MCU-only config (no board package) carries the
78
+ // chip facts under zephyr.* without any build.frameworks target (the
79
+ // build target comes from frameworkData instead — a custom board name).
80
+ const socsRaw = bc.get('zephyr.socs');
81
+ if (!boardTarget && !socsRaw)
51
82
  return null;
83
+ const socs = typeof socsRaw === 'string' && socsRaw.length > 0
84
+ ? socsRaw.split(',').map((s) => s.trim()).filter(Boolean)
85
+ : [];
86
+ // Chip identity: the board target when a board resolved it, else the SoC
87
+ // (an MCU-only chip — its "board" is the generated custom board).
88
+ const id = boardTarget ?? socs[0] ?? 'custom';
52
89
  const zGpioController = bc.get('zephyr.gpioController');
53
- const soc = bc.get('mcu.id') ?? '';
90
+ // The soc name: MCU packages key it under mcu.id; generated board
91
+ // manifests carry it as the second segment of the qualified target.
92
+ const soc = bc.get('mcu.id')
93
+ ?? (socs.length > 0 ? socs[0] : undefined)
94
+ ?? (boardTarget && boardTarget.includes('/') ? boardTarget.split('/')[1] : '');
54
95
  // ── Build mutable sub-objects, then construct the final descriptor ──────
55
96
  const gc = collectIndexed(bc, 'zephyr.gpioControllers', (m, i) => {
56
97
  const nodelabel = m.get(`zephyr.gpioControllers.${i}.nodelabel`);
@@ -85,6 +126,7 @@ export function resolveChipFromBoard(bc) {
85
126
  const channel = m.get(`zephyr.pwm.specs.${i}.channel`);
86
127
  const periodNs = m.get(`zephyr.pwm.specs.${i}.periodNs`);
87
128
  const polarity = m.get(`zephyr.pwm.specs.${i}.polarity`);
129
+ const pinctrl = m.get(`zephyr.pwm.specs.${i}.pinctrl`);
88
130
  // Board-shipped alias form (dtSpec) or synthesized form (controller +
89
131
  // channel → overlay-generated alias) — at least one, else drop the entry.
90
132
  if (pin == null || !(dtSpec || (controller && channel != null)))
@@ -96,9 +138,20 @@ export function resolveChipFromBoard(bc) {
96
138
  ...(channel != null ? { channel } : {}),
97
139
  ...(periodNs != null ? { periodNs } : {}),
98
140
  ...(polarity ? { polarity } : {}),
141
+ ...(pinctrl ? { pinctrl } : {}),
99
142
  };
100
143
  });
101
144
  const adcNodeLabel = bc.get('zephyr.adc.nodeLabel');
145
+ // PWM timer input clock — feeds the overlay's 16-bit prescaler derivation.
146
+ const pwmClockHz = bc.get('zephyr.pwm.clockHz');
147
+ // PWM matrix (ESP32 LEDC): any listed pin can carry any channel; channels
148
+ // are assigned to the driven pins at build time (dt-config/overlay.ts).
149
+ const matrixController = bc.get('zephyr.pwm.matrix.controller');
150
+ const matrixChannelCount = bc.get('zephyr.pwm.matrix.channelCount');
151
+ const matrixPins = collectIndexed(bc, 'zephyr.pwm.matrix.pins', (m, i) => m.get(`zephyr.pwm.matrix.pins.${i}`) ?? null);
152
+ const pwmMatrix = matrixController && matrixChannelCount
153
+ ? { controller: matrixController, channelCount: matrixChannelCount, pins: matrixPins }
154
+ : undefined;
102
155
  const adcResolution = bc.get('zephyr.adc.resolution');
103
156
  const adcVref = bc.get('zephyr.adc.vrefMv');
104
157
  const adcGain = bc.get('zephyr.adc.gain');
@@ -107,20 +160,139 @@ export function resolveChipFromBoard(bc) {
107
160
  const pin = m.get(`zephyr.adc.channels.${i}.pin`);
108
161
  const channel = m.get(`zephyr.adc.channels.${i}.channel`);
109
162
  const pinctrl = m.get(`zephyr.adc.channels.${i}.pinctrl`);
163
+ const controller = m.get(`zephyr.adc.channels.${i}.controller`);
164
+ if (pin != null && channel != null) {
165
+ return { pin, channel, ...(pinctrl ? { pinctrl } : {}), ...(controller ? { controller } : {}) };
166
+ }
167
+ return null;
168
+ });
169
+ const dacDevice = bc.get('zephyr.dac.device');
170
+ const dacChannels = collectIndexed(bc, 'zephyr.dac.channels', (m, i) => {
171
+ const pin = m.get(`zephyr.dac.channels.${i}.pin`);
172
+ const channel = m.get(`zephyr.dac.channels.${i}.channel`);
173
+ const resolution = m.get(`zephyr.dac.channels.${i}.resolution`);
110
174
  if (pin != null && channel != null) {
111
- return { pin, channel, ...(pinctrl ? { pinctrl } : {}) };
175
+ return { pin, channel, resolution: resolution ?? 12 };
112
176
  }
113
177
  return null;
114
178
  });
115
179
  const wdtNodeLabel = bc.get('zephyr.wdt.nodeLabel');
180
+ // Hardware counters (Zephyr counter devices): nodeLabel always; the child
181
+ // form carries the timer parent the overlay attaches the (label-less)
182
+ // counter child to.
183
+ const hwtimerControllers = collectIndexed(bc, 'zephyr.hwtimer.controllers', (m, i) => {
184
+ const nodeLabel = m.get(`zephyr.hwtimer.controllers.${i}.nodeLabel`);
185
+ const counterParent = m.get(`zephyr.hwtimer.controllers.${i}.counterParent`);
186
+ if (!nodeLabel)
187
+ return null;
188
+ return { nodeLabel, ...(counterParent ? { counterParent } : {}) };
189
+ });
190
+ // Storage partition synthesis (boards whose DTS ships no storage_partition
191
+ // — see ZephyrChipDescriptor.storage).
192
+ const storageOffset = bc.get('zephyr.storage.offset');
193
+ const storageSize = bc.get('zephyr.storage.size');
194
+ const storagePreexisting = bc.get('zephyr.storage.preexisting');
195
+ // USB device (CDC-ACM): zephyr.usb.controller + zephyr.usb.cdcInstances
196
+ // (+ optional vid/pid for the device descriptor).
197
+ const usbController = bc.get('zephyr.usb.controller');
198
+ const usbCdcInstances = bc.get('zephyr.usb.cdcInstances');
199
+ const usbVid = bc.get('zephyr.usb.vid');
200
+ const usbPid = bc.get('zephyr.usb.pid');
201
+ // Optional 1200-baud touch-to-reset data (BOSSA-bootloader boards).
202
+ const trFlag = bc.get('zephyr.usb.touchReset.flagAddress');
203
+ const trMagic = bc.get('zephyr.usb.touchReset.magic');
204
+ const trVid = bc.get('zephyr.usb.touchReset.bootloaderVid');
205
+ const trPid = bc.get('zephyr.usb.touchReset.bootloaderPid');
206
+ const probeMethods = collectIndexed(bc, 'zephyr.probeMethods', (m, i) => {
207
+ const id = m.get(`zephyr.probeMethods.${i}.id`);
208
+ const runner = m.get(`zephyr.probeMethods.${i}.runner`);
209
+ if (!id || !runner)
210
+ return null;
211
+ const argsRaw = m.get(`zephyr.probeMethods.${i}.args`);
212
+ const description = m.get(`zephyr.probeMethods.${i}.description`);
213
+ const debugRaw = m.get(`zephyr.probeMethods.${i}.debug`);
214
+ const debugInterface = m.get(`zephyr.probeMethods.${i}.debugInterface`);
215
+ const debugDevice = m.get(`zephyr.probeMethods.${i}.debugDevice`);
216
+ const debugCfgRaw = m.get(`zephyr.probeMethods.${i}.debugCfg`);
217
+ const debugCfgSourceRaw = m.get(`zephyr.probeMethods.${i}.debugCfgSource`);
218
+ // Args are comma-joined (west flags never carry commas); cfg lines are
219
+ // newline-joined (tcl legally contains commas).
220
+ const split = (v) => typeof v === 'string' && v.length > 0 ? v.split(',') : undefined;
221
+ const splitLines = (v) => typeof v === 'string' && v.length > 0 ? v.split('\n') : undefined;
222
+ const args = split(argsRaw);
223
+ return {
224
+ id,
225
+ runner,
226
+ ...(args ? { args } : {}),
227
+ ...(description ? { description } : {}),
228
+ ...(debugRaw !== undefined ? { debug: debugRaw } : {}),
229
+ ...(debugInterface ? { debugInterface: debugInterface } : {}),
230
+ ...(debugDevice ? { debugDevice } : {}),
231
+ ...(split(debugCfgRaw) ? { debugCfg: splitLines(debugCfgRaw) } : {}),
232
+ ...(split(debugCfgSourceRaw) ? { debugCfgSource: splitLines(debugCfgSourceRaw) } : {}),
233
+ };
234
+ });
116
235
  const wifiSupported = bc.get('zephyr.wifi.supported');
236
+ // ── Custom-board generator inputs (MCU silicon) ─────────────────────────
237
+ // Only complete when the MCU package carried a default console + clock
238
+ // plan — without those there is nothing to generate a board from.
239
+ const consoleNodeLabel = bc.get('zephyr.console.nodeLabel');
240
+ const consoleTx = bc.get('zephyr.console.tx');
241
+ const consoleRx = bc.get('zephyr.console.rx');
242
+ const consoleSpeed = bc.get('zephyr.console.speed');
243
+ const hseMHz = bc.get('zephyr.clocks.hseMHz');
244
+ const pllDivM = bc.get('zephyr.clocks.pll.divM');
245
+ const pllMulN = bc.get('zephyr.clocks.pll.mulN');
246
+ const pllDivP = bc.get('zephyr.clocks.pll.divP');
247
+ const pllDivQ = bc.get('zephyr.clocks.pll.divQ');
248
+ const sysMHz = bc.get('zephyr.clocks.sysMHz');
249
+ const dtsIncludesRaw = bc.get('zephyr.dtsIncludes');
250
+ const usbNode = bc.get('zephyr.usb.usbNode');
251
+ const usbPinctrlRaw = bc.get('zephyr.usb.pinctrl');
252
+ const usbPinctrl = typeof usbPinctrlRaw === 'string' && usbPinctrlRaw.length > 0
253
+ ? usbPinctrlRaw.split(',').map((s) => s.trim()).filter(Boolean)
254
+ : undefined;
255
+ const cbAdcNodeLabel = bc.get('zephyr.adcNode.nodeLabel');
256
+ const cbAdcClockSource = bc.get('zephyr.adcNode.clockSource');
257
+ const cbAdcPrescaler = bc.get('zephyr.adcNode.prescaler');
258
+ const cbAdcPinctrl = bc.get('zephyr.adcNode.pinctrl');
259
+ const customBoard = socs.length > 0 &&
260
+ consoleNodeLabel && consoleTx && consoleRx &&
261
+ hseMHz != null && pllDivM != null && pllMulN != null && pllDivP != null &&
262
+ pllDivQ != null && sysMHz != null
263
+ ? {
264
+ socs,
265
+ dtsIncludes: typeof dtsIncludesRaw === 'string' && dtsIncludesRaw.length > 0
266
+ ? dtsIncludesRaw.split(',').map((s) => s.trim()).filter(Boolean)
267
+ : [],
268
+ console: {
269
+ nodeLabel: consoleNodeLabel,
270
+ tx: consoleTx,
271
+ rx: consoleRx,
272
+ speed: consoleSpeed ?? 115200,
273
+ },
274
+ clocks: {
275
+ hseMHz,
276
+ pll: { divM: pllDivM, mulN: pllMulN, divP: pllDivP, divQ: pllDivQ },
277
+ sysMHz,
278
+ ahbPrescaler: bc.get('zephyr.clocks.ahbPrescaler') ?? 1,
279
+ apb1Prescaler: bc.get('zephyr.clocks.apb1Prescaler') ?? 1,
280
+ apb2Prescaler: bc.get('zephyr.clocks.apb2Prescaler') ?? 1,
281
+ },
282
+ ...(usbNode ? { usbNode } : {}),
283
+ ...(usbPinctrl ? { usbPinctrl } : {}),
284
+ ...(cbAdcNodeLabel && cbAdcClockSource && cbAdcPrescaler != null && cbAdcPinctrl
285
+ ? { adcNode: { nodeLabel: cbAdcNodeLabel, clockSource: cbAdcClockSource, prescaler: cbAdcPrescaler, pinctrl: cbAdcPinctrl } }
286
+ : {}),
287
+ }
288
+ : undefined;
117
289
  // ── Construct the final readonly descriptor ─────────────────────────────
118
290
  const gpio = {
119
291
  dtSpecs,
120
292
  ...(intPins.length > 0 ? { interruptPins: intPins } : {}),
121
293
  };
122
294
  return {
123
- id: boardTarget,
295
+ id,
124
296
  soc,
125
297
  gpioController: zGpioController ?? 'gpio0',
126
298
  ...(gc.length > 0 ? { gpioControllers: gc } : {}),
@@ -128,7 +300,15 @@ export function resolveChipFromBoard(bc) {
128
300
  ...(i2cControllers.length > 0 ? { i2c: { controllers: i2cControllers } } : {}),
129
301
  ...(spiControllers.length > 0 ? { spi: { controllers: spiControllers } } : {}),
130
302
  ...(uartControllers.length > 0 ? { uart: { controllers: uartControllers } } : {}),
131
- ...(pwmSpecs.length > 0 ? { pwm: { specs: pwmSpecs } } : {}),
303
+ ...(pwmSpecs.length > 0 || pwmMatrix
304
+ ? {
305
+ pwm: {
306
+ specs: pwmSpecs,
307
+ ...(pwmMatrix ? { matrix: pwmMatrix } : {}),
308
+ ...(pwmClockHz ? { clockHz: pwmClockHz } : {}),
309
+ },
310
+ }
311
+ : {}),
132
312
  ...(adcNodeLabel || adcResolution != null || adcVref != null || adcChannels.length > 0
133
313
  ? {
134
314
  adc: {
@@ -141,7 +321,84 @@ export function resolveChipFromBoard(bc) {
141
321
  },
142
322
  }
143
323
  : {}),
324
+ ...(dacDevice && dacChannels.length > 0 ? { dac: { device: dacDevice, channels: dacChannels } } : {}),
144
325
  ...(wdtNodeLabel ? { wdt: { nodeLabel: wdtNodeLabel } } : {}),
326
+ ...(hwtimerControllers.length > 0 ? { hwtimer: { controllers: hwtimerControllers } } : {}),
327
+ ...(storageOffset != null && storageSize != null
328
+ ? { storage: { offset: storageOffset, size: storageSize, ...(storagePreexisting ? { preexisting: true } : {}) } }
329
+ : {}),
330
+ ...(usbController && usbCdcInstances && usbCdcInstances > 0
331
+ ? {
332
+ usb: {
333
+ controller: usbController,
334
+ cdcInstances: usbCdcInstances,
335
+ ...(usbVid ? { vid: usbVid } : {}),
336
+ ...(usbPid ? { pid: usbPid } : {}),
337
+ ...(trFlag != null && trMagic != null
338
+ ? {
339
+ touchReset: {
340
+ flagAddress: trFlag,
341
+ magic: trMagic,
342
+ ...(trVid ? { bootloaderVid: trVid } : {}),
343
+ ...(trPid ? { bootloaderPid: trPid } : {}),
344
+ },
345
+ }
346
+ : {}),
347
+ },
348
+ }
349
+ : {}),
145
350
  ...(wifiSupported ? { wifi: { supported: true } } : {}),
351
+ ...(probeMethods.length > 0 ? { probeMethods } : {}),
352
+ ...(customBoard ? { customBoard } : {}),
353
+ };
354
+ }
355
+ /**
356
+ * Board-level PWM specs from the generated board manifest (zephyr.pwm.specs.*
357
+ * — the pwm-leds facts boardgen emits on virtual pins 8192+). The active
358
+ * chip's spec table is silicon-curated; these JOIN it (see mergeBoardPwmSpecs)
359
+ * so a board's own devicetree-aliased PWM LED is addressable without curation.
360
+ */
361
+ export function boardPwmSpecsFromConstants(bc) {
362
+ if (!bc)
363
+ return [];
364
+ const specs = [];
365
+ for (let i = 0; i < 256; i++) {
366
+ const pin = bc.get(`zephyr.pwm.specs.${i}.pin`);
367
+ if (pin == null)
368
+ break;
369
+ const dtSpec = bc.get(`zephyr.pwm.specs.${i}.dtSpec`);
370
+ const controller = bc.get(`zephyr.pwm.specs.${i}.controller`);
371
+ const channel = bc.get(`zephyr.pwm.specs.${i}.channel`);
372
+ if (!(dtSpec || (controller && channel != null)))
373
+ continue;
374
+ specs.push({
375
+ pin,
376
+ ...(dtSpec ? { dtSpec } : {}),
377
+ ...(controller ? { controller } : {}),
378
+ ...(channel != null ? { channel } : {}),
379
+ });
380
+ }
381
+ return specs;
382
+ }
383
+ /**
384
+ * Merge board-level PWM specs into a chip descriptor. Collision rule: a
385
+ * board spec whose pin OR dtSpec already exists in the chip's table is
386
+ * dropped (the curated entry wins — e.g. the XIAO's hand-tuned pwm-led0
387
+ * spec). Returns the chip unchanged when nothing joins.
388
+ */
389
+ export function mergeBoardPwmSpecs(chip, boardSpecs) {
390
+ if (boardSpecs.length === 0)
391
+ return chip;
392
+ const existing = chip.pwm?.specs ?? [];
393
+ const joining = boardSpecs.filter((s) => !existing.some((e) => e.pin === s.pin || (s.dtSpec != null && e.dtSpec === s.dtSpec)));
394
+ if (joining.length === 0)
395
+ return chip;
396
+ return {
397
+ ...chip,
398
+ pwm: {
399
+ specs: [...existing, ...joining],
400
+ ...(chip.pwm?.matrix ? { matrix: chip.pwm.matrix } : {}),
401
+ ...(chip.pwm?.clockHz ? { clockHz: chip.pwm.clockHz } : {}),
402
+ },
146
403
  };
147
404
  }
@@ -0,0 +1,2 @@
1
+ import type { ZephyrChipDescriptor } from '../types.js';
2
+ export declare const _SOC: ZephyrChipDescriptor;