@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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// I2S lowering — Zephyr's i2s API over the harvested controller node
|
|
3
|
+
//
|
|
4
|
+
// The controller is addressed by its DT nodelabel (chip.i2s, the harvested
|
|
5
|
+
// i2s@ node — ESP32 I2S0/1). Each direction lazy-inits on first use (the
|
|
6
|
+
// PWM discipline): configure + trigger START once, then every write/read is
|
|
7
|
+
// one block. The shim owns the mem slab (2 blocks), the TX packing buffer,
|
|
8
|
+
// and the RX receive buffer; blockAt/readAt index the last received block.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
12
|
+
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
13
|
+
|
|
14
|
+
/** The C variable names for an I2S controller's state. */
|
|
15
|
+
function prefix(idx: number): string {
|
|
16
|
+
return `__tc_i2s${idx}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Which i2s.* verbs the program actually uses on a controller — the init
|
|
20
|
+
* block emits only the state those verbs reference (a write-only program
|
|
21
|
+
* must not carry an unused rx buffer: -Werror fails the build). */
|
|
22
|
+
export interface I2sUsage {
|
|
23
|
+
write: boolean;
|
|
24
|
+
read: boolean;
|
|
25
|
+
readAt: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Emit the per-controller I2S state: device handle, mem slab, TX/RX block
|
|
30
|
+
* buffers, and the lazy per-direction init helpers. Called from shimLines
|
|
31
|
+
* when the program uses i2s.* on this controller and the chip declares it.
|
|
32
|
+
* The once-flags live HERE (shim scope), not in the lowered call sites — a
|
|
33
|
+
* second write() statement must not reconfigure/re-start a running stream
|
|
34
|
+
* (i2s_configure is legal only before the first trigger).
|
|
35
|
+
*/
|
|
36
|
+
export function i2sInitLines(chip: ZephyrChipDescriptor, controllerIndex: number, blockBytes: number, usage: I2sUsage): string[] {
|
|
37
|
+
const ctrl = chip.i2s?.controllers[controllerIndex];
|
|
38
|
+
if (!ctrl) return [];
|
|
39
|
+
const p = prefix(controllerIndex);
|
|
40
|
+
// readAt rides the RX engine too (its lowering lazily starts RX like read
|
|
41
|
+
// does) — a readAt-only program still references the whole engine state.
|
|
42
|
+
const needsEngine = usage.write || usage.read || usage.readAt;
|
|
43
|
+
const lines: string[] = [
|
|
44
|
+
'// CUTTLEFISH_I2S_BEGIN',
|
|
45
|
+
`static const struct device* ${p}_dev = DEVICE_DT_GET(DT_NODELABEL(${ctrl.nodeLabel}));`,
|
|
46
|
+
];
|
|
47
|
+
if (needsEngine) {
|
|
48
|
+
lines.push(
|
|
49
|
+
`K_MEM_SLAB_DEFINE_STATIC(${p}_slab, ${blockBytes}, 2, 4);`,
|
|
50
|
+
`K_MEM_SLAB_DEFINE_STATIC(${p}_rxslab, ${blockBytes}, 2, 4);`,
|
|
51
|
+
`static void ${p}_init(uint32_t hz, uint8_t channels, uint8_t bits, size_t block_bytes) {`,
|
|
52
|
+
' struct i2s_config cfg = { 0 };',
|
|
53
|
+
' cfg.word_size = bits;',
|
|
54
|
+
' cfg.channels = channels;',
|
|
55
|
+
' cfg.format = I2S_FMT_DATA_FORMAT_I2S;',
|
|
56
|
+
' cfg.options = 0;',
|
|
57
|
+
' cfg.frame_clk_freq = hz;',
|
|
58
|
+
` cfg.mem_slab = &${p}_slab;`,
|
|
59
|
+
' cfg.block_size = block_bytes;',
|
|
60
|
+
' cfg.timeout = 200;',
|
|
61
|
+
` int err = i2s_configure(${p}_dev, I2S_DIR_TX, &cfg);`,
|
|
62
|
+
' if (err != 0) { printk("typecad-hal i2s: tx configure failed: %d\\n", err); }',
|
|
63
|
+
` cfg.mem_slab = &${p}_rxslab;`,
|
|
64
|
+
` err = i2s_configure(${p}_dev, I2S_DIR_RX, &cfg);`,
|
|
65
|
+
' if (err != 0) { printk("typecad-hal i2s: rx configure failed: %d\\n", err); }',
|
|
66
|
+
'}',
|
|
67
|
+
`static bool ${p}_init_done = false;`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (usage.write) {
|
|
71
|
+
lines.push(
|
|
72
|
+
`static int16_t ${p}_txbuf[${blockBytes / 2}];`,
|
|
73
|
+
`static bool ${p}_tx_started = false;`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
if (usage.read || usage.readAt) {
|
|
77
|
+
lines.push(`static int16_t ${p}_rxbuf[${blockBytes / 2}];`);
|
|
78
|
+
lines.push(`static bool ${p}_rx_started = false;`);
|
|
79
|
+
}
|
|
80
|
+
lines.push('// CUTTLEFISH_I2S_END');
|
|
81
|
+
return lines;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolve a HAL i2s.* op to Zephyr C++.
|
|
86
|
+
* Returns `{ code }` for write, `{ expression }` for read/readAt.
|
|
87
|
+
*/
|
|
88
|
+
export function lowerI2s(
|
|
89
|
+
op: HALOpIR,
|
|
90
|
+
chip: ZephyrChipDescriptor,
|
|
91
|
+
): { code?: string; expression?: string } {
|
|
92
|
+
const o = op as any;
|
|
93
|
+
// DAC discipline: no harvested i2s@ node lowers to the honest comment.
|
|
94
|
+
if (!chip.i2s || chip.i2s.controllers.length === 0) {
|
|
95
|
+
return { code: `/* ${op.operation}: this board's chip data declares no I2S controller (no harvested i2s@ node) */` };
|
|
96
|
+
}
|
|
97
|
+
const idx = Math.min(Number(o.instance ?? 0), chip.i2s.controllers.length - 1);
|
|
98
|
+
const p = prefix(idx);
|
|
99
|
+
const hz = Number(o.hz ?? 16000);
|
|
100
|
+
const channels = Number(o.channels ?? 2);
|
|
101
|
+
const bits = Number(o.bits ?? 16);
|
|
102
|
+
const blockFrames = Number(o.blockFrames ?? 64);
|
|
103
|
+
const blockBytes = blockFrames * channels * (bits / 8);
|
|
104
|
+
|
|
105
|
+
switch (op.operation) {
|
|
106
|
+
case 'i2s.write': {
|
|
107
|
+
const samples = (o.samples ?? []) as (number | string)[];
|
|
108
|
+
const cap = blockFrames * channels;
|
|
109
|
+
if (samples.length > cap) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`I2S write: one block carries at most ${cap} samples (${blockFrames} frames × ${channels} channels) — got ${samples.length}.`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
// Zero-pad to the full block; samples are 16-bit.
|
|
115
|
+
const writes: string[] = [];
|
|
116
|
+
for (let i = 0; i < cap; i++) {
|
|
117
|
+
const v = i < samples.length ? samples[i] : 0;
|
|
118
|
+
writes.push(`${p}_txbuf[${i}] = static_cast<int16_t>(${v});`);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
code: `{ if (!${p}_init_done) { ${p}_init_done = true; ${p}_init(${hz}U, ${channels}U, ${bits}U, ${blockBytes}U); } ${writes.join(' ')} int err = i2s_buf_write(${p}_dev, ${p}_txbuf, ${blockBytes}U); if (err != 0) { printk("typecad-hal i2s: write failed: %d\\n", err); } else { if (!${p}_tx_started) { ${p}_tx_started = true; err = i2s_trigger(${p}_dev, I2S_DIR_TX, I2S_TRIGGER_START); if (err != 0) { printk("typecad-hal i2s: tx start failed: %d\\n", err); } } } }`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
case 'i2s.read': {
|
|
125
|
+
return {
|
|
126
|
+
expression: `({ if (!${p}_init_done) { ${p}_init_done = true; ${p}_init(${hz}U, ${channels}U, ${bits}U, ${blockBytes}U); } if (!${p}_rx_started) { ${p}_rx_started = true; int err = i2s_trigger(${p}_dev, I2S_DIR_RX, I2S_TRIGGER_START); if (err != 0) { printk("typecad-hal i2s: rx start failed: %d\\n", err); } } size_t got = 0U; int err = i2s_buf_read(${p}_dev, ${p}_rxbuf, &got); if (err != 0) { printk("typecad-hal i2s: read failed: %d\\n", err); } static_cast<double>(${p}_rxbuf[0]); })`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
case 'i2s.read_at': {
|
|
130
|
+
// Same lazy RX start as read(): readAt indexes the last RECEIVED
|
|
131
|
+
// block, so the stream must be running — and a readAt-only program
|
|
132
|
+
// references (and therefore compiles in) the whole engine state.
|
|
133
|
+
return {
|
|
134
|
+
expression: `({ if (!${p}_init_done) { ${p}_init_done = true; ${p}_init(${hz}U, ${channels}U, ${bits}U, ${blockBytes}U); } if (!${p}_rx_started) { ${p}_rx_started = true; int err = i2s_trigger(${p}_dev, I2S_DIR_RX, I2S_TRIGGER_START); if (err != 0) { printk("typecad-hal i2s: rx start failed: %d\\n", err); } } int32_t at = static_cast<int32_t>(${o.index}); double v = 0.0; if (at >= 0 && static_cast<uint32_t>(at) < (sizeof(${p}_rxbuf) / sizeof(${p}_rxbuf[0]))) { v = static_cast<double>(${p}_rxbuf[at]); } v; })`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
default:
|
|
138
|
+
throw new Error(
|
|
139
|
+
`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
140
|
+
`Open an issue or use rawCpp() to emit it manually.`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
package/src/lowering/index.ts
CHANGED
|
@@ -40,12 +40,21 @@ import { lowerFs } from './fs.js';
|
|
|
40
40
|
import { lowerSensor } from './sensor.js';
|
|
41
41
|
import { lowerHwtimer, lowerCounter } from './hwtimer.js';
|
|
42
42
|
import { lowerThread } from './thread.js';
|
|
43
|
+
import { lowerTrace } from './trace.js';
|
|
44
|
+
import { lowerStrip } from './strip.js';
|
|
45
|
+
import { lowerHid } from './hid.js';
|
|
46
|
+
import { lowerMatrix } from './matrix.js';
|
|
47
|
+
import { lowerPower } from './power.js';
|
|
48
|
+
import { lowerClock } from './clock.js';
|
|
49
|
+
import { lowerCan } from './can.js';
|
|
50
|
+
import { lowerI2s } from './i2s.js';
|
|
43
51
|
|
|
44
52
|
export {
|
|
45
53
|
lowerGpio, lowerTiming, lowerAdc, lowerPwm, lowerI2c, lowerSpi, lowerUart,
|
|
46
54
|
lowerUsb, lowerInterrupt, lowerWdt, lowerBle,
|
|
47
55
|
lowerWifi, lowerHttp, lowerMqtt, lowerPreferences, lowerBoard, lowerRandom,
|
|
48
|
-
lowerDac, lowerFs, lowerHwtimer, lowerCounter, lowerSensor, lowerThread,
|
|
56
|
+
lowerDac, lowerFs, lowerHwtimer, lowerCounter, lowerSensor, lowerThread, lowerTrace,
|
|
57
|
+
lowerStrip, lowerHid, lowerMatrix, lowerPower, lowerClock, lowerCan, lowerI2s,
|
|
49
58
|
};
|
|
50
59
|
|
|
51
60
|
/**
|
|
@@ -83,6 +92,14 @@ export function lowerHalOp(
|
|
|
83
92
|
if (op.operation.startsWith('random.')) return lowerRandom(op);
|
|
84
93
|
if (op.operation.startsWith('sensor.')) return lowerSensor(op);
|
|
85
94
|
if (op.operation.startsWith('thread.')) return lowerThread(op);
|
|
95
|
+
if (op.operation.startsWith('trace.')) return lowerTrace(op);
|
|
96
|
+
if (op.operation.startsWith('strip.')) return lowerStrip(op, chip);
|
|
97
|
+
if (op.operation.startsWith('hid.')) return lowerHid(op, chip);
|
|
98
|
+
if (op.operation.startsWith('matrix.')) return lowerMatrix(op);
|
|
99
|
+
if (op.operation.startsWith('power.')) return lowerPower(op, chip);
|
|
100
|
+
if (op.operation.startsWith('clock.')) return lowerClock(op);
|
|
101
|
+
if (op.operation.startsWith('can.')) return lowerCan(op, chip);
|
|
102
|
+
if (op.operation.startsWith('i2s.')) return lowerI2s(op, chip);
|
|
86
103
|
|
|
87
104
|
// raw / snprintf.emit / display.* / ... — not lowered by this
|
|
88
105
|
// framework. Return undefined so the transpiler falls back and the manifest
|
|
@@ -116,6 +116,12 @@ export function collectInterruptPins(program: unknown): Set<number> {
|
|
|
116
116
|
}
|
|
117
117
|
};
|
|
118
118
|
visit(program);
|
|
119
|
+
// Ops resolved to text while inlining one HAL call into another never become
|
|
120
|
+
// IR nodes — they ride on ProgramIR.resolvedHalOps (see strategy.ts's
|
|
121
|
+
// visitResolvedHalOps for the full story).
|
|
122
|
+
for (const op of (program as { resolvedHalOps?: unknown[] })?.resolvedHalOps ?? []) {
|
|
123
|
+
visit({ operation: op });
|
|
124
|
+
}
|
|
119
125
|
return pins;
|
|
120
126
|
}
|
|
121
127
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Matrix lowering — gpio-kbd-matrix node + input-event trampoline
|
|
3
|
+
//
|
|
4
|
+
// The gpio-kbd-matrix driver (Zephyr input subsystem) scans the grid and
|
|
5
|
+
// reports each key as ABS_X (column) + ABS_Y (row) + BTN_TOUCH (press).
|
|
6
|
+
// The shim decodes the triple and calls the user's lowered callback as
|
|
7
|
+
// (row, col, pressed). The overlay generator synthesizes the DT node with
|
|
8
|
+
// row-gpios/col-gpios phandle arrays from the construction pad lists —
|
|
9
|
+
// the lowering's comment marker carries rows/cols to the toolchain scan
|
|
10
|
+
// (the pwm user-facts marker precedent).
|
|
11
|
+
// ----------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Emit the matrix trampoline state. Called from shimLines when the program
|
|
17
|
+
* uses matrix.* — the listener targets the synthesized tc_matrix node, the
|
|
18
|
+
* handler pointer is assigned by the on_key lowering.
|
|
19
|
+
*/
|
|
20
|
+
export function matrixInitLines(): string[] {
|
|
21
|
+
return [
|
|
22
|
+
'// CUTTLEFISH_MATRIX_BEGIN',
|
|
23
|
+
'static void (*__tc_matrix_on_key)(double, double, bool) = NULL;',
|
|
24
|
+
'static int32_t __tc_matrix_row = -1;',
|
|
25
|
+
'static int32_t __tc_matrix_col = -1;',
|
|
26
|
+
'static void __tc_matrix_cb(struct input_event* evt, void* user_data) {',
|
|
27
|
+
' (void)user_data;',
|
|
28
|
+
' switch (evt->code) {',
|
|
29
|
+
' case INPUT_ABS_X:',
|
|
30
|
+
' __tc_matrix_col = evt->value;',
|
|
31
|
+
' break;',
|
|
32
|
+
' case INPUT_ABS_Y:',
|
|
33
|
+
' __tc_matrix_row = evt->value;',
|
|
34
|
+
' break;',
|
|
35
|
+
' case INPUT_BTN_TOUCH:',
|
|
36
|
+
' if (__tc_matrix_on_key != NULL && __tc_matrix_row >= 0 && __tc_matrix_col >= 0) {',
|
|
37
|
+
' __tc_matrix_on_key(static_cast<double>(__tc_matrix_row), static_cast<double>(__tc_matrix_col), evt->value != 0);',
|
|
38
|
+
' }',
|
|
39
|
+
' break;',
|
|
40
|
+
' default:',
|
|
41
|
+
' break;',
|
|
42
|
+
' }',
|
|
43
|
+
'}',
|
|
44
|
+
'INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_NODELABEL(tc_matrix)), __tc_matrix_cb, NULL);',
|
|
45
|
+
'// CUTTLEFISH_MATRIX_END',
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve a HAL matrix.* op to Zephyr C++.
|
|
51
|
+
* Returns `{ code }` for statement ops.
|
|
52
|
+
*/
|
|
53
|
+
export function lowerMatrix(op: HALOpIR): { code?: string; expression?: string } {
|
|
54
|
+
const o = op as any;
|
|
55
|
+
|
|
56
|
+
switch (op.operation) {
|
|
57
|
+
case 'matrix.on_key': {
|
|
58
|
+
// The comment marker carries the construction pad lists to the
|
|
59
|
+
// toolchain's overlay scan (the pwm user-facts precedent).
|
|
60
|
+
return {
|
|
61
|
+
code: `/* cuttlefish-matrix: rows=${o.rows} cols=${o.cols} */ __tc_matrix_on_key = ${o.handler};`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
default:
|
|
65
|
+
throw new Error(
|
|
66
|
+
`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
67
|
+
`Open an issue or use rawCpp() to emit it manually.`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/lowering/mqtt.ts
CHANGED
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
// shim getaddrinfo-resolves the broker host itself (like the HTTP shim).
|
|
14
14
|
// URI scheme: mqtt:// → 1883 plain TCP, mqtts:// → 8883 TLS
|
|
15
15
|
// (MQTT_TRANSPORT_SECURE + mqtt_sec_config, reusing the same mbedTLS matrix
|
|
16
|
-
// the HTTP TLS path established).
|
|
16
|
+
// the HTTP TLS path established). caCert pins the broker's CA — the PEM is
|
|
17
|
+
// DER-decoded at emit time and registered via tls_credential_add as a
|
|
18
|
+
// CA_CERTIFICATE sec tag with TLS_PEER_VERIFY_REQUIRED (verified TLS, the
|
|
19
|
+
// MQTT analogue of Request's caCert); mqtts:// without a CA stays
|
|
20
|
+
// encrypted-but-unverified (TLS_PEER_VERIFY_NONE).
|
|
17
21
|
//
|
|
18
22
|
// One client slot (HAL singleton). on_message callbacks are dispatched on the
|
|
19
23
|
// poll thread.
|
|
@@ -49,6 +53,8 @@ export function mqttInitLines(): string[] {
|
|
|
49
53
|
`// at connect time; host drives both DNS resolution and the TLS SNI/hostname.`,
|
|
50
54
|
`// on_message is the user callback (set via mqtt.on_message); it fires on the`,
|
|
51
55
|
`// poll thread for every incoming PUBLISH on a subscribed topic.`,
|
|
56
|
+
`// ca_der is the pinned CA (DER, user-owned from the caCert fact); nullptr`,
|
|
57
|
+
`// means mqtts:// runs encrypted-but-unverified.`,
|
|
52
58
|
`typedef void (*__tc_mqtt_msg_cb_t)(const char* topic, const char* payload);`,
|
|
53
59
|
`static struct {`,
|
|
54
60
|
` struct mqtt_client client;`,
|
|
@@ -59,6 +65,16 @@ export function mqttInitLines(): string[] {
|
|
|
59
65
|
` uint16_t port; // 1883 / 8883 unless overridden in the URI`,
|
|
60
66
|
` char client_id[64];`,
|
|
61
67
|
` bool tls; // scheme == "mqtts"`,
|
|
68
|
+
` const uint8_t* ca_der; // pinned CA (DER), nullptr = no CA`,
|
|
69
|
+
` size_t ca_der_len;`,
|
|
70
|
+
` // What this shim last registered under __TC_MQTT_SEC_TAG (DER arrays`,
|
|
71
|
+
` // are function-static, so pointer identity is stable). Zephyr's`,
|
|
72
|
+
` // credential store never REPLACES a tag — repeats are skipped, a`,
|
|
73
|
+
` // different CA at an occupied tag is reported, not silent.`,
|
|
74
|
+
` const uint8_t* ca_added;`,
|
|
75
|
+
` size_t ca_added_len;`,
|
|
76
|
+
` bool ca_added_set;`,
|
|
77
|
+
` sec_tag_t sec_tags[1]; // backs tls.config.sec_tag_list (static lifetime)`,
|
|
62
78
|
` volatile bool connected;`,
|
|
63
79
|
` volatile bool stop_poll; // set by disconnect to terminate the poll thread`,
|
|
64
80
|
` __tc_mqtt_msg_cb_t on_message;`,
|
|
@@ -200,6 +216,35 @@ export function mqttInitLines(): string[] {
|
|
|
200
216
|
` return host_len > 0U;`,
|
|
201
217
|
`}`,
|
|
202
218
|
``,
|
|
219
|
+
`// Strict IPv4 dotted-quad predicate: exactly four 0-255 groups. A`,
|
|
220
|
+
`// digit-and-dot scan would also swallow numeric-only DNS names ("123"),`,
|
|
221
|
+
`// silently skipping the TLS hostname check for them (same helper as the`,
|
|
222
|
+
`// HTTP shim).`,
|
|
223
|
+
`static bool __tc_mqtt_host_is_ipv4(const char* h) {`,
|
|
224
|
+
` uint32_t quads = 0U;`,
|
|
225
|
+
` uint32_t val = 0U;`,
|
|
226
|
+
` uint32_t digits = 0U;`,
|
|
227
|
+
` for (const char* p = h; ; p++) {`,
|
|
228
|
+
` if (*p >= '0' && *p <= '9') {`,
|
|
229
|
+
` val = (val * 10U) + static_cast<uint32_t>(*p - '0');`,
|
|
230
|
+
` digits++;`,
|
|
231
|
+
` if (digits > 3U || val > 255U) return false;`,
|
|
232
|
+
` } else if (*p == '.') {`,
|
|
233
|
+
` if (digits == 0U || quads >= 3U) return false;`,
|
|
234
|
+
` quads++;`,
|
|
235
|
+
` val = 0U;`,
|
|
236
|
+
` digits = 0U;`,
|
|
237
|
+
` } else if (*p == '\\0') {`,
|
|
238
|
+
` if (digits == 0U) return false;`,
|
|
239
|
+
` quads++;`,
|
|
240
|
+
` break;`,
|
|
241
|
+
` } else {`,
|
|
242
|
+
` return false;`,
|
|
243
|
+
` }`,
|
|
244
|
+
` }`,
|
|
245
|
+
` return quads == 4U;`,
|
|
246
|
+
`}`,
|
|
247
|
+
``,
|
|
203
248
|
`// ── connect ─────────────────────────────────────────────────────────────`,
|
|
204
249
|
`// Parse URI → resolve broker → init client → mqtt_connect → spawn poll thread.`,
|
|
205
250
|
`// Blocks until CONNACK (the poll thread isn't running yet, so we drive a few`,
|
|
@@ -248,14 +293,47 @@ export function mqttInitLines(): string[] {
|
|
|
248
293
|
` __tc_mqtt.client.transport.type = MQTT_TRANSPORT_SECURE;`,
|
|
249
294
|
` struct mqtt_sec_config* tls_cfg = &__tc_mqtt.client.transport.tls.config;`,
|
|
250
295
|
` (void)memset(tls_cfg, 0, sizeof(*tls_cfg));`,
|
|
251
|
-
`
|
|
252
|
-
`
|
|
253
|
-
`
|
|
254
|
-
`
|
|
255
|
-
`
|
|
256
|
-
`
|
|
296
|
+
` if (__tc_mqtt.ca_der != nullptr) {`,
|
|
297
|
+
` // Pinned CA (the caCert construction fact): register it as a`,
|
|
298
|
+
` // CA_CERTIFICATE sec tag and REQUIRE peer verification — the MQTT`,
|
|
299
|
+
` // analogue of the HTTPS caCert path. Zephyr's credential store`,
|
|
300
|
+
` // never REPLACES a tag+type pair: re-registering the same DER`,
|
|
301
|
+
` // (pointer identity — the arrays are function-static) is skipped`,
|
|
302
|
+
` // silently; a DIFFERENT CA hitting an occupied tag is reported so`,
|
|
303
|
+
` // a wrong-CA verify is never silent; any other failure keeps the`,
|
|
304
|
+
` // tag list empty and verification required — fail closed.`,
|
|
305
|
+
` if (!__tc_mqtt.ca_added_set || __tc_mqtt.ca_added != __tc_mqtt.ca_der`,
|
|
306
|
+
` || __tc_mqtt.ca_added_len != __tc_mqtt.ca_der_len) {`,
|
|
307
|
+
` int cred_rc = tls_credential_add(__TC_MQTT_SEC_TAG,`,
|
|
308
|
+
` TLS_CREDENTIAL_CA_CERTIFICATE,`,
|
|
309
|
+
` __tc_mqtt.ca_der, __tc_mqtt.ca_der_len);`,
|
|
310
|
+
` __tc_mqtt.ca_added = __tc_mqtt.ca_der;`,
|
|
311
|
+
` __tc_mqtt.ca_added_len = __tc_mqtt.ca_der_len;`,
|
|
312
|
+
` __tc_mqtt.ca_added_set = true;`,
|
|
313
|
+
` if (cred_rc == -17 /* -EEXIST */) {`,
|
|
314
|
+
` printk("tc-mqtt: sec tag %d already holds a CA; the first one stays active (rc=%d)\\n", __TC_MQTT_SEC_TAG, cred_rc);`,
|
|
315
|
+
` } else if (cred_rc != 0) {`,
|
|
316
|
+
` printk("tc-mqtt: CA registration failed (rc=%d) - verifying against no credentials\\n", cred_rc);`,
|
|
317
|
+
` }`,
|
|
318
|
+
` }`,
|
|
319
|
+
` if (__tc_mqtt.ca_added_set) {`,
|
|
320
|
+
` __tc_mqtt.sec_tags[0] = __TC_MQTT_SEC_TAG;`,
|
|
321
|
+
` tls_cfg->sec_tag_list = __tc_mqtt.sec_tags;`,
|
|
322
|
+
` tls_cfg->sec_tag_count = 1U;`,
|
|
323
|
+
` }`,
|
|
324
|
+
` tls_cfg->peer_verify = TLS_PEER_VERIFY_REQUIRED;`,
|
|
325
|
+
` } else {`,
|
|
326
|
+
` // No CA pinned: the session is encrypted but the broker's identity`,
|
|
327
|
+
` // is not verified — the strongest the no-caCert form can express.`,
|
|
328
|
+
` tls_cfg->peer_verify = TLS_PEER_VERIFY_NONE;`,
|
|
329
|
+
` }`,
|
|
257
330
|
` tls_cfg->cipher_list = nullptr;`,
|
|
258
|
-
`
|
|
331
|
+
` // mbedtls does not match IP literals against SAN entries — set the`,
|
|
332
|
+
` // hostname (SNI + identity check) only for name hosts; IP brokers`,
|
|
333
|
+
` // verify chain-only (same policy as the HTTPS path).`,
|
|
334
|
+
` if (!__tc_mqtt_host_is_ipv4(__tc_mqtt.host)) {`,
|
|
335
|
+
` tls_cfg->hostname = __tc_mqtt.host;`,
|
|
336
|
+
` }`,
|
|
259
337
|
`#else`,
|
|
260
338
|
` printk("tc-mqtt: mqtts:// requested but CONFIG_MQTT_LIB_TLS is off\\n");`,
|
|
261
339
|
` return;`,
|
|
@@ -307,6 +385,10 @@ export function mqttInitLines(): string[] {
|
|
|
307
385
|
` __tc_mqtt.on_message = fn;`,
|
|
308
386
|
`}`,
|
|
309
387
|
``,
|
|
388
|
+
`// ca_cert is the DER-decoded PEM from the caCert construction fact`,
|
|
389
|
+
`// (user-owned static array; nullptr = no pinned CA).`,
|
|
390
|
+
`static inline void __tc_mqtt_set_ca_cert_der(const uint8_t* der, size_t len) { __tc_mqtt.ca_der = der; __tc_mqtt.ca_der_len = len; }`,
|
|
391
|
+
``,
|
|
310
392
|
`static inline void __tc_mqtt_subscribe(const char* topic) {`,
|
|
311
393
|
` static struct mqtt_topic topics[4];`,
|
|
312
394
|
` static uint16_t msg_id = 1;`,
|
|
@@ -361,6 +443,25 @@ export function lowerMqtt(op: HALOpIR): { code?: string; expression?: string } {
|
|
|
361
443
|
switch (op.operation) {
|
|
362
444
|
case 'mqtt.connect':
|
|
363
445
|
return { code: `__tc_mqtt_connect(${s(o.brokerUri)}, ${s(o.clientId)});` };
|
|
446
|
+
case 'mqtt.set_ca_cert': {
|
|
447
|
+
// PEM → DER at emit time (this tree's tf-psa-crypto mbedTLS has no PEM
|
|
448
|
+
// parser — same decode as http.set_ca_cert; tls_credential_add takes
|
|
449
|
+
// the DER directly).
|
|
450
|
+
const pem = String(o.pem ?? '').replace(/^"|"$/g, '').replace(/\\n/g, '\n');
|
|
451
|
+
if (pem.replace(/\s+/g, '') === '') {
|
|
452
|
+
// No-CA sentinel (Mqtt always emits the op from connect(); opts.caCert
|
|
453
|
+
// defaults to '') — elide silently so plain-mqtt:// carries no comment.
|
|
454
|
+
return { code: '' };
|
|
455
|
+
}
|
|
456
|
+
const b64 = pem.replace(/-----[A-Z ]+-----/g, '').replace(/\s+/g, '');
|
|
457
|
+
const der = Buffer.from(b64, 'base64');
|
|
458
|
+
// Every X.509 DER starts with the ASN.1 SEQUENCE tag (0x30); a long
|
|
459
|
+
// but non-cert payload fails here instead of at a confusing handshake
|
|
460
|
+
// error on the target (same check as http.set_ca_cert).
|
|
461
|
+
if (der.length < 100 || der[0] !== 0x30) return { code: `// tc-mqtt: caCert PEM failed to decode` };
|
|
462
|
+
const hex = [...der].map((b) => `0x${b.toString(16).padStart(2, '0')}`).join(', ');
|
|
463
|
+
return { code: `{ static const uint8_t __tc_mqtt_ca_der[] = { ${hex} }; __tc_mqtt_set_ca_cert_der(__tc_mqtt_ca_der, sizeof(__tc_mqtt_ca_der)); }` };
|
|
464
|
+
}
|
|
364
465
|
case 'mqtt.on_message':
|
|
365
466
|
return { code: `__tc_mqtt_set_on_message(${s(o.handler)});` };
|
|
366
467
|
case 'mqtt.subscribe':
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Power lowering — sys_poweroff() for explicit soft-off entry
|
|
3
|
+
//
|
|
4
|
+
// Both SoC families implement the kernel's sys_poweroff(): ESP32 parks the
|
|
5
|
+
// RTC domain and calls esp_deep_sleep_start(); STM32 enters standby-class
|
|
6
|
+
// deep sleep. The call never returns — wake is a reset or a wake source
|
|
7
|
+
// (board-specific: the Black Pill's WKUP pin is PA0; ESP32 GPIOs armed as
|
|
8
|
+
// wake triggers). Statement emission continues after the call site, but
|
|
9
|
+
// nothing there runs — the transpiler treats it as any other void call.
|
|
10
|
+
// ----------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
13
|
+
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a HAL power.* op to Zephyr C++.
|
|
17
|
+
* Returns `{ code }` for statement ops.
|
|
18
|
+
*/
|
|
19
|
+
export function lowerPower(op: HALOpIR, chip?: ZephyrChipDescriptor): { code?: string; expression?: string } {
|
|
20
|
+
const o = op as any;
|
|
21
|
+
switch (op.operation) {
|
|
22
|
+
case 'power.off':
|
|
23
|
+
return { code: 'sys_poweroff();' };
|
|
24
|
+
case 'power.off_for': {
|
|
25
|
+
// DAC discipline: without the RTC wake-timer fact, a timed off would
|
|
26
|
+
// sleep with no wake source — lower to a comment naming it.
|
|
27
|
+
if (!chip?.powerWakeTimer) {
|
|
28
|
+
return { code: `/* Power.offFor(${o.ms} ms): this board's SoC declares no RTC wake timer — soft-off here has no armed wake; use Power.off() */` };
|
|
29
|
+
}
|
|
30
|
+
// Arm the RTC timer (µs), then enter soft-off; wake is a reboot.
|
|
31
|
+
return {
|
|
32
|
+
code: `{ esp_sleep_enable_timer_wakeup(static_cast<uint64_t>(${o.ms}) * 1000ULL); sys_poweroff(); }`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
default:
|
|
36
|
+
throw new Error(
|
|
37
|
+
`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
38
|
+
`Open an issue or use rawCpp() to emit it manually.`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|