@typecad/framework-zephyr 1.0.0-alpha.10
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 +21 -0
- package/README.md +100 -0
- package/dist/async/timer-polyfill.d.ts +10 -0
- package/dist/async/timer-polyfill.js +95 -0
- package/dist/chips/controllers.d.ts +21 -0
- package/dist/chips/controllers.js +66 -0
- package/dist/chips/esp32.d.ts +2 -0
- package/dist/chips/esp32.js +59 -0
- package/dist/chips/esp32s3.d.ts +2 -0
- package/dist/chips/esp32s3.js +54 -0
- package/dist/chips/index.d.ts +15 -0
- package/dist/chips/index.js +45 -0
- package/dist/chips/resolve.d.ts +9 -0
- package/dist/chips/resolve.js +121 -0
- package/dist/chips/types.d.ts +154 -0
- package/dist/chips/types.js +14 -0
- package/dist/chips/xiao-ble.d.ts +2 -0
- package/dist/chips/xiao-ble.js +61 -0
- package/dist/debug-codegen.d.ts +33 -0
- package/dist/debug-codegen.js +162 -0
- package/dist/display/gfx.d.ts +17 -0
- package/dist/display/gfx.js +171 -0
- package/dist/display/index.d.ts +20 -0
- package/dist/display/index.js +53 -0
- package/dist/display/profiles.d.ts +25 -0
- package/dist/display/profiles.js +41 -0
- package/dist/display/touch-adapter.d.ts +9 -0
- package/dist/display/touch-adapter.js +95 -0
- package/dist/display/ui-adapter.d.ts +21 -0
- package/dist/display/ui-adapter.js +534 -0
- package/dist/doctor.d.ts +6 -0
- package/dist/doctor.js +48 -0
- package/dist/dt-config/kconfig.d.ts +27 -0
- package/dist/dt-config/kconfig.js +283 -0
- package/dist/dt-config/overlay.d.ts +35 -0
- package/dist/dt-config/overlay.js +246 -0
- package/dist/framework.manifest.d.ts +101 -0
- package/dist/framework.manifest.js +536 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +17 -0
- package/dist/lowering/adc.d.ts +19 -0
- package/dist/lowering/adc.js +76 -0
- package/dist/lowering/ble.d.ts +20 -0
- package/dist/lowering/ble.js +419 -0
- package/dist/lowering/board.d.ts +9 -0
- package/dist/lowering/board.js +33 -0
- package/dist/lowering/gpio.d.ts +12 -0
- package/dist/lowering/gpio.js +123 -0
- package/dist/lowering/http.d.ts +23 -0
- package/dist/lowering/http.js +482 -0
- package/dist/lowering/i2c.d.ts +15 -0
- package/dist/lowering/i2c.js +114 -0
- package/dist/lowering/index.d.ts +29 -0
- package/dist/lowering/index.js +93 -0
- package/dist/lowering/interrupts.d.ts +21 -0
- package/dist/lowering/interrupts.js +91 -0
- package/dist/lowering/mqtt.d.ts +19 -0
- package/dist/lowering/mqtt.js +366 -0
- package/dist/lowering/power.d.ts +9 -0
- package/dist/lowering/power.js +64 -0
- package/dist/lowering/preferences.d.ts +23 -0
- package/dist/lowering/preferences.js +345 -0
- package/dist/lowering/pulse.d.ts +7 -0
- package/dist/lowering/pulse.js +82 -0
- package/dist/lowering/pwm.d.ts +15 -0
- package/dist/lowering/pwm.js +59 -0
- package/dist/lowering/random.d.ts +18 -0
- package/dist/lowering/random.js +129 -0
- package/dist/lowering/spi.d.ts +17 -0
- package/dist/lowering/spi.js +113 -0
- package/dist/lowering/timing.d.ts +9 -0
- package/dist/lowering/timing.js +63 -0
- package/dist/lowering/tone.d.ts +10 -0
- package/dist/lowering/tone.js +50 -0
- package/dist/lowering/uart.d.ts +15 -0
- package/dist/lowering/uart.js +102 -0
- package/dist/lowering/util.d.ts +4 -0
- package/dist/lowering/util.js +12 -0
- package/dist/lowering/wdt.d.ts +15 -0
- package/dist/lowering/wdt.js +80 -0
- package/dist/lowering/wifi.d.ts +21 -0
- package/dist/lowering/wifi.js +447 -0
- package/dist/lowering/worker-backing.d.ts +14 -0
- package/dist/lowering/worker-backing.js +79 -0
- package/dist/lowering/worker.d.ts +6 -0
- package/dist/lowering/worker.js +14 -0
- package/dist/strategy.d.ts +168 -0
- package/dist/strategy.js +1094 -0
- package/dist/toolchain/compat.d.ts +38 -0
- package/dist/toolchain/compat.js +133 -0
- package/dist/toolchain/debug-config.d.ts +82 -0
- package/dist/toolchain/debug-config.js +359 -0
- package/dist/toolchain/index.d.ts +73 -0
- package/dist/toolchain/index.js +444 -0
- package/dist/toolchain/scaffold.d.ts +15 -0
- package/dist/toolchain/scaffold.js +196 -0
- package/dist/toolchain/west-discover.d.ts +40 -0
- package/dist/toolchain/west-discover.js +203 -0
- package/dist/toolchain/west-spawn.d.ts +39 -0
- package/dist/toolchain/west-spawn.js +117 -0
- package/package.json +82 -0
- package/src/async/timer-polyfill.ts +107 -0
- package/src/chips/controllers.ts +74 -0
- package/src/chips/esp32.ts +62 -0
- package/src/chips/esp32s3.ts +57 -0
- package/src/chips/index.ts +54 -0
- package/src/chips/resolve.ts +146 -0
- package/src/chips/types.ts +163 -0
- package/src/chips/xiao-ble.ts +64 -0
- package/src/debug-codegen.ts +207 -0
- package/src/display/gfx.ts +190 -0
- package/src/display/index.ts +70 -0
- package/src/display/profiles.ts +63 -0
- package/src/display/touch-adapter.ts +100 -0
- package/src/display/ui-adapter.ts +558 -0
- package/src/doctor.ts +56 -0
- package/src/dt-config/kconfig.ts +308 -0
- package/src/dt-config/overlay.ts +293 -0
- package/src/framework.manifest.ts +558 -0
- package/src/index.ts +29 -0
- package/src/lowering/adc.ts +102 -0
- package/src/lowering/ble.ts +426 -0
- package/src/lowering/board.ts +38 -0
- package/src/lowering/gpio.ts +148 -0
- package/src/lowering/http.ts +488 -0
- package/src/lowering/i2c.ts +126 -0
- package/src/lowering/index.ts +86 -0
- package/src/lowering/interrupts.ts +114 -0
- package/src/lowering/mqtt.ts +371 -0
- package/src/lowering/power.ts +70 -0
- package/src/lowering/preferences.ts +354 -0
- package/src/lowering/pulse.ts +93 -0
- package/src/lowering/pwm.ts +74 -0
- package/src/lowering/random.ts +135 -0
- package/src/lowering/spi.ts +124 -0
- package/src/lowering/timing.ts +72 -0
- package/src/lowering/tone.ts +60 -0
- package/src/lowering/uart.ts +111 -0
- package/src/lowering/util.ts +12 -0
- package/src/lowering/wdt.ts +84 -0
- package/src/lowering/wifi.ts +452 -0
- package/src/lowering/worker-backing.ts +94 -0
- package/src/lowering/worker.ts +17 -0
- package/src/strategy.ts +1265 -0
- package/src/toolchain/compat.ts +145 -0
- package/src/toolchain/debug-config.ts +399 -0
- package/src/toolchain/index.ts +482 -0
- package/src/toolchain/scaffold.ts +196 -0
- package/src/toolchain/west-discover.ts +233 -0
- package/src/toolchain/west-spawn.ts +141 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 typecad0
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# @typecad/framework-zephyr
|
|
2
|
+
|
|
3
|
+
TypeCAD framework package that lowers HAL operation IR to **native Zephyr RTOS
|
|
4
|
+
driver API calls**. Generates a real Zephyr application (`main` + `src/main.cpp`
|
|
5
|
+
+ `CMakeLists.txt` + `prj.conf`), compiled through Zephyr's `west` / CMake /
|
|
6
|
+
Ninja build system.
|
|
7
|
+
|
|
8
|
+
GPIO is lowered through devicetree specs (`gpio_pin_*_dt`) so an active-low
|
|
9
|
+
LED's polarity is honored by the DT flags, not by the generated code.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Zephyr RTOS ≥ 3.x installed (the SDK + a west-enabled Python environment).
|
|
14
|
+
- The framework auto-discovers `west` without requiring you to activate the
|
|
15
|
+
venv first. Discovery cascade (first usable wins):
|
|
16
|
+
1. `west` already on `PATH` (env already activated / global install).
|
|
17
|
+
2. `$ZEPHYR_BASE` sibling venv: `${ZEPHYR_BASE}/../.venv/<python> -m west`.
|
|
18
|
+
3. Well-known workspace layouts (`~/zephyrproject/.venv`,
|
|
19
|
+
`/opt/zephyrproject/.venv`, etc.).
|
|
20
|
+
4. System pythons (`python`, `python3`, `py`) via `-m west`.
|
|
21
|
+
|
|
22
|
+
If none is found, the build errors with an actionable message pointing at the
|
|
23
|
+
fix (`pip install west`, set `ZEPHYR_BASE`, or activate the venv).
|
|
24
|
+
|
|
25
|
+
## How it works
|
|
26
|
+
|
|
27
|
+
- HAL ops (`gpio.write`, `i2c.begin`, etc.) lower to native Zephyr driver calls
|
|
28
|
+
(`gpio_pin_set_dt`, `i2c_write`, etc.) — no Arduino API.
|
|
29
|
+
- The generated `src/main.cpp` defines `int main(void)` — the standard Zephyr
|
|
30
|
+
C entry point. `main` runs the synthesizer-emitted `setup()` once, then loops
|
|
31
|
+
`loop()` forever, yielding to the scheduler with `k_msleep(1)` each iteration
|
|
32
|
+
(cheap cooperative yield — matches the `app_main` pattern in framework-esp32).
|
|
33
|
+
- The framework emits a complete Zephyr application: root `CMakeLists.txt`
|
|
34
|
+
(`find_package(Zephyr)` + `file(GLOB src/*.cpp)`) and `prj.conf` (the Kconfig
|
|
35
|
+
symbols for the lowered peripherals). Both are regenerated idempotently —
|
|
36
|
+
only rewritten when their content changes, so Ninja's incremental build is
|
|
37
|
+
preserved.
|
|
38
|
+
- The MVP target is the **Seeed Studio XIAO nRF52840** (`xiao_ble` board).
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
cuttlefish init # select "Zephyr RTOS" for an nRF52840-family target
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Or in `cuttlefish.config.ts`:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export default {
|
|
50
|
+
framework: '@typecad/framework-zephyr',
|
|
51
|
+
frameworkData: {
|
|
52
|
+
buildTarget: 'xiao_ble',
|
|
53
|
+
},
|
|
54
|
+
toolchain: { type: 'west' },
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## HAL coverage
|
|
59
|
+
|
|
60
|
+
The manifest (`src/framework.manifest.ts`) is the honest, complete record of
|
|
61
|
+
what lowers. Fully supported: **gpio, pwm, adc, i2c, spi, uart, wdt, ble,
|
|
62
|
+
shift, board constants** (Board.definition.* / Pins.definition.* folding),
|
|
63
|
+
**random** (xorshift32 PRNG seeded from the Zephyr entropy tap).
|
|
64
|
+
Partial (some ops lower, some are deferred stubs): **timing, power,
|
|
65
|
+
interrupts, tone, pulse**. Unsupported for this target: **wifi** (nRF52840 has
|
|
66
|
+
no WiFi), **http** (no networking stack lowered), **display** (deferred),
|
|
67
|
+
**dac** (no DAC on nRF52840).
|
|
68
|
+
|
|
69
|
+
Run `npm test` (the manifest validator) to confirm the declared coverage matches
|
|
70
|
+
the actual lowering behavior.
|
|
71
|
+
|
|
72
|
+
## Hardware tests
|
|
73
|
+
|
|
74
|
+
On-device tests live in `tests/`. Run them against real hardware:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
npm run test:hw # all groups
|
|
78
|
+
npm run test:hw:gpio # just GPIO
|
|
79
|
+
npm run test:hw:timers # just timing
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Update `test.port` in `cuttlefish.config.ts` to your XIAO's serial device
|
|
83
|
+
before running. The tests transpile → `west build` → flash → read results over
|
|
84
|
+
the console.
|
|
85
|
+
|
|
86
|
+
## Notes / limitations
|
|
87
|
+
|
|
88
|
+
- **Minimal C++ libc.** Zephyr's default C++ support (`lib/cpp/minimal`) has no
|
|
89
|
+
`<vector>`, `<string>`, `<iostream>`, `<functional>`, exceptions, or RTTI.
|
|
90
|
+
`prj.conf` enables `CONFIG_NEWLIB_LIBC` for `std::string`/`std::vector`; array
|
|
91
|
+
literals still promote to the StaticArray wrapper, not `std::vector`.
|
|
92
|
+
- **Timer ops unsupported.** `timing.set_interval`/`set_timeout`/`clear_*` need
|
|
93
|
+
a polyfill-backed async runtime this framework does not emit yet. A Zephyr
|
|
94
|
+
workqueue / `k_thread` backing is a follow-on.
|
|
95
|
+
- **Debug mode.** `cuttlefish build --debug` routes through `printk` (always
|
|
96
|
+
available, no `CONFIG_CONSOLE` dependency) rather than `std::cout`, since the
|
|
97
|
+
minimal libc has no iostream. Breakpoints halt on console input (`ENTER`
|
|
98
|
+
continues, `s` skips).
|
|
99
|
+
- **AUTOSAR compliance.** All emitted C++ uses `static_cast`/`reinterpret_cast`
|
|
100
|
+
(no C-style casts) so the shim bytes pass `--autosar=strict`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RuntimePolyfillIR } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
export interface TimerPolyfillIR extends RuntimePolyfillIR {
|
|
3
|
+
/** The clamped pool size the C++ was generated with. */
|
|
4
|
+
maxTimers: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Build the timer_methods polyfill IR. `requestedTimers` is sized from
|
|
8
|
+
* ctx.analysis.timerCallCount; it is clamped to [1, 16].
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildTimerPolyfill(requestedTimers: number): TimerPolyfillIR;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Timer methods polyfill — Zephyr k_timer + k_work (heap-free)
|
|
3
|
+
//
|
|
4
|
+
// setInterval / setTimeout / clearInterval / clearTimeout backed by a fixed
|
|
5
|
+
// pool of k_timer + k_work pairs. The timer's expiry function (ISR context)
|
|
6
|
+
// submits a k_work item to the system workqueue; the work handler runs the
|
|
7
|
+
// user callback in thread context. This split keeps the ISR cheap and lets the
|
|
8
|
+
// callback run on a real thread (so it can do real work — but must not block,
|
|
9
|
+
// since the system workqueue is shared).
|
|
10
|
+
//
|
|
11
|
+
// EMIT BOUNDARY: emitted bytes land in user firmware. Covered by the TypeCAD
|
|
12
|
+
// Runtime Exception (RUNTIME_EXCEPTION.md at the repo root).
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
const MIN_TIMERS = 1;
|
|
15
|
+
const MAX_TIMERS_CAP = 16;
|
|
16
|
+
/**
|
|
17
|
+
* Build the timer_methods polyfill IR. `requestedTimers` is sized from
|
|
18
|
+
* ctx.analysis.timerCallCount; it is clamped to [1, 16].
|
|
19
|
+
*/
|
|
20
|
+
export function buildTimerPolyfill(requestedTimers) {
|
|
21
|
+
const maxTimers = Math.min(MAX_TIMERS_CAP, Math.max(MIN_TIMERS, requestedTimers));
|
|
22
|
+
const helperStructs = [`
|
|
23
|
+
// cuttlefish timer runtime — k_timer + k_work pool (heap-free).
|
|
24
|
+
struct __tc_TimerSlot {
|
|
25
|
+
struct k_timer timer;
|
|
26
|
+
struct k_work work;
|
|
27
|
+
void (*callback)(void);
|
|
28
|
+
bool active;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
static struct __tc_TimerSlot __tc_timer_slots[${maxTimers}];
|
|
32
|
+
|
|
33
|
+
static void __tc_timer_work_handler(struct k_work* w) {
|
|
34
|
+
// Runs on the system workqueue thread (NOT ISR). Find the owning slot by
|
|
35
|
+
// address and invoke the callback. Must not block.
|
|
36
|
+
for (int32_t i = 0; i < ${maxTimers}; i++) {
|
|
37
|
+
if (&__tc_timer_slots[i].work == w) {
|
|
38
|
+
if (__tc_timer_slots[i].callback != nullptr) { __tc_timer_slots[i].callback(); }
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static void __tc_timer_expiry_fn(struct k_timer* t) {
|
|
45
|
+
// ISR context: submit the work item, do NOT run the callback here.
|
|
46
|
+
for (int32_t i = 0; i < ${maxTimers}; i++) {
|
|
47
|
+
if (&__tc_timer_slots[i].timer == t) {
|
|
48
|
+
(void)k_work_submit(&__tc_timer_slots[i].work);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static int32_t __tc_timer_add(void (*cb)(void), int32_t ms, bool repeat) {
|
|
55
|
+
for (int32_t i = 0; i < ${maxTimers}; i++) {
|
|
56
|
+
if (!__tc_timer_slots[i].active) {
|
|
57
|
+
__tc_timer_slots[i].callback = cb;
|
|
58
|
+
__tc_timer_slots[i].active = true;
|
|
59
|
+
k_timer_init(&__tc_timer_slots[i].timer, __tc_timer_expiry_fn, nullptr);
|
|
60
|
+
k_work_init(&__tc_timer_slots[i].work, __tc_timer_work_handler);
|
|
61
|
+
k_timer_start(&__tc_timer_slots[i].timer, K_MSEC(ms), repeat ? K_MSEC(ms) : K_FOREVER);
|
|
62
|
+
return i + 1; // 1-based id (Arduino parity)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return 0; // pool full
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static void __tc_timer_clear(int32_t id) {
|
|
69
|
+
if (id > 0 && id <= ${maxTimers}) {
|
|
70
|
+
int32_t i = id - 1;
|
|
71
|
+
k_timer_stop(&__tc_timer_slots[i].timer);
|
|
72
|
+
__tc_timer_slots[i].active = false;
|
|
73
|
+
__tc_timer_slots[i].callback = nullptr;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
`];
|
|
77
|
+
const helperFunctions = [`
|
|
78
|
+
int32_t __tc_setInterval(void (*cb)(), int32_t ms) { return __tc_timer_add(cb, ms, true); }
|
|
79
|
+
int32_t __tc_setTimeout(void (*cb)(), int32_t ms) { return __tc_timer_add(cb, ms, false); }
|
|
80
|
+
void __tc_clearInterval(int32_t id) { __tc_timer_clear(id); }
|
|
81
|
+
void __tc_clearTimeout(int32_t id) { __tc_timer_clear(id); }
|
|
82
|
+
`];
|
|
83
|
+
return {
|
|
84
|
+
kind: 'polyfill',
|
|
85
|
+
id: 'timer_methods',
|
|
86
|
+
domain: 'embedded',
|
|
87
|
+
requiredIncludes: [],
|
|
88
|
+
forwardDeclarations: [],
|
|
89
|
+
helperStructs,
|
|
90
|
+
helperFunctions,
|
|
91
|
+
shimMacros: [],
|
|
92
|
+
dependencies: [],
|
|
93
|
+
maxTimers,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ZephyrChipDescriptor } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the devicetree nodelabel of the GPIO controller that owns `pin`.
|
|
4
|
+
*
|
|
5
|
+
* For single-controller SoCs (no `gpioControllers`) this is always
|
|
6
|
+
* `chip.gpioController`. For multi-controller SoCs it finds the entry whose
|
|
7
|
+
* [minPin, maxPin] range contains `pin`, falling back to `chip.gpioController`
|
|
8
|
+
* for out-of-range pins (e.g. the manifest probe's synthetic pin 0).
|
|
9
|
+
*/
|
|
10
|
+
export declare function controllerNodelabelForPin(chip: ZephyrChipDescriptor, pin: number): string;
|
|
11
|
+
/**
|
|
12
|
+
* Emit the C++ source for a runtime pin → GPIO-device dispatcher.
|
|
13
|
+
*
|
|
14
|
+
* Returns lines defining `static inline const struct device* __tc_gpio_dev(uint32_t pin)`.
|
|
15
|
+
* Each branch resolves its controller via `DEVICE_DT_GET(DT_NODELABEL(...))` at
|
|
16
|
+
* compile time (the macro is evaluated per branch, so it is always statically
|
|
17
|
+
* valid); only `pin` is runtime. For a single-controller SoC this collapses to
|
|
18
|
+
* a one-liner returning that controller, so the existing XIAO nRF52840 behavior
|
|
19
|
+
* is byte-for-byte unchanged.
|
|
20
|
+
*/
|
|
21
|
+
export declare function emitGpioDevDispatcher(chip: ZephyrChipDescriptor): string[];
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// GPIO controller resolution — pin → devicetree nodelabel
|
|
3
|
+
//
|
|
4
|
+
// Most SoCs expose a single GPIO controller, so a HAL pin maps to
|
|
5
|
+
// `chip.gpioController` unconditionally. SoCs that split GPIO across multiple
|
|
6
|
+
// devicetree nodes (ESP32-S3: `gpio0` 0–31, `gpio1` 32–48) list a range per
|
|
7
|
+
// controller in `chip.gpioControllers`. These helpers route a HAL pin to the
|
|
8
|
+
// owning controller.
|
|
9
|
+
//
|
|
10
|
+
// Two forms:
|
|
11
|
+
// - controllerNodelabelForPin(): used by the compile-time lowering paths
|
|
12
|
+
// (gpio/pulse/spi raw writes), where the pin number is a literal known at
|
|
13
|
+
// transpile time. Resolves to a single nodelabel so the emitted
|
|
14
|
+
// `DT_NODELABEL(<nodelabel>)` macro is statically valid.
|
|
15
|
+
// - emitGpioDevDispatcher(): used by paths that take a RUNTIME pin (the
|
|
16
|
+
// @typecad/safety __tc_gpio_read/__tc_gpio_write shims). Emits a tiny
|
|
17
|
+
// `__tc_gpio_dev(uint32_t pin)` that returns the owning `const struct
|
|
18
|
+
// device*`, so a single shim body handles any pin. Single-controller SoCs
|
|
19
|
+
// collapse to a one-liner.
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the devicetree nodelabel of the GPIO controller that owns `pin`.
|
|
23
|
+
*
|
|
24
|
+
* For single-controller SoCs (no `gpioControllers`) this is always
|
|
25
|
+
* `chip.gpioController`. For multi-controller SoCs it finds the entry whose
|
|
26
|
+
* [minPin, maxPin] range contains `pin`, falling back to `chip.gpioController`
|
|
27
|
+
* for out-of-range pins (e.g. the manifest probe's synthetic pin 0).
|
|
28
|
+
*/
|
|
29
|
+
export function controllerNodelabelForPin(chip, pin) {
|
|
30
|
+
const ranges = chip.gpioControllers;
|
|
31
|
+
if (ranges && ranges.length > 0) {
|
|
32
|
+
const hit = ranges.find((r) => pin >= r.minPin && pin <= r.maxPin);
|
|
33
|
+
if (hit)
|
|
34
|
+
return hit.nodelabel;
|
|
35
|
+
}
|
|
36
|
+
return chip.gpioController;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Emit the C++ source for a runtime pin → GPIO-device dispatcher.
|
|
40
|
+
*
|
|
41
|
+
* Returns lines defining `static inline const struct device* __tc_gpio_dev(uint32_t pin)`.
|
|
42
|
+
* Each branch resolves its controller via `DEVICE_DT_GET(DT_NODELABEL(...))` at
|
|
43
|
+
* compile time (the macro is evaluated per branch, so it is always statically
|
|
44
|
+
* valid); only `pin` is runtime. For a single-controller SoC this collapses to
|
|
45
|
+
* a one-liner returning that controller, so the existing XIAO nRF52840 behavior
|
|
46
|
+
* is byte-for-byte unchanged.
|
|
47
|
+
*/
|
|
48
|
+
export function emitGpioDevDispatcher(chip) {
|
|
49
|
+
const ranges = chip.gpioControllers;
|
|
50
|
+
if (!ranges || ranges.length === 0) {
|
|
51
|
+
return [
|
|
52
|
+
'static inline const struct device* __tc_gpio_dev(uint32_t pin) {',
|
|
53
|
+
` (void)pin;`,
|
|
54
|
+
` return DEVICE_DT_GET(DT_NODELABEL(${chip.gpioController}));`,
|
|
55
|
+
'}',
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
const lines = [
|
|
59
|
+
'static inline const struct device* __tc_gpio_dev(uint32_t pin) {',
|
|
60
|
+
];
|
|
61
|
+
for (const r of ranges) {
|
|
62
|
+
lines.push(` if (pin >= ${r.minPin} && pin <= ${r.maxPin}) { return DEVICE_DT_GET(DT_NODELABEL(${r.nodelabel})); }`);
|
|
63
|
+
}
|
|
64
|
+
lines.push(` return DEVICE_DT_GET(DT_NODELABEL(${chip.gpioController}));`, '}');
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Espressif ESP32 (esp32_devkitc) — Zephyr board descriptor
|
|
3
|
+
//
|
|
4
|
+
// Board target: `esp32_devkitc` (mainline Zephyr,
|
|
5
|
+
// boards/espressif/esp32_devkitc). Programmed over USB via the esptool runner
|
|
6
|
+
// (see toolchain/index.ts), the same flash path as the ESP32-S3.
|
|
7
|
+
//
|
|
8
|
+
// GPIO is split across TWO devicetree controllers — `gpio0` (pins 0–31) and
|
|
9
|
+
// `gpio1` (pins 32–39) — so this descriptor lists both in `gpioControllers`.
|
|
10
|
+
// The lowering routes each HAL pin to its owning controller at runtime; see
|
|
11
|
+
// chips/controllers.ts. (Same shape as the S3, but the ESP32's highest GPIO is
|
|
12
|
+
// 39, not 48 — `gpio1` has ngpios=8, not 17.)
|
|
13
|
+
//
|
|
14
|
+
// Minimal-by-design: the only DT facts carried here are the ones a compile-time
|
|
15
|
+
// DT macro cannot reach — the runtime pin→controller split, plus the `sw0`
|
|
16
|
+
// alias for the BOOT button (used by the devicetree-spec GPIO path). Every other
|
|
17
|
+
// DT fact (UART/I2C/SPI/`wdt` nodelabels) is resolved by Zephyr's own
|
|
18
|
+
// devicetree via emitted DT_NODELABEL macros, not hand-copied here.
|
|
19
|
+
//
|
|
20
|
+
// Verified against the Zephyr board DTS:
|
|
21
|
+
// boards/espressif/esp32_devkitc/esp32_devkitc_procpu.dts
|
|
22
|
+
// aliases { sw0 = &button0; } → button_0: pin 0 on gpio0, active-low + pull-up
|
|
23
|
+
// &gpio0/&gpio1 { status = "okay" } (esp32_devkitc_procpu.dts:64-70)
|
|
24
|
+
// &wifi { status = "okay" } (esp32_devkitc_procpu.dts:143-145)
|
|
25
|
+
// GPIO controller coverage:
|
|
26
|
+
// dts/xtensa/espressif/esp32/esp32_common.dtsi:314-337
|
|
27
|
+
// gpio0: ngpios = <32> (pins 0–31)
|
|
28
|
+
// gpio1: ngpios = <8> (pins 32–39)
|
|
29
|
+
//
|
|
30
|
+
// Note: GPIO 34–39 are input-only pads on ESP32 silicon (not modeled here —
|
|
31
|
+
// the DT does not encode output restrictions per pin; an output config on those
|
|
32
|
+
// pins fails at runtime against the raw controller, which is the expected
|
|
33
|
+
// silicon-accurate behavior).
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
export const ESP32_DEVKITC = {
|
|
36
|
+
id: 'esp32_devkitc',
|
|
37
|
+
soc: 'esp32',
|
|
38
|
+
gpioController: 'gpio0',
|
|
39
|
+
gpioControllers: [
|
|
40
|
+
{ nodelabel: 'gpio0', minPin: 0, maxPin: 31 },
|
|
41
|
+
{ nodelabel: 'gpio1', minPin: 32, maxPin: 39 },
|
|
42
|
+
],
|
|
43
|
+
gpio: {
|
|
44
|
+
// The BOOT button (GPIO0) is the board's only DT-aliased GPIO. Listed so a
|
|
45
|
+
// program reading/interrupting pin 0 goes through the polarity-correct
|
|
46
|
+
// devicetree-spec path (GPIO_ACTIVE_LOW honored by the DT flags).
|
|
47
|
+
dtSpecs: [
|
|
48
|
+
{ pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
|
|
49
|
+
],
|
|
50
|
+
interruptPins: [
|
|
51
|
+
{ pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
// WiFi: the ESP32 has a 2.4GHz radio; conn_mgr + the esp32 wifi driver
|
|
55
|
+
// (CONFIG_WIFI_ESP32) provide connectivity. WIFI_ESP32 depends on !SMP, and
|
|
56
|
+
// the ESP32 is AMP (dual-image procpu/appcpu), not SMP, by default — so the
|
|
57
|
+
// dependency is satisfied. Omitted on radioless targets.
|
|
58
|
+
wifi: { supported: true },
|
|
59
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Espressif ESP32-S3 (esp32s3_devkitc) — Zephyr board descriptor
|
|
3
|
+
//
|
|
4
|
+
// Board target: `esp32s3_devkitc` (mainline Zephyr,
|
|
5
|
+
// boards/espressif/esp32s3_devkitc). Programmed over USB via the esptool
|
|
6
|
+
// runner (see toolchain/index.ts), unlike the J-Link/nrfjprog path used for
|
|
7
|
+
// the XIAO nRF52840.
|
|
8
|
+
//
|
|
9
|
+
// GPIO is split across TWO devicetree controllers — `gpio0` (pins 0–31) and
|
|
10
|
+
// `gpio1` (pins 32–48) — so this descriptor lists both in `gpioControllers`.
|
|
11
|
+
// The lowering routes each HAL pin to its owning controller at runtime; see
|
|
12
|
+
// chips/controllers.ts.
|
|
13
|
+
//
|
|
14
|
+
// Minimal-by-design: the only DT facts carried here are the ones a
|
|
15
|
+
// compile-time DT macro cannot reach — the runtime pin→controller split, plus
|
|
16
|
+
// the `sw0` alias for the BOOT button (used by the devicetree-spec GPIO path).
|
|
17
|
+
// Every other DT fact (UART/I2C/SPI/`wdt` nodelabels) is resolved by Zephyr's
|
|
18
|
+
// own devicetree via emitted DT_NODELABEL macros, not hand-copied here.
|
|
19
|
+
//
|
|
20
|
+
// Verified against the Zephyr board DTS:
|
|
21
|
+
// boards/espressif/esp32s3_devkitc/esp32s3_devkitc_procpu.dts
|
|
22
|
+
// aliases { sw0 = &button0; } → button_0: pin 0 on gpio0, active-low + pull-up
|
|
23
|
+
// (the DevKitC board DTS defines no led0 alias — the onboard RGB is a WS2812
|
|
24
|
+
// on GPIO38, not a plain GPIO LED, so it is intentionally NOT listed here.)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
export const ESP32S3_DEVKITC = {
|
|
27
|
+
id: 'esp32s3_devkitc',
|
|
28
|
+
soc: 'esp32s3',
|
|
29
|
+
gpioController: 'gpio0',
|
|
30
|
+
gpioControllers: [
|
|
31
|
+
{ nodelabel: 'gpio0', minPin: 0, maxPin: 31 },
|
|
32
|
+
{ nodelabel: 'gpio1', minPin: 32, maxPin: 48 },
|
|
33
|
+
],
|
|
34
|
+
gpio: {
|
|
35
|
+
// The BOOT button (GPIO0) is the board's only DT-aliased GPIO. Listed so a
|
|
36
|
+
// program reading/interrupting pin 0 goes through the polarity-correct
|
|
37
|
+
// devicetree-spec path (GPIO_ACTIVE_LOW honored by the DT flags). Every
|
|
38
|
+
// other GPIO pin uses the raw-controller path against its owning controller.
|
|
39
|
+
dtSpecs: [
|
|
40
|
+
{ pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
|
|
41
|
+
],
|
|
42
|
+
interruptPins: [
|
|
43
|
+
{ pin: 0, dtSpec: 'sw0' }, // BOOT button (GPIO0)
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
// UART/I2C/SPI/`wdt` are intentionally omitted: their devicetree nodelabels
|
|
47
|
+
// (uart0/uart1/uart2, i2c0/i2c1, spi2/spi3, wdt0) are resolved by Zephyr's
|
|
48
|
+
// devicetree at compile time and don't need to be carried as data here. ADC is
|
|
49
|
+
// omitted as well — added when a demo needs analog reads, with the verified
|
|
50
|
+
// ESP32-S3 ADC1/ADC2 pin→channel map.
|
|
51
|
+
// WiFi: the ESP32-S3 has a 2.4GHz radio; conn_mgr + the esp32 wifi driver
|
|
52
|
+
// (CONFIG_WIFI_ESP32) provide connectivity. Omitted on radioless targets.
|
|
53
|
+
wifi: { supported: true },
|
|
54
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ZephyrChipDescriptor } from './types.js';
|
|
2
|
+
import { XIAO_BLE } from './xiao-ble.js';
|
|
3
|
+
import { ESP32S3_DEVKITC } from './esp32s3.js';
|
|
4
|
+
import { ESP32_DEVKITC } from './esp32.js';
|
|
5
|
+
export { XIAO_BLE, ESP32S3_DEVKITC, ESP32_DEVKITC };
|
|
6
|
+
export type { ZephyrChipDescriptor, ZephyrGpioDtSpec } from './types.js';
|
|
7
|
+
export declare function setActiveChip(d: ZephyrChipDescriptor): void;
|
|
8
|
+
export declare function getActiveChip(): ZephyrChipDescriptor;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a chip descriptor from the Zephyr board target string
|
|
11
|
+
* (frameworkData.buildTarget / frameworkData.target). Accepts the bare board
|
|
12
|
+
* id ('xiao_ble', 'esp32s3_devkitc', 'esp32_devkitc') or a board/qualifier
|
|
13
|
+
* path ('esp32s3_devkitc/esp32s3/procpu', 'esp32_devkitc/esp32/procpu').
|
|
14
|
+
*/
|
|
15
|
+
export declare function chipForTarget(target?: string): ZephyrChipDescriptor;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Chip descriptor registry — maps Zephyr board target → ZephyrChipDescriptor
|
|
3
|
+
//
|
|
4
|
+
// Mirrors framework-esp32/src/chips/index.ts: a module-level activeChip,
|
|
5
|
+
// setActiveChip/getActiveChip, and chipForTarget resolving from
|
|
6
|
+
// frameworkData.buildTarget. The strategy calls setActiveChip during profile
|
|
7
|
+
// resolution so the lowering reads a single cached descriptor.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
import { XIAO_BLE } from './xiao-ble.js';
|
|
10
|
+
import { ESP32S3_DEVKITC } from './esp32s3.js';
|
|
11
|
+
import { ESP32_DEVKITC } from './esp32.js';
|
|
12
|
+
export { XIAO_BLE, ESP32S3_DEVKITC, ESP32_DEVKITC };
|
|
13
|
+
/**
|
|
14
|
+
* Default chip used when no buildTarget is supplied. The XIAO nRF52840 is the
|
|
15
|
+
* canonical MVP target; subsequent board additions extend the switch below.
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_CHIP = XIAO_BLE;
|
|
18
|
+
let activeChip = DEFAULT_CHIP;
|
|
19
|
+
export function setActiveChip(d) {
|
|
20
|
+
activeChip = d;
|
|
21
|
+
}
|
|
22
|
+
export function getActiveChip() {
|
|
23
|
+
return activeChip;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a chip descriptor from the Zephyr board target string
|
|
27
|
+
* (frameworkData.buildTarget / frameworkData.target). Accepts the bare board
|
|
28
|
+
* id ('xiao_ble', 'esp32s3_devkitc', 'esp32_devkitc') or a board/qualifier
|
|
29
|
+
* path ('esp32s3_devkitc/esp32s3/procpu', 'esp32_devkitc/esp32/procpu').
|
|
30
|
+
*/
|
|
31
|
+
export function chipForTarget(target) {
|
|
32
|
+
const t = (target ?? '').trim().toLowerCase();
|
|
33
|
+
const boardId = t.split('/')[0];
|
|
34
|
+
switch (boardId) {
|
|
35
|
+
case 'xiao_ble':
|
|
36
|
+
return XIAO_BLE;
|
|
37
|
+
case 'esp32s3_devkitc':
|
|
38
|
+
return ESP32S3_DEVKITC;
|
|
39
|
+
case 'esp32_devkitc':
|
|
40
|
+
return ESP32_DEVKITC;
|
|
41
|
+
case '':
|
|
42
|
+
default:
|
|
43
|
+
return DEFAULT_CHIP;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BoardConstants } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
import type { ZephyrChipDescriptor } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Try to derive a ZephyrChipDescriptor from board/MCU package constants.
|
|
5
|
+
*
|
|
6
|
+
* Returns null when no zephyr info is available in the board constants
|
|
7
|
+
* (the caller should fall back to the hardcoded chipForTarget registry).
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveChipFromBoard(bc: BoardConstants | undefined): ZephyrChipDescriptor | null;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Derive ZephyrChipDescriptor from board/MCU package constants
|
|
3
|
+
//
|
|
4
|
+
// The board constants resolver extracts flat dot-path scalars from the board
|
|
5
|
+
// and MCU definition files. This utility reconstructs the structured
|
|
6
|
+
// ZephyrChipDescriptor from those flat keys, merging SoC-level defaults
|
|
7
|
+
// (from the MCU package's zephyr field) with board-level overrides (from the
|
|
8
|
+
// board package's zephyr field).
|
|
9
|
+
//
|
|
10
|
+
// When a board package carries Zephyr config, this path replaces the
|
|
11
|
+
// hardcoded chip descriptor registry. When it doesn't (legacy), chipForTarget
|
|
12
|
+
// still works as the fallback.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/** Collect an indexed array of objects reconstructed from flat dot-path keys. */
|
|
15
|
+
function collectIndexed(bc, prefix, build) {
|
|
16
|
+
const result = [];
|
|
17
|
+
for (let i = 0; i < 256; i++) {
|
|
18
|
+
const checkKey = `${prefix}.${i}`;
|
|
19
|
+
let hasAny = false;
|
|
20
|
+
for (const [k] of bc) {
|
|
21
|
+
if (k.startsWith(checkKey)) {
|
|
22
|
+
hasAny = true;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!hasAny)
|
|
27
|
+
break;
|
|
28
|
+
const item = build(bc, i);
|
|
29
|
+
if (item)
|
|
30
|
+
result.push(item);
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function collectBusControllers(bc, prefix) {
|
|
35
|
+
return collectIndexed(bc, prefix, (m, i) => {
|
|
36
|
+
const nodeLabel = m.get(`${prefix}.${i}.nodeLabel`);
|
|
37
|
+
return nodeLabel ? { nodeLabel } : null;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Try to derive a ZephyrChipDescriptor from board/MCU package constants.
|
|
42
|
+
*
|
|
43
|
+
* Returns null when no zephyr info is available in the board constants
|
|
44
|
+
* (the caller should fall back to the hardcoded chipForTarget registry).
|
|
45
|
+
*/
|
|
46
|
+
export function resolveChipFromBoard(bc) {
|
|
47
|
+
if (!bc)
|
|
48
|
+
return null;
|
|
49
|
+
const boardTarget = bc.get('build.frameworks.zephyr');
|
|
50
|
+
if (!boardTarget)
|
|
51
|
+
return null;
|
|
52
|
+
const zGpioController = bc.get('zephyr.gpioController');
|
|
53
|
+
const soc = bc.get('mcu.id') ?? '';
|
|
54
|
+
// ── Build mutable sub-objects, then construct the final descriptor ──────
|
|
55
|
+
const gc = collectIndexed(bc, 'zephyr.gpioControllers', (m, i) => {
|
|
56
|
+
const nodelabel = m.get(`zephyr.gpioControllers.${i}.nodelabel`);
|
|
57
|
+
const minPin = m.get(`zephyr.gpioControllers.${i}.minPin`);
|
|
58
|
+
const maxPin = m.get(`zephyr.gpioControllers.${i}.maxPin`);
|
|
59
|
+
if (nodelabel != null && minPin != null && maxPin != null) {
|
|
60
|
+
return { nodelabel, minPin, maxPin };
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
});
|
|
64
|
+
const dtSpecs = collectIndexed(bc, 'zephyr.gpio.dtSpecs', (m, i) => {
|
|
65
|
+
const pin = m.get(`zephyr.gpio.dtSpecs.${i}.pin`);
|
|
66
|
+
const dtSpec = m.get(`zephyr.gpio.dtSpecs.${i}.dtSpec`);
|
|
67
|
+
if (pin != null && dtSpec)
|
|
68
|
+
return { pin, dtSpec };
|
|
69
|
+
return null;
|
|
70
|
+
});
|
|
71
|
+
const intPins = collectIndexed(bc, 'zephyr.gpio.interruptPins', (m, i) => {
|
|
72
|
+
const pin = m.get(`zephyr.gpio.interruptPins.${i}.pin`);
|
|
73
|
+
const dtSpec = m.get(`zephyr.gpio.interruptPins.${i}.dtSpec`);
|
|
74
|
+
if (pin != null && dtSpec)
|
|
75
|
+
return { pin, dtSpec };
|
|
76
|
+
return null;
|
|
77
|
+
});
|
|
78
|
+
const i2cControllers = collectBusControllers(bc, 'zephyr.i2c.controllers');
|
|
79
|
+
const spiControllers = collectBusControllers(bc, 'zephyr.spi.controllers');
|
|
80
|
+
const uartControllers = collectBusControllers(bc, 'zephyr.uart.controllers');
|
|
81
|
+
const pwmSpecs = collectIndexed(bc, 'zephyr.pwm.specs', (m, i) => {
|
|
82
|
+
const pin = m.get(`zephyr.pwm.specs.${i}.pin`);
|
|
83
|
+
const dtSpec = m.get(`zephyr.pwm.specs.${i}.dtSpec`);
|
|
84
|
+
if (pin != null && dtSpec)
|
|
85
|
+
return { pin, dtSpec };
|
|
86
|
+
return null;
|
|
87
|
+
});
|
|
88
|
+
const adcNodeLabel = bc.get('zephyr.adc.nodeLabel');
|
|
89
|
+
const adcResolution = bc.get('zephyr.adc.resolution');
|
|
90
|
+
const adcVref = bc.get('zephyr.adc.vrefMv');
|
|
91
|
+
const adcChannels = collectIndexed(bc, 'zephyr.adc.channels', (m, i) => {
|
|
92
|
+
const pin = m.get(`zephyr.adc.channels.${i}.pin`);
|
|
93
|
+
const channel = m.get(`zephyr.adc.channels.${i}.channel`);
|
|
94
|
+
if (pin != null && channel != null)
|
|
95
|
+
return { pin, channel };
|
|
96
|
+
return null;
|
|
97
|
+
});
|
|
98
|
+
const wdtNodeLabel = bc.get('zephyr.wdt.nodeLabel');
|
|
99
|
+
const wifiSupported = bc.get('zephyr.wifi.supported');
|
|
100
|
+
// ── Construct the final readonly descriptor ─────────────────────────────
|
|
101
|
+
const gpio = {
|
|
102
|
+
dtSpecs,
|
|
103
|
+
...(intPins.length > 0 ? { interruptPins: intPins } : {}),
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
id: boardTarget,
|
|
107
|
+
soc,
|
|
108
|
+
gpioController: zGpioController ?? 'gpio0',
|
|
109
|
+
...(gc.length > 0 ? { gpioControllers: gc } : {}),
|
|
110
|
+
gpio,
|
|
111
|
+
...(i2cControllers.length > 0 ? { i2c: { controllers: i2cControllers } } : {}),
|
|
112
|
+
...(spiControllers.length > 0 ? { spi: { controllers: spiControllers } } : {}),
|
|
113
|
+
...(uartControllers.length > 0 ? { uart: { controllers: uartControllers } } : {}),
|
|
114
|
+
...(pwmSpecs.length > 0 ? { pwm: { specs: pwmSpecs } } : {}),
|
|
115
|
+
...(adcNodeLabel || adcResolution != null || adcVref != null || adcChannels.length > 0
|
|
116
|
+
? { adc: { nodeLabel: adcNodeLabel ?? 'adc', resolution: adcResolution ?? 12, vrefMv: adcVref ?? 3000, channels: adcChannels } }
|
|
117
|
+
: {}),
|
|
118
|
+
...(wdtNodeLabel ? { wdt: { nodeLabel: wdtNodeLabel } } : {}),
|
|
119
|
+
...(wifiSupported ? { wifi: { supported: true } } : {}),
|
|
120
|
+
};
|
|
121
|
+
}
|