@typecad/framework-zephyr 1.0.0-alpha.13 → 1.0.0-alpha.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/README.md +22 -35
  2. package/dist/as-built.d.ts +33 -0
  3. package/dist/as-built.js +188 -0
  4. package/dist/async/timer-polyfill.js +1 -1
  5. package/dist/boardgen.d.ts +128 -0
  6. package/dist/boardgen.js +1366 -0
  7. package/dist/chips/board-overrides.d.ts +7 -0
  8. package/dist/chips/board-overrides.js +11 -0
  9. package/dist/chips/controllers.d.ts +28 -8
  10. package/dist/chips/controllers.js +49 -12
  11. package/dist/chips/esp32s3.js +49 -0
  12. package/dist/chips/index.d.ts +6 -12
  13. package/dist/chips/index.js +18 -34
  14. package/dist/chips/resolve.d.ts +16 -2
  15. package/dist/chips/resolve.js +298 -15
  16. package/dist/chips/soc/.d.ts +2 -0
  17. package/dist/chips/soc/.js +129 -0
  18. package/dist/chips/soc/esp32.d.ts +2 -0
  19. package/dist/chips/soc/esp32.js +120 -0
  20. package/dist/chips/soc/esp32c3.d.ts +2 -0
  21. package/dist/chips/soc/esp32c3.js +90 -0
  22. package/dist/chips/soc/esp32c6.d.ts +2 -0
  23. package/dist/chips/soc/esp32c6.js +109 -0
  24. package/dist/chips/soc/esp32s3.d.ts +2 -0
  25. package/dist/chips/soc/esp32s3.js +189 -0
  26. package/dist/chips/soc/index.d.ts +2 -0
  27. package/dist/chips/soc/index.js +23 -0
  28. package/dist/chips/soc/nrf52840.d.ts +2 -0
  29. package/dist/chips/soc/nrf52840.js +130 -0
  30. package/dist/chips/soc/rp2040.d.ts +2 -0
  31. package/dist/chips/soc/rp2040.js +141 -0
  32. package/dist/chips/soc/rp2350a.d.ts +2 -0
  33. package/dist/chips/soc/rp2350a.js +145 -0
  34. package/dist/chips/soc/samd21g18a.d.ts +2 -0
  35. package/dist/chips/soc/samd21g18a.js +143 -0
  36. package/dist/chips/soc/stm32f411xe.d.ts +2 -0
  37. package/dist/chips/soc/stm32f411xe.js +251 -0
  38. package/dist/chips/types.d.ts +382 -12
  39. package/dist/chips/xiao-ble.js +33 -0
  40. package/dist/display/profiles.js +1 -1
  41. package/dist/doctor.js +38 -4
  42. package/dist/dt-config/custom-board.d.ts +23 -0
  43. package/dist/dt-config/custom-board.js +227 -0
  44. package/dist/dt-config/kconfig.d.ts +83 -1
  45. package/dist/dt-config/kconfig.js +116 -27
  46. package/dist/dt-config/overlay.js +623 -18
  47. package/dist/framework.manifest.d.ts +20 -30
  48. package/dist/framework.manifest.js +124 -178
  49. package/dist/index.d.ts +4 -1
  50. package/dist/index.js +8 -2
  51. package/dist/lowering/adc.d.ts +26 -5
  52. package/dist/lowering/adc.js +137 -31
  53. package/dist/lowering/ble.js +31 -28
  54. package/dist/lowering/dac.js +9 -9
  55. package/dist/lowering/fs.js +18 -10
  56. package/dist/lowering/gpio.d.ts +2 -0
  57. package/dist/lowering/gpio.js +90 -10
  58. package/dist/lowering/http.js +39 -14
  59. package/dist/lowering/hwtimer.d.ts +4 -0
  60. package/dist/lowering/hwtimer.js +35 -10
  61. package/dist/lowering/i2c.js +46 -54
  62. package/dist/lowering/index.d.ts +5 -5
  63. package/dist/lowering/index.js +15 -16
  64. package/dist/lowering/interrupts.d.ts +22 -5
  65. package/dist/lowering/interrupts.js +104 -22
  66. package/dist/lowering/mqtt.js +12 -6
  67. package/dist/lowering/power.js +0 -4
  68. package/dist/lowering/preferences.js +49 -77
  69. package/dist/lowering/pulse.js +5 -36
  70. package/dist/lowering/pwm.d.ts +32 -3
  71. package/dist/lowering/pwm.js +105 -17
  72. package/dist/lowering/random.js +3 -3
  73. package/dist/lowering/sensor.d.ts +37 -0
  74. package/dist/lowering/sensor.js +119 -0
  75. package/dist/lowering/spi.d.ts +12 -0
  76. package/dist/lowering/spi.js +65 -40
  77. package/dist/lowering/thread.d.ts +14 -0
  78. package/dist/lowering/thread.js +55 -0
  79. package/dist/lowering/timing.js +20 -46
  80. package/dist/lowering/tone.js +18 -5
  81. package/dist/lowering/uart.d.ts +13 -0
  82. package/dist/lowering/uart.js +72 -51
  83. package/dist/lowering/usb.d.ts +34 -0
  84. package/dist/lowering/usb.js +206 -0
  85. package/dist/lowering/wdt.d.ts +1 -1
  86. package/dist/lowering/wdt.js +17 -7
  87. package/dist/lowering/wifi.d.ts +3 -2
  88. package/dist/lowering/wifi.js +120 -111
  89. package/dist/sdk/board-catalog-sync.d.ts +16 -0
  90. package/dist/sdk/board-catalog-sync.js +41 -0
  91. package/dist/sdk/board-data.generated.d.ts +2 -0
  92. package/dist/sdk/board-data.generated.js +4 -0
  93. package/dist/sdk/catalog-walker.d.ts +90 -0
  94. package/dist/sdk/catalog-walker.js +682 -0
  95. package/dist/sdk/dts-reader.d.ts +83 -0
  96. package/dist/sdk/dts-reader.js +596 -0
  97. package/dist/strategy.d.ts +136 -17
  98. package/dist/strategy.js +881 -212
  99. package/dist/toolchain/bossac-touch.d.ts +18 -0
  100. package/dist/toolchain/bossac-touch.js +88 -0
  101. package/dist/toolchain/compat.d.ts +2 -2
  102. package/dist/toolchain/compat.js +8 -3
  103. package/dist/toolchain/debug-config.d.ts +21 -26
  104. package/dist/toolchain/debug-config.js +212 -72
  105. package/dist/toolchain/env-check.d.ts +4 -4
  106. package/dist/toolchain/env-check.js +5 -5
  107. package/dist/toolchain/index.d.ts +66 -1
  108. package/dist/toolchain/index.js +713 -58
  109. package/dist/toolchain/scaffold.d.ts +9 -0
  110. package/dist/toolchain/scaffold.js +84 -7
  111. package/dist/toolchain/west-discover.d.ts +8 -1
  112. package/dist/toolchain/west-discover.js +19 -6
  113. package/dist/toolchain/west-spawn.js +17 -5
  114. package/installer/CHANGELOG.md +473 -0
  115. package/installer/README.md +328 -0
  116. package/installer/environment.yml +46 -0
  117. package/installer/etc/conda/activate.d/zephyr.bat +7 -0
  118. package/installer/etc/conda/activate.d/zephyr.ps1 +12 -0
  119. package/installer/etc/conda/activate.d/zephyr.sh +20 -0
  120. package/installer/etc/conda/deactivate.d/zephyr.bat +6 -0
  121. package/installer/etc/conda/deactivate.d/zephyr.ps1 +6 -0
  122. package/installer/etc/conda/deactivate.d/zephyr.sh +4 -0
  123. package/installer/install.mjs +580 -0
  124. package/installer/install.ps1 +619 -0
  125. package/installer/install.sh +310 -0
  126. package/installer/lib/detect-platform.sh +62 -0
  127. package/installer/lib/fetch-sdk.sh +304 -0
  128. package/installer/lib/init-workspace.sh +153 -0
  129. package/installer/lib/write-activation.sh +76 -0
  130. package/installer/patches/tfm-c-include-separators.patch +19 -0
  131. package/installer/patches/zephyr-tfm-nordic-zephyr-include.patch +24 -0
  132. package/installer/templates/project/.typecad/activate-zephyr.ps1 +31 -0
  133. package/installer/templates/project/.typecad/activate-zephyr.sh +31 -0
  134. package/installer/templates/project/.vscode/settings.json +30 -0
  135. package/installer/templates/project/README.md +58 -0
  136. package/installer/versions.env +110 -0
  137. package/package.json +16 -16
  138. package/src/as-built.ts +206 -0
  139. package/src/boardgen.ts +1428 -0
  140. package/src/chips/controllers.ts +61 -12
  141. package/src/chips/index.ts +35 -54
  142. package/src/chips/resolve.ts +316 -14
  143. package/src/chips/types.ts +388 -13
  144. package/src/display/profiles.ts +1 -1
  145. package/src/doctor.ts +110 -77
  146. package/src/dt-config/custom-board.ts +325 -0
  147. package/src/dt-config/kconfig.ts +177 -19
  148. package/src/dt-config/overlay.ts +1058 -450
  149. package/src/framework.manifest.ts +125 -178
  150. package/src/index.ts +57 -41
  151. package/src/lowering/adc.ts +222 -102
  152. package/src/lowering/ble.ts +32 -28
  153. package/src/lowering/dac.ts +82 -82
  154. package/src/lowering/fs.ts +18 -10
  155. package/src/lowering/gpio.ts +242 -152
  156. package/src/lowering/http.ts +512 -488
  157. package/src/lowering/hwtimer.ts +139 -101
  158. package/src/lowering/i2c.ts +116 -126
  159. package/src/lowering/index.ts +91 -93
  160. package/src/lowering/interrupts.ts +113 -21
  161. package/src/lowering/mqtt.ts +12 -6
  162. package/src/lowering/preferences.ts +327 -354
  163. package/src/lowering/pwm.ts +167 -74
  164. package/src/lowering/random.ts +3 -3
  165. package/src/lowering/sensor.ts +155 -0
  166. package/src/lowering/spi.ts +164 -124
  167. package/src/lowering/thread.ts +63 -0
  168. package/src/lowering/timing.ts +46 -72
  169. package/src/lowering/uart.ts +73 -51
  170. package/src/lowering/usb.ts +221 -0
  171. package/src/lowering/wdt.ts +95 -84
  172. package/src/lowering/wifi.ts +120 -111
  173. package/src/sdk/board-catalog-sync.ts +62 -0
  174. package/src/strategy.ts +888 -229
  175. package/src/toolchain/bossac-touch.ts +112 -0
  176. package/src/toolchain/compat.ts +8 -3
  177. package/src/toolchain/debug-config.ts +666 -522
  178. package/src/toolchain/env-check.ts +9 -9
  179. package/src/toolchain/index.ts +765 -44
  180. package/src/toolchain/scaffold.ts +81 -7
  181. package/src/toolchain/west-discover.ts +334 -316
  182. package/src/toolchain/west-spawn.ts +17 -5
  183. package/src/async/timer-polyfill.ts +0 -107
  184. package/src/chips/esp32.ts +0 -74
  185. package/src/chips/esp32s3.ts +0 -57
  186. package/src/chips/xiao-ble.ts +0 -70
  187. package/src/lowering/power.ts +0 -70
  188. package/src/lowering/pulse.ts +0 -93
  189. package/src/lowering/tone.ts +0 -60
  190. package/src/lowering/worker-backing.ts +0 -94
  191. package/src/lowering/worker.ts +0 -17
package/README.md CHANGED
@@ -24,74 +24,61 @@ fix (`pip install west`, set `ZEPHYR_BASE`, or activate the venv).
24
24
 
25
25
  ## How it works
26
26
 
27
- - HAL ops (`gpio.write`, `i2c.begin`, etc.) lower to native Zephyr driver calls
28
- (`gpio_pin_set_dt`, `i2c_write`, etc.) — no Arduino API.
27
+ - HAL ops (`gpio.write`, `i2c.reg_read`, etc.) lower to native Zephyr driver
28
+ calls (`gpio_pin_set_dt`, `i2c_reg_read_byte`, etc.) — no Arduino API.
29
29
  - The generated `src/main.cpp` defines `int main(void)` — the standard Zephyr
30
- C entry point. `main` runs the synthesizer-emitted `setup()` once, then loops
31
- `loop()` forever, yielding to the scheduler with `k_msleep(1)` each iteration
32
- (cheap cooperative yield matches the `app_main` pattern in framework-esp32).
30
+ C entry point. Top-level statements lower straight into `main()` (no
31
+ `setup()`/`loop()` pair); event-driven programs get their scheduler loop
32
+ appended inside `main()` by the driver machinery.
33
33
  - The framework emits a complete Zephyr application: root `CMakeLists.txt`
34
34
  (`find_package(Zephyr)` + `file(GLOB src/*.cpp)`) and `prj.conf` (the Kconfig
35
35
  symbols for the lowered peripherals). Both are regenerated idempotently —
36
36
  only rewritten when their content changes, so Ninja's incremental build is
37
37
  preserved.
38
- - The MVP target is the **Seeed Studio XIAO nRF52840** (`xiao_ble` board).
38
+ - Board targets are not curated: **every board variant in the generated board
39
+ catalog** resolves the same way (`resolveChipFromBoard` over the catalog's
40
+ board data — there is no hardcoded chip registry or target list). The
41
+ catalog is machine-local, generated from your Zephyr tree by
42
+ `cuttlefish board sync` (and refreshed automatically by builds).
39
43
 
40
44
  ## Installation
41
45
 
42
46
  ```sh
43
- cuttlefish init # select "Zephyr RTOS" for an nRF52840-family target
47
+ cuttlefish create # pick a board from the catalog; the Zephyr framework is the default
44
48
  ```
45
49
 
46
50
  Or in `cuttlefish.config.ts`:
47
51
 
48
52
  ```ts
49
53
  export default {
54
+ entry: './src/main.ts',
55
+ board: 'xiao_ble/nrf52840',
50
56
  framework: '@typecad/framework-zephyr',
51
- frameworkData: {
52
- buildTarget: 'xiao_ble',
53
- },
54
- toolchain: { type: 'west' },
55
57
  };
56
58
  ```
57
59
 
58
60
  ## HAL coverage
59
61
 
60
62
  The manifest (`src/framework.manifest.ts`) is the honest, complete record of
61
- what lowers. Fully supported: **gpio, pwm, adc, i2c, spi, uart, wdt, ble,
62
- shift, board constants** (Board.definition.* / Pins.definition.* folding),
63
- **random** (xorshift32 PRNG seeded from the Zephyr entropy tap).
64
- Partial (some ops lower, some are deferred stubs): **timing, power,
65
- interrupts, tone, pulse**. Unsupported for this target: **wifi** (nRF52840 has
66
- no WiFi), **http** (no networking stack lowered), **display** (deferred),
67
- **dac** (no DAC on nRF52840).
63
+ what lowers. Fully lowered: **gpio, pwm, adc, dac** (pin-fact-gated),
64
+ **i2c, spi, uart, interrupts, timing, wdt, counter, thread, shift, board
65
+ constants**, **random** (xorshift32 PRNG seeded from the Zephyr entropy tap),
66
+ **preferences (Store)**, **fs (File)**, **sensor**, **ble**, **snprintf**, and
67
+ **wifi/http/mqtt** (ESP32-class radio targets only nRF52840 has no WiFi).
68
+ **Display** lowers via the generic `<zephyr/drivers/display.h>` GFX runtime.
69
+ Declared unsupported on this target: **i2s, twai/CAN, ethernet, espnow,
70
+ hardware crypto, pcnt, mcpwm**. USB CDC is board-gated: it lowers only on
71
+ boards whose DTS enables the USB device controller.
68
72
 
69
73
  Run `npm test` (the manifest validator) to confirm the declared coverage matches
70
74
  the actual lowering behavior.
71
75
 
72
- ## Hardware tests
73
-
74
- On-device tests live in `tests/`. Run them against real hardware:
75
-
76
- ```sh
77
- npm run test:hw # all groups
78
- npm run test:hw:gpio # just GPIO
79
- npm run test:hw:timers # just timing
80
- ```
81
-
82
- Update `test.port` in `cuttlefish.config.ts` to your XIAO's serial device
83
- before running. The tests transpile → `west build` → flash → read results over
84
- the console.
85
-
86
76
  ## Notes / limitations
87
77
 
88
78
  - **Minimal C++ libc.** Zephyr's default C++ support (`lib/cpp/minimal`) has no
89
79
  `<vector>`, `<string>`, `<iostream>`, `<functional>`, exceptions, or RTTI.
90
80
  `prj.conf` enables `CONFIG_NEWLIB_LIBC` for `std::string`/`std::vector`; array
91
81
  literals still promote to the StaticArray wrapper, not `std::vector`.
92
- - **Timer ops unsupported.** `timing.set_interval`/`set_timeout`/`clear_*` need
93
- a polyfill-backed async runtime this framework does not emit yet. A Zephyr
94
- workqueue / `k_thread` backing is a follow-on.
95
82
  - **Debug mode.** `cuttlefish build --debug` routes through `printk` (always
96
83
  available, no `CONFIG_CONSOLE` dependency) rather than `std::cout`, since the
97
84
  minimal libc has no iostream. Breakpoints halt on console input (`ENTER`
@@ -0,0 +1,33 @@
1
+ export interface AsBuiltRoute {
2
+ source: string;
3
+ channel: number;
4
+ port: string;
5
+ bit: number;
6
+ pinctrl: string;
7
+ }
8
+ export interface AsBuiltFacts {
9
+ adc: AsBuiltRoute[];
10
+ pwm: AsBuiltRoute[];
11
+ dac: AsBuiltRoute[];
12
+ }
13
+ /** The .cuttlefish/as-built.json shape (machine-authored facts file). */
14
+ export interface AsBuiltFile {
15
+ version: 1;
16
+ board: string;
17
+ generatedAt: string;
18
+ routes: {
19
+ adcPins: AsBuiltRoute[];
20
+ pwmPins: AsBuiltRoute[];
21
+ dacPins: AsBuiltRoute[];
22
+ };
23
+ }
24
+ /**
25
+ * Parse a resolved zephyr.dts into raw pin routes. Labels are matched as
26
+ * whole tokens (`label:` or `label {`) — in dtc output every node carries
27
+ * its labels inline before the node name.
28
+ */
29
+ export declare function parseZephyrDts(text: string): AsBuiltFacts;
30
+ /** Parse + shape-validate .cuttlefish/as-built.json; errors name the file. */
31
+ export declare function parseAsBuiltJson(text: string, source?: string): AsBuiltFile;
32
+ /** Serialize the as-built snapshot (deterministic field order). */
33
+ export declare function asBuiltJson(board: string, facts: AsBuiltFacts): string;
@@ -0,0 +1,188 @@
1
+ // ---------------------------------------------------------------------------
2
+ // as-built.ts — harvest pin routes from a build's resolved zephyr.dts
3
+ //
4
+ // The third fact source: after any successful `west build`, the fully
5
+ // merged devicetree at <build>/zephyr/zephyr.dts contains the board's
6
+ // pinctrl nodes with LABELS verbatim — the stable name grammar
7
+ // (`adc1_in1_pa1`, `tim4_ch1_pb6`, `iomuxc_<pad>_adc1_in1`) — with the
8
+ // vendor macro values already expanded away. Parsing names only needs ONE
9
+ // grammar per shape, immune to vendor macro churn, and corrects a broken
10
+ // catalog harvest per-pin before the next build (the walker's macro-value
11
+ // grammars are the fragile half; this is the robust half).
12
+ //
13
+ // Scope is honest: only node-name families appear (STM32-style dtsi nodes
14
+ // and the i.MX RT label grammar with its in-band pad join). Header-matrix
15
+ // families (Kinetis/LPC/GD32 macro routing, ESP32/RP2 matrices) never
16
+ // exist as DT nodes — the walker + family tables remain their source.
17
+ // ----------------------------------------------------------------------------
18
+ // STM32-style pinctrl dtsi labels (all spellings the walker knows):
19
+ // adc1_in1_pa1 / adc_in0_pa0 / adc1_inp16_pa0, tim4_ch1_pb6,
20
+ // dac1_out1_pa4. Labels appear verbatim in zephyr.dts
21
+ // (`adc1_in0_pa0: adc1_in0_pa0 {`). The `inn` negative inputs stay out —
22
+ // the thin HAL is single-ended. The F1 (AFIO) family uses `tim1_ch1_pwm_out_pa8`
23
+ // and `dac_out1_pa4` — separate grammars.
24
+ const ST_ADC = /^([a-z]*adc\d*_(?:in|inp)\d+_p([a-z])(\d+))$/;
25
+ const ST_PWM = /^(tim(\d+)_ch(\d+)_p([a-z])(\d+))$/;
26
+ const ST_PWM_F1 = /^(tim(\d+)_ch(\d+)_pwm_out_p([a-z])(\d+))$/;
27
+ const ST_DAC = /^(dac(\d+)_out(\d+)_p([a-z])(\d+))$/;
28
+ const ST_DAC_F1 = /^(dac_out(\d+)_p([a-z])(\d+))$/;
29
+ // i.MX RT labels: iomuxc_<pad>_adc1_in1 / iomuxc_<pad>_flexpwm2_pwma3,
30
+ // with the pad→GPIO join as sibling iomuxc_<pad>_gpio1_io12 labels. The NEW
31
+ // RT parts (rt11xx/rt116x/rt118x) use `…_flexpwm1_pwm0_a` (pwm<K>_<a|b> →
32
+ // A=0/B=1). Their ADC is the LPADC (`lpadc<N>`) and stays out — see
33
+ // dts-reader.ts.
34
+ const IMX_GPIO = /^iomuxc_([a-z0-9_]+)_gpio(\d+)_io(\d+)$/;
35
+ const IMX_ADC = /^iomuxc_([a-z0-9_]+)_adc(\d+)_in(\d+)$/;
36
+ const IMX_PWM = /^iomuxc_([a-z0-9_]+)_flexpwm(\d+)_pwm([ab])(\d+)$/;
37
+ const IMX_PWM2 = /^iomuxc_([a-z0-9_]+)_flexpwm(\d+)_pwm(\d+)_([ab])$/;
38
+ /**
39
+ * Parse a resolved zephyr.dts into raw pin routes. Labels are matched as
40
+ * whole tokens (`label:` or `label {`) — in dtc output every node carries
41
+ * its labels inline before the node name.
42
+ */
43
+ export function parseZephyrDts(text) {
44
+ const facts = { adc: [], pwm: [], dac: [] };
45
+ const imxPadToGpio = new Map();
46
+ const seen = new Set();
47
+ // Every label occurrence: `label:` (label before node) — zephyr.dts
48
+ // renders `label: nodename {`, INDENTED with tabs. Whole-token labels.
49
+ for (const m of text.matchAll(/^\s*([a-z][a-z0-9_]*):\s/gm)) {
50
+ const label = m[1];
51
+ if (seen.has(label))
52
+ continue;
53
+ let g;
54
+ if ((g = label.match(ST_ADC))) {
55
+ seen.add(label);
56
+ // Source: the unit prefix from the name — `adc1_in1…` → adc1, the
57
+ // single-unit spelling `adc_in0…` → adc.
58
+ const unit = label.match(/^([a-z]+?\d*)_in/);
59
+ facts.adc.push({
60
+ source: unit ? unit[1] : 'adc',
61
+ channel: Number(label.match(/_(?:in|inp)(\d+)_/)[1]),
62
+ port: g[2].toUpperCase(),
63
+ bit: Number(g[3]),
64
+ pinctrl: label,
65
+ });
66
+ }
67
+ else if ((g = label.match(ST_PWM))) {
68
+ seen.add(label);
69
+ facts.pwm.push({
70
+ source: `tim${g[2]}`,
71
+ channel: Number(g[3]),
72
+ port: g[4].toUpperCase(),
73
+ bit: Number(g[5]),
74
+ pinctrl: label,
75
+ });
76
+ }
77
+ else if ((g = label.match(ST_PWM_F1))) {
78
+ seen.add(label);
79
+ facts.pwm.push({
80
+ source: `tim${g[2]}`,
81
+ channel: Number(g[3]),
82
+ port: g[4].toUpperCase(),
83
+ bit: Number(g[5]),
84
+ pinctrl: label,
85
+ });
86
+ }
87
+ else if ((g = label.match(ST_DAC))) {
88
+ seen.add(label);
89
+ facts.dac.push({
90
+ source: `dac${g[2]}`,
91
+ channel: Number(g[3]),
92
+ port: g[4].toUpperCase(),
93
+ bit: Number(g[5]),
94
+ pinctrl: label,
95
+ });
96
+ }
97
+ else if ((g = label.match(ST_DAC_F1))) {
98
+ seen.add(label);
99
+ facts.dac.push({
100
+ source: 'dac1',
101
+ channel: Number(g[2]),
102
+ port: g[3].toUpperCase(),
103
+ bit: Number(g[4]),
104
+ pinctrl: label,
105
+ });
106
+ }
107
+ else if ((g = label.match(IMX_GPIO))) {
108
+ seen.add(label);
109
+ if (!imxPadToGpio.has(g[1]))
110
+ imxPadToGpio.set(g[1], { port: g[2], bit: Number(g[3]) });
111
+ }
112
+ }
113
+ // i.MX routes resolve after the join map is complete.
114
+ for (const m of text.matchAll(/^\s*([a-z][a-z0-9_]*):\s/gm)) {
115
+ const label = m[1];
116
+ let g;
117
+ if ((g = label.match(IMX_ADC))) {
118
+ const gpio = imxPadToGpio.get(g[1]);
119
+ if (!gpio || seen.has(label))
120
+ continue;
121
+ seen.add(label);
122
+ facts.adc.push({
123
+ source: `adc${g[2]}`,
124
+ channel: Number(g[3]),
125
+ port: gpio.port,
126
+ bit: gpio.bit,
127
+ pinctrl: label,
128
+ });
129
+ }
130
+ else if ((g = label.match(IMX_PWM))) {
131
+ const gpio = imxPadToGpio.get(g[1]);
132
+ if (!gpio || seen.has(label))
133
+ continue;
134
+ seen.add(label);
135
+ facts.pwm.push({
136
+ source: `flexpwm${g[2]}_pwm${g[4]}`,
137
+ channel: g[3] === 'b' ? 1 : 0,
138
+ port: gpio.port,
139
+ bit: gpio.bit,
140
+ pinctrl: label,
141
+ });
142
+ }
143
+ else if ((g = label.match(IMX_PWM2))) {
144
+ const gpio = imxPadToGpio.get(g[1]);
145
+ if (!gpio || seen.has(label))
146
+ continue;
147
+ seen.add(label);
148
+ facts.pwm.push({
149
+ source: `flexpwm${g[2]}_pwm${g[3]}`,
150
+ channel: g[4] === 'b' ? 1 : 0,
151
+ port: gpio.port,
152
+ bit: gpio.bit,
153
+ pinctrl: label,
154
+ });
155
+ }
156
+ }
157
+ return facts;
158
+ }
159
+ /** Parse + shape-validate .cuttlefish/as-built.json; errors name the file. */
160
+ export function parseAsBuiltJson(text, source = '.cuttlefish/as-built.json') {
161
+ let parsed;
162
+ try {
163
+ parsed = JSON.parse(text);
164
+ }
165
+ catch (err) {
166
+ throw new Error(`${source} is not valid JSON: ${err.message}`);
167
+ }
168
+ const file = parsed;
169
+ if (!file || typeof file !== 'object' || file.version !== 1 || typeof file.board !== 'string'
170
+ || !file.routes || !Array.isArray(file.routes.adcPins) || !Array.isArray(file.routes.pwmPins)) {
171
+ throw new Error(`${source} must carry version 1, a board target, and routes.{adcPins,pwmPins}.`);
172
+ }
173
+ return file;
174
+ }
175
+ /** Serialize the as-built snapshot (deterministic field order). */
176
+ export function asBuiltJson(board, facts) {
177
+ const byPin = (a, b) => a.port.localeCompare(b.port) || a.bit - b.bit || a.channel - b.channel;
178
+ return JSON.stringify({
179
+ version: 1,
180
+ board,
181
+ generatedAt: new Date().toISOString(),
182
+ routes: {
183
+ adcPins: [...facts.adc].sort(byPin),
184
+ pwmPins: [...facts.pwm].sort(byPin),
185
+ dacPins: [...facts.dac].sort(byPin),
186
+ },
187
+ }, null, 1);
188
+ }
@@ -59,7 +59,7 @@ static int32_t __tc_timer_add(void (*cb)(void), int32_t ms, bool repeat) {
59
59
  k_timer_init(&__tc_timer_slots[i].timer, __tc_timer_expiry_fn, nullptr);
60
60
  k_work_init(&__tc_timer_slots[i].work, __tc_timer_work_handler);
61
61
  k_timer_start(&__tc_timer_slots[i].timer, K_MSEC(ms), repeat ? K_MSEC(ms) : K_FOREVER);
62
- return i + 1; // 1-based id (Arduino parity)
62
+ return i + 1; // 1-based id
63
63
  }
64
64
  }
65
65
  return 0; // pool full
@@ -0,0 +1,128 @@
1
+ import type { BoardDataEntry } from '@typecad/cuttlefish/board-catalog';
2
+ export interface GeneratedBoard {
3
+ readonly boardTs: string;
4
+ readonly boardJson: string;
5
+ /** The resolved board record (identifier/name/vendor) — for build notes. */
6
+ readonly board: BoardDataEntry;
7
+ /** Non-fatal notes from generation — user facts shadowing harvested
8
+ * routes (the user won; the harvest said otherwise). */
9
+ readonly warnings?: readonly string[];
10
+ }
11
+ /** Look up a board variant by qualified target or bare board id. */
12
+ export declare function findBoardData(target: string): BoardDataEntry | undefined;
13
+ /** soc name from a qualified target ('board/soc/qual' → 'soc'). */
14
+ export declare function socOfTarget(target: string): string;
15
+ /**
16
+ * nRF SAADC AIN→P0-pad map per SoC — [channel, pad] pairs (see the synthesis
17
+ * block in buildModule for the verification trail). Undefined for SoCs
18
+ * without a verified map (including every /cpunet variant: the nRF5340
19
+ * network core declares no SAADC).
20
+ */
21
+ export declare function nrfSaadcAinPads(soc: string, identifier: string): readonly [number, number][] | undefined;
22
+ /**
23
+ * ESP32 DAC channel→GPIO pad map per SoC — [channel, pad] pairs, silicon-
24
+ * fixed (no pinctrl group; the DAC outputs through the RTC IO mux on a fixed
25
+ * pad). Only the original ESP32 and the ESP32-S2 carry the 8-bit DAC; the
26
+ * S3/C3/C6/H2 dropped it. Sourced from the HAL's dac_periph.c
27
+ * (`dac_channel_io_num[]`), like the nRF SAADC table is from the PS.
28
+ */
29
+ export declare function esp32DacPins(soc: string): readonly [number, number][] | undefined;
30
+ /**
31
+ * Parse a datasheet pin name into (controller nodelabel, controller-relative
32
+ * bit) per the soc's naming family — the inverse of the sweep naming:
33
+ * nRF P0.28 / P0_28 → gpio0.28
34
+ * RP2040 GP25 → gpio0.25
35
+ * ESP32 GPIO9 → gpio0.9 (GPIO32+ → gpio1.x)
36
+ * letters PA5 / PB6 → gpioa.5 / gpiob.6
37
+ * flat GPIO9 (non-esp32 socs, single flat controller) → gpio.9
38
+ */
39
+ export declare function parsePinName(soc: string, name: string): {
40
+ controller: string;
41
+ pin: number;
42
+ } | undefined;
43
+ /**
44
+ * Build the board module contents from one board record. This is the one
45
+ * builder for every kind of board: catalog records (from the local overlay
46
+ * generated off the installed SDK's tree) and contract records (custom PCB
47
+ * specs) both route through here.
48
+ */
49
+ /** One board's user-declared facts — the `boards['<target>']` section. */
50
+ export interface UserBoardFacts {
51
+ adc?: {
52
+ /** DT nodelabel of the ADC device (default: the board's primary, or 'adc'). */
53
+ device?: string;
54
+ channels: {
55
+ pin: number;
56
+ channel: number;
57
+ pinctrl?: string;
58
+ }[];
59
+ };
60
+ pwm?: {
61
+ /** Static specs — pin→controller/channel. Suppresses the auto nRF matrix. */
62
+ specs: {
63
+ pin: number;
64
+ controller: string;
65
+ channel: number;
66
+ pinctrl?: string;
67
+ }[];
68
+ };
69
+ dac?: {
70
+ device: string;
71
+ channels: {
72
+ pin: number;
73
+ channel: number;
74
+ resolution?: number;
75
+ }[];
76
+ };
77
+ }
78
+ /** The whole cuttlefish.facts.json shape. */
79
+ export interface UserFactsFile {
80
+ boards: Record<string, UserBoardFacts>;
81
+ }
82
+ /** Parse + shape-validate the facts file; a clear error names the file. */
83
+ export declare function parseUserFactsJson(text: string, source?: string): UserFactsFile;
84
+ /** The section matching a target — the same exact → lowercase → prefix
85
+ * leniency findBoardData applies to catalog keys. */
86
+ export declare function userFactsForTarget(file: UserFactsFile | undefined, target: string): UserBoardFacts | undefined;
87
+ export declare function buildModule(entry: BoardDataEntry, userFacts?: UserBoardFacts, factsSuffix?: string, seedWarnings?: readonly string[]): GeneratedBoard;
88
+ /**
89
+ * Generate the board module contents for a qualified Zephyr board target
90
+ * ('esp32s3_devkitc/esp32s3/procpu'). Bare SoC names are not targets —
91
+ * every board resolves through the catalog like any other.
92
+ */
93
+ export declare function generateBoard(target: string, opts?: {
94
+ factsJson?: string;
95
+ asBuiltJson?: string;
96
+ }): GeneratedBoard;
97
+ /**
98
+ * Generate the board module for a CONTRACT board — a custom PCB with no
99
+ * Zephyr board record. The contract (exported from a TypeCAD project)
100
+ * supplies the wired pads by datasheet name and which bus families the PCB
101
+ * routes; the SoC's bus controller nodelabels come from the INSTALLED
102
+ * Zephyr tree's soc dtsi (the SDK is the source of truth — a contract build
103
+ * compiles against that tree).
104
+ *
105
+ * Returns a full board module (the transpiler's pin map + chip resolution
106
+ * read board.json). The caller layers the narrowed board.ts on top, so the
107
+ * firmware can only touch pads the PCB actually wired.
108
+ */
109
+ export declare function generateBoardModuleFromContract(opts: {
110
+ soc: string;
111
+ zephyrBase: string;
112
+ /** Wired pad names in the soc's datasheet form (PA5, P0.28, GP25...). */
113
+ pinNames: readonly string[];
114
+ /** Functional names to export as aliases of a wired pad (TX → PA9). */
115
+ padAliases?: readonly {
116
+ exportName: string;
117
+ padName: string;
118
+ }[];
119
+ /** Which bus families the PCB routes. */
120
+ peripherals: {
121
+ i2c: boolean;
122
+ spi: boolean;
123
+ uart: boolean;
124
+ };
125
+ }): {
126
+ boardTs: string;
127
+ boardJson: string;
128
+ };