@typecad/framework-zephyr 1.0.0-alpha.13 → 1.0.0-alpha.15
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/README.md +22 -35
- package/dist/as-built.d.ts +33 -0
- package/dist/as-built.js +188 -0
- package/dist/async/timer-polyfill.js +1 -1
- package/dist/boardgen.d.ts +128 -0
- package/dist/boardgen.js +1366 -0
- package/dist/chips/board-overrides.d.ts +7 -0
- package/dist/chips/board-overrides.js +11 -0
- package/dist/chips/controllers.d.ts +28 -8
- package/dist/chips/controllers.js +49 -12
- package/dist/chips/esp32s3.js +49 -0
- package/dist/chips/index.d.ts +6 -12
- package/dist/chips/index.js +18 -34
- package/dist/chips/resolve.d.ts +16 -2
- package/dist/chips/resolve.js +298 -15
- package/dist/chips/soc/.d.ts +2 -0
- package/dist/chips/soc/.js +129 -0
- package/dist/chips/soc/esp32.d.ts +2 -0
- package/dist/chips/soc/esp32.js +120 -0
- package/dist/chips/soc/esp32c3.d.ts +2 -0
- package/dist/chips/soc/esp32c3.js +90 -0
- package/dist/chips/soc/esp32c6.d.ts +2 -0
- package/dist/chips/soc/esp32c6.js +109 -0
- package/dist/chips/soc/esp32s3.d.ts +2 -0
- package/dist/chips/soc/esp32s3.js +189 -0
- package/dist/chips/soc/index.d.ts +2 -0
- package/dist/chips/soc/index.js +23 -0
- package/dist/chips/soc/nrf52840.d.ts +2 -0
- package/dist/chips/soc/nrf52840.js +130 -0
- package/dist/chips/soc/rp2040.d.ts +2 -0
- package/dist/chips/soc/rp2040.js +141 -0
- package/dist/chips/soc/rp2350a.d.ts +2 -0
- package/dist/chips/soc/rp2350a.js +145 -0
- package/dist/chips/soc/samd21g18a.d.ts +2 -0
- package/dist/chips/soc/samd21g18a.js +143 -0
- package/dist/chips/soc/stm32f411xe.d.ts +2 -0
- package/dist/chips/soc/stm32f411xe.js +251 -0
- package/dist/chips/types.d.ts +382 -12
- package/dist/chips/xiao-ble.js +33 -0
- package/dist/display/profiles.js +1 -1
- package/dist/doctor.js +38 -4
- package/dist/dt-config/custom-board.d.ts +23 -0
- package/dist/dt-config/custom-board.js +227 -0
- package/dist/dt-config/kconfig.d.ts +83 -1
- package/dist/dt-config/kconfig.js +116 -27
- package/dist/dt-config/overlay.js +623 -18
- package/dist/framework.manifest.d.ts +20 -30
- package/dist/framework.manifest.js +124 -178
- package/dist/index.d.ts +4 -1
- package/dist/index.js +8 -2
- package/dist/lowering/adc.d.ts +26 -5
- package/dist/lowering/adc.js +137 -31
- package/dist/lowering/ble.js +31 -28
- package/dist/lowering/dac.js +9 -9
- package/dist/lowering/fs.js +18 -10
- package/dist/lowering/gpio.d.ts +2 -0
- package/dist/lowering/gpio.js +90 -10
- package/dist/lowering/http.js +39 -14
- package/dist/lowering/hwtimer.d.ts +4 -0
- package/dist/lowering/hwtimer.js +35 -10
- package/dist/lowering/i2c.js +46 -54
- package/dist/lowering/index.d.ts +5 -5
- package/dist/lowering/index.js +15 -16
- package/dist/lowering/interrupts.d.ts +22 -5
- package/dist/lowering/interrupts.js +104 -22
- package/dist/lowering/mqtt.js +12 -6
- package/dist/lowering/power.js +0 -4
- package/dist/lowering/preferences.js +49 -77
- package/dist/lowering/pulse.js +5 -36
- package/dist/lowering/pwm.d.ts +32 -3
- package/dist/lowering/pwm.js +105 -17
- package/dist/lowering/random.js +3 -3
- package/dist/lowering/sensor.d.ts +37 -0
- package/dist/lowering/sensor.js +119 -0
- package/dist/lowering/spi.d.ts +12 -0
- package/dist/lowering/spi.js +65 -40
- package/dist/lowering/thread.d.ts +14 -0
- package/dist/lowering/thread.js +55 -0
- package/dist/lowering/timing.js +20 -46
- package/dist/lowering/tone.js +18 -5
- package/dist/lowering/uart.d.ts +13 -0
- package/dist/lowering/uart.js +72 -51
- package/dist/lowering/usb.d.ts +34 -0
- package/dist/lowering/usb.js +206 -0
- package/dist/lowering/wdt.d.ts +1 -1
- package/dist/lowering/wdt.js +17 -7
- package/dist/lowering/wifi.d.ts +3 -2
- package/dist/lowering/wifi.js +120 -111
- package/dist/sdk/board-catalog-sync.d.ts +16 -0
- package/dist/sdk/board-catalog-sync.js +41 -0
- package/dist/sdk/board-data.generated.d.ts +2 -0
- package/dist/sdk/board-data.generated.js +4 -0
- package/dist/sdk/catalog-walker.d.ts +90 -0
- package/dist/sdk/catalog-walker.js +682 -0
- package/dist/sdk/dts-reader.d.ts +83 -0
- package/dist/sdk/dts-reader.js +596 -0
- package/dist/strategy.d.ts +136 -17
- package/dist/strategy.js +881 -212
- package/dist/toolchain/bossac-touch.d.ts +18 -0
- package/dist/toolchain/bossac-touch.js +88 -0
- package/dist/toolchain/compat.d.ts +2 -2
- package/dist/toolchain/compat.js +8 -3
- package/dist/toolchain/debug-config.d.ts +21 -26
- package/dist/toolchain/debug-config.js +212 -72
- package/dist/toolchain/env-check.d.ts +4 -4
- package/dist/toolchain/env-check.js +5 -5
- package/dist/toolchain/index.d.ts +66 -1
- package/dist/toolchain/index.js +713 -58
- package/dist/toolchain/scaffold.d.ts +9 -0
- package/dist/toolchain/scaffold.js +84 -7
- package/dist/toolchain/west-discover.d.ts +8 -1
- package/dist/toolchain/west-discover.js +19 -6
- package/dist/toolchain/west-spawn.js +17 -5
- package/installer/CHANGELOG.md +473 -0
- package/installer/README.md +328 -0
- package/installer/environment.yml +46 -0
- package/installer/etc/conda/activate.d/zephyr.bat +7 -0
- package/installer/etc/conda/activate.d/zephyr.ps1 +12 -0
- package/installer/etc/conda/activate.d/zephyr.sh +20 -0
- package/installer/etc/conda/deactivate.d/zephyr.bat +6 -0
- package/installer/etc/conda/deactivate.d/zephyr.ps1 +6 -0
- package/installer/etc/conda/deactivate.d/zephyr.sh +4 -0
- package/installer/install.mjs +580 -0
- package/installer/install.ps1 +619 -0
- package/installer/install.sh +310 -0
- package/installer/lib/detect-platform.sh +62 -0
- package/installer/lib/fetch-sdk.sh +304 -0
- package/installer/lib/init-workspace.sh +153 -0
- package/installer/lib/write-activation.sh +76 -0
- package/installer/patches/tfm-c-include-separators.patch +19 -0
- package/installer/patches/zephyr-tfm-nordic-zephyr-include.patch +24 -0
- package/installer/templates/project/.typecad/activate-zephyr.ps1 +31 -0
- package/installer/templates/project/.typecad/activate-zephyr.sh +31 -0
- package/installer/templates/project/.vscode/settings.json +30 -0
- package/installer/templates/project/README.md +58 -0
- package/installer/versions.env +110 -0
- package/package.json +16 -16
- package/src/as-built.ts +206 -0
- package/src/boardgen.ts +1428 -0
- package/src/chips/controllers.ts +61 -12
- package/src/chips/index.ts +35 -54
- package/src/chips/resolve.ts +316 -14
- package/src/chips/types.ts +388 -13
- package/src/display/profiles.ts +1 -1
- package/src/doctor.ts +110 -77
- package/src/dt-config/custom-board.ts +325 -0
- package/src/dt-config/kconfig.ts +177 -19
- package/src/dt-config/overlay.ts +1058 -450
- package/src/framework.manifest.ts +125 -178
- package/src/index.ts +57 -41
- package/src/lowering/adc.ts +222 -102
- package/src/lowering/ble.ts +32 -28
- package/src/lowering/dac.ts +82 -82
- package/src/lowering/fs.ts +18 -10
- package/src/lowering/gpio.ts +242 -152
- package/src/lowering/http.ts +512 -488
- package/src/lowering/hwtimer.ts +139 -101
- package/src/lowering/i2c.ts +116 -126
- package/src/lowering/index.ts +91 -93
- package/src/lowering/interrupts.ts +113 -21
- package/src/lowering/mqtt.ts +12 -6
- package/src/lowering/preferences.ts +327 -354
- package/src/lowering/pwm.ts +167 -74
- package/src/lowering/random.ts +3 -3
- package/src/lowering/sensor.ts +155 -0
- package/src/lowering/spi.ts +164 -124
- package/src/lowering/thread.ts +63 -0
- package/src/lowering/timing.ts +46 -72
- package/src/lowering/uart.ts +73 -51
- package/src/lowering/usb.ts +221 -0
- package/src/lowering/wdt.ts +95 -84
- package/src/lowering/wifi.ts +120 -111
- package/src/sdk/board-catalog-sync.ts +62 -0
- package/src/strategy.ts +888 -229
- package/src/toolchain/bossac-touch.ts +112 -0
- package/src/toolchain/compat.ts +8 -3
- package/src/toolchain/debug-config.ts +666 -522
- package/src/toolchain/env-check.ts +9 -9
- package/src/toolchain/index.ts +765 -44
- package/src/toolchain/scaffold.ts +81 -7
- package/src/toolchain/west-discover.ts +334 -316
- package/src/toolchain/west-spawn.ts +17 -5
- package/src/async/timer-polyfill.ts +0 -107
- package/src/chips/esp32.ts +0 -74
- package/src/chips/esp32s3.ts +0 -57
- package/src/chips/xiao-ble.ts +0 -70
- package/src/lowering/power.ts +0 -70
- package/src/lowering/pulse.ts +0 -93
- package/src/lowering/tone.ts +0 -60
- package/src/lowering/worker-backing.ts +0 -94
- package/src/lowering/worker.ts +0 -17
package/dist/lowering/adc.d.ts
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
2
2
|
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the SAADC channel index for an adc.read argument; -1 if unmapped.
|
|
5
|
+
*
|
|
6
|
+
* The numeric argument serves two HAL forms: `InputPin.readAnalog()` passes
|
|
7
|
+
* a GPIO number, while the Arduino-compat `ADC.read(channel)` passes an ADC
|
|
8
|
+
* channel number. Pin-first resolution keeps readAnalog behavior unchanged;
|
|
9
|
+
* the channel fallback makes `ADC.read(n)` resolve on targets where channel
|
|
10
|
+
* n does not alias GPIO n (e.g. the XIAO's AIN2 = P0.28). Where a number
|
|
11
|
+
* matches both a mapped pin and another channel's index, the pin wins.
|
|
12
|
+
*/
|
|
4
13
|
export declare function adcChannelForPin(chip: ZephyrChipDescriptor, pin: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the owning ADC controller's node label for an adc.read argument.
|
|
16
|
+
* Defaults to the descriptor's primary (`chip.adc.nodeLabel`) — channels on
|
|
17
|
+
* additional controllers (STM32 adc3-only routes, ESP32 adc2 pads) carry
|
|
18
|
+
* their controller explicitly, and their device handle is
|
|
19
|
+
* `__tc_adc_<label>_dev` (the primary keeps the unsuffixed `__tc_adc_dev`
|
|
20
|
+
* so single-controller output is unchanged).
|
|
21
|
+
*/
|
|
22
|
+
export declare function adcControllerForPin(chip: ZephyrChipDescriptor, pin: number): string;
|
|
5
23
|
/**
|
|
6
24
|
* Emit the per-channel ADC setup state. One block per channel in the chip
|
|
7
|
-
* descriptor
|
|
8
|
-
*
|
|
9
|
-
*
|
|
25
|
+
* descriptor that the PROGRAM ACTUALLY READS (`usedPins`) — an unread
|
|
26
|
+
* channel's `static` setup function would trip -Wunused-function in the
|
|
27
|
+
* single generated TU. When `usedPins` is omitted (probe paths with no
|
|
28
|
+
* program), every descriptor channel is emitted. Each block is guarded by a
|
|
29
|
+
* `static bool __tc_adc<N>_ready` so the first read configures it and
|
|
30
|
+
* subsequent reads skip. Called from shimLines when the program uses ADC.
|
|
10
31
|
*/
|
|
11
|
-
export declare function adcInitLines(chip: ZephyrChipDescriptor): string[];
|
|
32
|
+
export declare function adcInitLines(chip: ZephyrChipDescriptor, usedPins?: ReadonlySet<number>, overrideDevices?: ReadonlySet<string>): string[];
|
|
12
33
|
/**
|
|
13
34
|
* Resolve a HAL adc.* op to Zephyr C++.
|
|
14
35
|
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
package/dist/lowering/adc.js
CHANGED
|
@@ -11,26 +11,115 @@
|
|
|
11
11
|
// then just read. A static `adc_channel_cfg` + a per-channel "configured" flag
|
|
12
12
|
// guards the setup, so repeated reads don't reconfigure.
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
|
-
|
|
14
|
+
import { ZEPHYR_ADC_GAINS, ZEPHYR_ADC_REFERENCES } from '@typecad/hal';
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the SAADC channel index for an adc.read argument; -1 if unmapped.
|
|
17
|
+
*
|
|
18
|
+
* The numeric argument serves two HAL forms: `InputPin.readAnalog()` passes
|
|
19
|
+
* a GPIO number, while the Arduino-compat `ADC.read(channel)` passes an ADC
|
|
20
|
+
* channel number. Pin-first resolution keeps readAnalog behavior unchanged;
|
|
21
|
+
* the channel fallback makes `ADC.read(n)` resolve on targets where channel
|
|
22
|
+
* n does not alias GPIO n (e.g. the XIAO's AIN2 = P0.28). Where a number
|
|
23
|
+
* matches both a mapped pin and another channel's index, the pin wins.
|
|
24
|
+
*/
|
|
15
25
|
export function adcChannelForPin(chip, pin) {
|
|
16
|
-
const
|
|
17
|
-
|
|
26
|
+
const channels = chip.adc?.channels ?? [];
|
|
27
|
+
const byPin = channels.find((c) => c.pin === pin);
|
|
28
|
+
if (byPin)
|
|
29
|
+
return byPin.channel;
|
|
30
|
+
const byChannel = channels.find((c) => c.channel === pin);
|
|
31
|
+
return byChannel ? byChannel.channel : -1;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the owning ADC controller's node label for an adc.read argument.
|
|
35
|
+
* Defaults to the descriptor's primary (`chip.adc.nodeLabel`) — channels on
|
|
36
|
+
* additional controllers (STM32 adc3-only routes, ESP32 adc2 pads) carry
|
|
37
|
+
* their controller explicitly, and their device handle is
|
|
38
|
+
* `__tc_adc_<label>_dev` (the primary keeps the unsuffixed `__tc_adc_dev`
|
|
39
|
+
* so single-controller output is unchanged).
|
|
40
|
+
*/
|
|
41
|
+
export function adcControllerForPin(chip, pin) {
|
|
42
|
+
const channels = chip.adc?.channels ?? [];
|
|
43
|
+
const byPin = channels.find((c) => c.pin === pin);
|
|
44
|
+
if (byPin)
|
|
45
|
+
return byPin.controller ?? chip.adc?.nodeLabel ?? 'adc';
|
|
46
|
+
const byChannel = channels.find((c) => c.channel === pin);
|
|
47
|
+
return byChannel?.controller ?? chip.adc?.nodeLabel ?? 'adc';
|
|
48
|
+
}
|
|
49
|
+
/** The C++ device-handle symbol for an ADC controller nodelabel. The primary
|
|
50
|
+
* controller maps to the historical unsuffixed `__tc_adc_dev`. */
|
|
51
|
+
function adcDevSymbol(controller, primary) {
|
|
52
|
+
return controller === primary ? '__tc_adc_dev' : `__tc_adc_${controller}_dev`;
|
|
53
|
+
}
|
|
54
|
+
// ── Thin ADC (hal/adc-pin.ts) — gain/reference tokens ──────────────────────
|
|
55
|
+
//
|
|
56
|
+
// "ADC.GAIN_1_4" / "ADC.REF_INTERNAL" token text maps name-for-
|
|
57
|
+
// name onto the enum adc_gain / adc_reference macros. The name sets come from
|
|
58
|
+
// the GENERATED Zephyr token tables (scripts/gen-zephyr-hal-tokens.mjs —
|
|
59
|
+
// parsed from the pinned tree's headers, so the set cannot drift from
|
|
60
|
+
// upstream); empty/absent tokens mean "use the chip descriptor's pair".
|
|
61
|
+
const ADC_GAIN_TOKENS = Object.fromEntries(ZEPHYR_ADC_GAINS.map((g) => [`ADC.GAIN_${g}`, `ADC_GAIN_${g}`]));
|
|
62
|
+
const ADC_REF_TOKENS = Object.fromEntries(ZEPHYR_ADC_REFERENCES.map((r) => [`ADC.REF_${r}`, `ADC_REF_${r}`]));
|
|
63
|
+
/** Resolve one thin-ADC token to its macro; undefined when absent/empty.
|
|
64
|
+
* Unknown tokens are build errors naming the valid spellings. */
|
|
65
|
+
function adcThinToken(token, map, kind) {
|
|
66
|
+
const t = String(token ?? '').trim();
|
|
67
|
+
if (!t || t === '0' || t === "''")
|
|
68
|
+
return undefined;
|
|
69
|
+
const m = map[t];
|
|
70
|
+
if (!m) {
|
|
71
|
+
throw new Error(`ADC ${kind} token '${t}' is not a known ADC.* token — valid ${kind}s: ${Object.keys(map).join(', ')}.`);
|
|
72
|
+
}
|
|
73
|
+
return m;
|
|
18
74
|
}
|
|
19
75
|
/**
|
|
20
76
|
* Emit the per-channel ADC setup state. One block per channel in the chip
|
|
21
|
-
* descriptor
|
|
22
|
-
*
|
|
23
|
-
*
|
|
77
|
+
* descriptor that the PROGRAM ACTUALLY READS (`usedPins`) — an unread
|
|
78
|
+
* channel's `static` setup function would trip -Wunused-function in the
|
|
79
|
+
* single generated TU. When `usedPins` is omitted (probe paths with no
|
|
80
|
+
* program), every descriptor channel is emitted. Each block is guarded by a
|
|
81
|
+
* `static bool __tc_adc<N>_ready` so the first read configures it and
|
|
82
|
+
* subsequent reads skip. Called from shimLines when the program uses ADC.
|
|
24
83
|
*/
|
|
25
|
-
export function adcInitLines(chip) {
|
|
26
|
-
const
|
|
84
|
+
export function adcInitLines(chip, usedPins, overrideDevices) {
|
|
85
|
+
const primary = chip.adc?.nodeLabel ?? 'adc';
|
|
27
86
|
const res = chip.adc?.resolution ?? 12;
|
|
28
87
|
const vref = chip.adc?.vrefMv ?? 3000;
|
|
88
|
+
// Gain/reference are SoC-specific: the nRF SAADC scheme (gain 1/4 against
|
|
89
|
+
// the 0.6V internal ref, vref-mv 3000 = VDD) is the default; the STM32
|
|
90
|
+
// driver requires exactly ADC_GAIN_1 + ADC_REF_INTERNAL (Zephyr maps
|
|
91
|
+
// "internal" to the VREF+ pad) with vref-mv = VDDA. The descriptor carries
|
|
92
|
+
// the SoC's pair so the emitted channel setup validates in the driver.
|
|
93
|
+
const gain = chip.adc?.gain ?? 'ADC_GAIN_1_4';
|
|
94
|
+
const reference = chip.adc?.reference ?? 'ADC_REF_INTERNAL';
|
|
95
|
+
const owns = (c, label) => (c.controller ?? primary) === label;
|
|
96
|
+
const channels = (chip.adc?.channels ?? []).filter((c) => !usedPins ||
|
|
97
|
+
// Pin-form usage (the ADC/readAnalog path): the exact channel.
|
|
98
|
+
[...usedPins].some((n) => n === c.pin) ||
|
|
99
|
+
// Channel-number fallback (the Arduino-compat ADC.read(n) form) — must
|
|
100
|
+
// match the CONTROLLER too, or colliding indices across controllers
|
|
101
|
+
// (both ESP32 SARADC units expose channel 0) over-include.
|
|
102
|
+
[...usedPins].some((n) => adcChannelForPin(chip, n) === c.channel && owns(c, adcControllerForPin(chip, n))));
|
|
29
103
|
const lines = ['// CUTTLEFISH_ADC_BEGIN'];
|
|
30
|
-
lines.push(`static const struct device* __tc_adc_dev = ${
|
|
31
|
-
|
|
104
|
+
lines.push(`static const struct device* __tc_adc_dev = DEVICE_DT_GET(DT_NODELABEL(${primary}));`);
|
|
105
|
+
// Additional controllers referenced by used channels get their own device
|
|
106
|
+
// handle (sorted for deterministic output). Channel indices collide across
|
|
107
|
+
// controllers, so non-primary setup symbols carry the controller label.
|
|
108
|
+
const extraControllers = [...new Set(channels.map((c) => c.controller).filter((x) => !!x && x !== primary))].sort();
|
|
109
|
+
// Inline-override devices (escape hatch): handles for the DT labels the
|
|
110
|
+
// construction opts name — they may not appear in the manifest at all.
|
|
111
|
+
for (const label of (overrideDevices ?? [])) {
|
|
112
|
+
if (label !== primary && !extraControllers.includes(label))
|
|
113
|
+
extraControllers.push(label);
|
|
114
|
+
}
|
|
115
|
+
for (const label of extraControllers) {
|
|
116
|
+
lines.push(`static const struct device* __tc_adc_${label}_dev = DEVICE_DT_GET(DT_NODELABEL(${label}));`);
|
|
117
|
+
}
|
|
118
|
+
for (const c of channels) {
|
|
32
119
|
const n = c.channel;
|
|
33
|
-
|
|
120
|
+
const dev = adcDevSymbol(c.controller ?? primary, primary);
|
|
121
|
+
const sym = c.controller && c.controller !== primary ? `__tc_adc_${c.controller}_${n}` : `__tc_adc${n}`;
|
|
122
|
+
lines.push(`static bool ${sym}_ready = false;`, `static void ${sym}_setup(void) {`, ` if (${sym}_ready) return;`, ` const struct adc_channel_cfg cfg = {`, ` .gain = ${gain},`, ` .reference = ${reference},`, ` .acquisition_time = ADC_ACQ_TIME_DEFAULT,`, ` .channel_id = ${n},`, ` .differential = 0,`, ` };`, ` adc_channel_setup(${dev}, &cfg);`, ` ${sym}_ready = true;`, `}`);
|
|
34
123
|
}
|
|
35
124
|
lines.push(`#define __TC_ADC_VREF_MV ${vref}`);
|
|
36
125
|
lines.push(`#define __TC_ADC_RESOLUTION ${res}`);
|
|
@@ -45,30 +134,47 @@ export function lowerAdc(op, chip) {
|
|
|
45
134
|
const o = op;
|
|
46
135
|
const res = chip.adc?.resolution ?? 12;
|
|
47
136
|
const vref = chip.adc?.vrefMv ?? 3000;
|
|
137
|
+
const gain = chip.adc?.gain ?? 'ADC_GAIN_1_4';
|
|
138
|
+
const reference = chip.adc?.reference ?? 'ADC_REF_INTERNAL';
|
|
48
139
|
switch (op.operation) {
|
|
49
|
-
case 'adc.
|
|
50
|
-
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
140
|
+
case 'adc.read_raw':
|
|
141
|
+
case 'adc.read_mv': {
|
|
142
|
+
// Construction-time overrides (hal/adc-pin.ts opts: channel/device/
|
|
143
|
+
// pinctrl): the user vouches for the routing on a pin the facts layer
|
|
144
|
+
// does not cover. The channel and device symbol come from the op; the
|
|
145
|
+
// marker comment carries device/pinctrl to the overlay regen (the
|
|
146
|
+
// transpiler cannot synthesize DT nodes) and suppresses the
|
|
147
|
+
// unavailable-pin diagnostic.
|
|
148
|
+
const hasOverride = (typeof o.channelOverride === 'number' && o.channelOverride >= 0)
|
|
149
|
+
|| (typeof o.deviceOverride === 'string' && o.deviceOverride !== '')
|
|
150
|
+
|| (typeof o.pinctrlOverride === 'string' && o.pinctrlOverride !== '');
|
|
151
|
+
const ch = hasOverride && typeof o.channelOverride === 'number' && o.channelOverride >= 0
|
|
152
|
+
? o.channelOverride
|
|
153
|
+
: adcChannelForPin(chip, o.pin);
|
|
154
|
+
const primary = chip.adc?.nodeLabel ?? 'adc';
|
|
155
|
+
const devCtrl = (typeof o.deviceOverride === 'string' && o.deviceOverride !== '')
|
|
156
|
+
? o.deviceOverride
|
|
157
|
+
: adcControllerForPin(chip, o.pin);
|
|
158
|
+
const dev = adcDevSymbol(devCtrl, primary);
|
|
159
|
+
const gainMacro = adcThinToken(o.gain, ADC_GAIN_TOKENS, 'gain');
|
|
160
|
+
const refMacro = adcThinToken(o.reference, ADC_REF_TOKENS, 'reference');
|
|
161
|
+
const g = gainMacro ?? gain;
|
|
162
|
+
const r = refMacro ?? reference;
|
|
163
|
+
const marker = hasOverride
|
|
164
|
+
? `/* cuttlefish-user-facts: adc pin=${o.pin}`
|
|
165
|
+
+ `${typeof o.deviceOverride === 'string' && o.deviceOverride !== '' ? ` device=${o.deviceOverride}` : ''}`
|
|
166
|
+
+ `${typeof o.pinctrlOverride === 'string' && o.pinctrlOverride !== '' ? ` pinctrl=${o.pinctrlOverride}` : ''}`
|
|
167
|
+
+ ` channel=${ch} */ `
|
|
168
|
+
: '';
|
|
169
|
+
const setup = `static bool __tc_adct${o.pin}_done = false; if (!__tc_adct${o.pin}_done) { const struct adc_channel_cfg __tc_adct${o.pin}_cfg = { .gain = ${g}, .reference = ${r}, .acquisition_time = ADC_ACQ_TIME_DEFAULT, .channel_id = ${ch}, .differential = 0 }; adc_channel_setup(${dev}, &__tc_adct${o.pin}_cfg); __tc_adct${o.pin}_done = true; }`;
|
|
170
|
+
const read = `int16_t __b = 0; struct adc_sequence __s = { .channels = BIT(${ch}), .buffer = &__b, .buffer_size = sizeof(__b), .resolution = ${res} }; adc_read(${dev}, &__s);`;
|
|
171
|
+
if (op.operation === 'adc.read_raw') {
|
|
172
|
+
return { expression: `({ ${marker}${setup} ${read} __b; })` };
|
|
173
|
+
}
|
|
60
174
|
return {
|
|
61
|
-
expression: `({
|
|
175
|
+
expression: `({ ${marker}${setup} ${read} int32_t __v = __b; adc_raw_to_millivolts(${vref}, ${g}, ${res}, &__v); __v; })`,
|
|
62
176
|
};
|
|
63
177
|
}
|
|
64
|
-
case 'adc.get_resolution':
|
|
65
|
-
return { expression: String(res) };
|
|
66
|
-
case 'adc.set_reference':
|
|
67
|
-
// Zephyr configures the reference at channel-setup time; runtime switching
|
|
68
|
-
// would require re-setup. Record the intent as a no-op statement.
|
|
69
|
-
return { code: `/* adc.set_reference(${o.reference}): configured at channel setup (ADC_REF_INTERNAL) */` };
|
|
70
|
-
case 'adc.get_reference':
|
|
71
|
-
return { expression: `0 /* DEFAULT (ADC_REF_INTERNAL) */` };
|
|
72
178
|
default:
|
|
73
179
|
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
74
180
|
`Open an issue or use rawCpp() to emit it manually.`);
|
package/dist/lowering/ble.js
CHANGED
|
@@ -88,6 +88,9 @@ export function bleInitLines() {
|
|
|
88
88
|
`// bt_gatt_chrc metadata for each characteristic declaration attr.`,
|
|
89
89
|
`static struct bt_gatt_chrc __tc_ble_chrc_meta[__TC_BLE_MAX_CHARS];`,
|
|
90
90
|
`// The flat attribute array + its wrapping service. Plus per-char CCC cfg.`,
|
|
91
|
+
`static struct bt_gatt_ccc_managed_user_data __tc_ble_ccc[__TC_BLE_MAX_CHARS];`,
|
|
92
|
+
`// write_ccc dereferences cfg_changed — every slot needs the no-op.`,
|
|
93
|
+
`static void __tc_ble_ccc_changed(const struct bt_gatt_attr* a, uint16_t v) { (void)a; (void)v; }`,
|
|
91
94
|
`static struct bt_gatt_attr __tc_ble_attrs[__TC_BLE_MAX_ATTRS];`,
|
|
92
95
|
`static size_t __tc_ble_attr_count = 0;`,
|
|
93
96
|
`static struct bt_gatt_service __tc_ble_svc;`,
|
|
@@ -142,8 +145,10 @@ export function bleInitLines() {
|
|
|
142
145
|
` if (idx < 0 || idx >= __TC_BLE_MAX_CHARS || !__tc_ble.on_read[idx]) return 0;`,
|
|
143
146
|
` const char* t = __tc_ble_char_defs[idx].type;`,
|
|
144
147
|
` if (t && strcmp(t, "utf8") == 0) {`,
|
|
145
|
-
`
|
|
146
|
-
`
|
|
148
|
+
` // Hoisted string callbacks return std::string BY VALUE — call through`,
|
|
149
|
+
` // the true signature and borrow c_str() for the copy below.`,
|
|
150
|
+
` std::string s_v = reinterpret_cast<std::string(*)(void)>(__tc_ble.on_read[idx])();`,
|
|
151
|
+
` return bt_gatt_attr_read(conn, attr, buf, len, offset, s_v.c_str(), s_v.size());`,
|
|
147
152
|
` } else if (t && strcmp(t, "float32") == 0) {`,
|
|
148
153
|
` double d = reinterpret_cast<double(*)(void)>(__tc_ble.on_read[idx])();`,
|
|
149
154
|
` float f = static_cast<float>(d);`,
|
|
@@ -263,6 +268,7 @@ export function bleInitLines() {
|
|
|
263
268
|
`// (declaration + value); the value attr carries the char index in user_data.`,
|
|
264
269
|
`static void __tc_ble_build_svc_table(void) {`,
|
|
265
270
|
` size_t a = 0;`,
|
|
271
|
+
` for (int c = 0; c < __TC_BLE_MAX_CHARS; c++) { __tc_ble_ccc[c].cfg_changed = __tc_ble_ccc_changed; }`,
|
|
266
272
|
` for (int s = 0; s < static_cast<int>(__tc_ble.svc_count); s++) {`,
|
|
267
273
|
` // Primary service declaration.`,
|
|
268
274
|
` __tc_ble_attrs[a].uuid = BT_UUID_GATT_PRIMARY;`,
|
|
@@ -298,6 +304,17 @@ export function bleInitLines() {
|
|
|
298
304
|
` __tc_ble_attrs[a].perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE;`,
|
|
299
305
|
` __tc_ble.val_attr_idx[c] = static_cast<int>(a);`,
|
|
300
306
|
` a++;`,
|
|
307
|
+
` if (__tc_ble_char_defs[c].perms & 4) {`,
|
|
308
|
+
` // CCC descriptor — without it a central cannot subscribe`,
|
|
309
|
+
` // and notifications never reach anyone.`,
|
|
310
|
+
` __tc_ble_attrs[a].uuid = BT_UUID_GATT_CCC;`,
|
|
311
|
+
` __tc_ble_attrs[a].read = bt_gatt_attr_read_ccc;`,
|
|
312
|
+
` __tc_ble_attrs[a].write = bt_gatt_attr_write_ccc;`,
|
|
313
|
+
` __tc_ble_attrs[a].user_data = &__tc_ble_ccc[c];`,
|
|
314
|
+
` __tc_ble_attrs[a].handle = 0;`,
|
|
315
|
+
` __tc_ble_attrs[a].perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE;`,
|
|
316
|
+
` a++;`,
|
|
317
|
+
` }`,
|
|
301
318
|
` }`,
|
|
302
319
|
` }`,
|
|
303
320
|
` __tc_ble_attr_count = a;`,
|
|
@@ -320,24 +337,28 @@ export function bleInitLines() {
|
|
|
320
337
|
` bt_gatt_service_register(&__tc_ble_svc);`,
|
|
321
338
|
` __tc_ble.status = 1; // Initializing`,
|
|
322
339
|
` bt_enable(NULL); // synchronous`,
|
|
340
|
+
` bt_enable(NULL); // synchronous`,
|
|
323
341
|
` __tc_ble.inited = true;`,
|
|
324
342
|
` __tc_ble_advertise_start();`,
|
|
325
343
|
`}`,
|
|
326
344
|
``,
|
|
327
345
|
`// ── Notify ──`,
|
|
346
|
+
`// Explicit-conn: the NULL (broadcast) form hit an ESP32 controller assert`,
|
|
347
|
+
`// (lld_con.c) on the first push after subscription on the S3.`,
|
|
328
348
|
`static void __tc_ble_notify(int idx, int16_t value) {`,
|
|
329
349
|
` if (idx < 0 || idx >= __TC_BLE_MAX_CHARS) return;`,
|
|
350
|
+
` if (__tc_ble.conn == NULL) return; // no link: a NULL-conn notify asserted the controller`,
|
|
330
351
|
` int ai = __tc_ble.val_attr_idx[idx];`,
|
|
331
352
|
` if (ai == 0) return;`,
|
|
332
353
|
` const char* t = __tc_ble_char_defs[idx].type;`,
|
|
333
354
|
` if (t && strcmp(t, "uint32") == 0) {`,
|
|
334
|
-
` uint32_t u = static_cast<uint32_t>(value); bt_gatt_notify(
|
|
355
|
+
` uint32_t u = static_cast<uint32_t>(value); bt_gatt_notify(__tc_ble.conn, &__tc_ble_attrs[ai], &u, sizeof(u));`,
|
|
335
356
|
` } else if (t && strcmp(t, "float32") == 0) {`,
|
|
336
|
-
` float f = static_cast<float>(value); bt_gatt_notify(
|
|
357
|
+
` float f = static_cast<float>(value); bt_gatt_notify(__tc_ble.conn, &__tc_ble_attrs[ai], &f, sizeof(f));`,
|
|
337
358
|
` } else if (t && strcmp(t, "boolean") == 0) {`,
|
|
338
|
-
` uint8_t b = value ? 1 : 0; bt_gatt_notify(
|
|
359
|
+
` uint8_t b = value ? 1 : 0; bt_gatt_notify(__tc_ble.conn, &__tc_ble_attrs[ai], &b, sizeof(b));`,
|
|
339
360
|
` } else {`,
|
|
340
|
-
` int16_t s16 = value; bt_gatt_notify(
|
|
361
|
+
` int16_t s16 = value; bt_gatt_notify(__tc_ble.conn, &__tc_ble_attrs[ai], &s16, sizeof(s16));`,
|
|
341
362
|
` }`,
|
|
342
363
|
`}`,
|
|
343
364
|
``,
|
|
@@ -345,18 +366,7 @@ export function bleInitLines() {
|
|
|
345
366
|
` return __tc_ble.status == 3 && __tc_ble.connected_clients > 0;`,
|
|
346
367
|
`}`,
|
|
347
368
|
`static inline int __tc_ble_client_count(void) { return __tc_ble.connected_clients; }`,
|
|
348
|
-
`static inline void __tc_ble_set_name(const char* name) { strncpy(__tc_ble.name, name, sizeof(__tc_ble.name) - 1); __tc_ble.name[sizeof(__tc_ble.name) - 1] = 0; }`,
|
|
349
|
-
`static inline void __tc_ble_set_tx_power(int dbm) { (void)dbm; /* nRF TX power via bt_le_set_tx_power — deferred */ }`,
|
|
350
369
|
``,
|
|
351
|
-
`static inline bool __tc_ble_until_connected(uint32_t timeout_ms) {`,
|
|
352
|
-
` int64_t deadline = static_cast<int64_t>(k_uptime_get()) + static_cast<int64_t>(timeout_ms);`,
|
|
353
|
-
` while (!__tc_ble_is_connected()) {`,
|
|
354
|
-
` if (timeout_ms > 0 && k_uptime_get() >= deadline) return false;`,
|
|
355
|
-
` k_msleep(50);`,
|
|
356
|
-
` }`,
|
|
357
|
-
` return true;`,
|
|
358
|
-
`}`,
|
|
359
|
-
`static inline void __tc_ble_until_connected_start(void) { /* __tc_ble_server_begin already started advertising */ }`,
|
|
360
370
|
`// CUTTLEFISH_BLE_END`,
|
|
361
371
|
``,
|
|
362
372
|
];
|
|
@@ -399,21 +409,14 @@ export function lowerBle(op) {
|
|
|
399
409
|
case 'ble.on_disconnect':
|
|
400
410
|
return { code: `__tc_ble.on_disconnect = (${s(o.handler)});` };
|
|
401
411
|
case 'ble.notify':
|
|
402
|
-
|
|
412
|
+
// Index-based: val_attr_idx[idx] resolves the registered attr (the
|
|
413
|
+
// declaration-order slot). current_char would be whatever add_char ran
|
|
414
|
+
// last — wrong for a standalone notify after later declarations.
|
|
415
|
+
return { expression: `__tc_ble_notify(${s(o.index ?? 0)}, ${s(o.value)})` };
|
|
403
416
|
case 'ble.is_connected':
|
|
404
417
|
return { expression: `__tc_ble_is_connected()` };
|
|
405
418
|
case 'ble.client_count':
|
|
406
419
|
return { expression: `__tc_ble_client_count()` };
|
|
407
|
-
case 'ble.status':
|
|
408
|
-
return { expression: `__tc_ble.status` };
|
|
409
|
-
case 'ble.set_name':
|
|
410
|
-
return { code: `__tc_ble_set_name(${s(o.name)});` };
|
|
411
|
-
case 'ble.set_tx_power':
|
|
412
|
-
return { code: `__tc_ble_set_tx_power(${s(o.dbm)});` };
|
|
413
|
-
case 'ble.until_connected':
|
|
414
|
-
return { expression: `__tc_ble_until_connected(${s(o.timeoutMs)})` };
|
|
415
|
-
case 'ble.until_connected_start':
|
|
416
|
-
return { code: `__tc_ble_until_connected_start();` };
|
|
417
420
|
default:
|
|
418
421
|
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
419
422
|
`Open an issue or use rawCpp() to emit it manually.`);
|
package/dist/lowering/dac.js
CHANGED
|
@@ -46,17 +46,17 @@ export function lowerDac(op, chip) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
switch (op.operation) {
|
|
49
|
-
case 'dac.
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
49
|
+
case 'dac.write_value': {
|
|
50
|
+
// Thin DAC: raw code with the construction resolution (0 = the
|
|
51
|
+
// descriptor channel's), lazy setup per pin.
|
|
52
|
+
const res = Number(o.resolution) > 0 ? Number(o.resolution) : ch.resolution;
|
|
53
53
|
return {
|
|
54
54
|
code: [
|
|
55
|
-
`{ static bool
|
|
56
|
-
` if (!
|
|
57
|
-
` static const struct dac_channel_cfg
|
|
58
|
-
` (void)dac_channel_setup(__tc_dac_dev, &
|
|
59
|
-
`
|
|
55
|
+
`{ static bool __tc_dact${o.pin}_done = false;`,
|
|
56
|
+
` if (!__tc_dact${o.pin}_done) {`,
|
|
57
|
+
` static const struct dac_channel_cfg __tc_dact${o.pin}_cfg = { .channel_id = ${ch.channel}, .resolution = ${res} };`,
|
|
58
|
+
` (void)dac_channel_setup(__tc_dac_dev, &__tc_dact${o.pin}_cfg);`,
|
|
59
|
+
` __tc_dact${o.pin}_done = true;`,
|
|
60
60
|
` }`,
|
|
61
61
|
` (void)dac_write_value(__tc_dac_dev, ${ch.channel}, ${o.value}); }`,
|
|
62
62
|
].join(' '),
|
package/dist/lowering/fs.js
CHANGED
|
@@ -31,21 +31,29 @@ export function fsInitLines() {
|
|
|
31
31
|
'FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(__tc_fs_data);',
|
|
32
32
|
'static struct fs_mount_t __tc_fs_mp_init = {',
|
|
33
33
|
' .type = FS_LITTLEFS,',
|
|
34
|
+
' .mnt_point = "/lfs",',
|
|
34
35
|
' .fs_data = &__tc_fs_data,',
|
|
35
36
|
' .storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),',
|
|
36
|
-
' .mnt_point = "/lfs",',
|
|
37
37
|
'};',
|
|
38
38
|
'',
|
|
39
39
|
'static void __tc_fs_ensure_mount(void) {',
|
|
40
40
|
' if (__tc_fs_mounted) return;',
|
|
41
41
|
' __tc_fs_mp = __tc_fs_mp_init;',
|
|
42
42
|
' int rc = fs_mount(&__tc_fs_mp);',
|
|
43
|
-
' if (rc
|
|
44
|
-
' //
|
|
43
|
+
' if (rc != 0) {',
|
|
44
|
+
' // Any mount failure with a valid config means the partition is',
|
|
45
|
+
' // unformatted or corrupt (littlefs reports -EIO on erased flash,',
|
|
46
|
+
' // not only -ENODATA/-EINVAL) — format it and remount once.',
|
|
45
47
|
' (void)fs_mkfs(FS_LITTLEFS, static_cast<uintptr_t>(FIXED_PARTITION_ID(storage_partition)), NULL, 0);',
|
|
46
|
-
'
|
|
48
|
+
' rc = fs_mount(&__tc_fs_mp);',
|
|
49
|
+
' }',
|
|
50
|
+
' if (rc == 0) {',
|
|
51
|
+
' __tc_fs_mounted = true;',
|
|
52
|
+
' } else {',
|
|
53
|
+
' char __tc_fs_err[48];',
|
|
54
|
+
' (void)snprintf(__tc_fs_err, sizeof(__tc_fs_err), "cuttlefish-fs: mount failed rc=%d", rc);',
|
|
55
|
+
' __tc_println(__tc_fs_err);',
|
|
47
56
|
' }',
|
|
48
|
-
' __tc_fs_mounted = true;',
|
|
49
57
|
'}',
|
|
50
58
|
'',
|
|
51
59
|
'// Join the mount point with the user path (paths are within the FS).',
|
|
@@ -66,8 +74,11 @@ export function fsInitLines() {
|
|
|
66
74
|
' struct fs_file_t f;',
|
|
67
75
|
' fs_file_t_init(&f);',
|
|
68
76
|
' if (fs_open(&f, __tc_fs_path(path), FS_O_READ) == 0) {',
|
|
69
|
-
'
|
|
70
|
-
'
|
|
77
|
+
' // Terminate at the READ length: the buffer is static and shared',
|
|
78
|
+
' // across reads, so bytes from a longer previous file survive a',
|
|
79
|
+
' // short read otherwise.',
|
|
80
|
+
' ssize_t n = fs_read(&f, buf, sizeof(buf) - 1);',
|
|
81
|
+
' buf[(n > 0) ? n : 0] = 0;',
|
|
71
82
|
' (void)fs_close(&f);',
|
|
72
83
|
' }',
|
|
73
84
|
' return buf;',
|
|
@@ -103,9 +114,6 @@ export function fsInitLines() {
|
|
|
103
114
|
export function lowerFs(op) {
|
|
104
115
|
const o = op;
|
|
105
116
|
switch (op.operation) {
|
|
106
|
-
case 'fs.begin':
|
|
107
|
-
// No payload (the op carries only its discriminator).
|
|
108
|
-
return { code: 'return __tc_fs_begin();' };
|
|
109
117
|
case 'fs.read_text':
|
|
110
118
|
return { code: `return __tc_fs_read_text(${o.path});` };
|
|
111
119
|
case 'fs.write_text':
|
package/dist/lowering/gpio.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
|
2
2
|
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
3
3
|
/** The C identifier emitted for a pin's gpio_dt_spec variable. */
|
|
4
4
|
export declare function dtSpecVarName(dtSpec: string): string;
|
|
5
|
+
/** Map thin-GPIO flag token text to the GPIO_* macro expression. */
|
|
6
|
+
export declare function gpioFlagsToMacros(flags: string): string;
|
|
5
7
|
/**
|
|
6
8
|
* Resolve a HAL gpio.* op to Zephyr C++.
|
|
7
9
|
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
package/dist/lowering/gpio.js
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
// The manifest validator's probe (which sends {operation, pin:0} with no
|
|
15
15
|
// port) hits this path, so it must return a lowered result, not undefined.
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
|
-
import { controllerNodelabelForPin } from '../chips/controllers.js';
|
|
17
|
+
import { controllerNodelabelForPin, controllerRawPinForPin } from '../chips/controllers.js';
|
|
18
|
+
import { ZEPHYR_GPIO_FLAGS } from '@typecad/hal';
|
|
18
19
|
/** The C identifier emitted for a pin's gpio_dt_spec variable. */
|
|
19
20
|
export function dtSpecVarName(dtSpec) {
|
|
20
21
|
return `__tc_dt_${dtSpec}`;
|
|
@@ -55,6 +56,50 @@ function flagsForMode(mode) {
|
|
|
55
56
|
const base = MODE_MAP[mode] ?? 'GPIO_INPUT';
|
|
56
57
|
return base + dtFlagsForMode(mode);
|
|
57
58
|
}
|
|
59
|
+
// ── Thin GPIO (hal/gpio-pin.ts) — flag tokens ─────────────────────────────
|
|
60
|
+
//
|
|
61
|
+
// "GPIO.OUTPUT | GPIO.PULL_UP" token text maps name-for-name onto the GPIO_*
|
|
62
|
+
// macros. The name set comes from the GENERATED Zephyr token table (parsed
|
|
63
|
+
// from the pinned tree's headers); unknown tokens are build errors listing
|
|
64
|
+
// the valid spellings — the sensor-catalog discipline.
|
|
65
|
+
const GPIO_FLAG_TOKENS = Object.fromEntries(ZEPHYR_GPIO_FLAGS.map((f) => [`GPIO.${f}`, `GPIO_${f}`]));
|
|
66
|
+
/** Map thin-GPIO flag token text to the GPIO_* macro expression. */
|
|
67
|
+
export function gpioFlagsToMacros(flags) {
|
|
68
|
+
const tokens = String(flags ?? '')
|
|
69
|
+
.split('|')
|
|
70
|
+
.map((t) => t.trim())
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
if (tokens.length === 0)
|
|
73
|
+
return 'GPIO_INPUT';
|
|
74
|
+
const macros = tokens.map((t) => {
|
|
75
|
+
const m = GPIO_FLAG_TOKENS[t];
|
|
76
|
+
if (!m) {
|
|
77
|
+
throw new Error(`GPIO flag '${t}' is not a known GPIO.* token — valid: ${Object.keys(GPIO_FLAG_TOKENS).join(', ')}.`);
|
|
78
|
+
}
|
|
79
|
+
return m;
|
|
80
|
+
});
|
|
81
|
+
return macros.join(' | ');
|
|
82
|
+
}
|
|
83
|
+
/** Guarded per-pin configure statement for the dt-spec path: construction
|
|
84
|
+
* flags apply exactly once, ahead of the first use. */
|
|
85
|
+
function dtSpecConfigure(varName, dtSpec, flags) {
|
|
86
|
+
return `{ ${dtSpecGuardConfigure(varName, dtSpec, flags)} }`;
|
|
87
|
+
}
|
|
88
|
+
/** The guard body only (no wrapping braces) — for fusing into
|
|
89
|
+
* statement-expressions. */
|
|
90
|
+
function dtSpecGuardConfigure(varName, dtSpec, flags) {
|
|
91
|
+
const done = `__tc_gpio_cfg_${dtSpec.replace(/-/g, '_')}_done`;
|
|
92
|
+
return `static bool ${done} = false; if (!${done}) { gpio_pin_configure_dt(&${varName}, ${gpioFlagsToMacros(flags)}); ${done} = true; }`;
|
|
93
|
+
}
|
|
94
|
+
/** Guarded per-pin configure statement for the raw-controller path. */
|
|
95
|
+
function rawConfigure(controller, rawPin, pin, flags) {
|
|
96
|
+
return `{ ${rawGuardConfigure(controller, rawPin, pin, flags)} }`;
|
|
97
|
+
}
|
|
98
|
+
/** The raw-path guard body only — for fusing into statement-expressions. */
|
|
99
|
+
function rawGuardConfigure(controller, rawPin, pin, flags) {
|
|
100
|
+
const done = `__tc_gpio_cfg_raw${pin}_done`;
|
|
101
|
+
return `static bool ${done} = false; if (!${done}) { gpio_pin_configure(${controller}, ${rawPin}, ${gpioFlagsToMacros(flags)}); ${done} = true; }`;
|
|
102
|
+
}
|
|
58
103
|
/**
|
|
59
104
|
* Resolve a HAL gpio.* op to Zephyr C++.
|
|
60
105
|
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
|
@@ -73,9 +118,17 @@ function lowerGpioDtSpec(op, dtSpec) {
|
|
|
73
118
|
const o = op;
|
|
74
119
|
const varName = dtSpecVarName(dtSpec);
|
|
75
120
|
switch (op.operation) {
|
|
76
|
-
case 'gpio.
|
|
77
|
-
|
|
78
|
-
|
|
121
|
+
case 'gpio.configure':
|
|
122
|
+
// Thin GPIO: construction flags, applied once per pin (guard).
|
|
123
|
+
return { code: dtSpecConfigure(varName, dtSpec, o.flags) };
|
|
124
|
+
case 'gpio.read_cfg':
|
|
125
|
+
// Thin GPIO get(): the guarded configure FUSED into the read — one
|
|
126
|
+
// statement-expression, correct in any expression position (a method's
|
|
127
|
+
// leading side-effect ops are dropped when the call sits in a pure
|
|
128
|
+
// expression, e.g. an if-condition).
|
|
129
|
+
return {
|
|
130
|
+
expression: `({ ${dtSpecGuardConfigure(varName, dtSpec, o.flags)} gpio_pin_get_dt(&${varName}); })`,
|
|
131
|
+
};
|
|
79
132
|
case 'gpio.write': {
|
|
80
133
|
// Literal 0/1 stays as-is; runtime expression coerced to int via ternary.
|
|
81
134
|
const v = o.value;
|
|
@@ -99,19 +152,46 @@ function lowerGpioRaw(op, chip) {
|
|
|
99
152
|
const o = op;
|
|
100
153
|
const pin = o.pin;
|
|
101
154
|
// Resolve the owning controller by pin range (ESP32-S3 splits GPIO across
|
|
102
|
-
// gpio0/gpio1). For single-controller SoCs
|
|
155
|
+
// gpio0/gpio1; STM32 across gpioa/gpiob/gpioc). For single-controller SoCs
|
|
156
|
+
// this is just chip.gpioController. The raw API takes the PORT-RELATIVE
|
|
157
|
+
// index (STM32 gpiob is 0-15), not the global HAL pin number.
|
|
103
158
|
const controller = `DEVICE_DT_GET(DT_NODELABEL(${controllerNodelabelForPin(chip, pin)}))`;
|
|
159
|
+
const rawPin = controllerRawPinForPin(chip, pin);
|
|
104
160
|
switch (op.operation) {
|
|
105
|
-
case 'gpio.
|
|
106
|
-
|
|
161
|
+
case 'gpio.configure':
|
|
162
|
+
// Thin GPIO: construction flags, applied once per pin (guard).
|
|
163
|
+
return { code: rawConfigure(controller, rawPin, pin, o.flags) };
|
|
164
|
+
case 'gpio.read_cfg':
|
|
165
|
+
// Fused guarded configure + raw read (see the dt-spec path above).
|
|
166
|
+
return {
|
|
167
|
+
expression: `({ ${rawGuardConfigure(controller, rawPin, pin, o.flags)} gpio_pin_get_raw(${controller}, ${rawPin}); })`,
|
|
168
|
+
};
|
|
169
|
+
case 'gpio.shift_out':
|
|
170
|
+
case 'gpio.shift_in': {
|
|
171
|
+
// Bit-bang over the raw-controller path (shift pairs are arbitrary
|
|
172
|
+
// pins): configure both once (guarded), then clock the bits. Zephyr
|
|
173
|
+
// verbs only — gpio_pin_set_raw / gpio_pin_get_raw / k_busy_wait.
|
|
174
|
+
const dataCtrl = `DEVICE_DT_GET(DT_NODELABEL(${controllerNodelabelForPin(chip, o.dataPin)}))`;
|
|
175
|
+
const dataRaw = controllerRawPinForPin(chip, o.dataPin);
|
|
176
|
+
const clkCtrl = `DEVICE_DT_GET(DT_NODELABEL(${controllerNodelabelForPin(chip, o.clockPin)}))`;
|
|
177
|
+
const clkRaw = controllerRawPinForPin(chip, o.clockPin);
|
|
178
|
+
const cfg = `{ static bool __tc_shf${o.dataPin}_${o.clockPin}_done = false; if (!__tc_shf${o.dataPin}_${o.clockPin}_done) { gpio_pin_configure(${dataCtrl}, ${dataRaw}, GPIO_INPUT); gpio_pin_configure(${clkCtrl}, ${clkRaw}, GPIO_OUTPUT); __tc_shf${o.dataPin}_${o.clockPin}_done = true; } }`;
|
|
179
|
+
if (op.operation === 'gpio.shift_out') {
|
|
180
|
+
return {
|
|
181
|
+
code: `${cfg} for (int __i = ${(o.msbFirst ?? true) ? '7' : '0'}; ${(o.msbFirst ?? true) ? '__i >= 0' : '__i < 8'}; ${(o.msbFirst ?? true) ? '__i--' : '__i++'}) { gpio_pin_set_raw(${dataCtrl}, ${dataRaw}, ((static_cast<uint8_t>(${o.value})) >> __i) & 1); gpio_pin_set_raw(${clkCtrl}, ${clkRaw}, 1); k_busy_wait(1); gpio_pin_set_raw(${clkCtrl}, ${clkRaw}, 0); }`,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
expression: `({ ${cfg} uint8_t __b = 0; for (int __i = ${(o.msbFirst ?? true) ? '7' : '0'}; ${(o.msbFirst ?? true) ? '__i >= 0' : '__i < 8'}; ${(o.msbFirst ?? true) ? '__i--' : '__i++'}) { gpio_pin_set_raw(${clkCtrl}, ${clkRaw}, 1); k_busy_wait(1); __b = static_cast<uint8_t>((__b << 1) | (gpio_pin_get_raw(${dataCtrl}, ${dataRaw}) & 1)); gpio_pin_set_raw(${clkCtrl}, ${clkRaw}, 0); } __b; })`,
|
|
186
|
+
};
|
|
107
187
|
}
|
|
108
188
|
case 'gpio.write': {
|
|
109
189
|
const v = o.value;
|
|
110
190
|
const rhs = typeof v === 'string' ? `((${v}) ? 1 : 0)` : v ? 1 : 0;
|
|
111
|
-
return { code: `gpio_pin_set_raw(${controller}, ${
|
|
191
|
+
return { code: `gpio_pin_set_raw(${controller}, ${rawPin}, ${rhs});` };
|
|
112
192
|
}
|
|
113
193
|
case 'gpio.read':
|
|
114
|
-
return { expression: `gpio_pin_get_raw(${controller}, ${
|
|
194
|
+
return { expression: `gpio_pin_get_raw(${controller}, ${rawPin})` };
|
|
115
195
|
case 'gpio.toggle':
|
|
116
196
|
// Native atomic toggle — never read-modify-write. gpio_pin_get_raw on
|
|
117
197
|
// a direction-only output reads the input latch, which is undefined on
|
|
@@ -119,7 +199,7 @@ function lowerGpioRaw(op, chip) {
|
|
|
119
199
|
// gpio_pin_toggle is the driver-level atomic toggle, and for pins
|
|
120
200
|
// configured without GPIO_ACTIVE_LOW the logical level equals the
|
|
121
201
|
// physical one, so it matches the get_raw/set_raw used elsewhere.
|
|
122
|
-
return { code: `gpio_pin_toggle(${controller}, ${
|
|
202
|
+
return { code: `gpio_pin_toggle(${controller}, ${rawPin});` };
|
|
123
203
|
default:
|
|
124
204
|
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
125
205
|
`Open an issue or use rawCpp() to emit it manually.`);
|