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

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 (210) hide show
  1. package/LICENSE +202 -21
  2. package/README.md +49 -87
  3. package/dist/as-built.d.ts +2 -2
  4. package/dist/as-built.js +2 -2
  5. package/dist/audit.d.ts +111 -0
  6. package/dist/audit.js +416 -0
  7. package/dist/boardgen.d.ts +1 -9
  8. package/dist/boardgen.js +288 -47
  9. package/dist/chips/resolve.js +20 -0
  10. package/dist/chips/types.d.ts +22 -1
  11. package/dist/debug-codegen.js +1 -1
  12. package/dist/display/bindings.d.ts +55 -0
  13. package/dist/display/bindings.js +316 -0
  14. package/dist/display/gfx.d.ts +2 -3
  15. package/dist/display/gfx.js +166 -154
  16. package/dist/display/index.js +20 -1
  17. package/dist/display/mipi-dbi-host.d.ts +9 -0
  18. package/dist/display/mipi-dbi-host.js +174 -0
  19. package/dist/display/profiles.d.ts +109 -4
  20. package/dist/display/profiles.js +270 -7
  21. package/dist/display/touch-adapter.js +119 -49
  22. package/dist/display/ui-adapter-eink.d.ts +2 -0
  23. package/dist/display/ui-adapter-eink.js +4 -0
  24. package/dist/display/ui-adapter-gray.d.ts +8 -0
  25. package/dist/display/ui-adapter-gray.js +170 -0
  26. package/dist/display/ui-adapter-mono.d.ts +13 -0
  27. package/dist/display/ui-adapter-mono.js +230 -0
  28. package/dist/display/ui-adapter-native.d.ts +10 -0
  29. package/dist/display/ui-adapter-native.js +295 -0
  30. package/dist/display/ui-adapter-shared.d.ts +11 -0
  31. package/dist/display/ui-adapter-shared.js +122 -0
  32. package/dist/display/ui-adapter.js +510 -558
  33. package/dist/doctor.js +4 -4
  34. package/dist/dt-config/custom-board.js +2 -2
  35. package/dist/dt-config/kconfig.d.ts +62 -1
  36. package/dist/dt-config/kconfig.js +141 -38
  37. package/dist/dt-config/overlay.d.ts +15 -2
  38. package/dist/dt-config/overlay.js +433 -18
  39. package/dist/framework.manifest.d.ts +10 -4
  40. package/dist/framework.manifest.js +133 -17
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.js +14 -6
  43. package/dist/licenses.d.ts +2 -2
  44. package/dist/licenses.js +13 -92
  45. package/dist/lowering/can.d.ts +25 -0
  46. package/dist/lowering/can.js +97 -0
  47. package/dist/lowering/clock.d.ts +17 -0
  48. package/dist/lowering/clock.js +58 -0
  49. package/dist/lowering/fs.js +1 -1
  50. package/dist/lowering/gpio.js +0 -32
  51. package/dist/lowering/hid.d.ts +27 -0
  52. package/dist/lowering/hid.js +244 -0
  53. package/dist/lowering/http.js +264 -32
  54. package/dist/lowering/i2c.d.ts +8 -0
  55. package/dist/lowering/i2c.js +137 -5
  56. package/dist/lowering/i2s.d.ts +27 -0
  57. package/dist/lowering/i2s.js +98 -0
  58. package/dist/lowering/index.d.ts +9 -1
  59. package/dist/lowering/index.js +25 -1
  60. package/dist/lowering/interrupts.js +6 -0
  61. package/dist/lowering/matrix.d.ts +15 -0
  62. package/dist/lowering/matrix.js +63 -0
  63. package/dist/lowering/mqtt.js +110 -8
  64. package/dist/lowering/power.d.ts +3 -2
  65. package/dist/lowering/power.js +20 -45
  66. package/dist/lowering/pwm.js +25 -0
  67. package/dist/lowering/sensor.d.ts +2 -2
  68. package/dist/lowering/sensor.js +8 -4
  69. package/dist/lowering/strip.d.ts +16 -0
  70. package/dist/lowering/strip.js +70 -0
  71. package/dist/lowering/thread.js +5 -1
  72. package/dist/lowering/trace.d.ts +44 -0
  73. package/dist/lowering/trace.js +239 -0
  74. package/dist/lowering/uart.js +6 -1
  75. package/dist/lowering/usb.d.ts +3 -1
  76. package/dist/lowering/usb.js +16 -13
  77. package/dist/lowering/wdt.js +2 -29
  78. package/dist/sbom.d.ts +181 -0
  79. package/dist/sbom.js +901 -0
  80. package/dist/sdk/board-catalog-sync.d.ts +1 -3
  81. package/dist/sdk/board-catalog-sync.js +4 -10
  82. package/dist/strategy.d.ts +82 -46
  83. package/dist/strategy.js +639 -182
  84. package/dist/tmp-probe.d.ts +2 -0
  85. package/dist/tmp-probe.js +9 -0
  86. package/dist/toolchain/debug-config.d.ts +50 -90
  87. package/dist/toolchain/debug-config.js +239 -510
  88. package/dist/toolchain/env-check.d.ts +1 -3
  89. package/dist/toolchain/env-check.js +2 -7
  90. package/dist/toolchain/index.d.ts +28 -3
  91. package/dist/toolchain/index.js +498 -62
  92. package/dist/toolchain/runners.d.ts +16 -0
  93. package/dist/toolchain/runners.js +75 -0
  94. package/dist/toolchain/scaffold.d.ts +5 -2
  95. package/dist/toolchain/scaffold.js +79 -15
  96. package/dist/toolchain/west-discover.d.ts +6 -0
  97. package/dist/toolchain/west-discover.js +36 -13
  98. package/dist/toolchain/west-spawn.js +8 -2
  99. package/dist/west-inventory.d.ts +25 -0
  100. package/dist/west-inventory.js +97 -0
  101. package/installer/README.md +328 -328
  102. package/installer/install.sh +2 -2
  103. package/installer/templates/project/.typecad/activate-zephyr.ps1 +1 -1
  104. package/installer/templates/project/.typecad/activate-zephyr.sh +1 -1
  105. package/installer/templates/project/.vscode/settings.json +1 -1
  106. package/installer/templates/project/README.md +2 -2
  107. package/package.json +5 -5
  108. package/src/as-built.ts +206 -206
  109. package/src/audit.ts +529 -0
  110. package/src/boardgen.ts +265 -50
  111. package/src/chips/resolve.ts +21 -0
  112. package/src/chips/types.ts +10 -1
  113. package/src/display/bindings.ts +347 -0
  114. package/src/display/gfx.ts +318 -306
  115. package/src/display/index.ts +87 -70
  116. package/src/display/mipi-dbi-host.ts +183 -0
  117. package/src/display/profiles.ts +458 -139
  118. package/src/display/touch-adapter.ts +119 -49
  119. package/src/display/ui-adapter-eink.ts +13 -0
  120. package/src/display/ui-adapter-gray.ts +178 -0
  121. package/src/display/ui-adapter-mono.ts +238 -0
  122. package/src/display/ui-adapter-native.ts +304 -0
  123. package/src/display/ui-adapter-shared.ts +125 -0
  124. package/src/display/ui-adapter.ts +732 -781
  125. package/src/doctor.ts +4 -4
  126. package/src/dt-config/custom-board.ts +2 -2
  127. package/src/dt-config/kconfig.ts +647 -505
  128. package/src/dt-config/overlay.ts +1475 -1058
  129. package/src/framework.manifest.ts +659 -535
  130. package/src/index.ts +16 -6
  131. package/src/licenses.ts +346 -425
  132. package/src/lowering/can.ts +140 -0
  133. package/src/lowering/clock.ts +91 -0
  134. package/src/lowering/fs.ts +135 -135
  135. package/src/lowering/gpio.ts +0 -33
  136. package/src/lowering/hid.ts +261 -0
  137. package/src/lowering/http.ts +264 -32
  138. package/src/lowering/i2c.ts +142 -5
  139. package/src/lowering/i2s.ts +143 -0
  140. package/src/lowering/index.ts +18 -1
  141. package/src/lowering/interrupts.ts +6 -0
  142. package/src/lowering/matrix.ts +70 -0
  143. package/src/lowering/mqtt.ts +109 -8
  144. package/src/lowering/power.ts +41 -0
  145. package/src/lowering/pwm.ts +192 -167
  146. package/src/lowering/sensor.ts +159 -155
  147. package/src/lowering/strip.ts +81 -0
  148. package/src/lowering/thread.ts +5 -1
  149. package/src/lowering/trace.ts +270 -0
  150. package/src/lowering/uart.ts +6 -1
  151. package/src/lowering/usb.ts +16 -13
  152. package/src/lowering/wdt.ts +2 -25
  153. package/src/sbom.ts +1117 -0
  154. package/src/sdk/board-catalog-sync.ts +4 -25
  155. package/src/strategy.ts +2680 -2309
  156. package/src/toolchain/debug-config.ts +262 -522
  157. package/src/toolchain/env-check.ts +279 -285
  158. package/src/toolchain/index.ts +1792 -1359
  159. package/src/toolchain/runners.ts +80 -0
  160. package/src/toolchain/scaffold.ts +355 -296
  161. package/src/toolchain/west-discover.ts +35 -13
  162. package/src/toolchain/west-spawn.ts +174 -168
  163. package/src/west-inventory.ts +102 -0
  164. package/dist/async/timer-polyfill.d.ts +0 -10
  165. package/dist/async/timer-polyfill.js +0 -95
  166. package/dist/chips/board-overrides.d.ts +0 -7
  167. package/dist/chips/board-overrides.js +0 -11
  168. package/dist/chips/esp32.d.ts +0 -2
  169. package/dist/chips/esp32.js +0 -71
  170. package/dist/chips/esp32s3.d.ts +0 -2
  171. package/dist/chips/esp32s3.js +0 -103
  172. package/dist/chips/soc/.d.ts +0 -2
  173. package/dist/chips/soc/.js +0 -129
  174. package/dist/chips/soc/esp32.d.ts +0 -2
  175. package/dist/chips/soc/esp32.js +0 -120
  176. package/dist/chips/soc/esp32c3.d.ts +0 -2
  177. package/dist/chips/soc/esp32c3.js +0 -90
  178. package/dist/chips/soc/esp32c6.d.ts +0 -2
  179. package/dist/chips/soc/esp32c6.js +0 -109
  180. package/dist/chips/soc/esp32s3.d.ts +0 -2
  181. package/dist/chips/soc/esp32s3.js +0 -189
  182. package/dist/chips/soc/index.d.ts +0 -2
  183. package/dist/chips/soc/index.js +0 -23
  184. package/dist/chips/soc/nrf52840.d.ts +0 -2
  185. package/dist/chips/soc/nrf52840.js +0 -130
  186. package/dist/chips/soc/rp2040.d.ts +0 -2
  187. package/dist/chips/soc/rp2040.js +0 -141
  188. package/dist/chips/soc/rp2350a.d.ts +0 -2
  189. package/dist/chips/soc/rp2350a.js +0 -145
  190. package/dist/chips/soc/samd21g18a.d.ts +0 -2
  191. package/dist/chips/soc/samd21g18a.js +0 -143
  192. package/dist/chips/soc/stm32f411xe.d.ts +0 -2
  193. package/dist/chips/soc/stm32f411xe.js +0 -251
  194. package/dist/chips/xiao-ble.d.ts +0 -2
  195. package/dist/chips/xiao-ble.js +0 -100
  196. package/dist/lowering/pulse.d.ts +0 -7
  197. package/dist/lowering/pulse.js +0 -51
  198. package/dist/lowering/tone.d.ts +0 -10
  199. package/dist/lowering/tone.js +0 -63
  200. package/dist/lowering/worker-backing.d.ts +0 -14
  201. package/dist/lowering/worker-backing.js +0 -79
  202. package/dist/lowering/worker.d.ts +0 -6
  203. package/dist/lowering/worker.js +0 -14
  204. package/dist/sdk/board-data.generated.d.ts +0 -2
  205. package/dist/sdk/board-data.generated.js +0 -4
  206. package/dist/sdk/catalog-walker.d.ts +0 -90
  207. package/dist/sdk/catalog-walker.js +0 -682
  208. package/dist/sdk/dts-reader.d.ts +0 -83
  209. package/dist/sdk/dts-reader.js +0 -596
  210. package/src/debug-codegen.ts +0 -207
@@ -0,0 +1,16 @@
1
+ export interface RunnersFacts {
2
+ /** The board's declared default debug runner (e.g. 'openocd', 'jlink'). */
3
+ readonly debugRunner?: string;
4
+ /** Absolute path to the arch gdb binary (config.gdb). */
5
+ readonly gdb?: string;
6
+ /** All runners the board configured. */
7
+ readonly runners: readonly string[];
8
+ }
9
+ /**
10
+ * Parse the subset of runners.yaml cuttlefish consumes. Exported for tests.
11
+ * Returns undefined on shapes it does not understand (callers treat as
12
+ * "no facts" — never crash a build over diagnostics data).
13
+ */
14
+ export declare function parseRunnersYaml(text: string): RunnersFacts | undefined;
15
+ /** Read the resolved runner facts for a build dir. undefined when absent/unparseable. */
16
+ export declare function readRunnersFacts(buildDir: string): RunnersFacts | undefined;
@@ -0,0 +1,75 @@
1
+ // ---------------------------------------------------------------------------
2
+ // runners.ts — read west's resolved runner facts from a built Zephyr tree.
3
+ //
4
+ // After any successful `west build`, Zephyr writes build/zephyr/runners.yaml —
5
+ // the FULLY RESOLVED debug/flash configuration for the board: the arch gdb
6
+ // binary, the openocd binary + its script search dirs, the jlink device name,
7
+ // the board's declared default debug runner. It is the single source of debug
8
+ // truth west itself uses (`west debug` / `west debugserver` read the same
9
+ // file), so cuttlefish reads it instead of reconstructing any of this.
10
+ //
11
+ // The parser below is deliberately minimal: it understands exactly the shape
12
+ // west's run_common.py emits — top-level scalars, a list section whose items
13
+ // sit at column 0, and a nested config map — and ignores everything else
14
+ // (e.g. the args section). A general YAML dependency is not worth it for one
15
+ // machine-generated file.
16
+ // ---------------------------------------------------------------------------
17
+ import fs from 'node:fs';
18
+ import { join } from 'node:path';
19
+ /**
20
+ * Parse the subset of runners.yaml cuttlefish consumes. Exported for tests.
21
+ * Returns undefined on shapes it does not understand (callers treat as
22
+ * "no facts" — never crash a build over diagnostics data).
23
+ */
24
+ export function parseRunnersYaml(text) {
25
+ const runners = [];
26
+ let debugRunner;
27
+ let gdb;
28
+ // Section state: '' = top level, 'runners' = the list, 'config' = the map.
29
+ let section = '';
30
+ for (const rawLine of text.split(/\r?\n/)) {
31
+ const trimmed = rawLine.trim();
32
+ if (trimmed.startsWith('#') || trimmed === '')
33
+ continue;
34
+ // List items — west writes the runners list at column 0 and
35
+ // openocd_search indented; both belong to the last-opened section.
36
+ if (trimmed.startsWith('- ')) {
37
+ const item = trimmed.slice(2).trim();
38
+ if (section === 'runners')
39
+ runners.push(item);
40
+ continue; // other sections' lists (openocd_search, args) are unused
41
+ }
42
+ const indent = rawLine.length - rawLine.trimStart().length;
43
+ const m = trimmed.match(/^([A-Za-z0-9_.-]+):\s*(.*)$/);
44
+ if (!m)
45
+ continue; // e.g. west's empty-list `[]` lines — skip, don't bail
46
+ const [, key, value] = m;
47
+ if (indent === 0) {
48
+ if (key === 'runners') {
49
+ section = 'runners';
50
+ continue;
51
+ }
52
+ if (key === 'config') {
53
+ section = 'config';
54
+ continue;
55
+ }
56
+ section = '';
57
+ if (key === 'debug-runner' && value)
58
+ debugRunner = value.trim();
59
+ continue;
60
+ }
61
+ if (section === 'config' && key === 'gdb' && value) {
62
+ gdb = value.trim().replace(/^['"]|['"]$/g, '');
63
+ }
64
+ }
65
+ return { runners, ...(debugRunner ? { debugRunner } : {}), ...(gdb ? { gdb } : {}) };
66
+ }
67
+ /** Read the resolved runner facts for a build dir. undefined when absent/unparseable. */
68
+ export function readRunnersFacts(buildDir) {
69
+ try {
70
+ return parseRunnersYaml(fs.readFileSync(join(buildDir, 'zephyr', 'runners.yaml'), 'utf-8'));
71
+ }
72
+ catch {
73
+ return undefined;
74
+ }
75
+ }
@@ -2,7 +2,7 @@
2
2
  * Returns true when the file was written (content changed or file was new). */
3
3
  export declare function writeIfChanged(filePath: string, content: string): boolean;
4
4
  /**
5
- * Append cuttlefish library packages' devicetree overlay fragments to the
5
+ * Append typecad-hal library packages' devicetree overlay fragments to the
6
6
  * generated overlay. Library entries come from the transpiler's libraries.json
7
7
  * sidecar (next to the emitted sources) and are already gated on the
8
8
  * library's include token appearing in the emitted sources — no re-detection.
@@ -21,4 +21,7 @@ export declare function appendLibraryOverlayFragments(overlay: string, projectRo
21
21
  *
22
22
  * Idempotent. Mirrors scaffoldEspIdfProject's writeIfChanged discipline.
23
23
  */
24
- export declare function scaffoldZephyrProject(projectRoot: string, debug?: boolean, userKconfig?: Record<string, string>, psram?: 'opi' | 'quad'): boolean;
24
+ export declare function scaffoldZephyrProject(projectRoot: string, debug?: boolean, userKconfig?: Record<string, string>, psram?: 'opi' | 'quad', trace?: {
25
+ enabled?: boolean;
26
+ intervalMs?: number;
27
+ }): boolean;
@@ -1,7 +1,7 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // Zephyr project scaffolding
3
3
  //
4
- // Writes the CMakeLists.txt + prj.conf around the cuttlefish-emitted src/main.cpp
4
+ // Writes the CMakeLists.txt + prj.conf around the typecad-hal-emitted src/main.cpp
5
5
  // so `west build` has a valid Zephyr application. Idempotent: overwrites the
6
6
  // generated files only when their content changes (avoids invalidating the
7
7
  // Ninja incremental build's mtime-based dependency tracking).
@@ -9,7 +9,9 @@
9
9
  import { writeFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
10
10
  import { join } from 'node:path';
11
11
  import { resolveKconfigFragments } from '../dt-config/kconfig.js';
12
- import { scanSensorParts } from './index.js';
12
+ import { scanSensorParts, scanStrips, scanHid, scanMatrix } from './index.js';
13
+ import { profileFromEmittedSource, transportFor, panelControllerFor } from '../display/profiles.js';
14
+ import { readDisplayBinding } from '../display/bindings.js';
13
15
  import { readCuttlefishLibrarySidecar } from '@typecad/cuttlefish/library-packages';
14
16
  import { SENSOR_PART_INFO } from '@typecad/hal';
15
17
  /** Write a file only if the content differs from the existing file.
@@ -28,7 +30,7 @@ export function writeIfChanged(filePath, content) {
28
30
  return true;
29
31
  }
30
32
  /**
31
- * Names of the cuttlefish-emitted C/C++ sources under src/ (top level only,
33
+ * Names of the typecad-hal-emitted C/C++ sources under src/ (top level only,
32
34
  * matching the old `src/*.cpp src/*.c` glob; sorted so the generated
33
35
  * CMakeLists.txt is stable across readdir orderings). Empty when no sources
34
36
  * exist yet (first prepare call).
@@ -61,7 +63,7 @@ function readEmittedSources(srcDir) {
61
63
  return out;
62
64
  }
63
65
  /**
64
- * Append cuttlefish library packages' devicetree overlay fragments to the
66
+ * Append typecad-hal library packages' devicetree overlay fragments to the
65
67
  * generated overlay. Library entries come from the transpiler's libraries.json
66
68
  * sidecar (next to the emitted sources) and are already gated on the
67
69
  * library's include token appearing in the emitted sources — no re-detection.
@@ -96,7 +98,7 @@ export function appendLibraryOverlayFragments(overlay, projectRoot) {
96
98
  *
97
99
  * Idempotent. Mirrors scaffoldEspIdfProject's writeIfChanged discipline.
98
100
  */
99
- export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, psram) {
101
+ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, psram, trace) {
100
102
  const srcDir = join(projectRoot, 'src');
101
103
  if (!existsSync(srcDir))
102
104
  mkdirSync(srcDir, { recursive: true });
@@ -122,6 +124,22 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
122
124
  const usage = {
123
125
  usesAdc: uses('adc_'),
124
126
  usesPwm: uses('pwm_'),
127
+ usesStrip: uses('led_strip'),
128
+ strips: scanStrips(src),
129
+ // Sensor parts drive CONFIG_W1 (1-Wire masters) and the exception
130
+ // lines; the same scan the exception block below re-runs.
131
+ sensorParts: scanSensorParts(src),
132
+ usesHid: uses('__tc_hid_'),
133
+ hidProtocol: scanHid(src),
134
+ usesMatrix: uses('__tc_matrix'),
135
+ matrix: scanMatrix(src),
136
+ usesPower: uses('sys_poweroff'),
137
+ usesClock: uses('__tc_rtc'),
138
+ usesCan: uses('can_'),
139
+ canLoopback: uses('CAN_MODE_LOOPBACK'),
140
+ usesI2s: uses('i2s_'),
141
+ // No chip facts at scaffold time — the prepare overlay omits the shim;
142
+ // the chip-aware build path synthesizes it.
125
143
  usesDac: uses('dac_') || uses('__tc_dac'),
126
144
  usesFS: uses('__tc_fs'),
127
145
  usesHwtimer: uses('counter_') || uses('__tc_hw'),
@@ -130,6 +148,9 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
130
148
  // call yet), but the shim still declared DEVICE_DT_GET(DT_NODELABEL(...))
131
149
  // so the overlay must enable the node or the device symbol is missing.
132
150
  usesI2c: uses('i2c_') || uses('__tc_i2c'),
151
+ // I2C target mode: every i2c.resp_* lowering calls i2c_target_register
152
+ // (and the shim's state carries the i2c_target_* vocabulary).
153
+ usesI2cTarget: uses('i2c_target_'),
133
154
  // DT-bound sensor parts: the __tc_sensor_* state references and the
134
155
  // sensor_sample_fetch/sensor_channel_get calls both carry the token.
135
156
  usesSensor: uses('sensor_') || uses('__tc_sensor'),
@@ -138,12 +159,32 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
138
159
  usesUart: uses('uart_') || uses('__tc_uart'),
139
160
  // USB CDC serial: every usb.* lowering calls into the __tc_usb<N>_* shim
140
161
  // (device + init helper emitted under usesUsb).
141
- usesUsb: uses('__tc_usb'),
162
+ usesUsb: uses('__tc_usb0'),
142
163
  // STM32F4 DBGMCU keep-SWD-alive init present (emitted for stm32f4 socs).
143
164
  usesStm32DebugSleep: uses('__tc_stm32_dbgmcu'),
144
165
  usesWdt: uses('wdt_'),
145
166
  usesBle: uses('bt_') || uses('bt_gatt') || uses('bt_le_'),
146
167
  usesDisplay: uses('display_write') || uses('display_init') || uses('display_fill_rect') || uses('__tc_display_dev') || uses('CuttlefishDisplayTarget'),
168
+ // Display transport + controller: recovered from the profile marker the
169
+ // display adapters stamp into the emitted source (the profile registry is
170
+ // the single source of truth — no geometry re-derivation here).
171
+ ...(() => {
172
+ const profile = profileFromEmittedSource(src);
173
+ if (!profile)
174
+ return {};
175
+ // Registry profiles name a DT compatible via dtCompatible (the driver
176
+ // id itself isn't one); drop-in profiles use the driver string directly.
177
+ const bus = readDisplayBinding(profile.dtCompatible ?? profile.driver)?.busFamily;
178
+ if (bus === 'i2c')
179
+ return { displayBus: 'i2c' };
180
+ // Board-provided panels (native_sim's sdl_dc) carry their own fragments
181
+ // and no bus at all.
182
+ if (profile.boardProvidesDisplay)
183
+ return { displayKconfigExtra: profile.kconfig };
184
+ if (transportFor(profile) !== 'zephyr-display')
185
+ return {};
186
+ return { displayTransport: 'zephyr-display', displayController: panelControllerFor(profile) };
187
+ })(),
147
188
  usesTouch: uses('ft6336u') || uses('touch_'),
148
189
  // (The power HAL is removed; its former pm_/k_sleep token scan is gone —
149
190
  // nothing here must match tx_power_dbm's `power` substring.)
@@ -174,7 +215,7 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
174
215
  // configChanged and reconfigures with the new list baked in.
175
216
  const sourceFiles = listEmittedSources(srcDir);
176
217
  const cmakeLists = [
177
- '# Auto-generated by @typecad/framework-zephyr from cuttlefish.config.ts.',
218
+ '# Auto-generated by @typecad/framework-zephyr from typecad-hal.config.ts.',
178
219
  'cmake_minimum_required(VERSION 3.20.0)',
179
220
  '',
180
221
  '# The app is its own board root: MCU-only targets generate an out-of-tree',
@@ -189,7 +230,7 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
189
230
  '',
190
231
  ...(sourceFiles.length > 0
191
232
  ? [
192
- '# Cuttlefish-emitted sources. This list is regenerated whenever the',
233
+ '# TypeCAD-emitted sources. This list is regenerated whenever the',
193
234
  '# emitted file set changes (the scaffold rewrites CMakeLists.txt).',
194
235
  'target_sources(app PRIVATE',
195
236
  ...sourceFiles.map((name) => ` src/${name}`),
@@ -217,12 +258,12 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
217
258
  // delegated to resolveKconfigFragments (unit-tested in dt-config/kconfig).
218
259
  const symbols = resolveKconfigFragments(usage, debug);
219
260
  const prjConf = [
220
- '# Auto-generated by @typecad/framework-zephyr from cuttlefish.config.ts.',
221
- '# Edit in cuttlefish.config.ts (frameworkData), not here.',
261
+ '# Auto-generated by @typecad/framework-zephyr from typecad-hal.config.ts.',
262
+ '# Edit in typecad-hal.config.ts (frameworkData), not here.',
222
263
  '# Driver symbols are usage-gated on the emitted source — only peripherals',
223
264
  '# the program references are enabled.',
224
265
  '',
225
- '# Route printf/stdout to the console UART (needed by the @typecad/expect',
266
+ '# Route printf/stdout to the console UART (needed by the typecad-hal',
226
267
  '# test runner protocol, which uses printf via __tc_print/__tc_println).',
227
268
  'CONFIG_STDOUT_CONSOLE=y',
228
269
  'CONFIG_PRINTK=y',
@@ -241,7 +282,7 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
241
282
  wifiHeaderEmitted = true;
242
283
  }
243
284
  // Skip auto-detected symbols that the user explicitly overrides
244
- // in cuttlefish.config.ts zephyr.kconfig — the user value is
285
+ // in typecad-hal.config.ts zephyr.kconfig — the user value is
245
286
  // emitted in the User Kconfig section below and takes precedence.
246
287
  if (userKconfig && userKconfig.hasOwnProperty(sym))
247
288
  continue;
@@ -256,7 +297,7 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
256
297
  // zephyr.kconfig overrides still win.
257
298
  const libraryEntries = readCuttlefishLibrarySidecar(srcDir);
258
299
  if (libraryEntries.length > 0) {
259
- prjConf.push('', '# Library packages (cuttlefish.library.json contributions).');
300
+ prjConf.push('', '# Library packages (typecad-hal.library.json contributions).');
260
301
  for (const entry of libraryEntries) {
261
302
  for (const line of entry.kconfig) {
262
303
  const sym = line.split('=')[0];
@@ -266,6 +307,29 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
266
307
  }
267
308
  }
268
309
  }
310
+ // ── Trace heartbeat Kconfig ────────────────────────────────────────────────
311
+ // zephyr.trace.enabled in typecad-hal.config.ts turns on the [TR: sampler
312
+ // (emitted by the strategy at transpile time from the same config record).
313
+ // The symbol set is exactly what the sampler calls: runtime stats
314
+ // (k_thread_runtime_stats_*), the monitor list (k_thread_foreach —
315
+ // kernel/thread_monitor.c only links under THREAD_MONITOR), thread names
316
+ // (k_thread_name_get/set), and stack inspection (0xAA fill +
317
+ // stack_info.size). User zephyr.kconfig overrides still win.
318
+ if (trace?.enabled === true) {
319
+ const traceSymbols = [
320
+ ['CONFIG_THREAD_RUNTIME_STATS', 'y'],
321
+ ['CONFIG_THREAD_MONITOR', 'y'],
322
+ ['CONFIG_THREAD_NAME', 'y'],
323
+ ['CONFIG_INIT_STACKS', 'y'],
324
+ ['CONFIG_THREAD_STACK_INFO', 'y'],
325
+ ];
326
+ prjConf.push('', '# Trace heartbeat (typecad-hal.config.ts → zephyr.trace).');
327
+ for (const [sym, val] of traceSymbols) {
328
+ if (userKconfig && userKconfig.hasOwnProperty(sym))
329
+ continue;
330
+ prjConf.push(`${sym}=${val}`);
331
+ }
332
+ }
269
333
  // Per-part Kconfig exceptions: catalog parts whose driver is NOT default-y
270
334
  // on its DT node carry extra lines here (empty today — every in-tree sensor
271
335
  // driver lights up from the node; the path exists so the first exception
@@ -279,10 +343,10 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
279
343
  prjConf.push(line);
280
344
  }
281
345
  }
282
- // Emit user-specified Kconfig from cuttlefish.config.ts zephyr.kconfig.
346
+ // Emit user-specified Kconfig from typecad-hal.config.ts zephyr.kconfig.
283
347
  // These override any matching auto-detected symbol (skipped above).
284
348
  if (userKconfig) {
285
- prjConf.push('', '# User Kconfig (cuttlefish.config.ts → zephyr.kconfig).');
349
+ prjConf.push('', '# User Kconfig (typecad-hal.config.ts → zephyr.kconfig).');
286
350
  for (const [sym, val] of Object.entries(userKconfig)) {
287
351
  prjConf.push(`${sym}=${val}`);
288
352
  }
@@ -30,6 +30,12 @@ export declare function discoverFromPath(): WestInstall | null;
30
30
  /** The canonical Zephyr workspace layout puts the venv beside the SDK:
31
31
  * <workspace>/{.venv, zephyr}. So ${ZEPHYR_BASE}/../.venv is the venv. */
32
32
  export declare function discoverFromZephyrBase(): WestInstall | null;
33
+ /** The installer-written SDK dir for the default micromamba env, when the
34
+ * installer has run on this machine. Read WITHOUT requiring micromamba
35
+ * mode — a PATH/venv west install is enriched with it so builds spawned
36
+ * through it still pin the installer's SDK (otherwise Zephyr's CMake
37
+ * searches $HOME and can pick up a stray, older SDK). */
38
+ export declare function installerSdkDir(): string | undefined;
33
39
  /** The installer-written ZEPHYR_BASE of the micromamba env, fs-only (no
34
40
  * spawn) — the cheap probe shared by board-catalog overlay discovery.
35
41
  * Undefined when the installer env or its env-vars file is absent. */
@@ -11,7 +11,7 @@
11
11
  // 1. `west` already on PATH (env already activated / global install).
12
12
  // 2. $ZEPHYR_BASE venv: ${ZEPHYR_BASE}/../.venv/<python> -m west.
13
13
  // 3. micromamba env from @typecad/zephyr-installer (invoked via `micromamba run`,
14
- // so cuttlefish builds work with NO manual activation).
14
+ // so typecad-hal builds work with NO manual activation).
15
15
  // 4. Well-known workspace layouts: ~/zephyrproject/.venv, /opt/zephyrproject/.
16
16
  // venv, etc.
17
17
  // 5. System pythons (`python`, `python3`, `py`) via `-m west`.
@@ -111,6 +111,34 @@ export function discoverFromZephyrBase() {
111
111
  source: 'zephyr-base-venv',
112
112
  };
113
113
  }
114
+ /** The installer-written SDK dir for the default micromamba env, when the
115
+ * installer has run on this machine. Read WITHOUT requiring micromamba
116
+ * mode — a PATH/venv west install is enriched with it so builds spawned
117
+ * through it still pin the installer's SDK (otherwise Zephyr's CMake
118
+ * searches $HOME and can pick up a stray, older SDK). */
119
+ export function installerSdkDir() {
120
+ const mm = findMicromamba();
121
+ if (!mm)
122
+ return undefined;
123
+ const envName = process.env.TYPECAD_ZEPHYR_ENV || 'zephyr';
124
+ const envDir = join(mm.rootPrefix, 'envs', envName);
125
+ if (!existsSync(envDir))
126
+ return undefined;
127
+ const sdk = readMicromambaEnvVar(envDir, 'TYPECAD_ZEPHYR_SDK_INSTALL_DIR');
128
+ return sdk && existsSync(sdk) ? sdk : undefined;
129
+ }
130
+ /**
131
+ * The micromamba env created by `@typecad/zephyr-installer`. The env's west
132
+ * lives at envs/<name>/bin/west (POSIX) or Scripts/west.exe (Windows). Found
133
+ * installs are invoked via `micromamba run -n <name> west …` (see
134
+ * west-spawn.ts), which sets up the env's full PATH (cmake/ninja/dtc) AND runs
135
+ * the activation hook (ZEPHYR_BASE / ZEPHYR_SDK_INSTALL_DIR) — so builds work
136
+ * with NO manual `micromamba activate`. This is what makes a fresh
137
+ * `typecad-hal build` succeed in any project without the user activating.
138
+ *
139
+ * Env name defaults to "zephyr"; override via TYPECAD_ZEPHYR_ENV. File-check
140
+ * based (no spawn) so it's cheap to run on every invocation.
141
+ */
114
142
  // ── Strategy 3: micromamba env (the @typecad/zephyr-installer install) ─────
115
143
  // Locate the micromamba binary + root prefix. The installer downloads
116
144
  // micromamba to $MAMBA_ROOT_PREFIX/bin (POSIX) or Library/bin (Windows); the
@@ -123,18 +151,6 @@ function findMicromamba() {
123
151
  : join(root, 'bin', 'micromamba');
124
152
  return existsSync(exe) ? { exe, rootPrefix: root } : null;
125
153
  }
126
- /**
127
- * The micromamba env created by `@typecad/zephyr-installer`. The env's west
128
- * lives at envs/<name>/bin/west (POSIX) or Scripts/west.exe (Windows). Found
129
- * installs are invoked via `micromamba run -n <name> west …` (see
130
- * west-spawn.ts), which sets up the env's full PATH (cmake/ninja/dtc) AND runs
131
- * the activation hook (ZEPHYR_BASE / ZEPHYR_SDK_INSTALL_DIR) — so cuttlefish
132
- * builds work with NO manual `micromamba activate`. This is what makes a fresh
133
- * `cuttlefish build` succeed in any project without the user activating.
134
- *
135
- * Env name defaults to "zephyr"; override via TYPECAD_ZEPHYR_ENV. File-check
136
- * based (no spawn) so it's cheap to run on every cuttlefish invocation.
137
- */
138
154
  /** Read a TYPECAD_ZEPHYR_* value from the installer-written env-vars file in
139
155
  * a micromamba env. Handles .sh (export VAR="val"), .bat (set "VAR=val"),
140
156
  * and .ps1 ($env:VAR = "val"). Returns undefined if absent/unreadable. */
@@ -284,6 +300,13 @@ export function discoverWest() {
284
300
  install = null;
285
301
  }
286
302
  if (install) {
303
+ // A PATH/venv west install knows nothing about the installer's SDK.
304
+ // Enrich it: west-spawn pins the child's ZEPHYR_SDK_INSTALL_DIR to the
305
+ // installer dir, so Zephyr's CMake cannot pick up a stray SDK from
306
+ // $HOME (a version mismatch that only surfaces at configure time).
307
+ if (!install.sdkInstallDir) {
308
+ install.sdkInstallDir = installerSdkDir();
309
+ }
287
310
  cachedDiscover = install;
288
311
  return install;
289
312
  }
@@ -49,6 +49,12 @@ export function buildEnv(install) {
49
49
  // flashing works without activation. $ZEPHYR_SDK_INSTALL_DIR (set by an
50
50
  // activated env) wins over the discovered install dir.
51
51
  const sdkRoot = env.ZEPHYR_SDK_INSTALL_DIR || install.sdkInstallDir;
52
+ // Pin the west/cmake child to the discovered SDK: without this, Zephyr's
53
+ // FindZephyr-sdk searches $HOME and can latch onto a stray, older SDK —
54
+ // a configure-time version failure that only surfaces in the build log.
55
+ if (!env.ZEPHYR_SDK_INSTALL_DIR && install.sdkInstallDir) {
56
+ env.ZEPHYR_SDK_INSTALL_DIR = install.sdkInstallDir;
57
+ }
52
58
  const openocdBin = sdkRoot ? join(sdkRoot, 'hosttools', 'openocd', 'bin') : undefined;
53
59
  const prepend = [
54
60
  openocdBin !== undefined && existsSync(openocdBin) ? openocdBin : undefined,
@@ -92,7 +98,7 @@ export function westSpawn(westArgs, baseOptions) {
92
98
  throw new Error([
93
99
  'west (the Zephyr build tool) was not found.',
94
100
  '',
95
- 'cuttlefish looked for it on PATH, in $ZEPHYR_BASE/.venv, in common',
101
+ 'typecad-hal looked for it on PATH, in $ZEPHYR_BASE/.venv, in common',
96
102
  'Zephyr workspace dirs (~/zephyrproject/.venv), and as a system',
97
103
  "Python module (`python -m west`). To fix:",
98
104
  '',
@@ -113,7 +119,7 @@ export function westSpawn(westArgs, baseOptions) {
113
119
  if (install.mode === 'micromamba' && install.micromambaExe) {
114
120
  // `micromamba run -n <env> west …` sets up the env's full PATH
115
121
  // (cmake/ninja/dtc) and runs the activation hook (ZEPHYR_BASE /
116
- // ZEPHYR_SDK_INSTALL_DIR), so cuttlefish builds work WITHOUT the user
122
+ // ZEPHYR_SDK_INSTALL_DIR), so typecad-hal builds work WITHOUT the user
117
123
  // activating the env. Inject MAMBA_ROOT_PREFIX so micromamba finds envs.
118
124
  const mmEnv = { ...env };
119
125
  if (install.mambaRootPrefix)
@@ -0,0 +1,25 @@
1
+ import type { ReadDir, ReadFile } from '@typecad/cuttlefish/api/shared';
2
+ /** Normalize a path for case-insensitive, separator-agnostic comparison. */
3
+ export declare function norm(p: string): string;
4
+ /**
5
+ * Filter the west manifest to the modules whose sources were compiled in the
6
+ * last build. A module is "linked" iff some compiled translation unit lives
7
+ * under its abspath. If the build data is unparseable, no filtering is applied
8
+ * (degrade to listing all modules rather than reporting nothing).
9
+ */
10
+ export declare function filterLinkedModules(ccText: string, modules: {
11
+ name: string;
12
+ abspath: string;
13
+ }[]): {
14
+ name: string;
15
+ abspath: string;
16
+ }[];
17
+ /**
18
+ * Find the build's compile_commands.json path under `cwd`. Cuttlefish's Zephyr
19
+ * project root is the transpile output dir (e.g. `<cwd>/src/out`), so the
20
+ * build dir is `<root>/build` — not necessarily `<cwd>/build`. Check the
21
+ * common layouts, then fall back to a bounded search (skipping
22
+ * node_modules/.git/dist and dot-directories). Returns the PATH (the caller
23
+ * decides whether to read it); undefined when no build exists.
24
+ */
25
+ export declare function findCompileCommandsPath(readFile: ReadFile, readdir: ReadDir, cwd?: string): string | undefined;
@@ -0,0 +1,97 @@
1
+ // ---------------------------------------------------------------------------
2
+ // @typecad/framework-zephyr — shared west workspace ground truth
3
+ //
4
+ // The as-built inventory both `typecad-hal licenses` and `typecad-hal sbom`
5
+ // report from: which west manifest modules the last build ACTUALLY compiled
6
+ // (linked-module filtering via the build's compile_commands.json), plus the
7
+ // build-directory discovery that locates it. A west manifest carries every
8
+ // vendor HAL/library; almost none are linked by a single project — both the
9
+ // license table and the SBOM scope to the linked set by default, so they stay
10
+ // honest about what is actually inside the firmware.
11
+ //
12
+ // Moved out of licenses.ts when the sbom command grew a second consumer; the
13
+ // behavior (case-insensitive, separator-agnostic path matching; degrade to
14
+ // "no filtering" on unparseable build data) is unchanged.
15
+ // ---------------------------------------------------------------------------
16
+ import * as path from 'node:path';
17
+ /** Normalize a path for case-insensitive, separator-agnostic comparison. */
18
+ export function norm(p) {
19
+ return p.split('\\').join('/').toLowerCase();
20
+ }
21
+ /**
22
+ * Filter the west manifest to the modules whose sources were compiled in the
23
+ * last build. A module is "linked" iff some compiled translation unit lives
24
+ * under its abspath. If the build data is unparseable, no filtering is applied
25
+ * (degrade to listing all modules rather than reporting nothing).
26
+ */
27
+ export function filterLinkedModules(ccText, modules) {
28
+ let arr;
29
+ try {
30
+ arr = JSON.parse(ccText);
31
+ }
32
+ catch {
33
+ return modules;
34
+ }
35
+ if (!Array.isArray(arr))
36
+ return modules;
37
+ // One normalized blob of every compiled source path; membership is then a
38
+ // substring check per module (O(modules) after an O(TUs) join).
39
+ const blob = arr
40
+ .map((e) => norm(typeof e?.file === 'string' ? e.file : ''))
41
+ .join('\n');
42
+ return modules.filter((m) => blob.includes(norm(m.abspath) + '/'));
43
+ }
44
+ /**
45
+ * Find the build's compile_commands.json path under `cwd`. Cuttlefish's Zephyr
46
+ * project root is the transpile output dir (e.g. `<cwd>/src/out`), so the
47
+ * build dir is `<root>/build` — not necessarily `<cwd>/build`. Check the
48
+ * common layouts, then fall back to a bounded search (skipping
49
+ * node_modules/.git/dist and dot-directories). Returns the PATH (the caller
50
+ * decides whether to read it); undefined when no build exists.
51
+ */
52
+ export function findCompileCommandsPath(readFile, readdir, cwd = process.cwd()) {
53
+ const direct = [
54
+ path.join(cwd, 'build', 'compile_commands.json'),
55
+ path.join(cwd, 'out', 'build', 'compile_commands.json'),
56
+ path.join(cwd, 'src', 'out', 'build', 'compile_commands.json'),
57
+ ];
58
+ for (const c of direct) {
59
+ if (readFile(c))
60
+ return c;
61
+ }
62
+ // Bounded recursive search for a build/compile_commands.json.
63
+ const isCc = (p) => {
64
+ const n = norm(p);
65
+ return n.endsWith('/build/compile_commands.json') || n.endsWith('\\build\\compile_commands.json');
66
+ };
67
+ let found;
68
+ const seen = new Set();
69
+ const walk = (dir, depth) => {
70
+ if (found || depth > 4 || seen.has(dir))
71
+ return;
72
+ seen.add(dir);
73
+ let entries;
74
+ try {
75
+ entries = readdir(dir);
76
+ }
77
+ catch {
78
+ return;
79
+ }
80
+ for (const e of entries) {
81
+ const full = path.join(dir, e);
82
+ if (e === 'compile_commands.json' && isCc(full)) {
83
+ found = full;
84
+ return;
85
+ }
86
+ }
87
+ for (const e of entries) {
88
+ if (found)
89
+ return;
90
+ if (e === 'node_modules' || e === '.git' || e === 'dist' || e.startsWith('.'))
91
+ continue;
92
+ walk(path.join(dir, e), depth + 1);
93
+ }
94
+ };
95
+ walk(cwd, 0);
96
+ return found;
97
+ }