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

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 (56) hide show
  1. package/dist/chips/esp32.js +12 -0
  2. package/dist/chips/types.d.ts +30 -0
  3. package/dist/chips/xiao-ble.js +6 -0
  4. package/dist/display/gfx.d.ts +12 -3
  5. package/dist/display/gfx.js +130 -17
  6. package/dist/display/profiles.js +13 -0
  7. package/dist/display/ui-adapter.js +6 -0
  8. package/dist/doctor.d.ts +3 -3
  9. package/dist/doctor.js +56 -29
  10. package/dist/dt-config/kconfig.d.ts +3 -0
  11. package/dist/dt-config/kconfig.js +18 -0
  12. package/dist/dt-config/overlay.js +5 -0
  13. package/dist/framework.manifest.js +37 -14
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.js +5 -0
  16. package/dist/licenses.d.ts +59 -0
  17. package/dist/licenses.js +347 -0
  18. package/dist/lowering/dac.d.ts +15 -0
  19. package/dist/lowering/dac.js +69 -0
  20. package/dist/lowering/fs.d.ts +16 -0
  21. package/dist/lowering/fs.js +121 -0
  22. package/dist/lowering/hwtimer.d.ts +15 -0
  23. package/dist/lowering/hwtimer.js +84 -0
  24. package/dist/lowering/index.d.ts +4 -1
  25. package/dist/lowering/index.js +12 -3
  26. package/dist/strategy.js +186 -14
  27. package/dist/toolchain/compat.js +10 -1
  28. package/dist/toolchain/env-check.d.ts +93 -0
  29. package/dist/toolchain/env-check.js +190 -0
  30. package/dist/toolchain/scaffold.js +3 -0
  31. package/dist/toolchain/west-discover.d.ts +11 -3
  32. package/dist/toolchain/west-discover.js +80 -6
  33. package/dist/toolchain/west-spawn.js +15 -0
  34. package/package.json +4 -4
  35. package/src/chips/esp32.ts +12 -0
  36. package/src/chips/types.ts +29 -0
  37. package/src/chips/xiao-ble.ts +6 -0
  38. package/src/display/gfx.ts +135 -19
  39. package/src/display/profiles.ts +13 -0
  40. package/src/display/ui-adapter.ts +5 -0
  41. package/src/doctor.ts +77 -56
  42. package/src/dt-config/kconfig.ts +19 -0
  43. package/src/dt-config/overlay.ts +5 -0
  44. package/src/framework.manifest.ts +38 -14
  45. package/src/index.ts +6 -0
  46. package/src/licenses.ts +425 -0
  47. package/src/lowering/dac.ts +82 -0
  48. package/src/lowering/fs.ts +127 -0
  49. package/src/lowering/hwtimer.ts +101 -0
  50. package/src/lowering/index.ts +9 -2
  51. package/src/strategy.ts +180 -14
  52. package/src/toolchain/compat.ts +154 -145
  53. package/src/toolchain/env-check.ts +285 -0
  54. package/src/toolchain/scaffold.ts +3 -0
  55. package/src/toolchain/west-discover.ts +88 -8
  56. package/src/toolchain/west-spawn.ts +15 -0
@@ -56,6 +56,19 @@ export const ZEPHYR_DISPLAY_PROFILES: Record<string, ZephyrDisplayProfile> = {
56
56
  rotation: 1,
57
57
  backlight: 'backlight',
58
58
  },
59
+ 'ssd1306-zephyr': {
60
+ // Monochrome OLED (SSD1306-class, 128x64, 1bpp). Driven through Zephyr's
61
+ // generic display API (the ssd1306 driver + a DT display node). The GFX
62
+ // runtime (gfx.ts mono branch) keeps a full page-framebuffer and pushes it
63
+ // on display_flush — the standard model for page-buffered OLEDs. Direct
64
+ // display.* ops only (no @typecad/ui CuttlefishGFX rendering on mono).
65
+ driver: 'ssd1306-zephyr',
66
+ dtLabel: 'display0',
67
+ width: 128,
68
+ height: 64,
69
+ colorFormat: 'mono',
70
+ rotation: 0,
71
+ },
59
72
  };
60
73
 
61
74
  /** The default profile used when resolveDisplayOp is probed without a display.init. */
@@ -551,6 +551,11 @@ static inline void display_targetPrint(CuttlefishDisplayTarget* t, const char* s
551
551
  export const zephyrDisplayAdapterGenerator: DisplayAdapterGenerator = (display) => {
552
552
  const profile = ZEPHYR_DISPLAY_PROFILES[display.driver];
553
553
  if (!profile) return undefined as unknown as DisplayAdapterCode;
554
+ // The UI adapter is RGB565/SPI (TFT) only. Monochrome panels (OLED) use the
555
+ // direct display.* GFX runtime (gfx.ts mono branch) — there is no
556
+ // CuttlefishGFX UI rendering path for mono. Decline so cuttlefish does not
557
+ // emit an incompatible RGB565 adapter for a mono profile.
558
+ if (profile.colorFormat === 'mono') return undefined as unknown as DisplayAdapterCode;
554
559
  return zephyrUiDisplayAdapter(profile, {
555
560
  scanlineSync: display.scanlineSync,
556
561
  miso: display.spiPins?.miso,
package/src/doctor.ts CHANGED
@@ -1,56 +1,77 @@
1
- // ---------------------------------------------------------------------------
2
- // @typecad/framework-zephyr — Zephyr environment doctor
3
- //
4
- // `cuttlefish doctor` (Zephyr framework) — verify the installed Zephyr RTOS is
5
- // reachable and inside the framework's declared compat range, and preview how
6
- // the configured board target resolves for that version. Exits 0 if the
7
- // environment is OK, non-zero otherwise. Mirrors framework-arduino's doctor
8
- // shape (dispatched via the framework's `doctor` export).
9
- // ---------------------------------------------------------------------------
10
-
11
- import * as ui from '@typecad/cuttlefish/utils/ui';
12
- import { loadCuttlefishConfig } from '@typecad/cuttlefish/config-loader';
13
- import { detectZephyrVersion, checkZephyrCompat, resolveBoardTarget } from './toolchain/compat.js';
14
-
15
- /**
16
- * Verify the installed Zephyr is reachable + inside the supported range, and
17
- * preview board-target normalization for the configured target. Sets
18
- * process.exitCode = 1 on an out-of-range Zephyr.
19
- */
20
- export function runDoctor(): void {
21
- ui.printHeader();
22
- ui.printStep('Checking Zephyr environment...');
23
-
24
- const base = process.env.ZEPHYR_BASE;
25
- const version = detectZephyrVersion();
26
- const result = checkZephyrCompat(version);
27
-
28
- ui.printInfo(`ZEPHYR_BASE ..... ${base ?? '(not set)'}`);
29
- ui.printInfo(`Zephyr version .. ${version ?? 'unknown (could not read ZEPHYR_BASE/VERSION)'}`);
30
- ui.printInfo(`Supported range . ${result.range ?? '(none declared)'}`);
31
-
32
- if (result.status === 'out-of-range') {
33
- ui.printError(`Zephyr ${version} is OUTSIDE the supported range (${result.range}).`);
34
- ui.printInfo(
35
- "Set ZEPHYR_BASE to a compatible Zephyr checkout, or install one via '@typecad/zephyr-installer'.",
36
- );
37
- process.exitCode = 1;
38
- return;
39
- }
40
-
41
- if (result.status === 'undetectable') {
42
- ui.printWarning('Could not detect the Zephyr version (is ZEPHYR_BASE set?) — compat check skipped.');
43
- } else {
44
- ui.printInfo('Zephyr compat ... OK');
45
- }
46
-
47
- // Preview how the configured board target resolves for this Zephyr version
48
- // (e.g. a stale bare id would be qualified at build time). The loader extracts
49
- // frameworkData.buildTarget to a top-level field.
50
- const config = loadCuttlefishConfig(process.cwd());
51
- const buildTarget = config?.buildTarget;
52
- if (buildTarget) {
53
- const resolved = resolveBoardTarget(buildTarget, version);
54
- ui.printInfo(`Board target .... ${buildTarget}${resolved === buildTarget ? '' : ` → ${resolved}`}`);
55
- }
56
- }
1
+ // ---------------------------------------------------------------------------
2
+ // @typecad/framework-zephyr — Zephyr environment doctor
3
+ //
4
+ // `cuttlefish doctor` (Zephyr framework) — verify west (the Zephyr build tool)
5
+ // is installed + responsive, the Zephyr RTOS is inside the framework's declared
6
+ // compat range, and the configured board target exists in the checkout. Exits 0
7
+ // if the environment is OK, non-zero otherwise. Mirrors framework-arduino's
8
+ // doctor shape (dispatched via the framework's `doctor` export) and reuses
9
+ // checkZephyrEnv so the detection logic can be shared with the build/test gates.
10
+ // ---------------------------------------------------------------------------
11
+
12
+ import * as ui from '@typecad/cuttlefish/utils/ui';
13
+ import { loadCuttlefishConfig } from '@typecad/cuttlefish/config-loader';
14
+ import { checkZephyrEnv } from './toolchain/env-check.js';
15
+
16
+ /**
17
+ * Verify west is installed + responsive, the Zephyr RTOS is inside the supported
18
+ * range, and the configured board target exists in the checkout. Sets
19
+ * process.exitCode = 1 on failure. Thin presenter over checkZephyrEnv.
20
+ */
21
+ export function runDoctor(): void {
22
+ ui.printHeader();
23
+ ui.printStep('Checking Zephyr environment...');
24
+
25
+ const config = loadCuttlefishConfig(process.cwd());
26
+ const buildTarget = config?.buildTarget;
27
+
28
+ const result = checkZephyrEnv(buildTarget);
29
+ const c = result.check;
30
+
31
+ // west (the Zephyr build tool) — the analog of arduino-cli presence.
32
+ if (c.westFound) {
33
+ const ver = c.westVersion ?? 'found';
34
+ const src = c.westSource ? ` (${c.westSource})` : '';
35
+ ui.printInfo(`west ............. ${ver} ✓${src}`);
36
+ } else {
37
+ ui.printError('west ............. NOT FOUND');
38
+ }
39
+
40
+ // Zephyr RTOS version + declared compat range.
41
+ ui.printInfo(`ZEPHYR_BASE ...... ${c.zephyrBase ?? '(not set)'}`);
42
+ ui.printInfo(`Zephyr version .. ${c.zephyrVersion ?? 'unknown (could not read ZEPHYR_BASE/VERSION)'}`);
43
+ ui.printInfo(`Supported range . ${c.compatRange ?? '(none declared)'}`);
44
+
45
+ if (c.compatStatus === 'out-of-range') {
46
+ ui.printError(`Zephyr ${c.zephyrVersion} is OUTSIDE the supported range (${c.compatRange}).`);
47
+ } else if (c.compatStatus === 'undetectable') {
48
+ ui.printWarning('Could not detect the Zephyr version (is ZEPHYR_BASE set?) compat check skipped.');
49
+ } else {
50
+ ui.printInfo('Zephyr compat ... OK');
51
+ }
52
+
53
+ // Board target the analog of the Arduino core presence check.
54
+ if (buildTarget) {
55
+ const resolved = c.resolvedBoardTarget ?? buildTarget;
56
+ const arrow = resolved === buildTarget ? '' : ` → ${resolved}`;
57
+ if (c.boardTargetSupported === false) {
58
+ ui.printError(`Board target .... ${buildTarget}${arrow} NOT found in this Zephyr checkout`);
59
+ ui.printInfo(' → check the board id, or run: west boards');
60
+ } else if (c.boardTargetSupported === undefined) {
61
+ ui.printInfo(`Board target .... ${buildTarget}${arrow}`);
62
+ ui.printInfo('(could not verify board presence — no ZEPHYR_BASE boards/ tree found)');
63
+ } else {
64
+ ui.printInfo(`Board target .... ${buildTarget}${arrow}`);
65
+ }
66
+ } else {
67
+ ui.printInfo('(no buildTarget in cuttlefish.config.ts — skipping board check)');
68
+ }
69
+
70
+ // Exit code — mirrors framework-arduino's doctor.
71
+ if (result.ok) {
72
+ ui.printSuccess('Environment OK');
73
+ return; // exitCode stays unset => 0
74
+ }
75
+ for (const line of result.messages) ui.printInfo(line);
76
+ process.exitCode = 1;
77
+ }
@@ -11,6 +11,9 @@
11
11
  export interface KconfigUsage {
12
12
  usesAdc?: boolean;
13
13
  usesPwm?: boolean;
14
+ usesDac?: boolean;
15
+ usesFS?: boolean;
16
+ usesHwtimer?: boolean;
14
17
  usesI2c?: boolean;
15
18
  usesSpi?: boolean;
16
19
  usesUart?: boolean;
@@ -49,9 +52,12 @@ export function resolveKconfigFragments(
49
52
 
50
53
  if (usage.usesAdc) m.set('CONFIG_ADC', 'y');
51
54
  if (usage.usesPwm) m.set('CONFIG_PWM', 'y');
55
+ if (usage.usesDac) m.set('CONFIG_DAC', 'y');
52
56
  if (usage.usesI2c) m.set('CONFIG_I2C', 'y');
53
57
  if (usage.usesSpi) m.set('CONFIG_SPI', 'y');
54
58
  if (usage.usesWdt) m.set('CONFIG_WATCHDOG', 'y');
59
+ // Hardware timers via the counter driver.
60
+ if (usage.usesHwtimer) m.set('CONFIG_COUNTER', 'y');
55
61
  if (usage.usesDisplay) {
56
62
  m.set('CONFIG_DISPLAY', 'y');
57
63
  m.set('CONFIG_SPI', 'y');
@@ -257,6 +263,19 @@ export function resolveKconfigFragments(
257
263
  m.set('CONFIG_SETTINGS', 'y');
258
264
  m.set('CONFIG_SETTINGS_ZMS', 'y');
259
265
  }
266
+ // Filesystem: littlefs on the storage partition. CONFIG_FILE_SYSTEM_LITTLEFS
267
+ // selects the littlefs backend but NOT FLASH/FLASH_MAP (the partition lookup
268
+ // needs them), so all three are set explicitly — same shape as the
269
+ // preferences/ZMS block. The overlay points the storage_partition at the FS
270
+ // (see dt-config/overlay.ts). NOTE: a program using BOTH fs.* and
271
+ // preferences.* shares the one storage_partition between littlefs and ZMS —
272
+ // dedicate separate partitions if both are needed (the manifest flags this).
273
+ if (usage.usesFS) {
274
+ m.set('CONFIG_FLASH', 'y');
275
+ m.set('CONFIG_FLASH_MAP', 'y');
276
+ m.set('CONFIG_FILE_SYSTEM', 'y');
277
+ m.set('CONFIG_FILE_SYSTEM_LITTLEFS', 'y');
278
+ }
260
279
  // usesUart: the board enables the console UART by default; the overlay (not
261
280
  // Kconfig) is where a UART node would be enabled, so no symbol here.
262
281
  // Random: <zephyr/random/random.h> sys_rand_get is backed by the random
@@ -84,6 +84,11 @@ export function generateOverlay(
84
84
  if (usage.usesUart && chip.uart) {
85
85
  for (const c of chip.uart.controllers) block(c.nodeLabel);
86
86
  }
87
+ // DAC: enable the chip's DAC device node when the program uses dac.*. The
88
+ // lowering references DEVICE_DT_GET(DT_NODELABEL(<dac.device>)).
89
+ if (usage.usesDac && chip.dac) {
90
+ block(chip.dac.device);
91
+ }
87
92
  if (display) {
88
93
  // Emit a full display DT node definition. Boards like the ESP32 devkit
89
94
  // have no display node in their base DT, so a bare `&display0 { status }`
@@ -113,9 +113,13 @@ export default defineFrameworkManifest({
113
113
  },
114
114
  },
115
115
  dac: {
116
- supported: false,
117
- unsupportedReason: 'No DAC lowering implemented in the framework (not applicable on nRF52840; ESP32 variants with DAC not yet wired).',
118
- ops: { 'dac.write': 'unsupported' },
116
+ // ESP32 DAC (2× 8-bit channels on GPIO25/26) via the Zephyr DAC driver
117
+ // (dac_channel_setup + dac_write_value). nRF52840 / ESP32-S3 have no DAC;
118
+ // usage there lowers to a comment and profileDiagnostics flags it
119
+ // (zephyr-dac-pin-unavailable).
120
+ supported: true,
121
+ partialCoverage: true,
122
+ ops: { 'dac.write': 'supported' },
119
123
  },
120
124
  interrupts: {
121
125
  supported: true,
@@ -287,7 +291,7 @@ export default defineFrameworkManifest({
287
291
  supported: true,
288
292
  partialCoverage: false,
289
293
  unsupportedReason: undefined,
290
- drivers: ['ili9341-zephyr', 'st7796-zephyr'],
294
+ drivers: ['ili9341-zephyr', 'st7796-zephyr', 'ssd1306-zephyr'],
291
295
  colorFormat: 'rgb565',
292
296
  ops: {
293
297
  'display.init': 'supported',
@@ -370,10 +374,17 @@ export default defineFrameworkManifest({
370
374
  },
371
375
  },
372
376
  fs: {
373
- supported: false,
374
- unsupportedReason: 'No filesystem lowering on Zephyr (Zephyr has its own FS API; not wired).',
377
+ // littlefs on the board's storage_partition, via <zephyr/fs/fs.h>. The
378
+ // shim mounts at /lfs lazily (formats on first use) and the HAL paths are
379
+ // treated as paths within the filesystem. Requires CONFIG_FILE_SYSTEM +
380
+ // CONFIG_FILE_SYSTEM_LITTLEFS (emitted by the scaffold when fs.* is used)
381
+ // and the storage_partition node.
382
+ supported: true,
375
383
  partialCoverage: false,
376
- ops: unsupportedOps('fs.'),
384
+ ops: {
385
+ 'fs.begin': 'supported', 'fs.read_text': 'supported', 'fs.write_text': 'supported',
386
+ 'fs.exists': 'supported', 'fs.remove': 'supported',
387
+ },
377
388
  },
378
389
  mdns: {
379
390
  supported: false,
@@ -410,10 +421,17 @@ export default defineFrameworkManifest({
410
421
  ops: { 'temp.read': 'unsupported' },
411
422
  },
412
423
  hwtimer: {
413
- supported: false,
414
- unsupportedReason: 'No hardware-timer lowering on Zephyr (timers are handled via the k_timer polyfill, not hwtimer.*).',
415
- partialCoverage: false,
416
- ops: unsupportedOps('hwtimer.'),
424
+ // Hardware timers via the Zephyr counter driver (<zephyr/drivers/counter.h>).
425
+ // set_frequency top value (counter_freq/hz) + on_overflow callback;
426
+ // start arms both; stop halts. A chip declares its free counters
427
+ // (e.g. nRF RTC1; RTC0 is kernel-owned). This is distinct from the JS
428
+ // setInterval/setTimeout k_timer polyfill, which is unaffected.
429
+ supported: true,
430
+ partialCoverage: true,
431
+ ops: {
432
+ 'hwtimer.set_frequency': 'supported', 'hwtimer.on_overflow': 'supported',
433
+ 'hwtimer.start': 'supported', 'hwtimer.stop': 'supported',
434
+ },
417
435
  },
418
436
  capacitive: {
419
437
  // FT6336U capacitive touch is handled via the strategy-owned touch adapter
@@ -481,6 +499,8 @@ export default defineFrameworkManifest({
481
499
  polyfills: {
482
500
  emitted: [
483
501
  { id: 'cuttlefish_halt', domain: 'standard', notes: 'Mapped to a k_msleep halt loop (exceptions disabled)' },
502
+ { id: 'string_methods', domain: 'embedded', notes: 'STL-free __tc_* string helpers (const char*, inline ASCII case conv, <cstring> only)' },
503
+ { id: 'static_array', domain: 'embedded', notes: 'STL-free __tc_StaticArray<T,N> wrapper for no-<vector> mutated/struct array literals' },
484
504
  { id: 'timer_methods', domain: 'embedded', notes: 'k_timer + k_work pool (system workqueue); callbacks run in thread context' },
485
505
  { id: 'async_runtime', domain: 'embedded', notes: 'Heap-free static Promise/microtask runtime (generateStaticAsyncRuntime), pumped in loop()' },
486
506
  ],
@@ -537,9 +557,9 @@ export default defineFrameworkManifest({
537
557
  // pure string-snapshot tests (no hardware); they are the safety net that
538
558
  // catches regressions like silent pull-resistor / interrupt no-ops.
539
559
  halResolutionTests: [
540
- 'adc', 'ble', 'board', 'dac', 'gpio', 'http', 'i2c', 'interrupts', 'mqtt',
541
- 'power', 'preferences', 'pulse', 'pwm', 'random', 'spi', 'timing', 'tone',
542
- 'uart', 'wdt', 'worker',
560
+ 'adc', 'ble', 'board', 'dac', 'fs', 'gpio', 'http', 'hwtimer', 'i2c',
561
+ 'interrupts', 'mqtt', 'power', 'preferences', 'pulse', 'pwm', 'random',
562
+ 'spi', 'timing', 'tone', 'uart', 'wdt', 'worker',
543
563
  ],
544
564
  },
545
565
 
@@ -555,4 +575,8 @@ export default defineFrameworkManifest({
555
575
  // `cuttlefish doctor` prints the detected Zephyr version + compat result and
556
576
  // previews how the configured board target resolves for that version.
557
577
  doctor: { available: true },
578
+
579
+ // `cuttlefish licenses` enumerates the Zephyr kernel + west manifest projects
580
+ // and resolves each one's SPDX license (mirrors framework-arduino).
581
+ licenses: { available: true },
558
582
  });
package/src/index.ts CHANGED
@@ -16,6 +16,12 @@ export { Toolchain } from './toolchain/index.js';
16
16
  // mod.doctor (see framework-package.ts).
17
17
  export { runDoctor as doctor } from './doctor.js';
18
18
 
19
+ // `cuttlefish licenses` — enumerate the Zephyr kernel + west manifest projects
20
+ // and resolve each one's SPDX license. Re-exported under the dispatcher-facing
21
+ // alias `licenses` so the loader picks it up as mod.licenses (see
22
+ // framework-package.ts). Mirrors framework-arduino's presenter.
23
+ export { runLicensesPresenter as licenses } from './licenses.js';
24
+
19
25
  // Chip descriptor registry (for downstream tooling / additional boards).
20
26
  export {
21
27
  chipForTarget,