@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/README.md CHANGED
@@ -24,86 +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
- - Supported board targets: **Seeed Studio XIAO nRF52840** (`xiao_ble`),
39
- **ESP32 DevKit** (`esp32_devkitc`), **ESP32-S3 DevKit** (`esp32s3_devkitc`),
40
- **ESP32-C3 DevKitM** (`esp32c3_devkitm/esp32c3`), **ESP32-C6 DevKitC**
41
- (`esp32c6_devkitc/esp32c6/hpcore` the qualified form is required, the
42
- board also ships an lpcore variant), **Raspberry Pi Pico** (`rpi_pico`),
43
- **Pico 2** (`rpi_pico2/rp2350a/m33`), and **WeAct Black Pill V2.0**
44
- (`blackpill_f411ce/stm32f411xe` — the first STM32 target; flashed over USB
45
- via the ROM DFU bootloader). The rpi_pico, esp32c3, esp32c6, and blackpill
46
- boards resolve their chip data from the board packages' `zephyr` field (see
47
- `resolveChipFromBoard`) rather than the hardcoded chip registry; the
48
- qualified Pico 2 target selects the Cortex-M33 cpucluster (Zephyr 4.3+
49
- rejects the bare `rpi_pico2` because the board also ships a Hazard3 variant
50
- with no default).
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).
51
43
 
52
44
  ## Installation
53
45
 
54
46
  ```sh
55
- 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
56
48
  ```
57
49
 
58
50
  Or in `cuttlefish.config.ts`:
59
51
 
60
52
  ```ts
61
53
  export default {
54
+ entry: './src/main.ts',
55
+ board: 'xiao_ble/nrf52840',
62
56
  framework: '@typecad/framework-zephyr',
63
- frameworkData: {
64
- buildTarget: 'xiao_ble',
65
- },
66
- toolchain: { type: 'west' },
67
57
  };
68
58
  ```
69
59
 
70
60
  ## HAL coverage
71
61
 
72
62
  The manifest (`src/framework.manifest.ts`) is the honest, complete record of
73
- what lowers. Fully supported: **gpio, pwm, adc, i2c, spi, uart, wdt, ble,
74
- shift, board constants** (Board.definition.* / Pins.definition.* folding),
75
- **random** (xorshift32 PRNG seeded from the Zephyr entropy tap).
76
- Partial (some ops lower, some are deferred stubs): **timing, power,
77
- interrupts, tone, pulse**. Unsupported for this target: **wifi** (nRF52840 has
78
- no WiFi), **http** (no networking stack lowered), **display** (deferred),
79
- **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.
80
72
 
81
73
  Run `npm test` (the manifest validator) to confirm the declared coverage matches
82
74
  the actual lowering behavior.
83
75
 
84
- ## Hardware tests
85
-
86
- On-device tests live in `tests/`. Run them against real hardware:
87
-
88
- ```sh
89
- npm run test:hw # all groups
90
- npm run test:hw:gpio # just GPIO
91
- npm run test:hw:timers # just timing
92
- ```
93
-
94
- Update `test.port` in `cuttlefish.config.ts` to your XIAO's serial device
95
- before running. The tests transpile → `west build` → flash → read results over
96
- the console.
97
-
98
76
  ## Notes / limitations
99
77
 
100
78
  - **Minimal C++ libc.** Zephyr's default C++ support (`lib/cpp/minimal`) has no
101
79
  `<vector>`, `<string>`, `<iostream>`, `<functional>`, exceptions, or RTTI.
102
80
  `prj.conf` enables `CONFIG_NEWLIB_LIBC` for `std::string`/`std::vector`; array
103
81
  literals still promote to the StaticArray wrapper, not `std::vector`.
104
- - **Timer ops unsupported.** `timing.set_interval`/`set_timeout`/`clear_*` need
105
- a polyfill-backed async runtime this framework does not emit yet. A Zephyr
106
- workqueue / `k_thread` backing is a follow-on.
107
82
  - **Debug mode.** `cuttlefish build --debug` routes through `printk` (always
108
83
  available, no `CONFIG_CONSOLE` dependency) rather than `std::cout`, since the
109
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
+ };