@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.
Files changed (114) hide show
  1. package/dist/audit.d.ts +111 -0
  2. package/dist/audit.js +416 -0
  3. package/dist/boardgen.js +63 -5
  4. package/dist/chips/resolve.js +20 -0
  5. package/dist/chips/types.d.ts +21 -0
  6. package/dist/display/bindings.d.ts +55 -0
  7. package/dist/display/bindings.js +316 -0
  8. package/dist/display/gfx.d.ts +2 -3
  9. package/dist/display/gfx.js +166 -154
  10. package/dist/display/index.js +20 -1
  11. package/dist/display/mipi-dbi-host.d.ts +9 -0
  12. package/dist/display/mipi-dbi-host.js +174 -0
  13. package/dist/display/profiles.d.ts +109 -4
  14. package/dist/display/profiles.js +270 -7
  15. package/dist/display/touch-adapter.js +118 -48
  16. package/dist/display/ui-adapter-eink.d.ts +2 -0
  17. package/dist/display/ui-adapter-eink.js +4 -0
  18. package/dist/display/ui-adapter-gray.d.ts +8 -0
  19. package/dist/display/ui-adapter-gray.js +170 -0
  20. package/dist/display/ui-adapter-mono.d.ts +13 -0
  21. package/dist/display/ui-adapter-mono.js +230 -0
  22. package/dist/display/ui-adapter-native.d.ts +10 -0
  23. package/dist/display/ui-adapter-native.js +295 -0
  24. package/dist/display/ui-adapter-shared.d.ts +11 -0
  25. package/dist/display/ui-adapter-shared.js +122 -0
  26. package/dist/display/ui-adapter.js +53 -101
  27. package/dist/dt-config/kconfig.d.ts +62 -1
  28. package/dist/dt-config/kconfig.js +123 -26
  29. package/dist/dt-config/overlay.d.ts +13 -0
  30. package/dist/dt-config/overlay.js +431 -16
  31. package/dist/framework.manifest.d.ts +9 -3
  32. package/dist/framework.manifest.js +123 -10
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.js +9 -1
  35. package/dist/licenses.js +5 -84
  36. package/dist/lowering/can.d.ts +25 -0
  37. package/dist/lowering/can.js +97 -0
  38. package/dist/lowering/clock.d.ts +17 -0
  39. package/dist/lowering/clock.js +58 -0
  40. package/dist/lowering/hid.d.ts +27 -0
  41. package/dist/lowering/hid.js +244 -0
  42. package/dist/lowering/i2c.d.ts +8 -0
  43. package/dist/lowering/i2c.js +140 -0
  44. package/dist/lowering/i2s.d.ts +27 -0
  45. package/dist/lowering/i2s.js +98 -0
  46. package/dist/lowering/index.d.ts +9 -1
  47. package/dist/lowering/index.js +25 -1
  48. package/dist/lowering/matrix.d.ts +15 -0
  49. package/dist/lowering/matrix.js +63 -0
  50. package/dist/lowering/power.d.ts +10 -0
  51. package/dist/lowering/power.js +35 -0
  52. package/dist/lowering/pwm.js +25 -0
  53. package/dist/lowering/sensor.d.ts +2 -2
  54. package/dist/lowering/sensor.js +8 -4
  55. package/dist/lowering/strip.d.ts +16 -0
  56. package/dist/lowering/strip.js +70 -0
  57. package/dist/lowering/thread.js +5 -1
  58. package/dist/lowering/trace.d.ts +44 -0
  59. package/dist/lowering/trace.js +239 -0
  60. package/dist/lowering/uart.js +6 -1
  61. package/dist/lowering/usb.d.ts +3 -1
  62. package/dist/lowering/usb.js +6 -3
  63. package/dist/sbom.d.ts +181 -0
  64. package/dist/sbom.js +901 -0
  65. package/dist/strategy.d.ts +60 -2
  66. package/dist/strategy.js +485 -25
  67. package/dist/toolchain/index.d.ts +12 -1
  68. package/dist/toolchain/index.js +115 -27
  69. package/dist/toolchain/scaffold.d.ts +4 -1
  70. package/dist/toolchain/scaffold.js +67 -3
  71. package/dist/west-inventory.d.ts +25 -0
  72. package/dist/west-inventory.js +97 -0
  73. package/package.json +6 -6
  74. package/src/audit.ts +529 -0
  75. package/src/boardgen.ts +54 -5
  76. package/src/chips/resolve.ts +21 -0
  77. package/src/chips/types.ts +576 -567
  78. package/src/display/bindings.ts +347 -0
  79. package/src/display/gfx.ts +318 -306
  80. package/src/display/index.ts +87 -70
  81. package/src/display/mipi-dbi-host.ts +183 -0
  82. package/src/display/profiles.ts +458 -139
  83. package/src/display/touch-adapter.ts +274 -204
  84. package/src/display/ui-adapter-eink.ts +13 -0
  85. package/src/display/ui-adapter-gray.ts +178 -0
  86. package/src/display/ui-adapter-mono.ts +238 -0
  87. package/src/display/ui-adapter-native.ts +304 -0
  88. package/src/display/ui-adapter-shared.ts +125 -0
  89. package/src/display/ui-adapter.ts +51 -100
  90. package/src/dt-config/kconfig.ts +647 -511
  91. package/src/dt-config/overlay.ts +433 -16
  92. package/src/framework.manifest.ts +131 -10
  93. package/src/index.ts +11 -1
  94. package/src/licenses.ts +5 -84
  95. package/src/lowering/can.ts +140 -0
  96. package/src/lowering/clock.ts +91 -0
  97. package/src/lowering/hid.ts +261 -0
  98. package/src/lowering/i2c.ts +146 -0
  99. package/src/lowering/i2s.ts +143 -0
  100. package/src/lowering/index.ts +18 -1
  101. package/src/lowering/matrix.ts +70 -0
  102. package/src/lowering/power.ts +41 -0
  103. package/src/lowering/pwm.ts +192 -167
  104. package/src/lowering/sensor.ts +159 -155
  105. package/src/lowering/strip.ts +81 -0
  106. package/src/lowering/thread.ts +5 -1
  107. package/src/lowering/trace.ts +270 -0
  108. package/src/lowering/uart.ts +6 -1
  109. package/src/lowering/usb.ts +224 -221
  110. package/src/sbom.ts +1117 -0
  111. package/src/strategy.ts +440 -25
  112. package/src/toolchain/index.ts +117 -28
  113. package/src/toolchain/scaffold.ts +62 -3
  114. package/src/west-inventory.ts +102 -0
@@ -1,155 +1,159 @@
1
- // ---------------------------------------------------------------------------
2
- // Sensor lowering — Zephyr's uniform sensor API over the generated catalog
3
- //
4
- // One code shape for every part in @typecad/hal's sensor catalog: the op's
5
- // part token resolves to the DT compatible, the bus string to the controller
6
- // index, and the address to the I2C reg. The names are derived identically
7
- // here (device handles), in strategy.ts state blocks, and in the overlay
8
- // scanner (dt-config/overlay.ts + toolchain/index.ts) — the tc-pwm<pin>
9
- // discipline: both sides derive from the same facts, so they cannot drift.
10
- //
11
- // Kconfig: nothing per-part. The in-tree driver Kconfigs are `default y` on
12
- // DT_HAS_<COMPAT>_ENABLED, so the overlay's child node IS the enable switch;
13
- // CONFIG_SENSOR (the umbrella under `if SENSOR`) is usage-gated in
14
- // dt-config/kconfig.ts off the lowered `sensor_` tokens.
15
- // ---------------------------------------------------------------------------
16
-
17
- import { SENSOR_PART_INFO } from '@typecad/hal';
18
- import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
19
- import { parseControllerIndex } from './util.js';
20
-
21
- /** Strip the 'SENSOR.' property-access prefix from a part token arg. */
22
- export function sensorPartKey(part: string): string {
23
- return part.replace(/^SENSOR\./, '');
24
- }
25
-
26
- /** Strip the 'CHAN.' property-access prefix from a channel arg. */
27
- export function sensorChanName(chan: string): string {
28
- return chan.replace(/^CHAN\./, '');
29
- }
30
-
31
- /** All names a constructed sensor is known by, derived from the op facts. */
32
- export interface SensorNames {
33
- /** DT nodelabel of the child node the overlay emits
34
- * (tc_<part>_i2c<N>_0x<addr> / tc_<part>_spi<N>_cs<pin>). */
35
- dtLabel: string;
36
- /** C device-handle variable (__tc_sensor_<stem>_dev). */
37
- devVar: string;
38
- /** C sensor_value scratch variable (__tc_sensor_<stem>_val). */
39
- valVar: string;
40
- /** The controller index the bus string carries. */
41
- busIndex: number;
42
- /** The bus port: I2C address or SPI CS pin. */
43
- port: number;
44
- /** 'i2c' | 'spi'. */
45
- busKind: 'i2c' | 'spi';
46
- }
47
-
48
- /** Derive a sensor's DT/C++ names. The scanner regexes the emitted __tc_
49
- * prefix form, so the part group must stay [a-z0-9_]+ and greedy. */
50
- export function sensorNames(part: string, bus: string, port: number | string, busKind: string = 'i2c'): SensorNames {
51
- const partKey = sensorPartKey(String(part));
52
- const busIndex = parseControllerIndex(typeof bus === 'string' ? bus : String(bus));
53
- const portNum = typeof port === 'number' ? port : parseInt(String(port));
54
- const stem = busKind === 'spi'
55
- ? `${partKey}_spi${busIndex}_cs${portNum}`
56
- : `${partKey}_i2c${busIndex}_0x${portNum.toString(16)}`;
57
- return {
58
- dtLabel: `tc_${stem}`,
59
- devVar: `__tc_sensor_${stem}_dev`,
60
- valVar: `__tc_sensor_${stem}_val`,
61
- busIndex,
62
- port: portNum,
63
- busKind: busKind === 'spi' ? 'spi' : 'i2c',
64
- };
65
- }
66
-
67
- /**
68
- * Resolve the part token against the generated catalog, validating the bus
69
- * kind and (when the driver scan knows them) the channel. Throws a build-time
70
- * error with the Zephyr-facing name — the same words the user would meet in
71
- * Zephyr docs, checked before west ever runs.
72
- */
73
- /** @internal */
74
- function requirePartInfo(partKey: string) {
75
- const info = SENSOR_PART_INFO[partKey];
76
- if (!info) {
77
- throw new Error(
78
- `Sensor part '${partKey}' is not in the Zephyr sensor catalog (SENSOR in @typecad/hal). ` +
79
- `Check the token against the catalog — it is the compatible with , and - replaced by _.`,
80
- );
81
- }
82
- return info;
83
- }
84
-
85
- /** Validate the op's bus kind against the part's bindings. */
86
- function requireBusKind(partKey: string, info: ReturnType<typeof requirePartInfo>, busKind: string) {
87
- if (!info.buses.includes(busKind)) {
88
- throw new Error(
89
- `Sensor part '${partKey}' (${info.compatible}) does not bind on ${busKind.toUpperCase()} — ` +
90
- `its bindings are ${info.buses.join('/').toUpperCase()}.`,
91
- );
92
- }
93
- }
94
-
95
- /**
96
- * Emit the per-sensor state block (device handle + sensor_value scratch).
97
- * Called from shimLines for each distinct sensor the program's ops reference.
98
- */
99
- export function sensorStateLines(part: string, bus: string, port: number | string, busKind: string = 'i2c', spiHz: number | string = 0, spiMode: number | string = 0, alertPin: number | string = -1): string[] {
100
- const partKey = sensorPartKey(String(part));
101
- requireBusKind(partKey, requirePartInfo(partKey), busKind);
102
- const n = sensorNames(part, bus, port, busKind);
103
- // The config comment carries construction facts (SPI clock, mode, alert
104
- // GPIO) to the overlay scanner — they shape the DT node, not the C++.
105
- const cfg = `// tc-sensor-cfg: ${n.dtLabel} hz=${Number(spiHz)} mode=${Number(spiMode)} alert=${Number(alertPin)}`;
106
- return [
107
- '// CUTTLEFISH_SENSOR_BEGIN',
108
- `static const struct device* ${n.devVar} = DEVICE_DT_GET(DT_NODELABEL(${n.dtLabel}));`,
109
- `static struct sensor_value ${n.valVar};`,
110
- cfg,
111
- '// CUTTLEFISH_SENSOR_END',
112
- ];
113
- }
114
-
115
- /**
116
- * Resolve a HAL sensor.* op to Zephyr C++.
117
- * Returns `{ code }` for fetch (statement), `{ expression }` for get (value).
118
- */
119
- export function lowerSensor(op: HALOpIR): { code?: string; expression?: string } {
120
- const o = op as any;
121
- const partKey = sensorPartKey(String(o.part));
122
- const info = requirePartInfo(partKey);
123
- requireBusKind(partKey, info, String(o.busKind ?? 'i2c'));
124
- const n = sensorNames(o.part, o.bus, o.port ?? o.address, String(o.busKind ?? 'i2c'));
125
-
126
- switch (op.operation) {
127
- case 'sensor.fetch':
128
- // sensor_sample_fetch. The leading (void) keeps the value scratch
129
- // referenced under Zephyr's -Werror when the program fetches but never
130
- // reads a channel (the state block is emitted per referenced sensor).
131
- return {
132
- code: `(void)${n.valVar}; sensor_sample_fetch(${n.devVar});`,
133
- };
134
- case 'sensor.get': {
135
- const chan = sensorChanName(String(o.chan));
136
- if (info.channels.length > 0 && !info.channels.includes(chan)) {
137
- throw new Error(
138
- `Sensor part '${partKey}' (${info.compatible}) does not serve channel '${chan}' — ` +
139
- `its driver serves: ${info.channels.join(', ')}.`,
140
- );
141
- }
142
- // sensor_value is val1 + val2 * 1e-6; evaluate once per read.
143
- return {
144
- expression:
145
- `(sensor_channel_get(${n.devVar}, SENSOR_CHAN_${chan}, &${n.valVar}), ` +
146
- `static_cast<double>(${n.valVar}.val1) + static_cast<double>(${n.valVar}.val2) / 1000000.0)`,
147
- };
148
- }
149
- default:
150
- throw new Error(
151
- `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
152
- `Open an issue or use rawCpp() to emit it manually.`,
153
- );
154
- }
155
- }
1
+ // ---------------------------------------------------------------------------
2
+ // Sensor lowering — Zephyr's uniform sensor API over the generated catalog
3
+ //
4
+ // One code shape for every part in @typecad/hal's sensor catalog: the op's
5
+ // part token resolves to the DT compatible, the bus string to the controller
6
+ // index, and the address to the I2C reg. The names are derived identically
7
+ // here (device handles), in strategy.ts state blocks, and in the overlay
8
+ // scanner (dt-config/overlay.ts + toolchain/index.ts) — the tc-pwm<pin>
9
+ // discipline: both sides derive from the same facts, so they cannot drift.
10
+ //
11
+ // Kconfig: nothing per-part. The in-tree driver Kconfigs are `default y` on
12
+ // DT_HAS_<COMPAT>_ENABLED, so the overlay's child node IS the enable switch;
13
+ // CONFIG_SENSOR (the umbrella under `if SENSOR`) is usage-gated in
14
+ // dt-config/kconfig.ts off the lowered `sensor_` tokens.
15
+ // ---------------------------------------------------------------------------
16
+
17
+ import { SENSOR_PART_INFO } from '@typecad/hal';
18
+ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
19
+ import { parseControllerIndex } from './util.js';
20
+
21
+ /** Strip the 'SENSOR.' property-access prefix from a part token arg. */
22
+ export function sensorPartKey(part: string): string {
23
+ return part.replace(/^SENSOR\./, '');
24
+ }
25
+
26
+ /** Strip the 'CHAN.' property-access prefix from a channel arg. */
27
+ export function sensorChanName(chan: string): string {
28
+ return chan.replace(/^CHAN\./, '');
29
+ }
30
+
31
+ /** All names a constructed sensor is known by, derived from the op facts. */
32
+ export interface SensorNames {
33
+ /** DT nodelabel of the child node the overlay emits
34
+ * (tc_<part>_i2c<N>_0x<addr> / tc_<part>_spi<N>_cs<pin>). */
35
+ dtLabel: string;
36
+ /** C device-handle variable (__tc_sensor_<stem>_dev). */
37
+ devVar: string;
38
+ /** C sensor_value scratch variable (__tc_sensor_<stem>_val). */
39
+ valVar: string;
40
+ /** The controller index the bus string carries. */
41
+ busIndex: number;
42
+ /** The bus port: I2C address or SPI CS pin. */
43
+ port: number;
44
+ /** 'i2c' | 'spi'. */
45
+ busKind: 'i2c' | 'spi' | 'w1';
46
+ }
47
+
48
+ /** Derive a sensor's DT/C++ names. The scanner regexes the emitted __tc_
49
+ * prefix form, so the part group must stay [a-z0-9_]+ and greedy. */
50
+ export function sensorNames(part: string, bus: string, port: number | string, busKind: string = 'i2c'): SensorNames {
51
+ const partKey = sensorPartKey(String(part));
52
+ const busIndex = parseControllerIndex(typeof bus === 'string' ? bus : String(bus));
53
+ const portNum = typeof port === 'number' ? port : parseInt(String(port));
54
+ const stem = busKind === 'spi'
55
+ ? `${partKey}_spi${busIndex}_cs${portNum}`
56
+ : busKind === 'w1'
57
+ ? `${partKey}_w1_p${portNum}`
58
+ : `${partKey}_i2c${busIndex}_0x${portNum.toString(16)}`;
59
+ return {
60
+ dtLabel: `tc_${stem}`,
61
+ devVar: `__tc_sensor_${stem}_dev`,
62
+ valVar: `__tc_sensor_${stem}_val`,
63
+ busIndex,
64
+ port: portNum,
65
+ busKind: busKind === 'spi' ? 'spi' : busKind === 'w1' ? 'w1' : 'i2c',
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Resolve the part token against the generated catalog, validating the bus
71
+ * kind and (when the driver scan knows them) the channel. Throws a build-time
72
+ * error with the Zephyr-facing name — the same words the user would meet in
73
+ * Zephyr docs, checked before west ever runs.
74
+ */
75
+ /** @internal */
76
+ function requirePartInfo(partKey: string) {
77
+ const info = SENSOR_PART_INFO[partKey];
78
+ if (!info) {
79
+ throw new Error(
80
+ `Sensor part '${partKey}' is not in the Zephyr sensor catalog (SENSOR in @typecad/hal). ` +
81
+ `Check the token against the catalog — it is the compatible with , and - replaced by _.`,
82
+ );
83
+ }
84
+ return info;
85
+ }
86
+
87
+ /** Validate the op's bus kind against the part's bindings. */
88
+ function requireBusKind(partKey: string, info: ReturnType<typeof requirePartInfo>, busKind: string) {
89
+ if (!info.buses.includes(busKind)) {
90
+ throw new Error(
91
+ `Sensor part '${partKey}' (${info.compatible}) does not bind on ${busKind.toUpperCase()} — ` +
92
+ `its bindings are ${info.buses.join('/').toUpperCase()}.`,
93
+ );
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Emit the per-sensor state block (device handle + sensor_value scratch).
99
+ * Called from shimLines for each distinct sensor the program's ops reference.
100
+ */
101
+ export function sensorStateLines(part: string, bus: string, port: number | string, busKind: string = 'i2c', spiHz: number | string = 0, spiMode: number | string = 0, alertPin: number | string = -1, resolution: number | string = 12): string[] {
102
+ const partKey = sensorPartKey(String(part));
103
+ requireBusKind(partKey, requirePartInfo(partKey), busKind);
104
+ const n = sensorNames(part, bus, port, busKind);
105
+ // The config comment carries construction facts (SPI clock, mode, alert
106
+ // GPIO) to the overlay scanner — they shape the DT node, not the C++.
107
+ const cfg = busKind === 'w1'
108
+ ? `// tc-sensor-cfg: ${n.dtLabel} res=${Number(resolution)}`
109
+ : `// tc-sensor-cfg: ${n.dtLabel} hz=${Number(spiHz)} mode=${Number(spiMode)} alert=${Number(alertPin)}`;
110
+ return [
111
+ '// CUTTLEFISH_SENSOR_BEGIN',
112
+ `static const struct device* ${n.devVar} = DEVICE_DT_GET(DT_NODELABEL(${n.dtLabel}));`,
113
+ `static struct sensor_value ${n.valVar};`,
114
+ cfg,
115
+ '// CUTTLEFISH_SENSOR_END',
116
+ ];
117
+ }
118
+
119
+ /**
120
+ * Resolve a HAL sensor.* op to Zephyr C++.
121
+ * Returns `{ code }` for fetch (statement), `{ expression }` for get (value).
122
+ */
123
+ export function lowerSensor(op: HALOpIR): { code?: string; expression?: string } {
124
+ const o = op as any;
125
+ const partKey = sensorPartKey(String(o.part));
126
+ const info = requirePartInfo(partKey);
127
+ requireBusKind(partKey, info, String(o.busKind ?? 'i2c'));
128
+ const n = sensorNames(o.part, o.bus, o.port ?? o.address, String(o.busKind ?? 'i2c'));
129
+
130
+ switch (op.operation) {
131
+ case 'sensor.fetch':
132
+ // sensor_sample_fetch. The leading (void) keeps the value scratch
133
+ // referenced under Zephyr's -Werror when the program fetches but never
134
+ // reads a channel (the state block is emitted per referenced sensor).
135
+ return {
136
+ code: `(void)${n.valVar}; sensor_sample_fetch(${n.devVar});`,
137
+ };
138
+ case 'sensor.get': {
139
+ const chan = sensorChanName(String(o.chan));
140
+ if (info.channels.length > 0 && !info.channels.includes(chan)) {
141
+ throw new Error(
142
+ `Sensor part '${partKey}' (${info.compatible}) does not serve channel '${chan}' — ` +
143
+ `its driver serves: ${info.channels.join(', ')}.`,
144
+ );
145
+ }
146
+ // sensor_value is val1 + val2 * 1e-6; evaluate once per read.
147
+ return {
148
+ expression:
149
+ `(sensor_channel_get(${n.devVar}, SENSOR_CHAN_${chan}, &${n.valVar}), ` +
150
+ `static_cast<double>(${n.valVar}.val1) + static_cast<double>(${n.valVar}.val2) / 1000000.0)`,
151
+ };
152
+ }
153
+ default:
154
+ throw new Error(
155
+ `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
156
+ `Open an issue or use rawCpp() to emit it manually.`,
157
+ );
158
+ }
159
+ }
@@ -0,0 +1,81 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Strip lowering — ws2812-spi child node + led_strip_update_rgb
3
+ //
4
+ // The strip is an spi-device child of one of the board's wired SPI
5
+ // controllers (Zephyr's ws2812-spi driver synthesizes the WS2812 waveform
6
+ // on the bus's MOSI line — the board-equal form: the strip uses whatever
7
+ // pads the board's devicetree routes that SPI to). The shim block declares
8
+ // the pixel buffer + device handle; the overlay generator synthesizes the
9
+ // DT child node (chain length + tuned frames — see dt-config/overlay.ts).
10
+ // set/fill edit the buffer; show() is one led_strip_update_rgb.
11
+ // ----------------------------------------------------------------------------
12
+
13
+ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
14
+ import type { ZephyrChipDescriptor } from '../chips/types.js';
15
+ import { parseControllerIndex } from './util.js';
16
+
17
+ /** The C variable names for a strip on controller index `idx`. */
18
+ function stripVars(idx: number): { buf: string; dev: string } {
19
+ return { buf: `__tc_strip${idx}_buf`, dev: `__tc_strip${idx}_dev` };
20
+ }
21
+
22
+ /**
23
+ * Emit the per-strip state. Called from shimLines for each SPI controller
24
+ * the program drives a strip on: one `struct led_rgb` buffer sized by the
25
+ * construction chain length + the DT device handle.
26
+ */
27
+ export function stripInitLines(chip: ZephyrChipDescriptor, busIndex: number, count: number): string[] {
28
+ const ctrl = chip.spi?.controllers[busIndex];
29
+ if (!ctrl) return [];
30
+ const { buf, dev } = stripVars(busIndex);
31
+ return [
32
+ '// CUTTLEFISH_STRIP_BEGIN',
33
+ `static struct led_rgb ${buf}[${count}];`,
34
+ `static const struct device* ${dev} = DEVICE_DT_GET(DT_NODELABEL(tc_strip${busIndex}));`,
35
+ '// CUTTLEFISH_STRIP_END',
36
+ ];
37
+ }
38
+
39
+ /**
40
+ * Resolve a HAL strip.* op to Zephyr C++.
41
+ * Returns `{ code }` for statement ops.
42
+ */
43
+ export function lowerStrip(
44
+ op: HALOpIR,
45
+ chip: ZephyrChipDescriptor,
46
+ ): { code?: string; expression?: string } {
47
+ const o = op as any;
48
+ // DAC discipline: a board with no wired SPI (or an out-of-range bus)
49
+ // lowers to a comment naming the constraint — the honest per-board answer
50
+ // (the manifest probe runs boardless; profileDiagnostics flags the usage).
51
+ const idx = parseControllerIndex(typeof o.bus === 'string' ? o.bus : String(o.bus));
52
+ if (!chip.spi || idx >= chip.spi.controllers.length) {
53
+ const wired = chip.spi ? `this board wires ${chip.spi.controllers.length} SPI controller(s)` : 'this board wires no SPI controller';
54
+ return { code: `/* strip on bus ${o.bus}: no such SPI controller — ${wired}; a Strip rides one of the board's SPI buses */` };
55
+ }
56
+ const { buf, dev } = stripVars(idx);
57
+ const count = Number(o.count ?? 1) || 1;
58
+
59
+ switch (op.operation) {
60
+ case 'strip.set_pixel': {
61
+ // Buffer assignment — runtime index and channel values splice as C++.
62
+ // The driver applies the strip's color order (GRB mapping) at show.
63
+ return {
64
+ code: `{ uint32_t __tc_strip_i = static_cast<uint32_t>(${o.index}); if (__tc_strip_i < ${count}U) { ${buf}[__tc_strip_i].r = static_cast<uint8_t>(${o.r}); ${buf}[__tc_strip_i].g = static_cast<uint8_t>(${o.g}); ${buf}[__tc_strip_i].b = static_cast<uint8_t>(${o.b}); } }`,
65
+ };
66
+ }
67
+ case 'strip.fill': {
68
+ return {
69
+ code: `{ for (uint32_t __tc_strip_j = 0U; __tc_strip_j < ${count}U; ++__tc_strip_j) { ${buf}[__tc_strip_j].r = static_cast<uint8_t>(${o.r}); ${buf}[__tc_strip_j].g = static_cast<uint8_t>(${o.g}); ${buf}[__tc_strip_j].b = static_cast<uint8_t>(${o.b}); } }`,
70
+ };
71
+ }
72
+ case 'strip.show': {
73
+ return { code: `(void)led_strip_update_rgb(${dev}, ${buf}, ${count}U);` };
74
+ }
75
+ default:
76
+ throw new Error(
77
+ `framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
78
+ `Open an issue or use rawCpp() to emit it manually.`,
79
+ );
80
+ }
81
+ }
@@ -48,8 +48,12 @@ export function lowerThread(op: HALOpIR): { code?: string; expression?: string }
48
48
  case 'thread.start': {
49
49
  // Store the entry, create + schedule immediately. The stack size comes
50
50
  // from the state block (the construction fact), via K_THREAD_STACK_SIZEOF.
51
+ // The thread gets a name so trace heartbeats report it as something
52
+ // other than "unnamed" — k_thread_name_set returns -ENOSYS without
53
+ // CONFIG_THREAD_NAME (which the scaffold turns on for traced builds),
54
+ // so the call is emitted unconditionally.
51
55
  return {
52
- code: `${p}_fn = (${o.handler}); (void)k_thread_create(&${p}_thread, ${p}_stack, K_THREAD_STACK_SIZEOF(${p}_stack), ${p}_tramp, NULL, NULL, NULL, ${o.priority ?? 5}, 0, K_NO_WAIT);`,
56
+ code: `${p}_fn = (${o.handler}); (void)k_thread_create(&${p}_thread, ${p}_stack, K_THREAD_STACK_SIZEOF(${p}_stack), ${p}_tramp, NULL, NULL, NULL, ${o.priority ?? 5}, 0, K_NO_WAIT); (void)k_thread_name_set(&${p}_thread, "tc_thread_${instance}");`,
53
57
  };
54
58
  }
55
59
  case 'thread.join':