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

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 (42) hide show
  1. package/dist/display/index.d.ts +1 -1
  2. package/dist/display/index.js +1 -1
  3. package/dist/display/profiles.d.ts +33 -0
  4. package/dist/display/profiles.js +36 -0
  5. package/dist/display/touch-adapter.d.ts +3 -4
  6. package/dist/display/touch-adapter.js +119 -16
  7. package/dist/display/ui-adapter.d.ts +4 -0
  8. package/dist/display/ui-adapter.js +348 -139
  9. package/dist/dt-config/kconfig.d.ts +5 -1
  10. package/dist/dt-config/kconfig.js +22 -5
  11. package/dist/dt-config/overlay.d.ts +26 -2
  12. package/dist/dt-config/overlay.js +138 -27
  13. package/dist/framework.manifest.d.ts +29 -28
  14. package/dist/framework.manifest.js +9 -3
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +5 -0
  17. package/dist/lowering/ble.js +3 -1
  18. package/dist/lowering/gpio.js +7 -3
  19. package/dist/strategy.d.ts +24 -0
  20. package/dist/strategy.js +326 -134
  21. package/dist/toolchain/debug-config.d.ts +43 -2
  22. package/dist/toolchain/debug-config.js +129 -17
  23. package/dist/toolchain/index.d.ts +13 -0
  24. package/dist/toolchain/index.js +104 -23
  25. package/dist/toolchain/scaffold.js +40 -18
  26. package/dist/toolchain/west-discover.js +4 -1
  27. package/package.json +4 -4
  28. package/src/display/index.ts +1 -1
  29. package/src/display/profiles.ts +63 -0
  30. package/src/display/touch-adapter.ts +119 -15
  31. package/src/display/ui-adapter.ts +357 -139
  32. package/src/dt-config/kconfig.ts +26 -6
  33. package/src/dt-config/overlay.ts +450 -298
  34. package/src/framework.manifest.ts +9 -3
  35. package/src/index.ts +6 -0
  36. package/src/lowering/ble.ts +3 -1
  37. package/src/lowering/gpio.ts +7 -3
  38. package/src/strategy.ts +355 -136
  39. package/src/toolchain/debug-config.ts +137 -14
  40. package/src/toolchain/index.ts +107 -24
  41. package/src/toolchain/scaffold.ts +39 -16
  42. package/src/toolchain/west-discover.ts +4 -1
@@ -44,15 +44,32 @@ export function resolveKconfigFragments(usage, debug) {
44
44
  // configured SPI clock (~80MHz) and drops into the low tens of ms. The
45
45
  // display overlay pairs this with dma-enabled + dmas on the spi2 node.
46
46
  m.set('CONFIG_DMA', 'y');
47
- // Disable the MIPI DBI SPI bridge + ST7796S drivers. The display adapter
48
- // drives the panel directly via spi_write. Binding these drivers would
49
- // allocate a tearing-effect GPIO interrupt that conflicts with the SPI/I2C
50
- // driver interrupts the VECDESC_FL_SHARED assertion crashes on touch.
47
+ // Disable the MIPI DBI SPI bridge + in-tree panel drivers (ILI9341,
48
+ // ST7796S). The display adapter drives the panel directly via spi_write.
49
+ // Binding these drivers would allocate a tearing-effect GPIO interrupt
50
+ // that conflicts with the SPI/I2C driver interrupts the
51
+ // VECDESC_FL_SHARED assertion crashes on touch. ILI9341 matters as much
52
+ // as the bridge: the driver auto-defaults on from the overlay's
53
+ // ilitek,ili9341 node and references the (disabled) mipi-dbi-spi
54
+ // controller's device struct, failing at link time with
55
+ // "undefined reference to __device_dts_ord_N". (Assign the prompted
56
+ // ILI9341, not the hidden ILI9XXX — promptless symbols reject prj.conf
57
+ // assignments.)
51
58
  m.set('CONFIG_MIPI_DBI_SPI', 'n');
59
+ m.set('CONFIG_ILI9341', 'n');
52
60
  m.set('CONFIG_ST7796S', 'n');
53
61
  }
54
62
  if (usage.usesTouch) {
55
- m.set('CONFIG_I2C', 'y'); // FT6336U touch on I2C
63
+ // FT6336U touch is on I2C; the XPT2046 shares the display's SPI bus.
64
+ // CONFIG_INPUT stays off either way: the adapters drive the controllers
65
+ // directly, and enabling it would build the in-tree input drivers
66
+ // (ft5336 / xpt2046) against nodes these adapters already own.
67
+ if (usage.touchController === 'xpt2046') {
68
+ m.set('CONFIG_SPI', 'y');
69
+ }
70
+ else {
71
+ m.set('CONFIG_I2C', 'y');
72
+ }
56
73
  }
57
74
  // PSRAM: enable the ESP SPIRAM driver + route malloc/heap to external RAM so
58
75
  // large canvas allocations (scroll viewports, lists) can use PSRAM instead of
@@ -10,6 +10,9 @@ export interface DisplayWiring {
10
10
  cs?: number;
11
11
  dc?: number;
12
12
  rst?: number;
13
+ /** Tearing-effect (TE) GPIO from display.tearingEffectPin — emitted as
14
+ * te-gpios on the display DT node. Opt-in; most boards don't wire TE. */
15
+ tearingEffectPin?: number;
13
16
  spiFrequency?: number;
14
17
  /** SPI bus pins. When present, the overlay remuxes the SPI controller's
15
18
  * pinctrl to these pins (the board defaults rarely match a breakout's
@@ -25,11 +28,32 @@ export interface DisplayWiring {
25
28
  * backlight init is guarded by `DT_HAS_ALIAS` and compiles away. */
26
29
  backlightPin?: number;
27
30
  }
28
- /** Touch wiring from cuttlefish.config.ts (irq/resetPin/sda/scl). */
31
+ /** Touch wiring from cuttlefish.config.ts (irq/resetPin/sda/scl; cs for SPI
32
+ * resistive controllers, calibration for the XPT2046 DT binding). */
29
33
  export interface TouchWiring {
34
+ /** Touch controller kind — selects the DT node shape (FT6336U node on I2C0
35
+ * vs XPT2046 node on the display's SPI bus). Default 'ft6336u'. */
36
+ controller?: 'ft6336u' | 'xpt2046';
30
37
  irq?: number;
31
38
  resetPin?: number;
32
39
  sda?: number;
33
40
  scl?: number;
41
+ /** XPT2046 only: SPI CS pin (second cs-gpios entry on the panel's bus). */
42
+ cs?: number;
43
+ /** XPT2046 only: raw ADC calibration — feeds the binding's min-x/max-x/
44
+ * min-y/max-y (required props). Defaults span the full 12-bit range. */
45
+ calibration?: {
46
+ xMin: number;
47
+ xMax: number;
48
+ yMin: number;
49
+ yMax: number;
50
+ };
51
+ /** XPT2046 only: pen-detect Z1 threshold (binding's z-threshold). Resistive
52
+ * panels need a few hundred 12-bit counts; default 400. */
53
+ minPressure?: number;
54
+ }
55
+ export interface OverlayDiagnostic {
56
+ severity: "warning" | "error";
57
+ message: string;
34
58
  }
35
- export declare function generateOverlay(chip: ZephyrChipDescriptor, usage: KconfigUsage, display: ZephyrDisplayProfile | undefined, wiring?: DisplayWiring, touch?: TouchWiring): string;
59
+ export declare function generateOverlay(chip: ZephyrChipDescriptor, usage: KconfigUsage, display: ZephyrDisplayProfile | undefined, wiring?: DisplayWiring, touch?: TouchWiring, diagnostics?: OverlayDiagnostic[]): string;
@@ -10,7 +10,19 @@
10
10
  // that brings external capabilities into a build. (Arduino does it by parsing
11
11
  // library headers into .d.ts; Zephyr does it by enabling DT nodes + Kconfig.)
12
12
  // ---------------------------------------------------------------------------
13
- export function generateOverlay(chip, usage, display, wiring, touch) {
13
+ import { PANEL_CONTROLLER_DEFAULTS, panelControllerFor } from '../display/profiles.js';
14
+ export function generateOverlay(chip, usage, display, wiring, touch, diagnostics = []) {
15
+ // An I2C touch controller with no explicit bus pins: the overlay enables
16
+ // i2c0 and instantiates the node, but nothing remuxes the controller to
17
+ // the wired SDA/SCL (the board's default I2C pins rarely match a
18
+ // breakout). Every I2C read then fails and touch silently does nothing —
19
+ // surface it at build time instead of leaving it to a multimeter.
20
+ if (touch && touch.controller !== 'xpt2046' && (touch.sda === undefined || touch.scl === undefined)) {
21
+ diagnostics.push({
22
+ severity: "warning",
23
+ message: `touch: I2C controller '${touch.controller}' has no sda/scl pins in cuttlefish.config.ts — the overlay enables the bus without a pin assignment, so the controller may never answer. Add touch.sda and touch.scl (the board's default I2C pins are rarely the wired ones).`,
24
+ });
25
+ }
14
26
  const lines = [
15
27
  '/* Auto-generated by @typecad/framework-zephyr from cuttlefish.config.ts. */',
16
28
  '/* Enables peripherals the program uses. West merges this over the board DT. */',
@@ -53,14 +65,18 @@ export function generateOverlay(chip, usage, display, wiring, touch) {
53
65
  // controller with the panel's compatible string + dimensions. The pin
54
66
  // wiring (cs/dc/rst) uses ESP32 GPIO defaults from the demo config; a
55
67
  // real board overlay would carry its own binding.
56
- emitDisplayNode(lines, display, wiring);
68
+ // An XPT2046 on the same bus needs its CS as the second cs-gpios entry,
69
+ // so thread it into the display block (DT assignment replaces the whole
70
+ // property — both entries must be written together).
71
+ emitDisplayNode(lines, display, wiring, touch?.controller === 'xpt2046' ? (touch?.cs ?? DEFAULT_XPT2046_CS) : undefined);
57
72
  }
58
- // FT6336U touch on I2C defined when the program uses touch (the UI touch
59
- // adapter references DT_NODELABEL(ft6336u)). Same rationale: the bare devkit
60
- // has no such node. Gated on usesTouch (not usesI2c/chip.i2c) so it emits
61
- // even when the chip descriptor doesn't declare I2C controllers (ESP32).
73
+ // Touch — FT6336U capacitive on I2C (references DT_NODELABEL(ft6336u)) or
74
+ // XPT2046 resistive on the display's SPI bus (references
75
+ // DT_NODELABEL(xpt2046)). Same rationale as the display node: the bare
76
+ // devkit has no such node. Gated on usesTouch (not usesI2c/chip.i2c) so it
77
+ // emits even when the chip descriptor doesn't declare bus controllers.
62
78
  if (usage.usesTouch) {
63
- emitTouchNode(lines, touch);
79
+ emitTouchNode(lines, touch, display);
64
80
  }
65
81
  // Preferences (ZMS settings backend): point the settings subsystem at the
66
82
  // board's storage_partition. The backend looks for /chosen
@@ -91,11 +107,21 @@ export function generateOverlay(chip, usage, display, wiring, touch) {
91
107
  return lines.join('\n');
92
108
  }
93
109
  /**
94
- * Emit a display DT node definition. The node is attached to spi2 (the ESP32's
95
- * first user SPI controller) via a MIPI DBI SPI bridge. Pin wiring comes from
96
- * the display config (cs/dc/rst); defaults match the demo-st wiring if absent.
110
+ * Emit a display DT node definition. The node is attached to the profile's SPI
111
+ * controller (default spi2, the ESP32's first user SPI controller) via a MIPI
112
+ * DBI SPI bridge. Pin wiring comes from the display config (cs/dc/rst);
113
+ * defaults match the demo-st wiring if absent. The compatible string + node
114
+ * props come from the profile's panel controller (st7796s carries the required
115
+ * pgc/ngc gamma + madctl; ili9341's binding defaults everything else).
116
+ *
117
+ * spiTouchCs: when an XPT2046 SPI touch controller shares the bus, its CS is
118
+ * appended as the second cs-gpios entry (the touch node uses reg = <1>) — DT
119
+ * property assignment replaces, so both entries must be written together.
97
120
  */
98
- function emitDisplayNode(lines, display, wiring) {
121
+ function emitDisplayNode(lines, display, wiring, spiTouchCs) {
122
+ const bus = display.busLabel ?? 'spi2';
123
+ const controller = panelControllerFor(display);
124
+ const compatible = display.dtCompatible ?? PANEL_CONTROLLER_DEFAULTS[controller].dtCompatible;
99
125
  const dc = wiring?.dc ?? 17;
100
126
  const rst = wiring?.rst ?? 16;
101
127
  const cs = wiring?.cs ?? 5;
@@ -116,7 +142,9 @@ function emitDisplayNode(lines, display, wiring) {
116
142
  // macros are used instead of the named SPIM2_*_GPIOxx tokens because the
117
143
  // bindings header omits GPIOs 22-25 from those lists.
118
144
  if (sck !== undefined && mosi !== undefined) {
119
- lines.push('&spim2_default {');
145
+ // 'spi2' → pinctrl group 'spim2_default' (ESP32 SPI-master naming).
146
+ const pinctrlGroup = bus.replace(/^spi(\d)$/, 'spim$1') + '_default';
147
+ lines.push(`&${pinctrlGroup} {`);
120
148
  lines.push(' group1 {');
121
149
  lines.push(` pinmux = <ESP32_PINMUX(${miso ?? 19}, ESP_FSPIQ_IN, ESP_NOSIG)>,`);
122
150
  lines.push(` <ESP32_PINMUX(${sck}, ESP_NOSIG, ESP_FSPICLK_OUT)>,`);
@@ -133,9 +161,9 @@ function emitDisplayNode(lines, display, wiring) {
133
161
  lines.push(' status = "okay";');
134
162
  lines.push('};');
135
163
  lines.push('');
136
- lines.push('&spi2 {');
164
+ lines.push(`&${bus} {`);
137
165
  lines.push(' status = "okay";');
138
- lines.push(` cs-gpios = <&${gpioController(cs)} ${cs} GPIO_ACTIVE_LOW>;`);
166
+ lines.push(` cs-gpios = <&${gpioController(cs)} ${cs} GPIO_ACTIVE_LOW>${spiTouchCs !== undefined ? `, <&${gpioController(spiTouchCs)} ${spiTouchCs} GPIO_ACTIVE_LOW>` : ''};`);
139
167
  // Enable GDMA for the SPI2 host. The ESP32 SPI driver uses DMA only when
140
168
  // dma-enabled is set AND dmas wires tx/rx channels to the GDMA controller;
141
169
  // without it, transfers run PIO through the 64-byte FIFO (~4MHz effective at
@@ -151,25 +179,44 @@ function emitDisplayNode(lines, display, wiring) {
151
179
  lines.push('/ {');
152
180
  lines.push(' mipi_dbi: mipi-dbi {');
153
181
  lines.push(' compatible = "zephyr,mipi-dbi-spi";');
154
- lines.push(' spi-dev = <&spi2>;');
182
+ lines.push(` spi-dev = <&${bus}>;`);
155
183
  lines.push(` dc-gpios = <&${gpioController(dc)} ${dc} GPIO_ACTIVE_HIGH>;`);
156
184
  lines.push(` reset-gpios = <&${gpioController(rst)} ${rst} GPIO_ACTIVE_LOW>;`);
157
185
  lines.push(' write-only;');
158
186
  lines.push(' #address-cells = <1>;');
159
187
  lines.push(' #size-cells = <0>;');
160
188
  lines.push(` ${display.dtLabel}: display@0 {`);
161
- lines.push(' compatible = "sitronix,st7796s";');
189
+ lines.push(` compatible = "${compatible}";`);
162
190
  lines.push(' reg = <0>;');
191
+ if (wiring?.tearingEffectPin !== undefined) {
192
+ const tePin = wiring.tearingEffectPin;
193
+ // Tearing-effect input on the display node: GPIO_DT_SPEC_GET(
194
+ // DT_NODELABEL(display0), te_gpios) in the adapter. Opt-in —
195
+ // most modules don't break the TE pad out.
196
+ lines.push(` te-gpios = <&${gpioController(tePin)} ${tePin} GPIO_ACTIVE_HIGH>;`);
197
+ }
163
198
  lines.push(` mipi-max-frequency = <${freq}>;`);
164
199
  lines.push(' mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE";');
200
+ // Required by the lcd-controller binding (Zephyr 4.x): 0 = RGB565,
201
+ // matching upstream ILI9341 boards (esp_wrover_kit) and the C++
202
+ // runtime, which drives these SPI TFTs as RGB565.
203
+ lines.push(' pixel-format = <0>;');
165
204
  lines.push(` width = <${nativeW}>;`);
166
205
  lines.push(` height = <${nativeH}>;`);
167
- // MADCTL: rotation 1 (landscape, MV=1) + BGR bit, matching the adapter's
168
- // direct-drive init (0x28). The DT copy keeps the stock driver's init
169
- // consistent if it is ever exercised.
170
- lines.push(' madctl = <0x28>;');
171
- lines.push(' pgc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
172
- lines.push(' ngc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
206
+ if (controller === 'st7796s') {
207
+ // MADCTL: rotation 1 (landscape, MV=1) + BGR bit, matching the adapter's
208
+ // direct-drive init (0x28). The DT copy keeps the stock driver's init
209
+ // consistent if it is ever exercised.
210
+ lines.push(' madctl = <0x28>;');
211
+ lines.push(' pgc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
212
+ lines.push(' ngc = [f0 09 0b 06 04 2e 46 46 39 13 15 12 15 12];');
213
+ }
214
+ else {
215
+ // ILI9341: the ilitek,ili9341 binding carries defaults for every register
216
+ // (gamma, power, porch) and expresses orientation via `rotation` (degrees)
217
+ // instead of a raw MADCTL — no panel-specific props are required.
218
+ lines.push(` rotation = <${display.rotation ?? 0}>;`);
219
+ }
173
220
  lines.push(' };');
174
221
  lines.push(' };');
175
222
  lines.push('};');
@@ -195,13 +242,33 @@ function emitDisplayNode(lines, display, wiring) {
195
242
  lines.push('');
196
243
  }
197
244
  }
245
+ /** Default XPT2046 CS/IRQ pins (ESP32-S3 GPIOs clear of the demo-st display
246
+ * wiring: 5/17/16/15 and the remuxed SPI pins). Config values override. */
247
+ const DEFAULT_XPT2046_CS = 6;
248
+ const DEFAULT_XPT2046_IRQ = 7;
198
249
  /**
199
- * Emit an FT6336U touch DT node on the first I2C controller. The node address
200
- * is the FT6336U default (0x38). The UI touch adapter references
201
- * DT_NODELABEL(ft6336u). Pin wiring (irq/resetPin/sda/scl) comes from the
202
- * touch config; defaults match the demo-st wiring if absent.
250
+ * Emit the touch DT node for the configured controller.
251
+ *
252
+ * FT6336U (capacitive, I2C): node on i2c0 at the FT6336U default address
253
+ * (0x38); the C++ touch adapter reads it via i2c_write_read_dt.
254
+ *
255
+ * XPT2046 (resistive, SPI): node on the display's SPI bus as CS index 1. The
256
+ * in-tree xptek,xpt2046 binding (drivers/input) is register-matched for the
257
+ * raw SPI access — CONFIG_INPUT stays off, so the in-tree input driver does
258
+ * not build and the adapter owns the chip (same pattern as FT6336U reusing
259
+ * the ft5336 binding). The binding requires int-gpios, touchscreen-size-*,
260
+ * and min/max calibration props, so defaults are filled for anything the
261
+ * config omits.
203
262
  */
204
- function emitTouchNode(lines, touch) {
263
+ function emitTouchNode(lines, touch, display) {
264
+ if (touch?.controller === 'xpt2046') {
265
+ emitXpt2046Node(lines, touch, display);
266
+ return;
267
+ }
268
+ emitFt6336uNode(lines, touch);
269
+ }
270
+ /** FT6336U capacitive touch node on the first I2C controller. */
271
+ function emitFt6336uNode(lines, touch) {
205
272
  const irq = touch?.irq ?? 15;
206
273
  const resetPin = touch?.resetPin;
207
274
  const sda = touch?.sda;
@@ -249,3 +316,47 @@ function emitTouchNode(lines, touch) {
249
316
  lines.push('};');
250
317
  lines.push('');
251
318
  }
319
+ /** XPT2046 resistive touch node on the display's SPI bus (CS index 1). */
320
+ function emitXpt2046Node(lines, touch, display) {
321
+ const bus = display?.busLabel ?? 'spi2';
322
+ const cs = touch.cs ?? DEFAULT_XPT2046_CS;
323
+ const irq = touch.irq ?? DEFAULT_XPT2046_IRQ;
324
+ const cal = touch.calibration;
325
+ // ESP32-S3 GPIOs 0-31 are on gpio0, 32-48 on gpio1.
326
+ const gpioController = (pin) => pin <= 31 ? 'gpio0' : 'gpio1';
327
+ const zThreshold = touch.minPressure ?? 400;
328
+ // touchscreen-size-* describe the panel the touch layer sits on (the display
329
+ // profile's effective size); a touch-only build falls back to the 12-bit
330
+ // full-scale range so the binding's required props still resolve.
331
+ const sizeX = display?.width ?? cal?.xMax ?? 320;
332
+ const sizeY = display?.height ?? cal?.yMax ?? 240;
333
+ // The display block already wrote cs-gpios with both entries (its CS at
334
+ // index 0, the touch CS at index 1). When there is no display block, enable
335
+ // the bus here with the touch CS as the only entry.
336
+ if (!display) {
337
+ lines.push(`&${bus} {`);
338
+ lines.push(' status = "okay";');
339
+ lines.push(` cs-gpios = <&${gpioController(cs)} ${cs} GPIO_ACTIVE_LOW>;`);
340
+ lines.push('};');
341
+ lines.push('');
342
+ }
343
+ lines.push(`&${bus} {`);
344
+ lines.push(' xpt2046: xpt2046@1 {');
345
+ lines.push(' compatible = "xptek,xpt2046";');
346
+ lines.push(' reg = <1>;');
347
+ // The XPT2046 datasheet max SPI clock is 2.5MHz — the panel bus may run at
348
+ // 80MHz, but this node's spi-max-frequency gates only its own transactions
349
+ // (the adapter's SPI_DT_SPEC picks it up).
350
+ lines.push(' spi-max-frequency = <2500000>;');
351
+ lines.push(` int-gpios = <&${gpioController(irq)} ${irq} GPIO_ACTIVE_LOW>;`);
352
+ lines.push(` touchscreen-size-x = <${sizeX}>;`);
353
+ lines.push(` touchscreen-size-y = <${sizeY}>;`);
354
+ lines.push(` min-x = <${cal?.xMin ?? 0}>;`);
355
+ lines.push(` max-x = <${cal?.xMax ?? 4095}>;`);
356
+ lines.push(` min-y = <${cal?.yMin ?? 0}>;`);
357
+ lines.push(` max-y = <${cal?.yMax ?? 4095}>;`);
358
+ lines.push(` z-threshold = <${zThreshold}>;`);
359
+ lines.push(' };');
360
+ lines.push('};');
361
+ lines.push('');
362
+ }
@@ -1,40 +1,40 @@
1
1
  declare const _default: {
2
+ hal: {
3
+ [x: string]: {
4
+ supported: boolean;
5
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
6
+ partialCoverage: boolean;
7
+ unsupportedReason?: string | undefined;
8
+ };
9
+ raw?: unknown;
10
+ } & {
11
+ [k: string]: {
12
+ supported: boolean;
13
+ ops: Record<string, "stub" | "polyfill" | "supported" | "unsupported" | "probe-inconclusive">;
14
+ partialCoverage: boolean;
15
+ unsupportedReason?: string | undefined;
16
+ };
17
+ };
18
+ packageName: string;
2
19
  schemaVersion: 1;
3
20
  frameworkId: string;
4
- packageName: string;
5
21
  canonical: boolean;
6
22
  displayName: string;
7
23
  description: string;
8
24
  implementationMode: "from-scratch" | "extends-canonical" | "extends-other";
9
25
  entrypoint: {
26
+ sourceExtension: "cpp" | "ino" | "cc" | "h";
10
27
  entrypointFunctionName: string;
11
28
  requiresLoopFunction: boolean;
12
- sourceExtension: "ino" | "cc" | "cpp" | "h";
13
29
  generateHeaderFile: boolean;
14
30
  overrideBaseName?: string | undefined;
15
31
  outputSubdirectory?: string | undefined;
16
32
  customBridgeShim?: string | undefined;
17
33
  };
18
34
  profile: {
19
- targets: string[];
20
35
  forcedIncludes: string[];
21
36
  symbolAliases: Record<string, string>;
22
- };
23
- hal: {
24
- [x: string]: {
25
- supported: boolean;
26
- ops: Record<string, "supported" | "stub" | "unsupported" | "probe-inconclusive" | "polyfill">;
27
- partialCoverage: boolean;
28
- unsupportedReason?: string | undefined;
29
- };
30
- raw?: unknown;
31
- } & {
32
- [k: string]: {
33
- supported: boolean;
34
- ops: Record<string, "supported" | "stub" | "unsupported" | "probe-inconclusive" | "polyfill">;
35
- partialCoverage: boolean;
36
- unsupportedReason?: string | undefined;
37
- };
37
+ targets: string[];
38
38
  };
39
39
  polyfills: {
40
40
  emitted: {
@@ -54,16 +54,17 @@ declare const _default: {
54
54
  compile: boolean;
55
55
  upload: boolean;
56
56
  monitor: boolean;
57
+ debug?: boolean | undefined;
57
58
  };
58
59
  reexportedFrom?: string | undefined;
59
60
  };
60
61
  typeEmission: {
61
- normalizeCppType: boolean;
62
- mathHeader: "none" | "<math.h>" | "<Arduino.h>";
62
+ needsIostream: boolean;
63
63
  needsStdString: boolean;
64
64
  needsStdVector: boolean;
65
- needsIostream: boolean;
66
65
  needsStdFunction: boolean;
66
+ mathHeader: "none" | "<math.h>" | "<Arduino.h>";
67
+ normalizeCppType: boolean;
67
68
  stdlibSupport: {
68
69
  hasVector: boolean;
69
70
  hasString: boolean;
@@ -79,6 +80,12 @@ declare const _default: {
79
80
  hardwareTestGroups: string[];
80
81
  halResolutionTests: string[];
81
82
  };
83
+ doctor?: {
84
+ available: boolean;
85
+ } | undefined;
86
+ licenses?: {
87
+ available: boolean;
88
+ } | undefined;
82
89
  basedOn?: string | undefined;
83
90
  inheritsStrategyId?: string | undefined;
84
91
  libraryResolution?: {
@@ -88,12 +95,6 @@ declare const _default: {
88
95
  tryGenerateLibDecl: boolean;
89
96
  reexportedFrom?: string | undefined;
90
97
  } | undefined;
91
- doctor?: {
92
- available: boolean;
93
- } | undefined;
94
- licenses?: {
95
- available: boolean;
96
- } | undefined;
97
98
  compat?: {
98
99
  zephyr?: string | undefined;
99
100
  } | undefined;
@@ -280,8 +280,13 @@ export default defineFrameworkManifest({
280
280
  },
281
281
  display: {
282
282
  supported: true,
283
- partialCoverage: false,
284
- unsupportedReason: undefined,
283
+ partialCoverage: true,
284
+ // Partial: mono profiles (ssd1306-zephyr) drive display.* ops via the
285
+ // direct GFX runtime only — no CuttlefishGFX UI rendering path. The
286
+ // ILI9341 UI adapter shares the ST7796S direct-drive transport with a
287
+ // per-controller init table (16-bit RGB565 wire format); hardware-tuned
288
+ // on ST7796S only. E-ink panels are out of scope at this time.
289
+ unsupportedReason: 'Mono panels (ssd1306) are direct-op only (no UI rendering); ili9341 UI path is ported but not yet hardware-verified; e-ink is out of scope at this time.',
285
290
  drivers: ['ili9341-zephyr', 'st7796-zephyr', 'ssd1306-zephyr'],
286
291
  colorFormat: 'rgb565',
287
292
  ops: {
@@ -484,6 +489,7 @@ export default defineFrameworkManifest({
484
489
  polyfills: {
485
490
  emitted: [
486
491
  { id: 'cuttlefish_halt', domain: 'standard', notes: 'Mapped to a k_msleep halt loop (exceptions disabled)' },
492
+ { id: 'wiring_compat', domain: 'standard', notes: 'HIGH/LOW/digitalRead/etc. macros routing Wiring tokens (referenced unconditionally by the UI runtime header) to the __tc_gpio_* helpers' },
487
493
  { id: 'string_methods', domain: 'embedded', notes: 'STL-free __tc_* string helpers (const char*, inline ASCII case conv, <cstring> only)' },
488
494
  { id: 'static_array', domain: 'embedded', notes: 'STL-free __tc_StaticArray<T,N> wrapper for no-<vector> mutated/struct array literals' },
489
495
  { id: 'timer_methods', domain: 'embedded', notes: 'k_timer + k_work pool (system workqueue); callbacks run in thread context' },
@@ -493,7 +499,7 @@ export default defineFrameworkManifest({
493
499
  },
494
500
  toolchain: {
495
501
  backend: 'west',
496
- operations: { prepare: true, compile: true, upload: true, monitor: true },
502
+ operations: { prepare: true, compile: true, upload: true, monitor: true, debug: true },
497
503
  },
498
504
  libraryResolution: {
499
505
  isFrameworkLibraryImport: false,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { ZephyrStrategy as FrameworkStrategy } from './strategy.js';
2
2
  export { ZephyrStrategy } from './strategy.js';
3
3
  export { Toolchain } from './toolchain/index.js';
4
+ export { writeProjectDebugArtifacts } from './toolchain/debug-config.js';
4
5
  export { runDoctor as doctor } from './doctor.js';
5
6
  export { runLicensesPresenter as licenses } from './licenses.js';
6
7
  export { chipForTarget, setActiveChip, getActiveChip, XIAO_BLE, } from './chips/index.js';
package/dist/index.js CHANGED
@@ -8,6 +8,11 @@
8
8
  export { ZephyrStrategy as FrameworkStrategy } from './strategy.js';
9
9
  export { ZephyrStrategy } from './strategy.js';
10
10
  export { Toolchain } from './toolchain/index.js';
11
+ // Create-time starter debug artifacts. The `cuttlefish create` flow reads this
12
+ // optional named export off the loaded framework module (same loader pattern
13
+ // as doctor/licenses) and calls it for freshly scaffolded projects, so F5 in
14
+ // VS Code works before the first build. No-ops for non-GDB targets.
15
+ export { writeProjectDebugArtifacts } from './toolchain/debug-config.js';
11
16
  // `cuttlefish doctor` — verify the installed Zephyr is reachable + inside the
12
17
  // declared compat range, and preview board-target normalization. Re-exported
13
18
  // under the dispatcher-facing alias `doctor` so the loader picks it up as
@@ -388,7 +388,9 @@ export function lowerBle(op) {
388
388
  case 'ble.on_read':
389
389
  // Store the typed read handler as void*; __tc_ble_attr_read casts it back
390
390
  // to the right signature based on the char's type field. reinterpret_cast
391
- // (not a C-style cast) keeps this AUTOSAR-compliant under --autosar=strict.
391
+ // (not a C-style cast) avoids M5-0-7, but M5-0-10 still flags it — the
392
+ // whole type-erased table is covered by a knownPatterns deviation on
393
+ // that rule (see rules.ts, "BLE type-erased callback table").
392
394
  return { code: `__tc_ble.on_read[__tc_ble.current_char] = reinterpret_cast<void*>(${s(o.handler)});` };
393
395
  case 'ble.on_write':
394
396
  return { code: `__tc_ble.on_write[__tc_ble.current_char] = (${s(o.handler)});` };
@@ -113,9 +113,13 @@ function lowerGpioRaw(op, chip) {
113
113
  case 'gpio.read':
114
114
  return { expression: `gpio_pin_get_raw(${controller}, ${pin})` };
115
115
  case 'gpio.toggle':
116
- return {
117
- code: `gpio_pin_set_raw(${controller}, ${pin}, !gpio_pin_get_raw(${controller}, ${pin}));`,
118
- };
116
+ // Native atomic toggle — never read-modify-write. gpio_pin_get_raw on
117
+ // a direction-only output reads the input latch, which is undefined on
118
+ // SoCs that don't latch it. Zephyr's toggle API has no _raw variant —
119
+ // gpio_pin_toggle is the driver-level atomic toggle, and for pins
120
+ // configured without GPIO_ACTIVE_LOW the logical level equals the
121
+ // physical one, so it matches the get_raw/set_raw used elsewhere.
122
+ return { code: `gpio_pin_toggle(${controller}, ${pin});` };
119
123
  default:
120
124
  throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
121
125
  `Open an issue or use rawCpp() to emit it manually.`);
@@ -32,6 +32,26 @@ export declare class ZephyrStrategy implements PlatformStrategy {
32
32
  * nodes whose resolved `raw` code references it.
33
33
  */
34
34
  private programUsesAsyncRuntime;
35
+ /** Pins referenced by gpio.* hal-ops in the program IR. lowerGpio routes a
36
+ * pin to its devicetree spec by pin NUMBER, so the structured hal-op pins
37
+ * are the authoritative signal for which __tc_dt_* specs are needed —
38
+ * regardless of when the final call text is rendered. */
39
+ private collectGpioPinUsage;
40
+ /** Run `re` (global) against every raw string in the IR — raw expression
41
+ * values plus raw hal-op codes — returning capture group 1 of each match
42
+ * (the full match when the regex has no group). This is how references the
43
+ * text scanners must see but that never appear as IR call nodes (e.g. a
44
+ * rawCpp() escape hatch naming `__tc_dt_sw0` directly) are discovered. */
45
+ private collectRawMatches;
46
+ /** Whether the wiring-compat GPIO read surface (__tc_gpio_read definition,
47
+ * __tc_gpio_dev dispatcher, and the wiring_compat polyfill's digitalRead /
48
+ * HIGH / LOW macros) must be emitted. Consumers: user digitalRead() calls
49
+ * (usesDigitalRead), the @typecad/safety voter (calls __tc_gpio_read
50
+ * directly via lowered raw text), and the UI runtime header's
51
+ * unconditional digitalRead() poll (entryHasUI — build-global, so every TU
52
+ * in a UI build carries the macros). With no analysis present (capability
53
+ * query), default to emitting — same convention as the uses() helper. */
54
+ private needsGpioReadShim;
35
55
  shimLines(program?: ProgramIR, ctx?: PlatformContext): string[];
36
56
  profileDiagnostics(program?: ProgramIR, ctx?: PlatformContext): Diagnostic[];
37
57
  /**
@@ -78,6 +98,10 @@ export declare class ZephyrStrategy implements PlatformStrategy {
78
98
  passthroughMacroNames(): ReadonlySet<string>;
79
99
  apiReservedEnumNames(): ReadonlySet<string>;
80
100
  apiReservedEnumGuard(): string;
101
+ isrUnsafeOperations(): Map<string, {
102
+ reason: string;
103
+ severity: 'warning' | 'info';
104
+ }>;
81
105
  ambientTypeDeclarations(): string[];
82
106
  needsIostream(): boolean;
83
107
  needsStdString(): boolean;