@typecad/framework-zephyr 1.0.0-alpha.16 → 1.0.0-alpha.19
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.
- package/LICENSE +202 -21
- package/README.md +49 -87
- package/dist/as-built.d.ts +2 -2
- package/dist/as-built.js +2 -2
- package/dist/audit.d.ts +111 -0
- package/dist/audit.js +416 -0
- package/dist/boardgen.d.ts +1 -9
- package/dist/boardgen.js +288 -47
- package/dist/chips/resolve.js +20 -0
- package/dist/chips/types.d.ts +22 -1
- package/dist/debug-codegen.js +1 -1
- package/dist/display/bindings.d.ts +55 -0
- package/dist/display/bindings.js +316 -0
- package/dist/display/gfx.d.ts +2 -3
- package/dist/display/gfx.js +166 -154
- package/dist/display/index.js +20 -1
- package/dist/display/mipi-dbi-host.d.ts +9 -0
- package/dist/display/mipi-dbi-host.js +174 -0
- package/dist/display/profiles.d.ts +109 -4
- package/dist/display/profiles.js +270 -7
- package/dist/display/touch-adapter.js +119 -49
- package/dist/display/ui-adapter-eink.d.ts +2 -0
- package/dist/display/ui-adapter-eink.js +4 -0
- package/dist/display/ui-adapter-gray.d.ts +8 -0
- package/dist/display/ui-adapter-gray.js +170 -0
- package/dist/display/ui-adapter-mono.d.ts +13 -0
- package/dist/display/ui-adapter-mono.js +230 -0
- package/dist/display/ui-adapter-native.d.ts +10 -0
- package/dist/display/ui-adapter-native.js +295 -0
- package/dist/display/ui-adapter-shared.d.ts +11 -0
- package/dist/display/ui-adapter-shared.js +122 -0
- package/dist/display/ui-adapter.js +510 -558
- package/dist/doctor.js +4 -4
- package/dist/dt-config/custom-board.js +2 -2
- package/dist/dt-config/kconfig.d.ts +62 -1
- package/dist/dt-config/kconfig.js +141 -38
- package/dist/dt-config/overlay.d.ts +15 -2
- package/dist/dt-config/overlay.js +433 -18
- package/dist/framework.manifest.d.ts +10 -4
- package/dist/framework.manifest.js +133 -17
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -6
- package/dist/licenses.d.ts +2 -2
- package/dist/licenses.js +13 -92
- package/dist/lowering/can.d.ts +25 -0
- package/dist/lowering/can.js +97 -0
- package/dist/lowering/clock.d.ts +17 -0
- package/dist/lowering/clock.js +58 -0
- package/dist/lowering/fs.js +1 -1
- package/dist/lowering/gpio.js +0 -32
- package/dist/lowering/hid.d.ts +27 -0
- package/dist/lowering/hid.js +244 -0
- package/dist/lowering/http.js +264 -32
- package/dist/lowering/i2c.d.ts +8 -0
- package/dist/lowering/i2c.js +137 -5
- package/dist/lowering/i2s.d.ts +27 -0
- package/dist/lowering/i2s.js +98 -0
- package/dist/lowering/index.d.ts +9 -1
- package/dist/lowering/index.js +25 -1
- package/dist/lowering/interrupts.js +6 -0
- package/dist/lowering/matrix.d.ts +15 -0
- package/dist/lowering/matrix.js +63 -0
- package/dist/lowering/mqtt.js +110 -8
- package/dist/lowering/power.d.ts +3 -2
- package/dist/lowering/power.js +20 -45
- package/dist/lowering/pwm.js +25 -0
- package/dist/lowering/sensor.d.ts +2 -2
- package/dist/lowering/sensor.js +8 -4
- package/dist/lowering/strip.d.ts +16 -0
- package/dist/lowering/strip.js +70 -0
- package/dist/lowering/thread.js +5 -1
- package/dist/lowering/trace.d.ts +44 -0
- package/dist/lowering/trace.js +239 -0
- package/dist/lowering/uart.js +6 -1
- package/dist/lowering/usb.d.ts +3 -1
- package/dist/lowering/usb.js +16 -13
- package/dist/lowering/wdt.js +2 -29
- package/dist/sbom.d.ts +181 -0
- package/dist/sbom.js +901 -0
- package/dist/sdk/board-catalog-sync.d.ts +1 -3
- package/dist/sdk/board-catalog-sync.js +4 -10
- package/dist/strategy.d.ts +82 -46
- package/dist/strategy.js +639 -182
- package/dist/tmp-probe.d.ts +2 -0
- package/dist/tmp-probe.js +9 -0
- package/dist/toolchain/debug-config.d.ts +50 -90
- package/dist/toolchain/debug-config.js +239 -510
- package/dist/toolchain/env-check.d.ts +1 -3
- package/dist/toolchain/env-check.js +2 -7
- package/dist/toolchain/index.d.ts +28 -3
- package/dist/toolchain/index.js +498 -62
- package/dist/toolchain/runners.d.ts +16 -0
- package/dist/toolchain/runners.js +75 -0
- package/dist/toolchain/scaffold.d.ts +5 -2
- package/dist/toolchain/scaffold.js +79 -15
- package/dist/toolchain/west-discover.d.ts +6 -0
- package/dist/toolchain/west-discover.js +36 -13
- package/dist/toolchain/west-spawn.js +8 -2
- package/dist/west-inventory.d.ts +25 -0
- package/dist/west-inventory.js +97 -0
- package/installer/README.md +328 -328
- package/installer/install.sh +2 -2
- package/installer/templates/project/.typecad/activate-zephyr.ps1 +1 -1
- package/installer/templates/project/.typecad/activate-zephyr.sh +1 -1
- package/installer/templates/project/.vscode/settings.json +1 -1
- package/installer/templates/project/README.md +2 -2
- package/package.json +5 -5
- package/src/as-built.ts +206 -206
- package/src/audit.ts +529 -0
- package/src/boardgen.ts +265 -50
- package/src/chips/resolve.ts +21 -0
- package/src/chips/types.ts +10 -1
- package/src/display/bindings.ts +347 -0
- package/src/display/gfx.ts +318 -306
- package/src/display/index.ts +87 -70
- package/src/display/mipi-dbi-host.ts +183 -0
- package/src/display/profiles.ts +458 -139
- package/src/display/touch-adapter.ts +119 -49
- package/src/display/ui-adapter-eink.ts +13 -0
- package/src/display/ui-adapter-gray.ts +178 -0
- package/src/display/ui-adapter-mono.ts +238 -0
- package/src/display/ui-adapter-native.ts +304 -0
- package/src/display/ui-adapter-shared.ts +125 -0
- package/src/display/ui-adapter.ts +732 -781
- package/src/doctor.ts +4 -4
- package/src/dt-config/custom-board.ts +2 -2
- package/src/dt-config/kconfig.ts +647 -505
- package/src/dt-config/overlay.ts +1475 -1058
- package/src/framework.manifest.ts +659 -535
- package/src/index.ts +16 -6
- package/src/licenses.ts +346 -425
- package/src/lowering/can.ts +140 -0
- package/src/lowering/clock.ts +91 -0
- package/src/lowering/fs.ts +135 -135
- package/src/lowering/gpio.ts +0 -33
- package/src/lowering/hid.ts +261 -0
- package/src/lowering/http.ts +264 -32
- package/src/lowering/i2c.ts +142 -5
- package/src/lowering/i2s.ts +143 -0
- package/src/lowering/index.ts +18 -1
- package/src/lowering/interrupts.ts +6 -0
- package/src/lowering/matrix.ts +70 -0
- package/src/lowering/mqtt.ts +109 -8
- package/src/lowering/power.ts +41 -0
- package/src/lowering/pwm.ts +192 -167
- package/src/lowering/sensor.ts +159 -155
- package/src/lowering/strip.ts +81 -0
- package/src/lowering/thread.ts +5 -1
- package/src/lowering/trace.ts +270 -0
- package/src/lowering/uart.ts +6 -1
- package/src/lowering/usb.ts +16 -13
- package/src/lowering/wdt.ts +2 -25
- package/src/sbom.ts +1117 -0
- package/src/sdk/board-catalog-sync.ts +4 -25
- package/src/strategy.ts +2680 -2309
- package/src/toolchain/debug-config.ts +262 -522
- package/src/toolchain/env-check.ts +279 -285
- package/src/toolchain/index.ts +1792 -1359
- package/src/toolchain/runners.ts +80 -0
- package/src/toolchain/scaffold.ts +355 -296
- package/src/toolchain/west-discover.ts +35 -13
- package/src/toolchain/west-spawn.ts +174 -168
- package/src/west-inventory.ts +102 -0
- package/dist/async/timer-polyfill.d.ts +0 -10
- package/dist/async/timer-polyfill.js +0 -95
- package/dist/chips/board-overrides.d.ts +0 -7
- package/dist/chips/board-overrides.js +0 -11
- package/dist/chips/esp32.d.ts +0 -2
- package/dist/chips/esp32.js +0 -71
- package/dist/chips/esp32s3.d.ts +0 -2
- package/dist/chips/esp32s3.js +0 -103
- package/dist/chips/soc/.d.ts +0 -2
- package/dist/chips/soc/.js +0 -129
- package/dist/chips/soc/esp32.d.ts +0 -2
- package/dist/chips/soc/esp32.js +0 -120
- package/dist/chips/soc/esp32c3.d.ts +0 -2
- package/dist/chips/soc/esp32c3.js +0 -90
- package/dist/chips/soc/esp32c6.d.ts +0 -2
- package/dist/chips/soc/esp32c6.js +0 -109
- package/dist/chips/soc/esp32s3.d.ts +0 -2
- package/dist/chips/soc/esp32s3.js +0 -189
- package/dist/chips/soc/index.d.ts +0 -2
- package/dist/chips/soc/index.js +0 -23
- package/dist/chips/soc/nrf52840.d.ts +0 -2
- package/dist/chips/soc/nrf52840.js +0 -130
- package/dist/chips/soc/rp2040.d.ts +0 -2
- package/dist/chips/soc/rp2040.js +0 -141
- package/dist/chips/soc/rp2350a.d.ts +0 -2
- package/dist/chips/soc/rp2350a.js +0 -145
- package/dist/chips/soc/samd21g18a.d.ts +0 -2
- package/dist/chips/soc/samd21g18a.js +0 -143
- package/dist/chips/soc/stm32f411xe.d.ts +0 -2
- package/dist/chips/soc/stm32f411xe.js +0 -251
- package/dist/chips/xiao-ble.d.ts +0 -2
- package/dist/chips/xiao-ble.js +0 -100
- package/dist/lowering/pulse.d.ts +0 -7
- package/dist/lowering/pulse.js +0 -51
- package/dist/lowering/tone.d.ts +0 -10
- package/dist/lowering/tone.js +0 -63
- package/dist/lowering/worker-backing.d.ts +0 -14
- package/dist/lowering/worker-backing.js +0 -79
- package/dist/lowering/worker.d.ts +0 -6
- package/dist/lowering/worker.js +0 -14
- package/dist/sdk/board-data.generated.d.ts +0 -2
- package/dist/sdk/board-data.generated.js +0 -4
- package/dist/sdk/catalog-walker.d.ts +0 -90
- package/dist/sdk/catalog-walker.js +0 -682
- package/dist/sdk/dts-reader.d.ts +0 -83
- package/dist/sdk/dts-reader.js +0 -596
- package/src/debug-codegen.ts +0 -207
package/src/boardgen.ts
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
|
-
// .
|
|
10
|
-
// @typecad/
|
|
11
|
-
//
|
|
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,7 +23,7 @@
|
|
|
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 `
|
|
26
|
+
// the strategy hook, or `typecad-hal board regen`) writes them.
|
|
26
27
|
// ----------------------------------------------------------------------------
|
|
27
28
|
|
|
28
29
|
import type { BoardDataEntry } from '@typecad/cuttlefish/board-catalog';
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
socBusLabelsFromTree,
|
|
37
38
|
} from '@typecad/cuttlefish/board-catalog';
|
|
38
39
|
import type { ZephyrGpioController, ZephyrProbeMethod } from './chips/types.js';
|
|
40
|
+
import { getBoardGateData } from '@typecad/cuttlefish/board-gate';
|
|
39
41
|
|
|
40
42
|
/** A generated pin: schematic name, JS-safe identifier, HAL number. */
|
|
41
43
|
interface GenPin {
|
|
@@ -56,7 +58,7 @@ export interface GeneratedBoard {
|
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
60
|
* The catalog lookups resolve against the local overlay — the user's own
|
|
59
|
-
* Zephyr tree (`
|
|
61
|
+
* Zephyr tree (`typecad-hal board sync`, auto-refreshed when the tree
|
|
60
62
|
* moves). There is no compiled-in database: a machine with no tree and no
|
|
61
63
|
* overlay has no boards, and the error below says exactly that.
|
|
62
64
|
*/
|
|
@@ -65,7 +67,7 @@ function activeBoardData(): Record<string, BoardDataEntry> {
|
|
|
65
67
|
if (!data) {
|
|
66
68
|
throw new Error(
|
|
67
69
|
`No board catalog on this machine. The catalog is generated from your Zephyr tree —\n` +
|
|
68
|
-
`run '
|
|
70
|
+
`run 'typecad-hal board sync' (or point TYPECAD_HAL_BOARD_CATALOG at a catalog file).`,
|
|
69
71
|
);
|
|
70
72
|
}
|
|
71
73
|
return data;
|
|
@@ -108,7 +110,7 @@ export function nrfSaadcAinPads(soc: string, identifier: string): readonly [numb
|
|
|
108
110
|
* S3/C3/C6/H2 dropped it. Sourced from the HAL's dac_periph.c
|
|
109
111
|
* (`dac_channel_io_num[]`), like the nRF SAADC table is from the PS.
|
|
110
112
|
*/
|
|
111
|
-
|
|
113
|
+
function esp32DacPins(soc: string): readonly [number, number][] | undefined {
|
|
112
114
|
const table: Record<string, readonly [number, number][]> = {
|
|
113
115
|
esp32: [[0, 25], [1, 26]],
|
|
114
116
|
esp32s2: [[0, 17], [1, 18]],
|
|
@@ -439,7 +441,7 @@ export function parsePinName(soc: string, name: string): { controller: string; p
|
|
|
439
441
|
* generated off the installed SDK's tree) and contract records (custom PCB
|
|
440
442
|
* specs) both route through here.
|
|
441
443
|
*/
|
|
442
|
-
// ── User facts (
|
|
444
|
+
// ── User facts (typecad-hal.facts.json) ──────────────────────────────────────
|
|
443
445
|
// The project-local escape hatch: facts the pipeline has not (or cannot)
|
|
444
446
|
// harvest, declared per board and merged into the manifest BEFORE anything
|
|
445
447
|
// else runs — board module exports, capability flags, validation, lowering
|
|
@@ -464,13 +466,13 @@ export interface UserBoardFacts {
|
|
|
464
466
|
};
|
|
465
467
|
}
|
|
466
468
|
|
|
467
|
-
/** The whole
|
|
469
|
+
/** The whole typecad-hal.facts.json shape. */
|
|
468
470
|
export interface UserFactsFile {
|
|
469
471
|
boards: Record<string, UserBoardFacts>;
|
|
470
472
|
}
|
|
471
473
|
|
|
472
474
|
/** Parse + shape-validate the facts file; a clear error names the file. */
|
|
473
|
-
export function parseUserFactsJson(text: string, source = '
|
|
475
|
+
export function parseUserFactsJson(text: string, source = 'typecad-hal.facts.json'): UserFactsFile {
|
|
474
476
|
let parsed: unknown;
|
|
475
477
|
try {
|
|
476
478
|
parsed = JSON.parse(text);
|
|
@@ -511,7 +513,11 @@ export function buildModule(
|
|
|
511
513
|
userFacts?: UserBoardFacts,
|
|
512
514
|
factsSuffix = '',
|
|
513
515
|
seedWarnings: readonly string[] = [],
|
|
514
|
-
): GeneratedBoard {
|
|
516
|
+
): GeneratedBoard {
|
|
517
|
+
// The ungated surface is derived from the project's own hal copy — same
|
|
518
|
+
// resolution (project → cwd → monorepo sibling) as the engine's HAL parser.
|
|
519
|
+
const { ungated: BOARD_UNGATED_EXPORTS, ungatedTypes: BOARD_UNGATED_TYPE_EXPORTS } = getBoardGateData();
|
|
520
|
+
const soc = socOfTarget(entry.identifier);
|
|
515
521
|
const conv = namingConvFor(soc);
|
|
516
522
|
|
|
517
523
|
// ── Controller table + datasheet sweep (the same for every board) ──────
|
|
@@ -667,7 +673,7 @@ export function buildModule(
|
|
|
667
673
|
if (!adcSources.includes(r.source)) adcSources.push(r.source);
|
|
668
674
|
}
|
|
669
675
|
}
|
|
670
|
-
// User facts (
|
|
676
|
+
// User facts (typecad-hal.facts.json) — the escape hatch. User routes win
|
|
671
677
|
// PER PIN over every harvested source above, and each takeover is warned.
|
|
672
678
|
// Seeded with the pinctrl harvest's own lint results (name↔value
|
|
673
679
|
// disagreements — routes dropped as untrustworthy upstream of here).
|
|
@@ -735,7 +741,7 @@ export function buildModule(
|
|
|
735
741
|
}
|
|
736
742
|
if (siliconDac.length > 0 && !dacSources.includes('dac')) dacSources.push('dac');
|
|
737
743
|
}
|
|
738
|
-
// User facts (
|
|
744
|
+
// User facts (typecad-hal.facts.json): dac channels win per pin, and the
|
|
739
745
|
// declared device joins the sources (no analogDevices cross-check — the
|
|
740
746
|
// user vouches for it).
|
|
741
747
|
if (userFacts?.dac) {
|
|
@@ -858,7 +864,7 @@ export function buildModule(
|
|
|
858
864
|
...pins.map((p) => p.ident),
|
|
859
865
|
'LED',
|
|
860
866
|
'BUTTON',
|
|
861
|
-
// '@typecad/hal' imports — a connector silkscreen label can literally be
|
|
867
|
+
// '@typecad/hal/core' imports — a connector silkscreen label can literally be
|
|
862
868
|
// 'Pin' (phyBOARD-Atlas), and `export const Pin` merges with the import.
|
|
863
869
|
'Pin',
|
|
864
870
|
'I2CBus',
|
|
@@ -866,6 +872,9 @@ export function buildModule(
|
|
|
866
872
|
'UART',
|
|
867
873
|
'USBConsole',
|
|
868
874
|
'PWM',
|
|
875
|
+
// The ungated re-export block: a label colliding with any re-exported
|
|
876
|
+
// hal name would be a duplicate module export.
|
|
877
|
+
...BOARD_UNGATED_EXPORTS,
|
|
869
878
|
]);
|
|
870
879
|
buses.i2c.forEach((_, i) => reservedNames.add(`I2C${i}`));
|
|
871
880
|
buses.spi.forEach((_, i) => reservedNames.add(`SPI${i}`));
|
|
@@ -879,40 +888,144 @@ export function buildModule(
|
|
|
879
888
|
connectorLabelOwner.set(label, pin);
|
|
880
889
|
}
|
|
881
890
|
|
|
891
|
+
// ── Per-pin editor annotations ──────────────────────────────────────────
|
|
892
|
+
// The same harvested facts board.json carries, stamped as JSDoc on the
|
|
893
|
+
// datasheet pin exports so plain tsserver surfaces them in hover and
|
|
894
|
+
// completion detail — board intelligence with zero editor extension. Only
|
|
895
|
+
// pins WITH a fact (silicon route, any-pad matrix membership, bus role, or
|
|
896
|
+
// an alias) get a doc line: bare pads stay bare, and the absence of an
|
|
897
|
+
// annotation says "plain GPIO" without stamping it on every line. Facts and
|
|
898
|
+
// module derive from the same route sets below, so they cannot disagree.
|
|
899
|
+
const pwmRouteByPin = new Map(siliconPwm.map((s) => [s.pin, s]));
|
|
900
|
+
const adcRouteByPin = new Map(siliconAdc.map((s) => [s.pin, s]));
|
|
901
|
+
const dacRouteByPin = new Map(siliconDac.map((s) => [s.pin, s]));
|
|
902
|
+
// Any-pad PWM matrices (ESP32 LEDC, nRF psel, RP2 slices) ride global pad
|
|
903
|
+
// numbers — same base arithmetic the manifest's matrix pins use.
|
|
904
|
+
const matrixBase = controllers.find((c) => c.nodelabel === 'gpio0')?.minPin ?? 0;
|
|
905
|
+
const matrixPwmPins = pwmMatrix
|
|
906
|
+
? new Set(pwmMatrix.pads.map((pad) => matrixBase + pad))
|
|
907
|
+
: undefined;
|
|
908
|
+
// Aliases (connector silkscreen labels, LED/BUTTON) keyed by the aliased
|
|
909
|
+
// pin's HAL number — connectorLabelOwner's values ARE the sweep's GenPin
|
|
910
|
+
// objects, so identity holds through the LED/BUTTON facts too.
|
|
911
|
+
const aliasesByPin = new Map<number, string[]>();
|
|
912
|
+
const noteAlias = (pin: GenPin | undefined, label: string): void => {
|
|
913
|
+
if (!pin) return;
|
|
914
|
+
const list = aliasesByPin.get(pin.halPin);
|
|
915
|
+
if (list) list.push(label);
|
|
916
|
+
else aliasesByPin.set(pin.halPin, [label]);
|
|
917
|
+
};
|
|
918
|
+
for (const [label, pin] of connectorLabelOwner) noteAlias(pin, label);
|
|
919
|
+
if (ledPinFinal) noteAlias(ledPinFinal, 'LED');
|
|
920
|
+
if (buttonPin) noteAlias(buttonPin, 'BUTTON');
|
|
921
|
+
extraLedPins.forEach((p, i) => noteAlias(p, `LED${i + 1}`));
|
|
922
|
+
extraButtonPins.forEach((p, i) => noteAlias(p, `BUTTON${i + 1}`));
|
|
923
|
+
// Bus roles (SDA/SCL/MOSI/…) keyed by the route's pad NAME — best-effort
|
|
924
|
+
// uppercase match; pads whose spelling the pin sweep can't place simply
|
|
925
|
+
// carry no role line. (busPads is defined further down; this walks
|
|
926
|
+
// entry.busPins directly to stay above it.)
|
|
927
|
+
const rolesByPinName = new Map<string, string[]>();
|
|
928
|
+
for (const b of entry.busPins ?? []) {
|
|
929
|
+
const index = buses[b.bus]?.indexOf(b.nodelabel) ?? -1;
|
|
930
|
+
if (index < 0) continue;
|
|
931
|
+
const instance = `${b.bus.toUpperCase()}${index}`;
|
|
932
|
+
for (const route of b.routes) {
|
|
933
|
+
if (!route.pad || !route.role) continue;
|
|
934
|
+
const name = route.pad.toUpperCase();
|
|
935
|
+
const role = `${instance} ${route.role.toUpperCase()}`;
|
|
936
|
+
const list = rolesByPinName.get(name);
|
|
937
|
+
if (list) {
|
|
938
|
+
if (!list.includes(role)) list.push(role);
|
|
939
|
+
} else {
|
|
940
|
+
rolesByPinName.set(name, [role]);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
const pinDoc = (p: GenPin): string | undefined => {
|
|
945
|
+
const parts: string[] = [];
|
|
946
|
+
const pwm = pwmRouteByPin.get(p.halPin);
|
|
947
|
+
if (pwm) parts.push(`PWM ${pwm.controller} ch${pwm.channel}`);
|
|
948
|
+
else if (pwmMatrix && matrixPwmPins?.has(p.halPin)) {
|
|
949
|
+
parts.push(`PWM ${pwmMatrix.controller} ch0-${pwmMatrix.channelCount - 1} (any pad)`);
|
|
950
|
+
}
|
|
951
|
+
const adc = adcRouteByPin.get(p.halPin);
|
|
952
|
+
// Primary-unit routes (pinctrl and ESP SARADC alike) omit the controller
|
|
953
|
+
// field — fall back to the pinctrl sources, then the ESP primary unit.
|
|
954
|
+
if (adc) parts.push(`ADC ${adc.controller ?? adcSources[0] ?? espAdcUnit ?? 'adc'} ch${adc.channel}`);
|
|
955
|
+
const dac = dacRouteByPin.get(p.halPin);
|
|
956
|
+
if (dac) parts.push(`DAC ch${dac.channel}`);
|
|
957
|
+
parts.push(...(rolesByPinName.get(p.name.toUpperCase()) ?? []));
|
|
958
|
+
const aliases = aliasesByPin.get(p.halPin);
|
|
959
|
+
if (aliases) parts.push(`aliases: ${aliases.join(', ')}`);
|
|
960
|
+
return parts.length > 0 ? parts.join(' · ') : undefined;
|
|
961
|
+
};
|
|
962
|
+
|
|
882
963
|
const ts: string[] = [];
|
|
883
|
-
ts.push(`// GENERATED by
|
|
964
|
+
ts.push(`// GENERATED by typecad-hal boardgen from the Zephyr board catalog —`);
|
|
884
965
|
ts.push(`// ${entry.identifier} (${entry.name}, ${entry.vendor}).`);
|
|
885
|
-
ts.push(`// Regenerate with: npx
|
|
966
|
+
ts.push(`// Regenerate with: npx typecad-hal board regen`);
|
|
886
967
|
ts.push('');
|
|
887
|
-
|
|
968
|
+
// The generated module IS the user's '@typecad/hal' (the project tsconfig
|
|
969
|
+
// maps that specifier here), so it reaches the implementation package via
|
|
970
|
+
// the './core' subpath — the one specifier the paths mapping does not
|
|
971
|
+
// capture, avoiding a circular self-reference.
|
|
972
|
+
ts.push(`import { Pin, I2CBus, SPIBus, UART${hasUsb ? ', USBConsole' : ''}${pwmLedSpecs.length > 0 ? ', PWM' : ''} } from '@typecad/hal/core';`);
|
|
888
973
|
ts.push('');
|
|
889
974
|
// ── Hardware-class gateway ─────────────────────────────────────────────
|
|
890
|
-
//
|
|
891
|
-
//
|
|
892
|
-
//
|
|
893
|
-
//
|
|
894
|
-
//
|
|
975
|
+
// This module is the NARROWED surface behind the user's '@typecad/hal'
|
|
976
|
+
// import: every hardware class gated on board facts is re-exported below
|
|
977
|
+
// only when this board's facts support it, so importing unavailable
|
|
978
|
+
// hardware fails at module resolution (editor + transpile) instead of at
|
|
979
|
+
// a deep diagnostic. Everything not gated (the lists hal ships in
|
|
980
|
+
// gate.ts) is re-exported verbatim so the full authoring surface stays
|
|
981
|
+
// importable from the same specifier ('@typecad/hal' is the one specifier —
|
|
982
|
+
// the old '@typecad/board' alias was removed with the rename).
|
|
983
|
+
ts.push('// Always-available HAL surface (not gated on board facts).');
|
|
984
|
+
ts.push(`export { ${BOARD_UNGATED_EXPORTS.join(', ')} } from '@typecad/hal/core';`);
|
|
985
|
+
ts.push(`export type { ${BOARD_UNGATED_TYPE_EXPORTS.join(', ')} } from '@typecad/hal/core';`);
|
|
986
|
+
ts.push('');
|
|
895
987
|
ts.push('// Hardware this board actually has — unavailable hardware is not importable.');
|
|
896
|
-
ts.push(`export {
|
|
897
|
-
if (
|
|
898
|
-
if (
|
|
899
|
-
if (
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
if (buses.
|
|
904
|
-
if (
|
|
905
|
-
if (
|
|
988
|
+
if (wdtNodeLabel) ts.push(`export { Watchdog } from '@typecad/hal/core';`);
|
|
989
|
+
if (siliconPwm.length > 0 || pwmLedSpecs.length > 0 || pwmMatrix) ts.push(`export { PWM, Servo } from '@typecad/hal/core';`);
|
|
990
|
+
if (siliconAdc.length > 0) ts.push(`export { ADC } from '@typecad/hal/core';`);
|
|
991
|
+
if (siliconDac.length > 0) ts.push(`export { DAC } from '@typecad/hal/core';`);
|
|
992
|
+
// The responder rides the same wired-bus fact as I2CTarget — target-mode
|
|
993
|
+
// support is a controller-driver property discovered at registration
|
|
994
|
+
// (-ENOSYS prints loudly), never a board-name gate.
|
|
995
|
+
if (buses.i2c.length > 0) ts.push(`export { I2CTarget, I2CResponder } from '@typecad/hal/core';`);
|
|
996
|
+
if (buses.spi.length > 0) ts.push(`export { SPITarget } from '@typecad/hal/core';`);
|
|
997
|
+
if (buses.uart.length > 0) ts.push(`export { UART } from '@typecad/hal/core';`);
|
|
998
|
+
if (hwtimerControllers.length > 0) ts.push(`export { Counter } from '@typecad/hal/core';`);
|
|
999
|
+
// Clock rides the same free-counter facts (the zephyr,rtc-counter shim
|
|
1000
|
+
// wraps the counter when the board ships no hardware rtc alias).
|
|
1001
|
+
if (hwtimerControllers.length > 0) ts.push(`export { Clock } from '@typecad/hal/core';`);
|
|
1002
|
+
// CAN rides the harvested controller nodes (ESP32 TWAI, STM32 bxCAN,
|
|
1003
|
+
// NXP FlexCAN…); a SoC with none exports nothing.
|
|
1004
|
+
if ((entry.canNodes ?? []).length > 0) ts.push(`export { CAN } from '@typecad/hal/core';`);
|
|
1005
|
+
// I2S rides the harvested i2s@ controllers.
|
|
1006
|
+
if ((entry.i2sNodes ?? []).length > 0) ts.push(`export { I2S } from '@typecad/hal/core';`);
|
|
1007
|
+
// Power rides the SoC's declared cpu-power-states (both the ESP32 family's
|
|
1008
|
+
// standby + soft-off pair and STM32's suspend-to-idle count; a SoC with no
|
|
1009
|
+
// declared states exports nothing).
|
|
1010
|
+
if ((entry.powerStates ?? []).length > 0) ts.push(`export { Power } from '@typecad/hal/core';`);
|
|
1011
|
+
// KEY/MOUSE are ungated token tables — the derived always-available
|
|
1012
|
+
// re-export line above already carries them on every board; only the HID
|
|
1013
|
+
// device classes ride the USB gate.
|
|
1014
|
+
if (hasUsb) ts.push(`export { USBConsole, Keyboard, Mouse } from '@typecad/hal/core';`);
|
|
906
1015
|
// Store/File: a persisted backend needs a storage region — either the
|
|
907
1016
|
// board's own storage_partition (harvested reg) or a synthesizable one
|
|
908
1017
|
// (flash size known, no existing partition to collide with).
|
|
909
1018
|
if (entry.storageReg || (entry.flashKb && !entry.hasStoragePartition)) {
|
|
910
|
-
ts.push(`export { Store, File } from '@typecad/hal';`);
|
|
1019
|
+
ts.push(`export { Store, File } from '@typecad/hal/core';`);
|
|
911
1020
|
}
|
|
912
1021
|
ts.push('');
|
|
913
1022
|
if (pins.length > 0) {
|
|
914
1023
|
ts.push(`// Datasheet-named pins (derived from the board's devicetree controllers)`);
|
|
1024
|
+
ts.push(`// Pins with harvested facts carry them as JSDoc (hover shows routes/roles/aliases);`);
|
|
1025
|
+
ts.push(`// a pin without a doc line is plain GPIO — no PWM/ADC/DAC route on this board.`);
|
|
915
1026
|
for (const p of pins) {
|
|
1027
|
+
const doc = pinDoc(p);
|
|
1028
|
+
if (doc) ts.push(`/** ${doc} */`);
|
|
916
1029
|
ts.push(`export const ${p.ident} = Pin.fromPort('${p.name}');`);
|
|
917
1030
|
}
|
|
918
1031
|
ts.push('');
|
|
@@ -932,27 +1045,53 @@ export function buildModule(
|
|
|
932
1045
|
if (!pins.some((x) => x.ident === p.ident) && !extraPinDecls.some((x) => x.ident === p.ident)) extraPinDecls.push(p);
|
|
933
1046
|
}
|
|
934
1047
|
for (const p of extraPinDecls) {
|
|
935
|
-
ts.push(`/** ${p.name} (derived from the devicetree controller label). */`);
|
|
936
1048
|
ts.push(`export const ${p.ident} = Pin.fromPort('${p.name}');`);
|
|
937
1049
|
}
|
|
938
1050
|
if (ledPinFinal) {
|
|
939
|
-
ts.push(`/** On-board LED
|
|
1051
|
+
ts.push(`/** On-board LED${entry.led ? '' : ' — addressable strip, this is its data pin'}. */`);
|
|
940
1052
|
ts.push(`export const LED = ${ledPinFinal.ident};`);
|
|
941
1053
|
}
|
|
942
1054
|
if (buttonPin) {
|
|
943
|
-
ts.push(`/** User button
|
|
1055
|
+
ts.push(`/** User button. */`);
|
|
944
1056
|
ts.push(`export const BUTTON = ${buttonPin.ident};`);
|
|
945
1057
|
}
|
|
946
1058
|
if (ledPinFinal || buttonPin || extraPinDecls.length > 0) ts.push('');
|
|
947
1059
|
|
|
948
|
-
// Bus instance exports from the board's own DTS-wired controllers.
|
|
1060
|
+
// Bus instance exports from the board's own DTS-wired controllers. When the
|
|
1061
|
+
// harvest parsed a controller's pinctrl overrides, the JSDoc carries its pad
|
|
1062
|
+
// routes ("SDA=PB7, SCL=PB8") — the same facts the pins.<bus> conflict-map
|
|
1063
|
+
// constants hold, surfaced for hover.
|
|
1064
|
+
const busPadPairs = (kind: 'i2c' | 'spi' | 'uart', nodelabel: string): string[] =>
|
|
1065
|
+
(entry.busPins ?? [])
|
|
1066
|
+
.filter((b) => b.bus === kind && b.nodelabel === nodelabel)
|
|
1067
|
+
.flatMap((b) => b.routes)
|
|
1068
|
+
.filter((r) => r.pad && r.role)
|
|
1069
|
+
.map((r) => `${r.role!.toUpperCase()}=${r.pad}`);
|
|
1070
|
+
const busDoc = (kind: 'i2c' | 'spi' | 'uart', index: number, nodelabel: string): string => {
|
|
1071
|
+
const label = kind === 'i2c' ? `I2C bus ${index}` : kind === 'spi' ? `SPI bus ${index}` : `UART ${index}`;
|
|
1072
|
+
const pads = busPadPairs(kind, nodelabel);
|
|
1073
|
+
return `Board-wired ${label}${pads.length > 0 ? ` (${pads.join(', ')})` : ''}.`;
|
|
1074
|
+
};
|
|
949
1075
|
const busLines: string[] = [];
|
|
950
|
-
buses.i2c.forEach((
|
|
951
|
-
|
|
952
|
-
|
|
1076
|
+
buses.i2c.forEach((nodelabel, i) => {
|
|
1077
|
+
busLines.push(`/** ${busDoc('i2c', i, nodelabel)} */`);
|
|
1078
|
+
busLines.push(`export const I2C${i} = new I2CBus('I2C${i}');`);
|
|
1079
|
+
});
|
|
1080
|
+
buses.spi.forEach((nodelabel, i) => {
|
|
1081
|
+
busLines.push(`/** ${busDoc('spi', i, nodelabel)} */`);
|
|
1082
|
+
busLines.push(`export const SPI${i} = new SPIBus('SPI${i}');`);
|
|
1083
|
+
});
|
|
1084
|
+
buses.uart.forEach((nodelabel, i) => {
|
|
1085
|
+
busLines.push(`/** ${busDoc('uart', i, nodelabel)} */`);
|
|
1086
|
+
busLines.push(`export const UART${i} = new UART('UART${i}');`);
|
|
1087
|
+
});
|
|
953
1088
|
// USB CDC instance (the thin HAL's USBConsole): the board's DTS turns the
|
|
954
|
-
// device controller on (zephyr_udc0 status okay).
|
|
955
|
-
|
|
1089
|
+
// device controller on (zephyr_udc0 status okay). CDC has no pinctrl pad
|
|
1090
|
+
// routes — the console rides the controller's silicon-fixed DP/DM pads.
|
|
1091
|
+
if (hasUsb) {
|
|
1092
|
+
busLines.push(`/** USB serial console — the board's USB connector (no pin to configure). */`);
|
|
1093
|
+
busLines.push(`export const USB0 = new USBConsole('USB0');`);
|
|
1094
|
+
}
|
|
956
1095
|
|
|
957
1096
|
// PWM-driven LEDs (pwm-leds): addressed by the board's own devicetree
|
|
958
1097
|
// alias (DT_ALIAS(pwm_led0) in the lowering) — no overlay needed. The
|
|
@@ -962,7 +1101,7 @@ export function buildModule(
|
|
|
962
1101
|
pwmLedSpecs.forEach((l, i) => {
|
|
963
1102
|
const period = l.periodNs ?? 1_000_000;
|
|
964
1103
|
const name = i === 0 ? 'PWMLED' : `PWMLED${i}`;
|
|
965
|
-
busLines.push(`/** Board PWM LED (
|
|
1104
|
+
busLines.push(`/** Board PWM-driven LED (already constructed — call setDuty() to dim it). */`);
|
|
966
1105
|
busLines.push(`export const ${name} = new PWM(${8192 + i}, { periodNs: ${period} });`);
|
|
967
1106
|
});
|
|
968
1107
|
if (busLines.length > 0) {
|
|
@@ -986,12 +1125,15 @@ export function buildModule(
|
|
|
986
1125
|
// GPIO driver API (`gpio_pin_configure` flags + `gpio_pin_interrupt_configure`):
|
|
987
1126
|
// every GPIO controller implements them, so they are universally true —
|
|
988
1127
|
// a platform fact, not a per-pad one. Analog/PWM ride the harvested
|
|
989
|
-
// silicon routes (honest per-pad facts)
|
|
1128
|
+
// silicon routes (honest per-pad facts); PWM counts both route kinds —
|
|
1129
|
+
// fixed pinctrl routes AND any-pad matrices (ESP32 LEDC, nRF psel), the
|
|
1130
|
+
// same set union the pin JSDoc and the PWM export gate read, so the
|
|
1131
|
+
// hover annotation and the capability flag cannot disagree.
|
|
990
1132
|
constants[`pins.all.${i}.capabilities.digitalInput`] = true;
|
|
991
1133
|
constants[`pins.all.${i}.capabilities.digitalOutput`] = true;
|
|
992
1134
|
constants[`pins.all.${i}.capabilities.analogInput`] = siliconAdcPins.has(p.halPin);
|
|
993
1135
|
constants[`pins.all.${i}.capabilities.analogOutput`] = siliconDacPins.has(p.halPin);
|
|
994
|
-
constants[`pins.all.${i}.capabilities.pwm`] = siliconPwmPins.has(p.halPin);
|
|
1136
|
+
constants[`pins.all.${i}.capabilities.pwm`] = siliconPwmPins.has(p.halPin) || (matrixPwmPins?.has(p.halPin) ?? false);
|
|
995
1137
|
constants[`pins.all.${i}.capabilities.interrupt`] = true;
|
|
996
1138
|
constants[`pins.all.${i}.capabilities.pullUp`] = true;
|
|
997
1139
|
constants[`pins.all.${i}.capabilities.pullDown`] = true;
|
|
@@ -1000,6 +1142,34 @@ export function buildModule(
|
|
|
1000
1142
|
constants[`pins.all.${i}.capabilities.touch`] = false;
|
|
1001
1143
|
constants[`pins.all.${i}.capabilities.openDrain`] = true;
|
|
1002
1144
|
});
|
|
1145
|
+
// CPU power states — the harvested PM capability facts (which levels
|
|
1146
|
+
// exist, their idle timing, and whether the automatic policy may enter
|
|
1147
|
+
// them; ESP32's soft-off is explicit-entry only).
|
|
1148
|
+
const powerStates = entry.powerStates ?? [];
|
|
1149
|
+
powerStates.forEach((ps, i) => {
|
|
1150
|
+
constants[`zephyr.power.states.${i}.name`] = ps.name;
|
|
1151
|
+
if (ps.minResidencyUs !== undefined) constants[`zephyr.power.states.${i}.minResidencyUs`] = ps.minResidencyUs;
|
|
1152
|
+
if (ps.exitLatencyUs !== undefined) constants[`zephyr.power.states.${i}.exitLatencyUs`] = ps.exitLatencyUs;
|
|
1153
|
+
constants[`zephyr.power.states.${i}.enabled`] = ps.enabled;
|
|
1154
|
+
});
|
|
1155
|
+
if (powerStates.length > 0) {
|
|
1156
|
+
constants['zephyr.power.softOff'] = powerStates.some((ps) => ps.name === 'soft-off');
|
|
1157
|
+
}
|
|
1158
|
+
if (entry.rtcWakeTimer) constants['zephyr.power.wakeTimer'] = true;
|
|
1159
|
+
// CAN controllers — the harvested labeled nodes (the CAN class gate). The
|
|
1160
|
+
// ESP32 TWAI pads ride along: loopback overlays route both TWAI functions
|
|
1161
|
+
// onto the TX pad.
|
|
1162
|
+
(entry.canNodes ?? []).forEach((c, i) => {
|
|
1163
|
+
constants[`zephyr.can.controllers.${i}.nodeLabel`] = c.nodeLabel;
|
|
1164
|
+
constants[`zephyr.can.controllers.${i}.compatible`] = c.compatible;
|
|
1165
|
+
if (c.txPad !== undefined) constants[`zephyr.can.controllers.${i}.txPad`] = c.txPad;
|
|
1166
|
+
if (c.rxPad !== undefined) constants[`zephyr.can.controllers.${i}.rxPad`] = c.rxPad;
|
|
1167
|
+
});
|
|
1168
|
+
// I2S controllers — the harvested labeled nodes (the I2S class gate).
|
|
1169
|
+
(entry.i2sNodes ?? []).forEach((c, i) => {
|
|
1170
|
+
constants[`zephyr.i2s.controllers.${i}.nodeLabel`] = c.nodeLabel;
|
|
1171
|
+
constants[`zephyr.i2s.controllers.${i}.compatible`] = c.compatible;
|
|
1172
|
+
});
|
|
1003
1173
|
constants['peripherals.i2c.count'] = buses.i2c.length;
|
|
1004
1174
|
constants['peripherals.spi.count'] = buses.spi.length;
|
|
1005
1175
|
constants['peripherals.uart.count'] = buses.uart.length;
|
|
@@ -1161,16 +1331,61 @@ export function buildModule(
|
|
|
1161
1331
|
|
|
1162
1332
|
// Bus controllers the board's own DTS wires up — resolveChipFromBoard
|
|
1163
1333
|
// reconstructs the bus lists from these, and the overlays enable the
|
|
1164
|
-
// nodes the program uses.
|
|
1334
|
+
// nodes the program uses. Each controller also carries its board-DTS
|
|
1335
|
+
// pinctrl routes when the harvest parsed them (`pinctrl` = the route
|
|
1336
|
+
// names, `pads` = `role=PAD` pairs) — the diagnostics report's
|
|
1337
|
+
// peripheral-usage table and the pins.<bus> conflict-map read them.
|
|
1338
|
+
const busPads = (kind: 'i2c' | 'spi' | 'uart', nodelabel: string) =>
|
|
1339
|
+
(entry.busPins ?? []).filter((b) => b.bus === kind && b.nodelabel === nodelabel).flatMap((b) => b.routes);
|
|
1340
|
+
const busPinMap = (kind: 'i2c' | 'spi' | 'uart'): string => {
|
|
1341
|
+
const parts: string[] = [];
|
|
1342
|
+
buses[kind].forEach((nodelabel, i) => {
|
|
1343
|
+
const pairs = busPads(kind, nodelabel)
|
|
1344
|
+
.filter((r) => r.pad && r.role)
|
|
1345
|
+
.map((r) => `${r.role}=${r.pad}`);
|
|
1346
|
+
if (pairs.length > 0) parts.push(`${i}:${pairs.join(',')}`);
|
|
1347
|
+
});
|
|
1348
|
+
return parts.join(';');
|
|
1349
|
+
};
|
|
1165
1350
|
buses.i2c.forEach((nodelabel, i) => {
|
|
1166
1351
|
constants[`zephyr.i2c.controllers.${i}.nodeLabel`] = nodelabel;
|
|
1352
|
+
const routes = busPads('i2c', nodelabel);
|
|
1353
|
+
if (routes.length > 0) {
|
|
1354
|
+
constants[`zephyr.i2c.controllers.${i}.pinctrl`] = routes.map((r) => r.name).join(',');
|
|
1355
|
+
constants[`zephyr.i2c.controllers.${i}.pads`] = routes.filter((r) => r.pad).map((r) => `${r.role ?? ''}=${r.pad}`).join(',');
|
|
1356
|
+
}
|
|
1167
1357
|
});
|
|
1168
1358
|
buses.spi.forEach((nodelabel, i) => {
|
|
1169
1359
|
constants[`zephyr.spi.controllers.${i}.nodeLabel`] = nodelabel;
|
|
1360
|
+
const routes = busPads('spi', nodelabel);
|
|
1361
|
+
if (routes.length > 0) {
|
|
1362
|
+
constants[`zephyr.spi.controllers.${i}.pinctrl`] = routes.map((r) => r.name).join(',');
|
|
1363
|
+
constants[`zephyr.spi.controllers.${i}.pads`] = routes.filter((r) => r.pad).map((r) => `${r.role ?? ''}=${r.pad}`).join(',');
|
|
1364
|
+
}
|
|
1170
1365
|
});
|
|
1171
1366
|
buses.uart.forEach((nodelabel, i) => {
|
|
1172
1367
|
constants[`zephyr.uart.controllers.${i}.nodeLabel`] = nodelabel;
|
|
1368
|
+
const routes = busPads('uart', nodelabel);
|
|
1369
|
+
if (routes.length > 0) {
|
|
1370
|
+
constants[`zephyr.uart.controllers.${i}.pinctrl`] = routes.map((r) => r.name).join(',');
|
|
1371
|
+
constants[`zephyr.uart.controllers.${i}.pads`] = routes.filter((r) => r.pad).map((r) => `${r.role ?? ''}=${r.pad}`).join(',');
|
|
1372
|
+
}
|
|
1173
1373
|
});
|
|
1374
|
+
// The board's console controller (chosen zephyr,console) — the
|
|
1375
|
+
// diagnostics annotate the UART instance the bootloader logs on.
|
|
1376
|
+
if (entry.console) constants['zephyr.console'] = entry.console;
|
|
1377
|
+
// Bus pad conflict maps (analyzeResources): "<inst>:<role>=<PAD>,...;..."
|
|
1378
|
+
const i2cPinMap = busPinMap('i2c');
|
|
1379
|
+
const spiPinMap = busPinMap('spi');
|
|
1380
|
+
const uartPinMap = busPinMap('uart');
|
|
1381
|
+
if (i2cPinMap) constants['pins.i2c'] = i2cPinMap;
|
|
1382
|
+
if (spiPinMap) constants['pins.spi'] = spiPinMap;
|
|
1383
|
+
if (uartPinMap) constants['pins.uart'] = uartPinMap;
|
|
1384
|
+
// Board silicon identity for the diagnostics metadata (the report's
|
|
1385
|
+
// SRAM/flash context) — flash is a devicetree fact, sram/clock are not
|
|
1386
|
+
// harvested and stay absent.
|
|
1387
|
+
if (soc) constants['mcu'] = soc;
|
|
1388
|
+
if (entry.flashKb) constants['memory.flash'] = entry.flashKb * 1024;
|
|
1174
1389
|
|
|
1175
1390
|
// USB device (CDC-ACM): the board's DTS turned the controller on.
|
|
1176
1391
|
if (hasUsb) {
|
|
@@ -1234,7 +1449,7 @@ export function buildModule(
|
|
|
1234
1449
|
constants,
|
|
1235
1450
|
// Source fingerprint: covers the record content, the extraction
|
|
1236
1451
|
// revision, and the tree provenance (when the record came from a
|
|
1237
|
-
// catalog). `
|
|
1452
|
+
// catalog). `typecad-hal build` recomputes it cheaply and regenerates
|
|
1238
1453
|
// this module when it moves — a board change in the config, the catalog
|
|
1239
1454
|
// overlay, or the Zephyr tree itself recreates the module.
|
|
1240
1455
|
source: {
|
|
@@ -1264,8 +1479,8 @@ export function generateBoard(
|
|
|
1264
1479
|
if (!found) {
|
|
1265
1480
|
const hint = loadBoardCatalogOverlay()
|
|
1266
1481
|
? `'${target}' is not a board target in the current catalog. ` +
|
|
1267
|
-
`It may be new in your Zephyr tree — run '
|
|
1268
|
-
: `No board catalog on this machine. Run '
|
|
1482
|
+
`It may be new in your Zephyr tree — run 'typecad-hal board sync' and retry.`
|
|
1483
|
+
: `No board catalog on this machine. Run 'typecad-hal board sync' first.`;
|
|
1269
1484
|
throw new Error(hint);
|
|
1270
1485
|
}
|
|
1271
1486
|
// The as-built snapshot (this project's last successful build's resolved
|
|
@@ -1286,7 +1501,7 @@ export function generateBoard(
|
|
|
1286
1501
|
asBuiltWarnings.push(`ignoring as-built snapshot: ${(err as Error).message}`);
|
|
1287
1502
|
}
|
|
1288
1503
|
}
|
|
1289
|
-
// The project's
|
|
1504
|
+
// The project's typecad-hal.facts.json, when present: the section for THIS
|
|
1290
1505
|
// board merges into the manifest (user routes win per pin), and the raw
|
|
1291
1506
|
// text hashes into the module's source fingerprint so edits regenerate.
|
|
1292
1507
|
let facts: UserBoardFacts | undefined;
|
package/src/chips/resolve.ts
CHANGED
|
@@ -230,6 +230,24 @@ export function resolveChipFromBoard(
|
|
|
230
230
|
// (+ optional vid/pid for the device descriptor).
|
|
231
231
|
const usbController = bc.get('zephyr.usb.controller') as string | undefined;
|
|
232
232
|
const usbCdcInstances = bc.get('zephyr.usb.cdcInstances') as number | undefined;
|
|
233
|
+
const wakeTimer = bc.get('zephyr.power.wakeTimer') as boolean | undefined;
|
|
234
|
+
const i2sControllers: { nodeLabel: string; compatible: string }[] = [];
|
|
235
|
+
for (let ii = 0; bc.get(`zephyr.i2s.controllers.${ii}.nodeLabel`) !== undefined; ii++) {
|
|
236
|
+
i2sControllers.push({
|
|
237
|
+
nodeLabel: String(bc.get(`zephyr.i2s.controllers.${ii}.nodeLabel`)),
|
|
238
|
+
compatible: String(bc.get(`zephyr.i2s.controllers.${ii}.compatible`)),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const canControllers: { nodeLabel: string; compatible: string; txPad?: number; rxPad?: number }[] = [];
|
|
242
|
+
for (let ci = 0; bc.get(`zephyr.can.controllers.${ci}.nodeLabel`) !== undefined; ci++) {
|
|
243
|
+
canControllers.push({
|
|
244
|
+
nodeLabel: String(bc.get(`zephyr.can.controllers.${ci}.nodeLabel`)),
|
|
245
|
+
compatible: String(bc.get(`zephyr.can.controllers.${ci}.compatible`)),
|
|
246
|
+
...(bc.get(`zephyr.can.controllers.${ci}.txPad`) !== undefined ? { txPad: Number(bc.get(`zephyr.can.controllers.${ci}.txPad`)) } : {}),
|
|
247
|
+
...(bc.get(`zephyr.can.controllers.${ci}.rxPad`) !== undefined ? { rxPad: Number(bc.get(`zephyr.can.controllers.${ci}.rxPad`)) } : {}),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
233
251
|
const usbVid = bc.get('zephyr.usb.vid') as string | undefined;
|
|
234
252
|
const usbPid = bc.get('zephyr.usb.pid') as string | undefined;
|
|
235
253
|
// Optional 1200-baud touch-to-reset data (BOSSA-bootloader boards).
|
|
@@ -367,6 +385,9 @@ export function resolveChipFromBoard(
|
|
|
367
385
|
...(storageOffset != null && storageSize != null
|
|
368
386
|
? { storage: { offset: storageOffset, size: storageSize, ...(storagePreexisting ? { preexisting: true } : {}) } }
|
|
369
387
|
: {}),
|
|
388
|
+
...(wakeTimer ? { powerWakeTimer: true } : {}),
|
|
389
|
+
...(canControllers.length > 0 ? { can: { controllers: canControllers } } : {}),
|
|
390
|
+
...(i2sControllers.length > 0 ? { i2s: { controllers: i2sControllers } } : {}),
|
|
370
391
|
...(usbController && usbCdcInstances && usbCdcInstances > 0
|
|
371
392
|
? {
|
|
372
393
|
usb: {
|
package/src/chips/types.ts
CHANGED
|
@@ -241,6 +241,15 @@ export interface ZephyrDacChannel {
|
|
|
241
241
|
* Pure-data descriptor for a Zephyr board + its SoC's peripheral layout.
|
|
242
242
|
*/
|
|
243
243
|
export interface ZephyrChipDescriptor {
|
|
244
|
+
/** The SoC's RTC wake timer (ESP32 family rtc_timer fact) — Power.offFor
|
|
245
|
+
* arms it (esp_sleep_enable_timer_wakeup) before soft-off. */
|
|
246
|
+
readonly powerWakeTimer?: boolean;
|
|
247
|
+
/** CAN controllers (harvested labeled can@ nodes) — the lowering
|
|
248
|
+
* addresses the first by its DT nodelabel. */
|
|
249
|
+
readonly can?: { controllers: readonly { nodeLabel: string; compatible: string; txPad?: number; rxPad?: number }[] };
|
|
250
|
+
/** I2S controllers (harvested labeled i2s@ nodes) — the lowering
|
|
251
|
+
* addresses each by its DT nodelabel. */
|
|
252
|
+
readonly i2s?: { controllers: readonly { nodeLabel: string; compatible: string }[] };
|
|
244
253
|
/** Zephyr board target (the `west build -b <id>` argument), e.g. 'xiao_ble'. */
|
|
245
254
|
readonly id: string;
|
|
246
255
|
/** SoC family, e.g. 'nrf52840'. */
|
|
@@ -446,7 +455,7 @@ export interface ZephyrChipDescriptor {
|
|
|
446
455
|
* verified with the board, instead of in user configs. One probe method
|
|
447
456
|
* serves BOTH flashing and debugging (the same attach session); entries
|
|
448
457
|
* that cannot debug (bootloaders) set `debug: false`. Users select one via
|
|
449
|
-
* `zephyr.probe` in
|
|
458
|
+
* `zephyr.probe` in typecad-hal.config.ts or `--probe` on the CLI;
|
|
450
459
|
* `zephyr.runner`/`runnerArgs` remain the raw escape hatch underneath.
|
|
451
460
|
*/
|
|
452
461
|
readonly probeMethods?: readonly ZephyrProbeMethod[];
|