@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
package/dist/doctor.js CHANGED
@@ -4,13 +4,17 @@
4
4
  // `cuttlefish doctor` (Zephyr framework) — verify west (the Zephyr build tool)
5
5
  // is installed + responsive, the Zephyr RTOS is inside the framework's declared
6
6
  // compat range, and the configured board target exists in the checkout. Exits 0
7
- // if the environment is OK, non-zero otherwise. Mirrors framework-arduino's
7
+ // if the environment is OK, non-zero otherwise. Follows the same contract
8
8
  // doctor shape (dispatched via the framework's `doctor` export) and reuses
9
9
  // checkZephyrEnv so the detection logic can be shared with the build/test gates.
10
10
  // ---------------------------------------------------------------------------
11
+ import { existsSync, readFileSync } from 'node:fs';
12
+ import { join } from 'node:path';
11
13
  import * as ui from '@typecad/cuttlefish/utils/ui';
12
14
  import { loadCuttlefishConfig } from '@typecad/cuttlefish/config-loader';
13
15
  import { checkZephyrEnv } from './toolchain/env-check.js';
16
+ import { PINNED_ZEPHYR_MANIFEST_REV, PINNED_ZEPHYR_SDK_VERSION, sdkFingerprint } from '@typecad/cuttlefish/board-catalog';
17
+ import { resolveChipFromBoard } from './chips/resolve.js';
14
18
  /**
15
19
  * Verify west is installed + responsive, the Zephyr RTOS is inside the supported
16
20
  * range, and the configured board target exists in the checkout. Sets
@@ -23,7 +27,7 @@ export function runDoctor() {
23
27
  const buildTarget = config?.buildTarget;
24
28
  const result = checkZephyrEnv(buildTarget);
25
29
  const c = result.check;
26
- // west (the Zephyr build tool) — the analog of arduino-cli presence.
30
+ // west (the Zephyr build tool) presence.
27
31
  if (c.westFound) {
28
32
  const ver = c.westVersion ?? 'found';
29
33
  const src = c.westSource ? ` (${c.westSource})` : '';
@@ -35,6 +39,9 @@ export function runDoctor() {
35
39
  // Zephyr RTOS version + declared compat range.
36
40
  ui.printInfo(`ZEPHYR_BASE ...... ${c.zephyrBase ?? '(not set)'}`);
37
41
  ui.printInfo(`Zephyr version .. ${c.zephyrVersion ?? 'unknown (could not read ZEPHYR_BASE/VERSION)'}`);
42
+ ui.printInfo(`Workspace pin ... ${PINNED_ZEPHYR_MANIFEST_REV} (SDK ${PINNED_ZEPHYR_SDK_VERSION})`);
43
+ const fp = c.zephyrBase ? sdkFingerprint(c.zephyrBase) : undefined;
44
+ ui.printInfo(`SDK fingerprint . ${fp ?? '(no tree — run ' + 'npx --package @typecad/framework-zephyr zephyr-installer)'}`);
38
45
  ui.printInfo(`Supported range . ${c.compatRange ?? '(none declared)'}`);
39
46
  if (c.compatStatus === 'out-of-range') {
40
47
  ui.printError(`Zephyr ${c.zephyrVersion} is OUTSIDE the supported range (${c.compatRange}).`);
@@ -45,7 +52,7 @@ export function runDoctor() {
45
52
  else {
46
53
  ui.printInfo('Zephyr compat ... OK');
47
54
  }
48
- // Board target — the analog of the Arduino core presence check.
55
+ // Board target availability.
49
56
  if (buildTarget) {
50
57
  const resolved = c.resolvedBoardTarget ?? buildTarget;
51
58
  const arrow = resolved === buildTarget ? '' : ` → ${resolved}`;
@@ -64,7 +71,34 @@ export function runDoctor() {
64
71
  else {
65
72
  ui.printInfo('(no buildTarget in cuttlefish.config.ts — skipping board check)');
66
73
  }
67
- // Exit codemirrors framework-arduino's doctor.
74
+ // Probe methodsfrom the board package's table, via the board constants
75
+ // the transpile persists (candidates cover the standard out-dir layouts).
76
+ // The debug line lists the debug-capable subset (bootloaders can't debug).
77
+ if (buildTarget) {
78
+ const bcPath = [
79
+ join(process.cwd(), 'out', 'src', 'board-constants.json'),
80
+ join(process.cwd(), 'out', 'board-constants.json'),
81
+ join(process.cwd(), 'src', 'out', 'src', 'board-constants.json'),
82
+ join(process.cwd(), 'src', 'out', 'board-constants.json'),
83
+ ].find((p) => existsSync(p));
84
+ if (bcPath) {
85
+ try {
86
+ const raw = JSON.parse(readFileSync(bcPath, 'utf8'));
87
+ const chip = resolveChipFromBoard(new Map(Object.entries(raw)));
88
+ const methods = chip?.probeMethods ?? [];
89
+ if (methods.length > 0) {
90
+ ui.printInfo(`Probe methods .... ${methods.map((m) => m.id).join(', ')} (zephyr.probe / --probe)`);
91
+ const debuggable = methods.filter((m) => m.debug !== false).map((m) => m.id);
92
+ ui.printInfo(`Debug methods .... ${debuggable.join(', ') || '(none — an external probe is required)'}`);
93
+ }
94
+ }
95
+ catch { /* best-effort listing */ }
96
+ }
97
+ else {
98
+ ui.printInfo('Probe methods .... (build once to list them)');
99
+ }
100
+ }
101
+ // Exit code — 0 ok, non-zero otherwise.
68
102
  if (result.ok) {
69
103
  ui.printSuccess('Environment OK');
70
104
  return; // exitCode stays unset => 0
@@ -0,0 +1,23 @@
1
+ import type { ZephyrChipDescriptor } from '../chips/types.js';
2
+ /**
3
+ * Sanitize a raw name into a legal Zephyr board name: lowercase, `[a-z0-9_]`
4
+ * (dashes/dots/spaces collapse to underscores, leading/trailing underscores
5
+ * trimmed). Board names become C identifiers in Kconfig symbols, so anything
6
+ * stricter would break `config BOARD_<NAME>`.
7
+ */
8
+ export declare function sanitizeBoardName(raw: string): string;
9
+ /** Generated-board file set, all under `<projectRoot>/boards/typecad/<name>/`. */
10
+ export interface GeneratedCustomBoard {
11
+ /** Absolute directory of the generated board. */
12
+ dir: string;
13
+ /** The sanitized board name (the `west build -b` argument). */
14
+ name: string;
15
+ /** Whether any file changed (idempotent regeneration signal). */
16
+ changed: boolean;
17
+ }
18
+ /**
19
+ * Emit the custom board for a chip whose descriptor carries `customBoard`
20
+ * silicon data. No-op (returns null) when the chip has no generator inputs —
21
+ * board-resolved chips don't need one.
22
+ */
23
+ export declare function generateCustomBoard(projectRoot: string, chip: ZephyrChipDescriptor, rawName: string): GeneratedCustomBoard | null;
@@ -0,0 +1,227 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Custom board generation — an out-of-tree Zephyr board for an MCU-only target
3
+ //
4
+ // When a project targets an MCU package with no board package (`mcu:` set,
5
+ // `board:` absent) there may be no Zephyr board for the user's hardware. The
6
+ // chip descriptor's `customBoard` block (silicon facts from the MCU package:
7
+ // SoC name, devicetree includes, default console mux, default clock plan) is
8
+ // all a minimal HWMv2 board needs, so we generate one into the project:
9
+ //
10
+ // boards/typecad/<name>/board.yml — board + SoC declaration
11
+ // boards/typecad/<name>/<name>.dts — includes, chosen, console, clocks
12
+ // boards/typecad/<name>/Kconfig.<name> — BOARD_<NAME> symbol selecting the SoC
13
+ // boards/typecad/<name>/<name>_defconfig— baseline (GPIO + console) every
14
+ // in-tree board carries
15
+ //
16
+ // The app's CMakeLists.txt adds the project root to BOARD_ROOT (see
17
+ // toolchain/scaffold.ts), so Zephyr discovers the board without touching the
18
+ // checkout. Everything else (buses, ADC, PWM, USB) stays usage-driven: the
19
+ // overlay generator keeps layering `boards/<name>.overlay` on top exactly as
20
+ // it does for upstream boards.
21
+ //
22
+ // Verified against Zephyr 4.3/4.4 (HWMv2): a minimal board is exactly these
23
+ // four files — Kconfig.<name> + CONFIG_GPIO=y are load-bearing (a board
24
+ // without them fails with "ARCH not defined" / gpio link errors).
25
+ // ---------------------------------------------------------------------------
26
+ import { mkdirSync } from 'node:fs';
27
+ import { join } from 'node:path';
28
+ import { writeIfChanged } from '../toolchain/scaffold.js';
29
+ /** The vendor segment of the generated board path + board.yml. */
30
+ const CUSTOM_BOARD_VENDOR = 'typecad';
31
+ /**
32
+ * Sanitize a raw name into a legal Zephyr board name: lowercase, `[a-z0-9_]`
33
+ * (dashes/dots/spaces collapse to underscores, leading/trailing underscores
34
+ * trimmed). Board names become C identifiers in Kconfig symbols, so anything
35
+ * stricter would break `config BOARD_<NAME>`.
36
+ */
37
+ export function sanitizeBoardName(raw) {
38
+ const cleaned = raw
39
+ .toLowerCase()
40
+ .replace(/[^a-z0-9_]+/g, '_')
41
+ .replace(/^_+|_+$/g, '');
42
+ return cleaned.length > 0 ? cleaned : 'custom_board';
43
+ }
44
+ /** Kconfig symbol fragment: `cb_f411` → `CB_F411`. */
45
+ function kconfigFragment(name) {
46
+ return name.toUpperCase();
47
+ }
48
+ /**
49
+ * Emit the custom board for a chip whose descriptor carries `customBoard`
50
+ * silicon data. No-op (returns null) when the chip has no generator inputs —
51
+ * board-resolved chips don't need one.
52
+ */
53
+ export function generateCustomBoard(projectRoot, chip, rawName) {
54
+ const cb = chip.customBoard;
55
+ if (!cb)
56
+ return null;
57
+ const name = sanitizeBoardName(rawName);
58
+ const soc = cb.socs[0];
59
+ if (!soc)
60
+ return null;
61
+ const dir = join(projectRoot, 'boards', CUSTOM_BOARD_VENDOR, name);
62
+ mkdirSync(dir, { recursive: true });
63
+ let changed = false;
64
+ // ── board.yml ────────────────────────────────────────────────────────────
65
+ changed =
66
+ writeIfChanged(join(dir, 'board.yml'), [
67
+ '# Auto-generated by @typecad/framework-zephyr from the MCU package',
68
+ '# silicon definition. Regenerated on every compile — edit the MCU',
69
+ '# package (or add a board package) instead of this file.',
70
+ 'board:',
71
+ ` name: ${name}`,
72
+ ` full_name: Cuttlefish custom board (${chip.soc})`,
73
+ ` vendor: ${CUSTOM_BOARD_VENDOR}`,
74
+ ' socs:',
75
+ ` - name: ${soc}`,
76
+ '',
77
+ ].join('\n')) || changed;
78
+ // ── Kconfig.<name> — the board symbol selecting the SoC ─────────────────
79
+ // Load-bearing: without the BOARD_<NAME> symbol selecting SOC_<SOC>, Kconfig
80
+ // completes with no SoC and the build dies with "ARCH not defined".
81
+ changed =
82
+ writeIfChanged(join(dir, `Kconfig.${name}`), [
83
+ '# Auto-generated by @typecad/framework-zephyr. Do not edit.',
84
+ '',
85
+ `config BOARD_${kconfigFragment(name)}`,
86
+ `\tselect SOC_${kconfigFragment(soc)}`,
87
+ '',
88
+ ].join('\n')) || changed;
89
+ // ── <name>.dts — includes, chosen, console, clock plan ───────────────────
90
+ const lines = [
91
+ '// Auto-generated by @typecad/framework-zephyr from the MCU package',
92
+ '// silicon definition. Regenerated on every compile — edit the MCU',
93
+ '// package (or add a board package) instead of this file.',
94
+ '/dts-v1/;',
95
+ ...cb.dtsIncludes.map((inc) => `#include <${inc}>`),
96
+ '',
97
+ '/ {',
98
+ ` model = "Cuttlefish custom board (${chip.soc})";`,
99
+ ` compatible = "${CUSTOM_BOARD_VENDOR},${name.replace(/_/g, '-')}";`,
100
+ '',
101
+ ' chosen {',
102
+ ` zephyr,console = &${cb.console.nodeLabel};`,
103
+ ' zephyr,sram = &sram0;',
104
+ ' zephyr,flash = &flash0;',
105
+ ' };',
106
+ '};',
107
+ '',
108
+ ];
109
+ // The chip descriptor's DT-aliased GPIOs (sw0/led0 from the reference
110
+ // board data): the generated firmware addresses them via DT_ALIAS, so the
111
+ // custom board must define the nodes. Pins map through gpioControllers
112
+ // (port-block numbering: pin = minPin + port bit).
113
+ const controllerFor = (pin) => {
114
+ const c = chip.gpioControllers?.find((c) => pin >= c.minPin && pin <= c.maxPin);
115
+ if (c)
116
+ return { label: c.nodelabel, bit: pin - c.minPin };
117
+ return undefined;
118
+ };
119
+ const keys = chip.gpio.dtSpecs.filter((s) => s.dtSpec.startsWith('sw') || s.dtSpec.startsWith('button'));
120
+ const leds = chip.gpio.dtSpecs.filter((s) => s.dtSpec.startsWith('led'));
121
+ if (keys.length > 0 || leds.length > 0) {
122
+ lines.push('/ {');
123
+ if (leds.length > 0) {
124
+ lines.push(' leds {', ' compatible = "gpio-leds";');
125
+ for (const l of leds) {
126
+ const c = controllerFor(l.pin);
127
+ if (!c)
128
+ continue;
129
+ lines.push(` ${l.dtSpec}_node: ${l.dtSpec.replace(/_/g, '-')} {`, ` gpios = <&${c.label} ${c.bit} GPIO_ACTIVE_LOW>;`, ' };');
130
+ }
131
+ lines.push(' };');
132
+ }
133
+ if (keys.length > 0) {
134
+ lines.push(' gpio_keys {', ' compatible = "gpio-keys";');
135
+ for (const k of keys) {
136
+ const c = controllerFor(k.pin);
137
+ if (!c)
138
+ continue;
139
+ lines.push(` ${k.dtSpec}_node: ${k.dtSpec.replace(/_/g, '-')} {`, ` gpios = <&${c.label} ${c.bit} (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;`, ' };');
140
+ }
141
+ lines.push(' };');
142
+ lines.push(' aliases {');
143
+ for (const k of keys)
144
+ lines.push(` ${k.dtSpec} = &${k.dtSpec}_node;`);
145
+ for (const l of leds)
146
+ lines.push(` ${l.dtSpec} = &${l.dtSpec}_node;`);
147
+ lines.push(' };');
148
+ }
149
+ lines.push('};', '');
150
+ }
151
+ lines.push(
152
+ // Console UART — the one controller the board enables unconditionally;
153
+ // every other bus is enabled by the usage-driven overlay.
154
+ `&${cb.console.nodeLabel} {`, ` pinctrl-0 = <&${cb.console.tx} &${cb.console.rx}>;`, ' pinctrl-names = "default";', ' status = "okay";', ` current-speed = <${cb.console.speed}>;`, '};', '');
155
+ // USB: alias the silicon node under the Zephyr convention the descriptor's
156
+ // usb.controller names (e.g. zephyr_udc0: &usbotg_fs), pre-enabled with its
157
+ // pinctrl — the st,stm32-otgfs binding requires pinctrl-0 on an enabled
158
+ // node, and the overlay's CDC composition only flips status/enables the
159
+ // stack (Kconfig-gated), never pins. Mirrors the blackpill board shape.
160
+ if (cb.usbNode && chip.usb) {
161
+ lines.push(`${chip.usb.controller}: &${cb.usbNode} {`, ...(cb.usbPinctrl && cb.usbPinctrl.length > 0
162
+ ? [
163
+ ` pinctrl-0 = <${cb.usbPinctrl.map((p) => `&${p}`).join(' ')}>;`,
164
+ ' pinctrl-names = "default";',
165
+ ]
166
+ : []), ' status = "okay";', '};', '');
167
+ }
168
+ // PWM: the overlay enables `&pwm<N>` and synthesizes pwm-leds consumers
169
+ // against it — but the pwm<N> LABEL only exists when a board DTS declares
170
+ // it (the SoC dtsi ships the timers' pwm child unlabeled), and the
171
+ // st,stm32-pwm binding requires pinctrl-0 on an enabled node. Emit the
172
+ // blackpill-shape block per controller whose specs all carry pinctrl
173
+ // tokens; controllers without full token coverage stay to board packages.
174
+ if (chip.pwm) {
175
+ const byController = new Map();
176
+ for (const s of chip.pwm.specs) {
177
+ if (!s.controller)
178
+ continue;
179
+ const timersMatch = s.controller.match(/^pwm(\d+)$/);
180
+ if (!timersMatch)
181
+ continue;
182
+ const entry = byController.get(s.controller) ?? { n: timersMatch[1], tokens: [], complete: true };
183
+ if (s.pinctrl) {
184
+ entry.tokens.push(s.pinctrl);
185
+ }
186
+ else {
187
+ entry.complete = false;
188
+ }
189
+ byController.set(s.controller, entry);
190
+ }
191
+ for (const [controller, entry] of byController) {
192
+ if (!entry.complete || entry.tokens.length === 0)
193
+ continue;
194
+ lines.push(`&timers${entry.n} {`, ' status = "okay";', '', ` ${controller}: pwm {`, ` pinctrl-0 = <${entry.tokens.map((t) => `&${t}`).join(' ')}>;`, ' pinctrl-names = "default";', ' status = "okay";', ' };', '};', '');
195
+ }
196
+ }
197
+ // ADC: some SoC drivers require board-DTS properties on an enabled adc node
198
+ // (STM32 F4: st,adc-clock-source + st,adc-prescaler, or the binding rejects
199
+ // the node when the usage-driven overlay enables it). Pre-enable with the
200
+ // silicon-required props + a baseline pinctrl; the overlay rewrites
201
+ // pinctrl-0 to the channels the program actually reads.
202
+ if (cb.adcNode) {
203
+ const a = cb.adcNode;
204
+ lines.push(`&${a.nodeLabel} {`, ` pinctrl-0 = <&${a.pinctrl}>;`, ' pinctrl-names = "default";', ` st,adc-clock-source = "${a.clockSource}";`, ` st,adc-prescaler = <${a.prescaler}>;`, ' status = "okay";', '};', '');
205
+ }
206
+ // Clock plan — STM32 F4 shape today (the only family carrying a clocks
207
+ // block so far): HSE crystal, PLL, RCC prescalers. SoCs whose dtsi clocks
208
+ // work out of the box ship no clocks block and emit nothing here.
209
+ const clk = cb.clocks;
210
+ lines.push(`&clk_hse {`, ` clock-frequency = <DT_FREQ_M(${clk.hseMHz})>;`, ' status = "okay";', '};', '', '&pll {', ` div-m = <${clk.pll.divM}>;`, ` mul-n = <${clk.pll.mulN}>;`, ` div-p = <${clk.pll.divP}>;`, ` div-q = <${clk.pll.divQ}>;`, ' clocks = <&clk_hse>;', ' status = "okay";', '};', '', '&rcc {', ' clocks = <&pll>;', ` clock-frequency = <DT_FREQ_M(${clk.sysMHz})>;`, ` ahb-prescaler = <${clk.ahbPrescaler}>;`, ` apb1-prescaler = <${clk.apb1Prescaler}>;`, ` apb2-prescaler = <${clk.apb2Prescaler}>;`, '};', '');
211
+ changed = writeIfChanged(join(dir, `${name}.dts`), lines.join('\n')) || changed;
212
+ // ── <name>_defconfig — the baseline every in-tree board carries ──────────
213
+ // Load-bearing: without CONFIG_GPIO the STM32 pinctrl driver's gpio_ports
214
+ // table references devices that are never built (undefined __device_dts_ord
215
+ // at link). Console/serial match what the DTS enables.
216
+ changed =
217
+ writeIfChanged(join(dir, `${name}_defconfig`), [
218
+ '# Auto-generated by @typecad/framework-zephyr. Do not edit.',
219
+ '',
220
+ 'CONFIG_GPIO=y',
221
+ 'CONFIG_SERIAL=y',
222
+ 'CONFIG_CONSOLE=y',
223
+ 'CONFIG_UART_CONSOLE=y',
224
+ '',
225
+ ].join('\n')) || changed;
226
+ return { dir, name, changed };
227
+ }
@@ -1,3 +1,18 @@
1
+ /** A sensor part's catalog facts, as the exceptions helper consumes them. */
2
+ interface SensorKconfigSource {
3
+ kconfig: readonly string[];
4
+ }
5
+ /**
6
+ * Apply the per-part Kconfig exceptions the generated catalog records, for
7
+ * exactly the parts the program constructs. Lines are CONFIG_<SYM>=<value>
8
+ * strings parsed into the map; a symbol the user set explicitly in
9
+ * zephyr.kconfig is left alone (scaffold.ts already skips user overrides for
10
+ * auto symbols, and Map.set idempotence covers the rest). The lookup is a
11
+ * parameter so tests can drive it with a synthetic catalog.
12
+ */
13
+ export declare function applySensorKconfigExceptions(m: Map<string, string>, parts: readonly {
14
+ part: string;
15
+ }[] | undefined, lookup?: Readonly<Record<string, SensorKconfigSource>>): void;
1
16
  export interface KconfigUsage {
2
17
  usesAdc?: boolean;
3
18
  usesPwm?: boolean;
@@ -7,21 +22,60 @@ export interface KconfigUsage {
7
22
  usesI2c?: boolean;
8
23
  usesSpi?: boolean;
9
24
  usesUart?: boolean;
25
+ /** USB CDC-ACM serial used (usb.* ops). Selects the "next" USB device
26
+ * stack + CDC class; the class instances themselves are composed in the
27
+ * DT overlay and auto-default on once the node exists (assigning the
28
+ * class symbol keeps prj.conf explicit and survives DT-only probes). */
29
+ usesUsb?: boolean;
10
30
  usesWdt?: boolean;
11
31
  usesBle?: boolean;
12
32
  usesDisplay?: boolean;
13
- usesPower?: boolean;
14
33
  usesWifi?: boolean;
15
34
  usesHttp?: boolean;
16
35
  usesMqtt?: boolean;
17
36
  usesPreferences?: boolean;
18
37
  usesRandom?: boolean;
38
+ /** A printf-family format specifier with a float conversion (%f, %.2f, %e,
39
+ * …) appears in the emitted source — Zephyr's cbprintf only links float
40
+ * conversions with FP_SUPPORT (which itself needs the COMPLETE impl). */
41
+ usesFloatFormat?: boolean;
42
+ /** DT-bound sensor parts used (sensor.* ops — the generic catalog). The
43
+ * umbrella under which every driver sensor Kconfig lives (`if SENSOR`);
44
+ * the per-driver symbols default on from their DT node presence. */
45
+ usesSensor?: boolean;
46
+ /** Distinct constructed sensors — only the overlay generator consumes this
47
+ * (one DT child node per entry, on the given I2C controller index); prj.conf
48
+ * ignores it. Same adcReadPins/pwmUsedPins pattern. */
49
+ sensorParts?: readonly {
50
+ part: string;
51
+ busIndex: number;
52
+ port: number;
53
+ busKind: 'i2c' | 'spi';
54
+ spiHz?: number;
55
+ spiMode?: number;
56
+ alertPin?: number;
57
+ }[];
58
+ /** Distinct thin SPI targets (hal/spi-target.ts) — one DT child node per
59
+ * entry (no compatible — a raw spi_dt_spec peer), appended after sensor
60
+ * CS entries in the controller's merged cs-gpios. */
61
+ spiTargets?: readonly {
62
+ busIndex: number;
63
+ cs: number;
64
+ hz?: number;
65
+ mode?: number;
66
+ }[];
19
67
  /** Touch controller referenced (UI touch adapter emits DT_NODELABEL(ft6336u)
20
68
  * or DT_NODELABEL(xpt2046)). Selects the bus driver the node needs. */
21
69
  usesTouch?: boolean;
22
70
  /** Which touch controller the program uses — FT6336U rides I2C, XPT2046
23
71
  * rides the display's SPI bus. Only meaningful with usesTouch. */
24
72
  touchController?: 'ft6336u' | 'xpt2046';
73
+ /** The emitted shim carries the STM32F4 DBGMCU keep-SWD-alive init
74
+ * (__tc_stm32_dbgmcu token). Selects Zephyr's own "debugger attach in
75
+ * stop/sleep" init (sets DBG_STOP via the LL headers); the shim's raw
76
+ * register poke additionally covers DBG_SLEEP, which the Zephyr F4 path
77
+ * does not set. */
78
+ usesStm32DebugSleep?: boolean;
25
79
  /** PSRAM type ('opi' | 'quad') when the target board has PSRAM. Emits the
26
80
  * CONFIG_SPIRAM symbols so the ESP heap serves PSRAM for canvas allocations. */
27
81
  psram?: 'opi' | 'quad';
@@ -30,12 +84,28 @@ export interface KconfigUsage {
30
84
  * consumes this (to rewrite the ADC node's pinctrl-0 to the used channels
31
85
  * on SoCs that need pad muxing, e.g. STM32); prj.conf ignores it. */
32
86
  adcReadPins?: readonly number[];
87
+ /** HAL pin numbers the program drives with dac.* — scanned from the
88
+ * emitted lazy-setup guards (__tc_dact<pin>_done) at compile time. Only
89
+ * the overlay generator consumes this (the DAC node's pinctrl-0 lists
90
+ * the used channels); prj.conf ignores it. */
91
+ dacWritePins?: readonly number[];
33
92
  /** HAL pin numbers the program drives with pwm.* — scanned from the
34
93
  * emitted `__tc_pwm_*` spec references at compile time. Only the overlay
35
94
  * generator consumes this (synthesized pwm-leds consumers + aliases are
36
95
  * emitted per used pin, so the DT carries no dead channels); prj.conf
37
96
  * ignores it. */
38
97
  pwmUsedPins?: readonly number[];
98
+ /** Controller indexes per bus the program actually drives — scanned from
99
+ * the emitted `__tc_<bus><N>_dev` state blocks at compile time. The
100
+ * overlay enables only those controllers (an enabled-but-unused
101
+ * controller claims its default pins — e.g. i2c0's GP4/GP5 on the Pico —
102
+ * which a program using the OTHER controller may want as GPIO). Absent
103
+ * (prepare-time overlays, driver-API-only users like the display
104
+ * adapter) → every declared controller is enabled, preserving the old
105
+ * behavior. prj.conf ignores these. */
106
+ i2cUsedInstances?: readonly number[];
107
+ spiUsedInstances?: readonly number[];
108
+ uartUsedInstances?: readonly number[];
39
109
  }
40
110
  /**
41
111
  * Resolve the Kconfig symbol→value map for a prj.conf. Returns a Map preserving
@@ -43,3 +113,4 @@ export interface KconfigUsage {
43
113
  * are always present; driver symbols are usage-gated.
44
114
  */
45
115
  export declare function resolveKconfigFragments(usage: KconfigUsage, debug: boolean): Map<string, string>;
116
+ export {};