@typecad/framework-zephyr 1.0.0-alpha.18 → 1.0.0-alpha.20
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/dist/audit.d.ts +111 -0
- package/dist/audit.js +416 -0
- package/dist/boardgen.js +63 -5
- package/dist/chips/resolve.js +20 -0
- package/dist/chips/types.d.ts +21 -0
- 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 +118 -48
- 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 +53 -101
- package/dist/dt-config/kconfig.d.ts +62 -1
- package/dist/dt-config/kconfig.js +123 -26
- package/dist/dt-config/overlay.d.ts +13 -0
- package/dist/dt-config/overlay.js +431 -16
- package/dist/framework.manifest.d.ts +9 -3
- package/dist/framework.manifest.js +123 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -1
- package/dist/licenses.js +5 -84
- 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/hid.d.ts +27 -0
- package/dist/lowering/hid.js +244 -0
- package/dist/lowering/i2c.d.ts +8 -0
- package/dist/lowering/i2c.js +140 -0
- 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/matrix.d.ts +15 -0
- package/dist/lowering/matrix.js +63 -0
- package/dist/lowering/power.d.ts +10 -0
- package/dist/lowering/power.js +35 -0
- 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 +6 -3
- package/dist/sbom.d.ts +181 -0
- package/dist/sbom.js +901 -0
- package/dist/strategy.d.ts +60 -2
- package/dist/strategy.js +485 -25
- package/dist/toolchain/index.d.ts +12 -1
- package/dist/toolchain/index.js +115 -27
- package/dist/toolchain/scaffold.d.ts +4 -1
- package/dist/toolchain/scaffold.js +67 -3
- package/dist/west-inventory.d.ts +25 -0
- package/dist/west-inventory.js +97 -0
- package/package.json +6 -6
- package/src/audit.ts +529 -0
- package/src/boardgen.ts +54 -5
- package/src/chips/resolve.ts +21 -0
- package/src/chips/types.ts +576 -567
- 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 +274 -204
- 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 +51 -100
- package/src/dt-config/kconfig.ts +647 -511
- package/src/dt-config/overlay.ts +433 -16
- package/src/framework.manifest.ts +131 -10
- package/src/index.ts +11 -1
- package/src/licenses.ts +5 -84
- package/src/lowering/can.ts +140 -0
- package/src/lowering/clock.ts +91 -0
- package/src/lowering/hid.ts +261 -0
- package/src/lowering/i2c.ts +146 -0
- package/src/lowering/i2s.ts +143 -0
- package/src/lowering/index.ts +18 -1
- package/src/lowering/matrix.ts +70 -0
- 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 +224 -221
- package/src/sbom.ts +1117 -0
- package/src/strategy.ts +440 -25
- package/src/toolchain/index.ts +117 -28
- package/src/toolchain/scaffold.ts +62 -3
- package/src/west-inventory.ts +102 -0
package/dist/toolchain/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { readdirSync, readFileSync, mkdirSync, rmSync, existsSync, writeFileSync
|
|
|
24
24
|
import { parseCompileErrors } from '@typecad/cuttlefish/api/shared';
|
|
25
25
|
import { scaffoldZephyrProject, writeIfChanged, appendLibraryOverlayFragments } from './scaffold.js';
|
|
26
26
|
import { parseZephyrDts, asBuiltJson } from '../as-built.js';
|
|
27
|
+
import { stampBuildSbom } from '../sbom.js';
|
|
27
28
|
import { westSpawn, buildEnv } from './west-spawn.js';
|
|
28
29
|
import { discoverWest } from './west-discover.js';
|
|
29
30
|
import { writeDebugConfig, resolveDebugLocations, debugArtifactsNeedRewrite, DEBUG_SERVER_PORT, DEBUG_SERVER_TCL_PORT } from './debug-config.js';
|
|
@@ -36,7 +37,7 @@ import { NO_BOARD_CHIP } from '../chips/index.js';
|
|
|
36
37
|
import { resolveChipFromBoard } from '../chips/resolve.js';
|
|
37
38
|
import { pwmDtAliasToken } from '../lowering/pwm.js';
|
|
38
39
|
import { detectZephyrVersion, checkZephyrCompat, resolveBoardTarget } from './compat.js';
|
|
39
|
-
import { DEFAULT_ZEPHYR_DISPLAY_PROFILE } from '../display/profiles.js';
|
|
40
|
+
import { DEFAULT_ZEPHYR_DISPLAY_PROFILE, profileFromEmittedSource } from '../display/profiles.js';
|
|
40
41
|
/**
|
|
41
42
|
* Resolve the chip for a build the same way the strategy does at emit time —
|
|
42
43
|
* from the board constants the transpile persisted next to the emitted
|
|
@@ -196,6 +197,36 @@ function scanPwmUsedPins(src, chip) {
|
|
|
196
197
|
}
|
|
197
198
|
return pins;
|
|
198
199
|
}
|
|
200
|
+
export function scanMatrix(src) {
|
|
201
|
+
// The on_key lowering's comment marker carries the construction lists.
|
|
202
|
+
const m = src.match(/cuttlefish-matrix: rows=([\d,]*) cols=([\d,]*)/);
|
|
203
|
+
if (!m)
|
|
204
|
+
return undefined;
|
|
205
|
+
const rows = m[1] ? m[1].split(',').filter(Boolean).map(Number) : [];
|
|
206
|
+
const cols = m[2] ? m[2].split(',').filter(Boolean).map(Number) : [];
|
|
207
|
+
if (rows.length === 0 || cols.length === 0)
|
|
208
|
+
return undefined;
|
|
209
|
+
return { rows, cols };
|
|
210
|
+
}
|
|
211
|
+
export function scanHid(src) {
|
|
212
|
+
// The shim's report buffer names are the presence signal.
|
|
213
|
+
if (src.includes('__tc_mouse_report'))
|
|
214
|
+
return 'mouse';
|
|
215
|
+
if (src.includes('__tc_kb_report'))
|
|
216
|
+
return 'keyboard';
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
export function scanStrips(src) {
|
|
220
|
+
// The shim's buffer declaration is the presence signal:
|
|
221
|
+
// `static struct led_rgb __tc_strip<N>_buf[<COUNT>];`
|
|
222
|
+
const out = new Map();
|
|
223
|
+
for (const m of src.matchAll(/__tc_strip(\d+)_buf\[(\d+)\]/g)) {
|
|
224
|
+
const idx = Number(m[1]);
|
|
225
|
+
const count = Number(m[2]);
|
|
226
|
+
out.set(idx, Math.max(out.get(idx) ?? 0, count));
|
|
227
|
+
}
|
|
228
|
+
return [...out.entries()].map(([index, count]) => ({ index, count }));
|
|
229
|
+
}
|
|
199
230
|
/**
|
|
200
231
|
* Bus controller indexes the emitted sources actually reference — the shim
|
|
201
232
|
* declares one `__tc_<bus><N>_dev` state block per used instance (gated by
|
|
@@ -230,13 +261,23 @@ export function scanSpiTargets(src) {
|
|
|
230
261
|
}
|
|
231
262
|
export function scanSensorParts(src) {
|
|
232
263
|
const out = new Map();
|
|
264
|
+
// 1-Wire sensors: the stem carries the data pin (w1 master's GPIO).
|
|
265
|
+
for (const m of src.matchAll(/__tc_sensor_([a-z0-9_]+)_w1_p(\d+)_dev\b/g)) {
|
|
266
|
+
out.set(`${m[1]}|w1|${m[2]}`, { part: m[1], busIndex: 0, port: parseInt(m[2], 10), busKind: 'w1', spiHz: 0, spiMode: 0, alertPin: -1, resolution: 12 });
|
|
267
|
+
}
|
|
233
268
|
for (const m of src.matchAll(/__tc_sensor_([a-z0-9_]+)_(i2c|spi)(\d+)_(0x[0-9a-f]+|cs[0-9]+)_dev\b/g)) {
|
|
234
269
|
const port = m[4].startsWith('0x') ? parseInt(m[4], 16) : parseInt(m[4].slice(2), 10);
|
|
235
|
-
const ref = { part: m[1], busIndex: parseInt(m[3], 10), port, busKind: m[2], spiHz: 0, spiMode: 0, alertPin: -1 };
|
|
270
|
+
const ref = { part: m[1], busIndex: parseInt(m[3], 10), port, busKind: m[2], spiHz: 0, spiMode: 0, alertPin: -1, resolution: 12 };
|
|
236
271
|
out.set(`${ref.part}|${ref.busKind}${ref.busIndex}|${ref.port}`, ref);
|
|
237
272
|
}
|
|
238
273
|
// Construction facts ride the state block's config comment — merge by
|
|
239
274
|
// nodelabel so the scanner stays the single source for the overlay.
|
|
275
|
+
for (const m of src.matchAll(/tc-sensor-cfg: tc_([a-z0-9_]+)_w1_p(\d+) res=(\d+)/g)) {
|
|
276
|
+
const key = `${m[1]}|w1|${m[2]}`;
|
|
277
|
+
const existing = out.get(key);
|
|
278
|
+
if (existing)
|
|
279
|
+
existing.resolution = parseInt(m[3], 10);
|
|
280
|
+
}
|
|
240
281
|
for (const m of src.matchAll(/tc-sensor-cfg: tc_([a-z0-9_]+)_(i2c|spi)(\d+)_(0x[0-9a-f]+|cs[0-9]+) hz=(\d+) mode=(\d+) alert=(-?\d+)/g)) {
|
|
241
282
|
const port = m[4].startsWith('0x') ? parseInt(m[4], 16) : parseInt(m[4].slice(2), 10);
|
|
242
283
|
const key = `${m[1]}|${m[2]}${m[3]}|${port}`;
|
|
@@ -631,11 +672,13 @@ export const Toolchain = {
|
|
|
631
672
|
const usesDisplay = uses('display_write') || uses('display_init')
|
|
632
673
|
|| uses('display_fill_rect') || uses('__tc_display_dev')
|
|
633
674
|
|| uses('CuttlefishDisplayTarget');
|
|
634
|
-
//
|
|
635
|
-
//
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
const displayProfile = usesDisplay
|
|
675
|
+
// Recover the exact display profile from the marker the adapter stamped
|
|
676
|
+
// into the emitted source (the registry is the single source of truth).
|
|
677
|
+
// Fall back to the default profile only when a display is used but no
|
|
678
|
+
// marker matched (e.g. hand-written source).
|
|
679
|
+
const displayProfile = usesDisplay
|
|
680
|
+
? (profileFromEmittedSource(src) ?? DEFAULT_ZEPHYR_DISPLAY_PROFILE)
|
|
681
|
+
: undefined;
|
|
639
682
|
// Touch controller kind comes from which DT nodelabel the emitted adapter
|
|
640
683
|
// references (FT6336U on I2C, XPT2046 on the display's SPI bus).
|
|
641
684
|
const usesTouch = uses('ft6336u') || uses('touch_');
|
|
@@ -654,12 +697,29 @@ export const Toolchain = {
|
|
|
654
697
|
spiTargets: spiTargetParts,
|
|
655
698
|
usesSpi: uses('spi_') || uses('__tc_spi') || spiTargetParts.length > 0,
|
|
656
699
|
usesUart: uses('__tc_uart'),
|
|
657
|
-
usesUsb: uses('
|
|
700
|
+
usesUsb: uses('__tc_usb0'),
|
|
658
701
|
usesPwm: uses('pwm_'),
|
|
659
702
|
usesAdc: uses('adc_'),
|
|
660
703
|
adcReadPins: scanAdcReadPins(src, chip),
|
|
661
704
|
dacWritePins: scanDacWritePins(src),
|
|
662
705
|
pwmUsedPins: scanPwmUsedPins(src, chip),
|
|
706
|
+
usesStrip: uses('led_strip'),
|
|
707
|
+
strips: scanStrips(src),
|
|
708
|
+
usesClock: uses('__tc_rtc'),
|
|
709
|
+
usesCan: uses('can_'),
|
|
710
|
+
canLoopback: uses('CAN_MODE_LOOPBACK'),
|
|
711
|
+
usesI2s: uses('i2s_'),
|
|
712
|
+
clockShimCounter: chip.hwtimer && chip.hwtimer.controllers.length > 0
|
|
713
|
+
? {
|
|
714
|
+
label: chip.hwtimer.controllers[0].nodeLabel,
|
|
715
|
+
...(chip.hwtimer.controllers[0].counterParent ? { parent: chip.hwtimer.controllers[0].counterParent } : {}),
|
|
716
|
+
}
|
|
717
|
+
: undefined,
|
|
718
|
+
usesHid: uses('__tc_hid_'),
|
|
719
|
+
hidProtocol: scanHid(src),
|
|
720
|
+
usesMatrix: uses('__tc_matrix'),
|
|
721
|
+
matrix: scanMatrix(src),
|
|
722
|
+
usesPower: uses('sys_poweroff'),
|
|
663
723
|
i2cUsedInstances: scanUsedBusInstances(src, chip.i2c?.controllers, 'i2c'),
|
|
664
724
|
spiUsedInstances: scanUsedBusInstances(src, chip.spi?.controllers, 'spi'),
|
|
665
725
|
uartUsedInstances: scanUsedBusInstances(src, chip.uart?.controllers, 'uart'),
|
|
@@ -723,7 +783,8 @@ export const Toolchain = {
|
|
|
723
783
|
const isGdbDebug = o.debug === true && debugMode === 'gdb';
|
|
724
784
|
const zc = o.zephyrConfig;
|
|
725
785
|
const userKconfig = zc?.kconfig;
|
|
726
|
-
const
|
|
786
|
+
const traceCfg = zc?.trace;
|
|
787
|
+
const configChanged = scaffoldZephyrProject(projectRoot, isGdbDebug, userKconfig, o.psram, traceCfg);
|
|
727
788
|
// Regenerate the DT overlay for the ACTUAL target board. prepare() writes
|
|
728
789
|
// it for the default board (the real target is unknown until compile), so
|
|
729
790
|
// the <default>.overlay it wrote does not match `west build -b <board>`.
|
|
@@ -757,28 +818,22 @@ export const Toolchain = {
|
|
|
757
818
|
const usesDisplay = uses('display_write') || uses('display_init')
|
|
758
819
|
|| uses('display_fill_rect') || uses('__tc_display_dev')
|
|
759
820
|
|| uses('CuttlefishDisplayTarget');
|
|
760
|
-
//
|
|
761
|
-
// (
|
|
762
|
-
//
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
displayProfile = {
|
|
771
|
-
...DEFAULT_ZEPHYR_DISPLAY_PROFILE,
|
|
772
|
-
width: parseInt(wMatch[1], 10),
|
|
773
|
-
height: parseInt(hMatch[1], 10),
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
}
|
|
821
|
+
// Recover the exact display profile from the marker the adapter emitted
|
|
822
|
+
// (`typecad-display-profile: <driver>`), so the DT overlay's compatible
|
|
823
|
+
// string, geometry, and rotation match the panel the adapter targets.
|
|
824
|
+
// The width/height regex this replaces could produce franken-profiles —
|
|
825
|
+
// e.g. an ST7796S build (480x320) on the default ILI9341 profile's
|
|
826
|
+
// controller/compatible, which emitted an ilitek,ili9341 DT node for a
|
|
827
|
+
// panel the ST7796S adapter drives.
|
|
828
|
+
const displayProfile = usesDisplay
|
|
829
|
+
? (profileFromEmittedSource(src) ?? DEFAULT_ZEPHYR_DISPLAY_PROFILE)
|
|
830
|
+
: undefined;
|
|
777
831
|
// Extract display pin wiring (cs/dc/rst/spiFrequency/spiPins) from the
|
|
778
832
|
// config display section so the DT overlay wires the MIPI DBI bridge to
|
|
779
833
|
// the correct GPIOs + SPI bus pins.
|
|
780
834
|
const dispCfg = o.display;
|
|
781
835
|
const spiPins = (dispCfg?.spiPins ?? undefined);
|
|
836
|
+
const i2cPins = (dispCfg?.i2cPins ?? undefined);
|
|
782
837
|
const wiring = dispCfg
|
|
783
838
|
? {
|
|
784
839
|
cs: typeof dispCfg.cs === 'number' ? dispCfg.cs : undefined,
|
|
@@ -788,8 +843,12 @@ export const Toolchain = {
|
|
|
788
843
|
sck: typeof spiPins?.sck === 'number' ? spiPins.sck : undefined,
|
|
789
844
|
mosi: typeof spiPins?.mosi === 'number' ? spiPins.mosi : undefined,
|
|
790
845
|
miso: typeof spiPins?.miso === 'number' ? spiPins.miso : undefined,
|
|
846
|
+
sda: typeof i2cPins?.sda === 'number' ? i2cPins.sda : undefined,
|
|
847
|
+
scl: typeof i2cPins?.scl === 'number' ? i2cPins.scl : undefined,
|
|
791
848
|
backlightPin: typeof dispCfg.backlightPin === 'number' ? dispCfg.backlightPin : undefined,
|
|
792
849
|
tearingEffectPin: typeof dispCfg.tearingEffectPin === 'number' ? dispCfg.tearingEffectPin : undefined,
|
|
850
|
+
busyPin: typeof dispCfg.busyPin === 'number' ? dispCfg.busyPin : undefined,
|
|
851
|
+
address: typeof dispCfg.address === 'number' ? dispCfg.address : undefined,
|
|
793
852
|
}
|
|
794
853
|
: undefined;
|
|
795
854
|
// Extract touch pin wiring from the config display.touch section so the
|
|
@@ -841,13 +900,30 @@ export const Toolchain = {
|
|
|
841
900
|
spiTargets: spiTargetParts,
|
|
842
901
|
usesSpi: uses('spi_') || uses('__tc_spi') || spiTargetParts.length > 0,
|
|
843
902
|
usesUart: uses('__tc_uart'),
|
|
844
|
-
usesUsb: uses('
|
|
903
|
+
usesUsb: uses('__tc_usb0'),
|
|
845
904
|
usesPwm: uses('pwm_'),
|
|
846
905
|
usesAdc: uses('adc_'),
|
|
847
906
|
adcReadPins: scanAdcReadPins(src, chip),
|
|
848
907
|
usesDac: uses('dac_') || uses('__tc_dac'),
|
|
849
908
|
dacWritePins: scanDacWritePins(src),
|
|
850
909
|
pwmUsedPins: scanPwmUsedPins(src, chip),
|
|
910
|
+
usesStrip: uses('led_strip'),
|
|
911
|
+
strips: scanStrips(src),
|
|
912
|
+
usesHid: uses('__tc_hid_'),
|
|
913
|
+
hidProtocol: scanHid(src),
|
|
914
|
+
usesMatrix: uses('__tc_matrix'),
|
|
915
|
+
matrix: scanMatrix(src),
|
|
916
|
+
usesPower: uses('sys_poweroff'),
|
|
917
|
+
usesClock: uses('__tc_rtc'),
|
|
918
|
+
usesCan: uses('can_'),
|
|
919
|
+
canLoopback: uses('CAN_MODE_LOOPBACK'),
|
|
920
|
+
usesI2s: uses('i2s_'),
|
|
921
|
+
clockShimCounter: chip.hwtimer && chip.hwtimer.controllers.length > 0
|
|
922
|
+
? {
|
|
923
|
+
label: chip.hwtimer.controllers[0].nodeLabel,
|
|
924
|
+
...(chip.hwtimer.controllers[0].counterParent ? { parent: chip.hwtimer.controllers[0].counterParent } : {}),
|
|
925
|
+
}
|
|
926
|
+
: undefined,
|
|
851
927
|
i2cUsedInstances: scanUsedBusInstances(src, chip.i2c?.controllers, 'i2c'),
|
|
852
928
|
spiUsedInstances: scanUsedBusInstances(src, chip.spi?.controllers, 'spi'),
|
|
853
929
|
uartUsedInstances: scanUsedBusInstances(src, chip.uart?.controllers, 'uart'),
|
|
@@ -1029,6 +1105,18 @@ export const Toolchain = {
|
|
|
1029
1105
|
}
|
|
1030
1106
|
catch { /* best-effort snapshot — nothing to harvest or unreadable */ }
|
|
1031
1107
|
}
|
|
1108
|
+
// SBOM stamp: after a successful build, record what the binary is made of
|
|
1109
|
+
// — kernel + linked west modules with commit SHAs + the hashed firmware
|
|
1110
|
+
// artifact (CycloneDX 1.6, next to zephyr.bin) — so `typecad-hal sbom
|
|
1111
|
+
// --check` can gate releases on the record still matching the build
|
|
1112
|
+
// (EU CRA essential-requirements support). Best-effort: a west-list or
|
|
1113
|
+
// hashing failure never blocks the build.
|
|
1114
|
+
if (result.status === 0) {
|
|
1115
|
+
try {
|
|
1116
|
+
stampBuildSbom({ buildDir, board, projectRoot });
|
|
1117
|
+
}
|
|
1118
|
+
catch { /* best-effort SBOM stamp */ }
|
|
1119
|
+
}
|
|
1032
1120
|
return {
|
|
1033
1121
|
success: result.status === 0,
|
|
1034
1122
|
output: header + output,
|
|
@@ -21,4 +21,7 @@ export declare function appendLibraryOverlayFragments(overlay: string, projectRo
|
|
|
21
21
|
*
|
|
22
22
|
* Idempotent. Mirrors scaffoldEspIdfProject's writeIfChanged discipline.
|
|
23
23
|
*/
|
|
24
|
-
export declare function scaffoldZephyrProject(projectRoot: string, debug?: boolean, userKconfig?: Record<string, string>, psram?: 'opi' | 'quad'
|
|
24
|
+
export declare function scaffoldZephyrProject(projectRoot: string, debug?: boolean, userKconfig?: Record<string, string>, psram?: 'opi' | 'quad', trace?: {
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
intervalMs?: number;
|
|
27
|
+
}): boolean;
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
import { writeFileSync, existsSync, mkdirSync, readFileSync, readdirSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { resolveKconfigFragments } from '../dt-config/kconfig.js';
|
|
12
|
-
import { scanSensorParts } from './index.js';
|
|
12
|
+
import { scanSensorParts, scanStrips, scanHid, scanMatrix } from './index.js';
|
|
13
|
+
import { profileFromEmittedSource, transportFor, panelControllerFor } from '../display/profiles.js';
|
|
14
|
+
import { readDisplayBinding } from '../display/bindings.js';
|
|
13
15
|
import { readCuttlefishLibrarySidecar } from '@typecad/cuttlefish/library-packages';
|
|
14
16
|
import { SENSOR_PART_INFO } from '@typecad/hal';
|
|
15
17
|
/** Write a file only if the content differs from the existing file.
|
|
@@ -96,7 +98,7 @@ export function appendLibraryOverlayFragments(overlay, projectRoot) {
|
|
|
96
98
|
*
|
|
97
99
|
* Idempotent. Mirrors scaffoldEspIdfProject's writeIfChanged discipline.
|
|
98
100
|
*/
|
|
99
|
-
export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, psram) {
|
|
101
|
+
export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, psram, trace) {
|
|
100
102
|
const srcDir = join(projectRoot, 'src');
|
|
101
103
|
if (!existsSync(srcDir))
|
|
102
104
|
mkdirSync(srcDir, { recursive: true });
|
|
@@ -122,6 +124,22 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
|
|
|
122
124
|
const usage = {
|
|
123
125
|
usesAdc: uses('adc_'),
|
|
124
126
|
usesPwm: uses('pwm_'),
|
|
127
|
+
usesStrip: uses('led_strip'),
|
|
128
|
+
strips: scanStrips(src),
|
|
129
|
+
// Sensor parts drive CONFIG_W1 (1-Wire masters) and the exception
|
|
130
|
+
// lines; the same scan the exception block below re-runs.
|
|
131
|
+
sensorParts: scanSensorParts(src),
|
|
132
|
+
usesHid: uses('__tc_hid_'),
|
|
133
|
+
hidProtocol: scanHid(src),
|
|
134
|
+
usesMatrix: uses('__tc_matrix'),
|
|
135
|
+
matrix: scanMatrix(src),
|
|
136
|
+
usesPower: uses('sys_poweroff'),
|
|
137
|
+
usesClock: uses('__tc_rtc'),
|
|
138
|
+
usesCan: uses('can_'),
|
|
139
|
+
canLoopback: uses('CAN_MODE_LOOPBACK'),
|
|
140
|
+
usesI2s: uses('i2s_'),
|
|
141
|
+
// No chip facts at scaffold time — the prepare overlay omits the shim;
|
|
142
|
+
// the chip-aware build path synthesizes it.
|
|
125
143
|
usesDac: uses('dac_') || uses('__tc_dac'),
|
|
126
144
|
usesFS: uses('__tc_fs'),
|
|
127
145
|
usesHwtimer: uses('counter_') || uses('__tc_hw'),
|
|
@@ -130,6 +148,9 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
|
|
|
130
148
|
// call yet), but the shim still declared DEVICE_DT_GET(DT_NODELABEL(...))
|
|
131
149
|
// so the overlay must enable the node or the device symbol is missing.
|
|
132
150
|
usesI2c: uses('i2c_') || uses('__tc_i2c'),
|
|
151
|
+
// I2C target mode: every i2c.resp_* lowering calls i2c_target_register
|
|
152
|
+
// (and the shim's state carries the i2c_target_* vocabulary).
|
|
153
|
+
usesI2cTarget: uses('i2c_target_'),
|
|
133
154
|
// DT-bound sensor parts: the __tc_sensor_* state references and the
|
|
134
155
|
// sensor_sample_fetch/sensor_channel_get calls both carry the token.
|
|
135
156
|
usesSensor: uses('sensor_') || uses('__tc_sensor'),
|
|
@@ -138,12 +159,32 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
|
|
|
138
159
|
usesUart: uses('uart_') || uses('__tc_uart'),
|
|
139
160
|
// USB CDC serial: every usb.* lowering calls into the __tc_usb<N>_* shim
|
|
140
161
|
// (device + init helper emitted under usesUsb).
|
|
141
|
-
usesUsb: uses('
|
|
162
|
+
usesUsb: uses('__tc_usb0'),
|
|
142
163
|
// STM32F4 DBGMCU keep-SWD-alive init present (emitted for stm32f4 socs).
|
|
143
164
|
usesStm32DebugSleep: uses('__tc_stm32_dbgmcu'),
|
|
144
165
|
usesWdt: uses('wdt_'),
|
|
145
166
|
usesBle: uses('bt_') || uses('bt_gatt') || uses('bt_le_'),
|
|
146
167
|
usesDisplay: uses('display_write') || uses('display_init') || uses('display_fill_rect') || uses('__tc_display_dev') || uses('CuttlefishDisplayTarget'),
|
|
168
|
+
// Display transport + controller: recovered from the profile marker the
|
|
169
|
+
// display adapters stamp into the emitted source (the profile registry is
|
|
170
|
+
// the single source of truth — no geometry re-derivation here).
|
|
171
|
+
...(() => {
|
|
172
|
+
const profile = profileFromEmittedSource(src);
|
|
173
|
+
if (!profile)
|
|
174
|
+
return {};
|
|
175
|
+
// Registry profiles name a DT compatible via dtCompatible (the driver
|
|
176
|
+
// id itself isn't one); drop-in profiles use the driver string directly.
|
|
177
|
+
const bus = readDisplayBinding(profile.dtCompatible ?? profile.driver)?.busFamily;
|
|
178
|
+
if (bus === 'i2c')
|
|
179
|
+
return { displayBus: 'i2c' };
|
|
180
|
+
// Board-provided panels (native_sim's sdl_dc) carry their own fragments
|
|
181
|
+
// and no bus at all.
|
|
182
|
+
if (profile.boardProvidesDisplay)
|
|
183
|
+
return { displayKconfigExtra: profile.kconfig };
|
|
184
|
+
if (transportFor(profile) !== 'zephyr-display')
|
|
185
|
+
return {};
|
|
186
|
+
return { displayTransport: 'zephyr-display', displayController: panelControllerFor(profile) };
|
|
187
|
+
})(),
|
|
147
188
|
usesTouch: uses('ft6336u') || uses('touch_'),
|
|
148
189
|
// (The power HAL is removed; its former pm_/k_sleep token scan is gone —
|
|
149
190
|
// nothing here must match tx_power_dbm's `power` substring.)
|
|
@@ -266,6 +307,29 @@ export function scaffoldZephyrProject(projectRoot, debug = false, userKconfig, p
|
|
|
266
307
|
}
|
|
267
308
|
}
|
|
268
309
|
}
|
|
310
|
+
// ── Trace heartbeat Kconfig ────────────────────────────────────────────────
|
|
311
|
+
// zephyr.trace.enabled in typecad-hal.config.ts turns on the [TR: sampler
|
|
312
|
+
// (emitted by the strategy at transpile time from the same config record).
|
|
313
|
+
// The symbol set is exactly what the sampler calls: runtime stats
|
|
314
|
+
// (k_thread_runtime_stats_*), the monitor list (k_thread_foreach —
|
|
315
|
+
// kernel/thread_monitor.c only links under THREAD_MONITOR), thread names
|
|
316
|
+
// (k_thread_name_get/set), and stack inspection (0xAA fill +
|
|
317
|
+
// stack_info.size). User zephyr.kconfig overrides still win.
|
|
318
|
+
if (trace?.enabled === true) {
|
|
319
|
+
const traceSymbols = [
|
|
320
|
+
['CONFIG_THREAD_RUNTIME_STATS', 'y'],
|
|
321
|
+
['CONFIG_THREAD_MONITOR', 'y'],
|
|
322
|
+
['CONFIG_THREAD_NAME', 'y'],
|
|
323
|
+
['CONFIG_INIT_STACKS', 'y'],
|
|
324
|
+
['CONFIG_THREAD_STACK_INFO', 'y'],
|
|
325
|
+
];
|
|
326
|
+
prjConf.push('', '# Trace heartbeat (typecad-hal.config.ts → zephyr.trace).');
|
|
327
|
+
for (const [sym, val] of traceSymbols) {
|
|
328
|
+
if (userKconfig && userKconfig.hasOwnProperty(sym))
|
|
329
|
+
continue;
|
|
330
|
+
prjConf.push(`${sym}=${val}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
269
333
|
// Per-part Kconfig exceptions: catalog parts whose driver is NOT default-y
|
|
270
334
|
// on its DT node carry extra lines here (empty today — every in-tree sensor
|
|
271
335
|
// driver lights up from the node; the path exists so the first exception
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ReadDir, ReadFile } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
/** Normalize a path for case-insensitive, separator-agnostic comparison. */
|
|
3
|
+
export declare function norm(p: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Filter the west manifest to the modules whose sources were compiled in the
|
|
6
|
+
* last build. A module is "linked" iff some compiled translation unit lives
|
|
7
|
+
* under its abspath. If the build data is unparseable, no filtering is applied
|
|
8
|
+
* (degrade to listing all modules rather than reporting nothing).
|
|
9
|
+
*/
|
|
10
|
+
export declare function filterLinkedModules(ccText: string, modules: {
|
|
11
|
+
name: string;
|
|
12
|
+
abspath: string;
|
|
13
|
+
}[]): {
|
|
14
|
+
name: string;
|
|
15
|
+
abspath: string;
|
|
16
|
+
}[];
|
|
17
|
+
/**
|
|
18
|
+
* Find the build's compile_commands.json path under `cwd`. Cuttlefish's Zephyr
|
|
19
|
+
* project root is the transpile output dir (e.g. `<cwd>/src/out`), so the
|
|
20
|
+
* build dir is `<root>/build` — not necessarily `<cwd>/build`. Check the
|
|
21
|
+
* common layouts, then fall back to a bounded search (skipping
|
|
22
|
+
* node_modules/.git/dist and dot-directories). Returns the PATH (the caller
|
|
23
|
+
* decides whether to read it); undefined when no build exists.
|
|
24
|
+
*/
|
|
25
|
+
export declare function findCompileCommandsPath(readFile: ReadFile, readdir: ReadDir, cwd?: string): string | undefined;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @typecad/framework-zephyr — shared west workspace ground truth
|
|
3
|
+
//
|
|
4
|
+
// The as-built inventory both `typecad-hal licenses` and `typecad-hal sbom`
|
|
5
|
+
// report from: which west manifest modules the last build ACTUALLY compiled
|
|
6
|
+
// (linked-module filtering via the build's compile_commands.json), plus the
|
|
7
|
+
// build-directory discovery that locates it. A west manifest carries every
|
|
8
|
+
// vendor HAL/library; almost none are linked by a single project — both the
|
|
9
|
+
// license table and the SBOM scope to the linked set by default, so they stay
|
|
10
|
+
// honest about what is actually inside the firmware.
|
|
11
|
+
//
|
|
12
|
+
// Moved out of licenses.ts when the sbom command grew a second consumer; the
|
|
13
|
+
// behavior (case-insensitive, separator-agnostic path matching; degrade to
|
|
14
|
+
// "no filtering" on unparseable build data) is unchanged.
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
/** Normalize a path for case-insensitive, separator-agnostic comparison. */
|
|
18
|
+
export function norm(p) {
|
|
19
|
+
return p.split('\\').join('/').toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Filter the west manifest to the modules whose sources were compiled in the
|
|
23
|
+
* last build. A module is "linked" iff some compiled translation unit lives
|
|
24
|
+
* under its abspath. If the build data is unparseable, no filtering is applied
|
|
25
|
+
* (degrade to listing all modules rather than reporting nothing).
|
|
26
|
+
*/
|
|
27
|
+
export function filterLinkedModules(ccText, modules) {
|
|
28
|
+
let arr;
|
|
29
|
+
try {
|
|
30
|
+
arr = JSON.parse(ccText);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return modules;
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(arr))
|
|
36
|
+
return modules;
|
|
37
|
+
// One normalized blob of every compiled source path; membership is then a
|
|
38
|
+
// substring check per module (O(modules) after an O(TUs) join).
|
|
39
|
+
const blob = arr
|
|
40
|
+
.map((e) => norm(typeof e?.file === 'string' ? e.file : ''))
|
|
41
|
+
.join('\n');
|
|
42
|
+
return modules.filter((m) => blob.includes(norm(m.abspath) + '/'));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Find the build's compile_commands.json path under `cwd`. Cuttlefish's Zephyr
|
|
46
|
+
* project root is the transpile output dir (e.g. `<cwd>/src/out`), so the
|
|
47
|
+
* build dir is `<root>/build` — not necessarily `<cwd>/build`. Check the
|
|
48
|
+
* common layouts, then fall back to a bounded search (skipping
|
|
49
|
+
* node_modules/.git/dist and dot-directories). Returns the PATH (the caller
|
|
50
|
+
* decides whether to read it); undefined when no build exists.
|
|
51
|
+
*/
|
|
52
|
+
export function findCompileCommandsPath(readFile, readdir, cwd = process.cwd()) {
|
|
53
|
+
const direct = [
|
|
54
|
+
path.join(cwd, 'build', 'compile_commands.json'),
|
|
55
|
+
path.join(cwd, 'out', 'build', 'compile_commands.json'),
|
|
56
|
+
path.join(cwd, 'src', 'out', 'build', 'compile_commands.json'),
|
|
57
|
+
];
|
|
58
|
+
for (const c of direct) {
|
|
59
|
+
if (readFile(c))
|
|
60
|
+
return c;
|
|
61
|
+
}
|
|
62
|
+
// Bounded recursive search for a build/compile_commands.json.
|
|
63
|
+
const isCc = (p) => {
|
|
64
|
+
const n = norm(p);
|
|
65
|
+
return n.endsWith('/build/compile_commands.json') || n.endsWith('\\build\\compile_commands.json');
|
|
66
|
+
};
|
|
67
|
+
let found;
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const walk = (dir, depth) => {
|
|
70
|
+
if (found || depth > 4 || seen.has(dir))
|
|
71
|
+
return;
|
|
72
|
+
seen.add(dir);
|
|
73
|
+
let entries;
|
|
74
|
+
try {
|
|
75
|
+
entries = readdir(dir);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
for (const e of entries) {
|
|
81
|
+
const full = path.join(dir, e);
|
|
82
|
+
if (e === 'compile_commands.json' && isCc(full)) {
|
|
83
|
+
found = full;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
for (const e of entries) {
|
|
88
|
+
if (found)
|
|
89
|
+
return;
|
|
90
|
+
if (e === 'node_modules' || e === '.git' || e === 'dist' || e.startsWith('.'))
|
|
91
|
+
continue;
|
|
92
|
+
walk(path.join(dir, e), depth + 1);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
walk(cwd, 0);
|
|
96
|
+
return found;
|
|
97
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typecad/framework-zephyr",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.20",
|
|
4
4
|
"description": "TypeCAD framework package for the Zephyr RTOS \u2014 west/CMake build, devicetree-driven GPIO",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"dry-run": "node installer/install.mjs --dry-run"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@typecad/cuttlefish": "1.0.0-alpha.
|
|
42
|
+
"@typecad/cuttlefish": "1.0.0-alpha.20"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@typecad/hal": "1.0.0-alpha.
|
|
45
|
+
"@typecad/hal": "1.0.0-alpha.20",
|
|
46
46
|
"typescript": "^5.7.3"
|
|
47
47
|
},
|
|
48
48
|
"license": "Apache-2.0",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
},
|
|
52
52
|
"repository": {
|
|
53
53
|
"type": "git",
|
|
54
|
-
"url": "git+https://github.com/
|
|
54
|
+
"url": "git+https://github.com/typecad/hal.git",
|
|
55
55
|
"directory": "packages/framework-zephyr"
|
|
56
56
|
},
|
|
57
|
-
"homepage": "https://github.com/
|
|
57
|
+
"homepage": "https://github.com/typecad/hal/tree/main/packages/framework-zephyr",
|
|
58
58
|
"bugs": {
|
|
59
|
-
"url": "https://github.com/
|
|
59
|
+
"url": "https://github.com/typecad/hal/issues"
|
|
60
60
|
},
|
|
61
61
|
"keywords": [
|
|
62
62
|
"ble",
|