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

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 (132) hide show
  1. package/LICENSE +202 -21
  2. package/README.md +49 -87
  3. package/dist/as-built.d.ts +2 -2
  4. package/dist/as-built.js +2 -2
  5. package/dist/boardgen.d.ts +1 -9
  6. package/dist/boardgen.js +228 -45
  7. package/dist/chips/types.d.ts +1 -1
  8. package/dist/debug-codegen.js +1 -1
  9. package/dist/display/touch-adapter.js +1 -1
  10. package/dist/display/ui-adapter.js +549 -549
  11. package/dist/doctor.js +4 -4
  12. package/dist/dt-config/custom-board.js +2 -2
  13. package/dist/dt-config/kconfig.js +18 -12
  14. package/dist/dt-config/overlay.d.ts +2 -2
  15. package/dist/dt-config/overlay.js +2 -2
  16. package/dist/framework.manifest.d.ts +3 -3
  17. package/dist/framework.manifest.js +10 -7
  18. package/dist/index.js +5 -5
  19. package/dist/licenses.d.ts +2 -2
  20. package/dist/licenses.js +8 -8
  21. package/dist/lowering/fs.js +1 -1
  22. package/dist/lowering/gpio.js +0 -32
  23. package/dist/lowering/http.js +264 -32
  24. package/dist/lowering/i2c.js +0 -8
  25. package/dist/lowering/interrupts.js +6 -0
  26. package/dist/lowering/mqtt.js +110 -8
  27. package/dist/lowering/usb.js +11 -11
  28. package/dist/lowering/wdt.js +2 -29
  29. package/dist/sdk/board-catalog-sync.d.ts +1 -3
  30. package/dist/sdk/board-catalog-sync.js +4 -10
  31. package/dist/strategy.d.ts +22 -44
  32. package/dist/strategy.js +152 -155
  33. package/dist/tmp-probe.d.ts +2 -0
  34. package/dist/tmp-probe.js +9 -0
  35. package/dist/toolchain/debug-config.d.ts +50 -90
  36. package/dist/toolchain/debug-config.js +239 -510
  37. package/dist/toolchain/env-check.d.ts +1 -3
  38. package/dist/toolchain/env-check.js +2 -7
  39. package/dist/toolchain/index.d.ts +16 -2
  40. package/dist/toolchain/index.js +383 -35
  41. package/dist/toolchain/runners.d.ts +16 -0
  42. package/dist/toolchain/runners.js +75 -0
  43. package/dist/toolchain/scaffold.d.ts +1 -1
  44. package/dist/toolchain/scaffold.js +12 -12
  45. package/dist/toolchain/west-discover.d.ts +6 -0
  46. package/dist/toolchain/west-discover.js +36 -13
  47. package/dist/toolchain/west-spawn.js +8 -2
  48. package/installer/README.md +328 -328
  49. package/installer/install.sh +2 -2
  50. package/installer/templates/project/.typecad/activate-zephyr.ps1 +1 -1
  51. package/installer/templates/project/.typecad/activate-zephyr.sh +1 -1
  52. package/installer/templates/project/.vscode/settings.json +1 -1
  53. package/installer/templates/project/README.md +2 -2
  54. package/package.json +5 -5
  55. package/src/as-built.ts +206 -206
  56. package/src/boardgen.ts +214 -48
  57. package/src/chips/types.ts +567 -567
  58. package/src/display/touch-adapter.ts +204 -204
  59. package/src/display/ui-adapter.ts +781 -781
  60. package/src/doctor.ts +4 -4
  61. package/src/dt-config/custom-board.ts +2 -2
  62. package/src/dt-config/kconfig.ts +18 -12
  63. package/src/dt-config/overlay.ts +1058 -1058
  64. package/src/framework.manifest.ts +538 -535
  65. package/src/index.ts +5 -5
  66. package/src/licenses.ts +425 -425
  67. package/src/lowering/fs.ts +135 -135
  68. package/src/lowering/gpio.ts +0 -33
  69. package/src/lowering/http.ts +264 -32
  70. package/src/lowering/i2c.ts +0 -9
  71. package/src/lowering/interrupts.ts +6 -0
  72. package/src/lowering/mqtt.ts +109 -8
  73. package/src/lowering/usb.ts +221 -221
  74. package/src/lowering/wdt.ts +2 -25
  75. package/src/sdk/board-catalog-sync.ts +4 -25
  76. package/src/strategy.ts +2265 -2309
  77. package/src/toolchain/debug-config.ts +262 -522
  78. package/src/toolchain/env-check.ts +279 -285
  79. package/src/toolchain/index.ts +1703 -1359
  80. package/src/toolchain/runners.ts +80 -0
  81. package/src/toolchain/scaffold.ts +296 -296
  82. package/src/toolchain/west-discover.ts +35 -13
  83. package/src/toolchain/west-spawn.ts +174 -168
  84. package/dist/async/timer-polyfill.d.ts +0 -10
  85. package/dist/async/timer-polyfill.js +0 -95
  86. package/dist/chips/board-overrides.d.ts +0 -7
  87. package/dist/chips/board-overrides.js +0 -11
  88. package/dist/chips/esp32.d.ts +0 -2
  89. package/dist/chips/esp32.js +0 -71
  90. package/dist/chips/esp32s3.d.ts +0 -2
  91. package/dist/chips/esp32s3.js +0 -103
  92. package/dist/chips/soc/.d.ts +0 -2
  93. package/dist/chips/soc/.js +0 -129
  94. package/dist/chips/soc/esp32.d.ts +0 -2
  95. package/dist/chips/soc/esp32.js +0 -120
  96. package/dist/chips/soc/esp32c3.d.ts +0 -2
  97. package/dist/chips/soc/esp32c3.js +0 -90
  98. package/dist/chips/soc/esp32c6.d.ts +0 -2
  99. package/dist/chips/soc/esp32c6.js +0 -109
  100. package/dist/chips/soc/esp32s3.d.ts +0 -2
  101. package/dist/chips/soc/esp32s3.js +0 -189
  102. package/dist/chips/soc/index.d.ts +0 -2
  103. package/dist/chips/soc/index.js +0 -23
  104. package/dist/chips/soc/nrf52840.d.ts +0 -2
  105. package/dist/chips/soc/nrf52840.js +0 -130
  106. package/dist/chips/soc/rp2040.d.ts +0 -2
  107. package/dist/chips/soc/rp2040.js +0 -141
  108. package/dist/chips/soc/rp2350a.d.ts +0 -2
  109. package/dist/chips/soc/rp2350a.js +0 -145
  110. package/dist/chips/soc/samd21g18a.d.ts +0 -2
  111. package/dist/chips/soc/samd21g18a.js +0 -143
  112. package/dist/chips/soc/stm32f411xe.d.ts +0 -2
  113. package/dist/chips/soc/stm32f411xe.js +0 -251
  114. package/dist/chips/xiao-ble.d.ts +0 -2
  115. package/dist/chips/xiao-ble.js +0 -100
  116. package/dist/lowering/power.d.ts +0 -9
  117. package/dist/lowering/power.js +0 -60
  118. package/dist/lowering/pulse.d.ts +0 -7
  119. package/dist/lowering/pulse.js +0 -51
  120. package/dist/lowering/tone.d.ts +0 -10
  121. package/dist/lowering/tone.js +0 -63
  122. package/dist/lowering/worker-backing.d.ts +0 -14
  123. package/dist/lowering/worker-backing.js +0 -79
  124. package/dist/lowering/worker.d.ts +0 -6
  125. package/dist/lowering/worker.js +0 -14
  126. package/dist/sdk/board-data.generated.d.ts +0 -2
  127. package/dist/sdk/board-data.generated.js +0 -4
  128. package/dist/sdk/catalog-walker.d.ts +0 -90
  129. package/dist/sdk/catalog-walker.js +0 -682
  130. package/dist/sdk/dts-reader.d.ts +0 -83
  131. package/dist/sdk/dts-reader.js +0 -596
  132. package/src/debug-codegen.ts +0 -207
package/dist/boardgen.js CHANGED
@@ -6,9 +6,10 @@
6
6
  // CONTRACT (custom PCB spec) — and emits the two artifacts a project
7
7
  // carries instead of a board package:
8
8
  //
9
- // .cuttlefish/board.ts — typed pin/bus/LED/BUTTON exports (the virtual
10
- // @typecad/board module points here)
11
- // .cuttlefish/board.json — the BoardConstants flat map (pins.all.*,
9
+ // .typecad-hal/board.ts — typed pin/bus/LED/BUTTON exports (the virtual
10
+ // '@typecad/hal' module resolves here via the
11
+ // project tsconfig — the single import surface)
12
+ // .typecad-hal/board.json — the BoardConstants flat map (pins.all.*,
12
13
  // peripherals.*, zephyr.*) the transpiler's
13
14
  // resolveChipFromBoard reconstructs its chip
14
15
  // view from
@@ -22,13 +23,14 @@
22
23
  // matrices) are absent for every board alike.
23
24
  //
24
25
  // Pure: returns the file contents; the caller (cuttlefish config-loader via
25
- // the strategy hook, or `cuttlefish board regen`) writes them.
26
+ // the strategy hook, or `typecad-hal board regen`) writes them.
26
27
  // ----------------------------------------------------------------------------
27
28
  import { parseAsBuiltJson } from './as-built.js';
28
29
  import { loadBoardCatalogOverlay, GENERATOR_REV, boardRecordFingerprint, factsFingerprint, findBoardInCatalog, socBusLabelsFromTree, } from '@typecad/cuttlefish/board-catalog';
30
+ import { getBoardGateData } from '@typecad/cuttlefish/board-gate';
29
31
  /**
30
32
  * The catalog lookups resolve against the local overlay — the user's own
31
- * Zephyr tree (`cuttlefish board sync`, auto-refreshed when the tree
33
+ * Zephyr tree (`typecad-hal board sync`, auto-refreshed when the tree
32
34
  * moves). There is no compiled-in database: a machine with no tree and no
33
35
  * overlay has no boards, and the error below says exactly that.
34
36
  */
@@ -36,7 +38,7 @@ function activeBoardData() {
36
38
  const data = loadBoardCatalogOverlay()?.data;
37
39
  if (!data) {
38
40
  throw new Error(`No board catalog on this machine. The catalog is generated from your Zephyr tree —\n` +
39
- `run 'cuttlefish board sync' (or point CUTTLEFISH_BOARD_CATALOG at a catalog file).`);
41
+ `run 'typecad-hal board sync' (or point TYPECAD_HAL_BOARD_CATALOG at a catalog file).`);
40
42
  }
41
43
  return data;
42
44
  }
@@ -75,7 +77,7 @@ export function nrfSaadcAinPads(soc, identifier) {
75
77
  * S3/C3/C6/H2 dropped it. Sourced from the HAL's dac_periph.c
76
78
  * (`dac_channel_io_num[]`), like the nRF SAADC table is from the PS.
77
79
  */
78
- export function esp32DacPins(soc) {
80
+ function esp32DacPins(soc) {
79
81
  const table = {
80
82
  esp32: [[0, 25], [1, 26]],
81
83
  esp32s2: [[0, 17], [1, 18]],
@@ -408,7 +410,7 @@ export function parsePinName(soc, name) {
408
410
  return undefined;
409
411
  }
410
412
  /** Parse + shape-validate the facts file; a clear error names the file. */
411
- export function parseUserFactsJson(text, source = 'cuttlefish.facts.json') {
413
+ export function parseUserFactsJson(text, source = 'typecad-hal.facts.json') {
412
414
  let parsed;
413
415
  try {
414
416
  parsed = JSON.parse(text);
@@ -445,6 +447,9 @@ export function userFactsForTarget(file, target) {
445
447
  return hit ? file.boards[hit] : undefined;
446
448
  }
447
449
  export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = []) {
450
+ // The ungated surface is derived from the project's own hal copy — same
451
+ // resolution (project → cwd → monorepo sibling) as the engine's HAL parser.
452
+ const { ungated: BOARD_UNGATED_EXPORTS, ungatedTypes: BOARD_UNGATED_TYPE_EXPORTS } = getBoardGateData();
448
453
  const soc = socOfTarget(entry.identifier);
449
454
  const conv = namingConvFor(soc);
450
455
  // ── Controller table + datasheet sweep (the same for every board) ──────
@@ -609,7 +614,7 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
609
614
  adcSources.push(r.source);
610
615
  }
611
616
  }
612
- // User facts (cuttlefish.facts.json) — the escape hatch. User routes win
617
+ // User facts (typecad-hal.facts.json) — the escape hatch. User routes win
613
618
  // PER PIN over every harvested source above, and each takeover is warned.
614
619
  // Seeded with the pinctrl harvest's own lint results (name↔value
615
620
  // disagreements — routes dropped as untrustworthy upstream of here).
@@ -679,7 +684,7 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
679
684
  if (siliconDac.length > 0 && !dacSources.includes('dac'))
680
685
  dacSources.push('dac');
681
686
  }
682
- // User facts (cuttlefish.facts.json): dac channels win per pin, and the
687
+ // User facts (typecad-hal.facts.json): dac channels win per pin, and the
683
688
  // declared device joins the sources (no analogDevices cross-check — the
684
689
  // user vouches for it).
685
690
  if (userFacts?.dac) {
@@ -803,7 +808,7 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
803
808
  ...pins.map((p) => p.ident),
804
809
  'LED',
805
810
  'BUTTON',
806
- // '@typecad/hal' imports — a connector silkscreen label can literally be
811
+ // '@typecad/hal/core' imports — a connector silkscreen label can literally be
807
812
  // 'Pin' (phyBOARD-Atlas), and `export const Pin` merges with the import.
808
813
  'Pin',
809
814
  'I2CBus',
@@ -811,6 +816,9 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
811
816
  'UART',
812
817
  'USBConsole',
813
818
  'PWM',
819
+ // The ungated re-export block: a label colliding with any re-exported
820
+ // hal name would be a duplicate module export.
821
+ ...BOARD_UNGATED_EXPORTS,
814
822
  ]);
815
823
  buses.i2c.forEach((_, i) => reservedNames.add(`I2C${i}`));
816
824
  buses.spi.forEach((_, i) => reservedNames.add(`SPI${i}`));
@@ -826,49 +834,149 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
826
834
  continue;
827
835
  connectorLabelOwner.set(label, pin);
828
836
  }
837
+ // ── Per-pin editor annotations ──────────────────────────────────────────
838
+ // The same harvested facts board.json carries, stamped as JSDoc on the
839
+ // datasheet pin exports so plain tsserver surfaces them in hover and
840
+ // completion detail — board intelligence with zero editor extension. Only
841
+ // pins WITH a fact (silicon route, any-pad matrix membership, bus role, or
842
+ // an alias) get a doc line: bare pads stay bare, and the absence of an
843
+ // annotation says "plain GPIO" without stamping it on every line. Facts and
844
+ // module derive from the same route sets below, so they cannot disagree.
845
+ const pwmRouteByPin = new Map(siliconPwm.map((s) => [s.pin, s]));
846
+ const adcRouteByPin = new Map(siliconAdc.map((s) => [s.pin, s]));
847
+ const dacRouteByPin = new Map(siliconDac.map((s) => [s.pin, s]));
848
+ // Any-pad PWM matrices (ESP32 LEDC, nRF psel, RP2 slices) ride global pad
849
+ // numbers — same base arithmetic the manifest's matrix pins use.
850
+ const matrixBase = controllers.find((c) => c.nodelabel === 'gpio0')?.minPin ?? 0;
851
+ const matrixPwmPins = pwmMatrix
852
+ ? new Set(pwmMatrix.pads.map((pad) => matrixBase + pad))
853
+ : undefined;
854
+ // Aliases (connector silkscreen labels, LED/BUTTON) keyed by the aliased
855
+ // pin's HAL number — connectorLabelOwner's values ARE the sweep's GenPin
856
+ // objects, so identity holds through the LED/BUTTON facts too.
857
+ const aliasesByPin = new Map();
858
+ const noteAlias = (pin, label) => {
859
+ if (!pin)
860
+ return;
861
+ const list = aliasesByPin.get(pin.halPin);
862
+ if (list)
863
+ list.push(label);
864
+ else
865
+ aliasesByPin.set(pin.halPin, [label]);
866
+ };
867
+ for (const [label, pin] of connectorLabelOwner)
868
+ noteAlias(pin, label);
869
+ if (ledPinFinal)
870
+ noteAlias(ledPinFinal, 'LED');
871
+ if (buttonPin)
872
+ noteAlias(buttonPin, 'BUTTON');
873
+ extraLedPins.forEach((p, i) => noteAlias(p, `LED${i + 1}`));
874
+ extraButtonPins.forEach((p, i) => noteAlias(p, `BUTTON${i + 1}`));
875
+ // Bus roles (SDA/SCL/MOSI/…) keyed by the route's pad NAME — best-effort
876
+ // uppercase match; pads whose spelling the pin sweep can't place simply
877
+ // carry no role line. (busPads is defined further down; this walks
878
+ // entry.busPins directly to stay above it.)
879
+ const rolesByPinName = new Map();
880
+ for (const b of entry.busPins ?? []) {
881
+ const index = buses[b.bus]?.indexOf(b.nodelabel) ?? -1;
882
+ if (index < 0)
883
+ continue;
884
+ const instance = `${b.bus.toUpperCase()}${index}`;
885
+ for (const route of b.routes) {
886
+ if (!route.pad || !route.role)
887
+ continue;
888
+ const name = route.pad.toUpperCase();
889
+ const role = `${instance} ${route.role.toUpperCase()}`;
890
+ const list = rolesByPinName.get(name);
891
+ if (list) {
892
+ if (!list.includes(role))
893
+ list.push(role);
894
+ }
895
+ else {
896
+ rolesByPinName.set(name, [role]);
897
+ }
898
+ }
899
+ }
900
+ const pinDoc = (p) => {
901
+ const parts = [];
902
+ const pwm = pwmRouteByPin.get(p.halPin);
903
+ if (pwm)
904
+ parts.push(`PWM ${pwm.controller} ch${pwm.channel}`);
905
+ else if (pwmMatrix && matrixPwmPins?.has(p.halPin)) {
906
+ parts.push(`PWM ${pwmMatrix.controller} ch0-${pwmMatrix.channelCount - 1} (any pad)`);
907
+ }
908
+ const adc = adcRouteByPin.get(p.halPin);
909
+ // Primary-unit routes (pinctrl and ESP SARADC alike) omit the controller
910
+ // field — fall back to the pinctrl sources, then the ESP primary unit.
911
+ if (adc)
912
+ parts.push(`ADC ${adc.controller ?? adcSources[0] ?? espAdcUnit ?? 'adc'} ch${adc.channel}`);
913
+ const dac = dacRouteByPin.get(p.halPin);
914
+ if (dac)
915
+ parts.push(`DAC ch${dac.channel}`);
916
+ parts.push(...(rolesByPinName.get(p.name.toUpperCase()) ?? []));
917
+ const aliases = aliasesByPin.get(p.halPin);
918
+ if (aliases)
919
+ parts.push(`aliases: ${aliases.join(', ')}`);
920
+ return parts.length > 0 ? parts.join(' · ') : undefined;
921
+ };
829
922
  const ts = [];
830
- ts.push(`// GENERATED by cuttlefish boardgen from the Zephyr board catalog —`);
923
+ ts.push(`// GENERATED by typecad-hal boardgen from the Zephyr board catalog —`);
831
924
  ts.push(`// ${entry.identifier} (${entry.name}, ${entry.vendor}).`);
832
- ts.push(`// Regenerate with: npx cuttlefish board regen`);
925
+ ts.push(`// Regenerate with: npx typecad-hal board regen`);
833
926
  ts.push('');
834
- ts.push(`import { Pin, I2CBus, SPIBus, UART${hasUsb ? ', USBConsole' : ''}${pwmLedSpecs.length > 0 ? ', PWM' : ''} } from '@typecad/hal';`);
927
+ // The generated module IS the user's '@typecad/hal' (the project tsconfig
928
+ // maps that specifier here), so it reaches the implementation package via
929
+ // the './core' subpath — the one specifier the paths mapping does not
930
+ // capture, avoiding a circular self-reference.
931
+ ts.push(`import { Pin, I2CBus, SPIBus, UART${hasUsb ? ', USBConsole' : ''}${pwmLedSpecs.length > 0 ? ', PWM' : ''} } from '@typecad/hal/core';`);
835
932
  ts.push('');
836
933
  // ── Hardware-class gateway ─────────────────────────────────────────────
837
- // The board module is the NARROWED surface: every hardware class re-export
838
- // here exists only when this board's facts support it, so importing
839
- // unavailable hardware fails at module resolution (editor + transpile)
840
- // instead of at a deep diagnostic. '@typecad/hal' stays the implementation
841
- // package; user code imports hardware from '@typecad/board'.
934
+ // This module is the NARROWED surface behind the user's '@typecad/hal'
935
+ // import: every hardware class gated on board facts is re-exported below
936
+ // only when this board's facts support it, so importing unavailable
937
+ // hardware fails at module resolution (editor + transpile) instead of at
938
+ // a deep diagnostic. Everything not gated (the lists hal ships in
939
+ // gate.ts) is re-exported verbatim so the full authoring surface stays
940
+ // importable from the same specifier ('@typecad/hal' is the one specifier —
941
+ // the old '@typecad/board' alias was removed with the rename).
942
+ ts.push('// Always-available HAL surface (not gated on board facts).');
943
+ ts.push(`export { ${BOARD_UNGATED_EXPORTS.join(', ')} } from '@typecad/hal/core';`);
944
+ ts.push(`export type { ${BOARD_UNGATED_TYPE_EXPORTS.join(', ')} } from '@typecad/hal/core';`);
945
+ ts.push('');
842
946
  ts.push('// Hardware this board actually has — unavailable hardware is not importable.');
843
- ts.push(`export { GPIO, Thread, Time, Sensor } from '@typecad/hal';`);
844
947
  if (wdtNodeLabel)
845
- ts.push(`export { Watchdog } from '@typecad/hal';`);
948
+ ts.push(`export { Watchdog } from '@typecad/hal/core';`);
846
949
  if (siliconPwm.length > 0 || pwmLedSpecs.length > 0 || pwmMatrix)
847
- ts.push(`export { PWM } from '@typecad/hal';`);
950
+ ts.push(`export { PWM } from '@typecad/hal/core';`);
848
951
  if (siliconAdc.length > 0)
849
- ts.push(`export { ADC } from '@typecad/hal';`);
952
+ ts.push(`export { ADC } from '@typecad/hal/core';`);
850
953
  if (siliconDac.length > 0)
851
- ts.push(`export { DAC } from '@typecad/hal';`);
954
+ ts.push(`export { DAC } from '@typecad/hal/core';`);
852
955
  if (buses.i2c.length > 0)
853
- ts.push(`export { I2CTarget } from '@typecad/hal';`);
956
+ ts.push(`export { I2CTarget } from '@typecad/hal/core';`);
854
957
  if (buses.spi.length > 0)
855
- ts.push(`export { SPITarget } from '@typecad/hal';`);
958
+ ts.push(`export { SPITarget } from '@typecad/hal/core';`);
856
959
  if (buses.uart.length > 0)
857
- ts.push(`export { UART } from '@typecad/hal';`);
960
+ ts.push(`export { UART } from '@typecad/hal/core';`);
858
961
  if (hwtimerControllers.length > 0)
859
- ts.push(`export { Counter } from '@typecad/hal';`);
962
+ ts.push(`export { Counter } from '@typecad/hal/core';`);
860
963
  if (hasUsb)
861
- ts.push(`export { USBConsole } from '@typecad/hal';`);
964
+ ts.push(`export { USBConsole } from '@typecad/hal/core';`);
862
965
  // Store/File: a persisted backend needs a storage region — either the
863
966
  // board's own storage_partition (harvested reg) or a synthesizable one
864
967
  // (flash size known, no existing partition to collide with).
865
968
  if (entry.storageReg || (entry.flashKb && !entry.hasStoragePartition)) {
866
- ts.push(`export { Store, File } from '@typecad/hal';`);
969
+ ts.push(`export { Store, File } from '@typecad/hal/core';`);
867
970
  }
868
971
  ts.push('');
869
972
  if (pins.length > 0) {
870
973
  ts.push(`// Datasheet-named pins (derived from the board's devicetree controllers)`);
974
+ ts.push(`// Pins with harvested facts carry them as JSDoc (hover shows routes/roles/aliases);`);
975
+ ts.push(`// a pin without a doc line is plain GPIO — no PWM/ADC/DAC route on this board.`);
871
976
  for (const p of pins) {
977
+ const doc = pinDoc(p);
978
+ if (doc)
979
+ ts.push(`/** ${doc} */`);
872
980
  ts.push(`export const ${p.ident} = Pin.fromPort('${p.name}');`);
873
981
  }
874
982
  ts.push('');
@@ -892,28 +1000,52 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
892
1000
  extraPinDecls.push(p);
893
1001
  }
894
1002
  for (const p of extraPinDecls) {
895
- ts.push(`/** ${p.name} (derived from the devicetree controller label). */`);
896
1003
  ts.push(`export const ${p.ident} = Pin.fromPort('${p.name}');`);
897
1004
  }
898
1005
  if (ledPinFinal) {
899
- ts.push(`/** On-board LED (${entry.led ? `devicetree ${entry.led.dtSpec}` : 'addressable strip — no gpio-leds node'}). */`);
1006
+ ts.push(`/** On-board LED${entry.led ? '' : ' — addressable strip, this is its data pin'}. */`);
900
1007
  ts.push(`export const LED = ${ledPinFinal.ident};`);
901
1008
  }
902
1009
  if (buttonPin) {
903
- ts.push(`/** User button (devicetree ${entry.button?.dtSpec}). */`);
1010
+ ts.push(`/** User button. */`);
904
1011
  ts.push(`export const BUTTON = ${buttonPin.ident};`);
905
1012
  }
906
1013
  if (ledPinFinal || buttonPin || extraPinDecls.length > 0)
907
1014
  ts.push('');
908
- // Bus instance exports from the board's own DTS-wired controllers.
1015
+ // Bus instance exports from the board's own DTS-wired controllers. When the
1016
+ // harvest parsed a controller's pinctrl overrides, the JSDoc carries its pad
1017
+ // routes ("SDA=PB7, SCL=PB8") — the same facts the pins.<bus> conflict-map
1018
+ // constants hold, surfaced for hover.
1019
+ const busPadPairs = (kind, nodelabel) => (entry.busPins ?? [])
1020
+ .filter((b) => b.bus === kind && b.nodelabel === nodelabel)
1021
+ .flatMap((b) => b.routes)
1022
+ .filter((r) => r.pad && r.role)
1023
+ .map((r) => `${r.role.toUpperCase()}=${r.pad}`);
1024
+ const busDoc = (kind, index, nodelabel) => {
1025
+ const label = kind === 'i2c' ? `I2C bus ${index}` : kind === 'spi' ? `SPI bus ${index}` : `UART ${index}`;
1026
+ const pads = busPadPairs(kind, nodelabel);
1027
+ return `Board-wired ${label}${pads.length > 0 ? ` (${pads.join(', ')})` : ''}.`;
1028
+ };
909
1029
  const busLines = [];
910
- buses.i2c.forEach((_, i) => busLines.push(`export const I2C${i} = new I2CBus('I2C${i}');`));
911
- buses.spi.forEach((_, i) => busLines.push(`export const SPI${i} = new SPIBus('SPI${i}');`));
912
- buses.uart.forEach((_, i) => busLines.push(`export const UART${i} = new UART('UART${i}');`));
1030
+ buses.i2c.forEach((nodelabel, i) => {
1031
+ busLines.push(`/** ${busDoc('i2c', i, nodelabel)} */`);
1032
+ busLines.push(`export const I2C${i} = new I2CBus('I2C${i}');`);
1033
+ });
1034
+ buses.spi.forEach((nodelabel, i) => {
1035
+ busLines.push(`/** ${busDoc('spi', i, nodelabel)} */`);
1036
+ busLines.push(`export const SPI${i} = new SPIBus('SPI${i}');`);
1037
+ });
1038
+ buses.uart.forEach((nodelabel, i) => {
1039
+ busLines.push(`/** ${busDoc('uart', i, nodelabel)} */`);
1040
+ busLines.push(`export const UART${i} = new UART('UART${i}');`);
1041
+ });
913
1042
  // USB CDC instance (the thin HAL's USBConsole): the board's DTS turns the
914
- // device controller on (zephyr_udc0 status okay).
915
- if (hasUsb)
1043
+ // device controller on (zephyr_udc0 status okay). CDC has no pinctrl pad
1044
+ // routes — the console rides the controller's silicon-fixed DP/DM pads.
1045
+ if (hasUsb) {
1046
+ busLines.push(`/** USB serial console — the board's USB connector (no pin to configure). */`);
916
1047
  busLines.push(`export const USB0 = new USBConsole('USB0');`);
1048
+ }
917
1049
  // PWM-driven LEDs (pwm-leds): addressed by the board's own devicetree
918
1050
  // alias (DT_ALIAS(pwm_led0) in the lowering) — no overlay needed. The
919
1051
  // construction period comes from the DTS cell when it is a plain number
@@ -922,7 +1054,7 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
922
1054
  pwmLedSpecs.forEach((l, i) => {
923
1055
  const period = l.periodNs ?? 1_000_000;
924
1056
  const name = i === 0 ? 'PWMLED' : `PWMLED${i}`;
925
- busLines.push(`/** Board PWM LED (devicetree ${l.alias}${l.flags?.length ? ', ' + l.flags.join(' ') : ''}). */`);
1057
+ busLines.push(`/** Board PWM-driven LED (already constructed — call setDuty() to dim it). */`);
926
1058
  busLines.push(`export const ${name} = new PWM(${8192 + i}, { periodNs: ${period} });`);
927
1059
  });
928
1060
  if (busLines.length > 0) {
@@ -1126,16 +1258,67 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
1126
1258
  });
1127
1259
  // Bus controllers the board's own DTS wires up — resolveChipFromBoard
1128
1260
  // reconstructs the bus lists from these, and the overlays enable the
1129
- // nodes the program uses.
1261
+ // nodes the program uses. Each controller also carries its board-DTS
1262
+ // pinctrl routes when the harvest parsed them (`pinctrl` = the route
1263
+ // names, `pads` = `role=PAD` pairs) — the diagnostics report's
1264
+ // peripheral-usage table and the pins.<bus> conflict-map read them.
1265
+ const busPads = (kind, nodelabel) => (entry.busPins ?? []).filter((b) => b.bus === kind && b.nodelabel === nodelabel).flatMap((b) => b.routes);
1266
+ const busPinMap = (kind) => {
1267
+ const parts = [];
1268
+ buses[kind].forEach((nodelabel, i) => {
1269
+ const pairs = busPads(kind, nodelabel)
1270
+ .filter((r) => r.pad && r.role)
1271
+ .map((r) => `${r.role}=${r.pad}`);
1272
+ if (pairs.length > 0)
1273
+ parts.push(`${i}:${pairs.join(',')}`);
1274
+ });
1275
+ return parts.join(';');
1276
+ };
1130
1277
  buses.i2c.forEach((nodelabel, i) => {
1131
1278
  constants[`zephyr.i2c.controllers.${i}.nodeLabel`] = nodelabel;
1279
+ const routes = busPads('i2c', nodelabel);
1280
+ if (routes.length > 0) {
1281
+ constants[`zephyr.i2c.controllers.${i}.pinctrl`] = routes.map((r) => r.name).join(',');
1282
+ constants[`zephyr.i2c.controllers.${i}.pads`] = routes.filter((r) => r.pad).map((r) => `${r.role ?? ''}=${r.pad}`).join(',');
1283
+ }
1132
1284
  });
1133
1285
  buses.spi.forEach((nodelabel, i) => {
1134
1286
  constants[`zephyr.spi.controllers.${i}.nodeLabel`] = nodelabel;
1287
+ const routes = busPads('spi', nodelabel);
1288
+ if (routes.length > 0) {
1289
+ constants[`zephyr.spi.controllers.${i}.pinctrl`] = routes.map((r) => r.name).join(',');
1290
+ constants[`zephyr.spi.controllers.${i}.pads`] = routes.filter((r) => r.pad).map((r) => `${r.role ?? ''}=${r.pad}`).join(',');
1291
+ }
1135
1292
  });
1136
1293
  buses.uart.forEach((nodelabel, i) => {
1137
1294
  constants[`zephyr.uart.controllers.${i}.nodeLabel`] = nodelabel;
1295
+ const routes = busPads('uart', nodelabel);
1296
+ if (routes.length > 0) {
1297
+ constants[`zephyr.uart.controllers.${i}.pinctrl`] = routes.map((r) => r.name).join(',');
1298
+ constants[`zephyr.uart.controllers.${i}.pads`] = routes.filter((r) => r.pad).map((r) => `${r.role ?? ''}=${r.pad}`).join(',');
1299
+ }
1138
1300
  });
1301
+ // The board's console controller (chosen zephyr,console) — the
1302
+ // diagnostics annotate the UART instance the bootloader logs on.
1303
+ if (entry.console)
1304
+ constants['zephyr.console'] = entry.console;
1305
+ // Bus pad conflict maps (analyzeResources): "<inst>:<role>=<PAD>,...;..."
1306
+ const i2cPinMap = busPinMap('i2c');
1307
+ const spiPinMap = busPinMap('spi');
1308
+ const uartPinMap = busPinMap('uart');
1309
+ if (i2cPinMap)
1310
+ constants['pins.i2c'] = i2cPinMap;
1311
+ if (spiPinMap)
1312
+ constants['pins.spi'] = spiPinMap;
1313
+ if (uartPinMap)
1314
+ constants['pins.uart'] = uartPinMap;
1315
+ // Board silicon identity for the diagnostics metadata (the report's
1316
+ // SRAM/flash context) — flash is a devicetree fact, sram/clock are not
1317
+ // harvested and stay absent.
1318
+ if (soc)
1319
+ constants['mcu'] = soc;
1320
+ if (entry.flashKb)
1321
+ constants['memory.flash'] = entry.flashKb * 1024;
1139
1322
  // USB device (CDC-ACM): the board's DTS turned the controller on.
1140
1323
  if (hasUsb) {
1141
1324
  constants['zephyr.usb.controller'] = entry.usbController ?? 'zephyr_udc0';
@@ -1202,7 +1385,7 @@ export function buildModule(entry, userFacts, factsSuffix = '', seedWarnings = [
1202
1385
  constants,
1203
1386
  // Source fingerprint: covers the record content, the extraction
1204
1387
  // revision, and the tree provenance (when the record came from a
1205
- // catalog). `cuttlefish build` recomputes it cheaply and regenerates
1388
+ // catalog). `typecad-hal build` recomputes it cheaply and regenerates
1206
1389
  // this module when it moves — a board change in the config, the catalog
1207
1390
  // overlay, or the Zephyr tree itself recreates the module.
1208
1391
  source: {
@@ -1227,8 +1410,8 @@ export function generateBoard(target, opts) {
1227
1410
  if (!found) {
1228
1411
  const hint = loadBoardCatalogOverlay()
1229
1412
  ? `'${target}' is not a board target in the current catalog. ` +
1230
- `It may be new in your Zephyr tree — run 'cuttlefish board sync' and retry.`
1231
- : `No board catalog on this machine. Run 'cuttlefish board sync' first.`;
1413
+ `It may be new in your Zephyr tree — run 'typecad-hal board sync' and retry.`
1414
+ : `No board catalog on this machine. Run 'typecad-hal board sync' first.`;
1232
1415
  throw new Error(hint);
1233
1416
  }
1234
1417
  // The as-built snapshot (this project's last successful build's resolved
@@ -1250,7 +1433,7 @@ export function generateBoard(target, opts) {
1250
1433
  asBuiltWarnings.push(`ignoring as-built snapshot: ${err.message}`);
1251
1434
  }
1252
1435
  }
1253
- // The project's cuttlefish.facts.json, when present: the section for THIS
1436
+ // The project's typecad-hal.facts.json, when present: the section for THIS
1254
1437
  // board merges into the manifest (user routes win per pin), and the raw
1255
1438
  // text hashes into the module's source fingerprint so edits regenerate.
1256
1439
  let facts;
@@ -436,7 +436,7 @@ export interface ZephyrChipDescriptor {
436
436
  * verified with the board, instead of in user configs. One probe method
437
437
  * serves BOTH flashing and debugging (the same attach session); entries
438
438
  * that cannot debug (bootloaders) set `debug: false`. Users select one via
439
- * `zephyr.probe` in cuttlefish.config.ts or `--probe` on the CLI;
439
+ * `zephyr.probe` in typecad-hal.config.ts or `--probe` on the CLI;
440
440
  * `zephyr.runner`/`runnerArgs` remain the raw escape hatch underneath.
441
441
  */
442
442
  readonly probeMethods?: readonly ZephyrProbeMethod[];
@@ -6,7 +6,7 @@
6
6
  // stdlibSupport.hasIostream=false), so the GenericStrategy's std::cout fallback
7
7
  // used by the debug preprocessor would NOT compile. This module routes every
8
8
  // debug line through printk("...\n") — the always-available Zephyr console,
9
- // with no CONFIG_CONSOLE dependency — so `cuttlefish build --debug` produces
9
+ // with no CONFIG_CONSOLE dependency — so `typecad-hal build --debug` produces
10
10
  // compiling output.
11
11
  //
12
12
  // The halt primitive is __tc_debug_wait_for_continue() (emitted in
@@ -1,5 +1,5 @@
1
1
  // ---------------------------------------------------------------------------
2
- // Zephyr touch adapters for the Cuttlefish UI rendering pipeline.
2
+ // Zephyr touch adapters for the TypeCAD UI rendering pipeline.
3
3
  //
4
4
  // Two controllers, both driven directly (polling, no in-tree driver):
5
5
  // - FT6336U capacitive over I2C (i2c_write_read_dt)