@typecad/framework-zephyr 1.0.0-alpha.14 → 1.0.0-alpha.16
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 -47
- 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/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 +267 -10
- 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 +319 -0
- package/dist/chips/xiao-ble.js +21 -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 +72 -1
- package/dist/dt-config/kconfig.js +115 -26
- package/dist/dt-config/overlay.js +548 -28
- package/dist/framework.manifest.d.ts +3 -3
- package/dist/framework.manifest.js +122 -185
- package/dist/index.d.ts +4 -1
- package/dist/index.js +8 -2
- package/dist/lowering/adc.d.ts +20 -2
- package/dist/lowering/adc.js +121 -29
- 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 +82 -5
- 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 +3 -5
- package/dist/lowering/power.js +0 -4
- package/dist/lowering/preferences.js +49 -77
- package/dist/lowering/pulse.js +0 -31
- package/dist/lowering/pwm.d.ts +12 -1
- package/dist/lowering/pwm.js +78 -14
- 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 +16 -4
- 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 +97 -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 +839 -222
- 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 +226 -76
- package/dist/toolchain/env-check.d.ts +4 -4
- package/dist/toolchain/env-check.js +5 -5
- package/dist/toolchain/index.d.ts +115 -1
- package/dist/toolchain/index.js +680 -71
- package/dist/toolchain/scaffold.js +39 -7
- package/dist/toolchain/west-discover.d.ts +4 -0
- package/dist/toolchain/west-discover.js +17 -6
- 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 +15 -15
- package/src/as-built.ts +206 -0
- package/src/boardgen.ts +1428 -0
- package/src/chips/index.ts +35 -54
- package/src/chips/resolve.ts +285 -10
- package/src/chips/types.ts +325 -1
- 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 +165 -18
- package/src/dt-config/overlay.ts +537 -25
- package/src/framework.manifest.ts +123 -185
- package/src/index.ts +57 -41
- package/src/lowering/adc.ts +222 -118
- 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 -155
- 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 +3 -5
- package/src/lowering/preferences.ts +327 -354
- package/src/lowering/pwm.ts +167 -99
- 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 +97 -112
- package/src/sdk/board-catalog-sync.ts +62 -0
- package/src/strategy.ts +848 -237
- package/src/toolchain/bossac-touch.ts +112 -0
- package/src/toolchain/compat.ts +8 -3
- package/src/toolchain/debug-config.ts +676 -522
- package/src/toolchain/env-check.ts +9 -9
- package/src/toolchain/index.ts +1359 -645
- package/src/toolchain/scaffold.ts +38 -7
- package/src/toolchain/west-discover.ts +19 -6
- 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 -82
- 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/spi.js
CHANGED
|
@@ -12,11 +12,54 @@
|
|
|
12
12
|
// DEVICE_DT_GET(DT_NODELABEL(spi2)) and call spi_transceive directly.
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
import { parseControllerIndex } from './util.js';
|
|
15
|
-
import { controllerNodelabelForPin, controllerRawPinForPin } from '../chips/controllers.js';
|
|
16
15
|
/** The C variable prefix for a controller's state. */
|
|
17
16
|
function prefix(idx) {
|
|
18
17
|
return `__tc_spi${idx}`;
|
|
19
18
|
}
|
|
19
|
+
/** Derive a thin SPI target's names from the op facts. */
|
|
20
|
+
export function spiTargetNames(bus, cs) {
|
|
21
|
+
const busIndex = parseControllerIndex(typeof bus === 'string' ? bus : String(bus));
|
|
22
|
+
const csNum = typeof cs === 'number' ? cs : parseInt(String(cs), 10);
|
|
23
|
+
const stem = `spit_spi${busIndex}_cs${csNum}`;
|
|
24
|
+
return { dtLabel: `tc_${stem}`, varName: `__tc_${stem}_spec`, busIndex, cs: csNum };
|
|
25
|
+
}
|
|
26
|
+
/** The per-target state block: one spi_dt_spec against the DT child node the
|
|
27
|
+
* overlay emits, plus the tc-spit-cfg comment the overlay scanner reads (the
|
|
28
|
+
* tc-sensor-cfg channel). Called from shimLines for each distinct target. */
|
|
29
|
+
export function spiTargetStateLines(bus, cs, hz = 0, mode = 0) {
|
|
30
|
+
const n = spiTargetNames(bus, cs);
|
|
31
|
+
return [
|
|
32
|
+
'// CUTTLEFISH_SPIT_BEGIN',
|
|
33
|
+
// Zephyr 4.4's SPI_DT_SPEC_GET takes the base operation explicitly (mode
|
|
34
|
+
// bits come from the DT node's spi-cpol/spi-cpha via SPI_CONFIG_DT).
|
|
35
|
+
`static const struct spi_dt_spec ${n.varName} = SPI_DT_SPEC_GET(DT_NODELABEL(${n.dtLabel}), SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0);`,
|
|
36
|
+
`// tc-spit-cfg: ${n.dtLabel} hz=${Number(hz)} mode=${Number(mode)}`,
|
|
37
|
+
'// CUTTLEFISH_SPIT_END',
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
/** Build the tx buffer declaration + set for a thin SPI op. A literal byte
|
|
41
|
+
* array becomes a local array; a single identifier is the user's own buffer
|
|
42
|
+
* (its storage + sizeof drive length). spi_buf.buf is void*, and the user's
|
|
43
|
+
* buffer may be const-qualified — cast through const void* to stay legal
|
|
44
|
+
* under -Werror without mutating anyone's qualifications. */
|
|
45
|
+
function spiTxBuffers(tx, tag) {
|
|
46
|
+
const bytes = (Array.isArray(tx) ? tx : []);
|
|
47
|
+
// Only an identifier-shaped string is a real caller buffer — the resolver
|
|
48
|
+
// collapses a single-byte literal array to buffer-kind with NUMERIC text
|
|
49
|
+
// (e.g. "159"), which must take the literal path below.
|
|
50
|
+
const isIdentifier = bytes.length === 1 && typeof bytes[0] === 'string' && /^[A-Za-z_][A-Za-z0-9_]*$/.test(bytes[0]);
|
|
51
|
+
if (isIdentifier) {
|
|
52
|
+
const name = bytes[0];
|
|
53
|
+
return {
|
|
54
|
+
decl: `const struct spi_buf __tb${tag} = { .buf = const_cast<void*>(static_cast<const void*>(${name})), .len = sizeof(${name}) }; const struct spi_buf_set __txs${tag} = { .buffers = &__tb${tag}, .count = 1 };`,
|
|
55
|
+
set: `__txs${tag}`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
decl: `uint8_t __tx${tag}[] = { ${bytes.join(', ')} }; const struct spi_buf __tb${tag} = { .buf = __tx${tag}, .len = sizeof(__tx${tag}) }; const struct spi_buf_set __txs${tag} = { .buffers = &__tb${tag}, .count = 1 };`,
|
|
60
|
+
set: `__txs${tag}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
20
63
|
/**
|
|
21
64
|
* Emit the per-controller SPI state. The bus device resolves at compile time;
|
|
22
65
|
* a static spi_config holds the base operation flags, and mutable runtime fields
|
|
@@ -39,7 +82,9 @@ export function spiInitLines(chip, controllerIndex) {
|
|
|
39
82
|
`};`,
|
|
40
83
|
`static void ${p}_init(void) {`,
|
|
41
84
|
` if (!${p}_ready) {`,
|
|
42
|
-
|
|
85
|
+
// NOTE: no .bus assignment — struct spi_config lost its `bus` member in
|
|
86
|
+
// Zephyr 4.x (deprecated 3.5, removed 4.0); spi_transceive takes the
|
|
87
|
+
// device alongside the config, which the ops below already do.
|
|
43
88
|
` ${p}_cfg.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8)`,
|
|
44
89
|
` | (${p}_lsb ? SPI_TRANSFER_LSB : SPI_TRANSFER_MSB)`,
|
|
45
90
|
` | ((${p}_mode & 0x1) ? SPI_MODE_CPOL : 0)`,
|
|
@@ -59,51 +104,31 @@ export function lowerSpi(op, chip) {
|
|
|
59
104
|
const idx = parseControllerIndex(o.bus);
|
|
60
105
|
const p = prefix(idx);
|
|
61
106
|
switch (op.operation) {
|
|
62
|
-
case 'spi.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case 'spi.end_transaction':
|
|
70
|
-
return { code: `(void)0;` };
|
|
71
|
-
case 'spi.set_mode':
|
|
72
|
-
// Apply CPOL/CPHA: store the mode byte then re-init so the next transfer
|
|
73
|
-
// picks up the rebuilt operation flags. The ready flag is cleared so
|
|
74
|
-
// _init() rebuilds rather than early-returning.
|
|
75
|
-
return { code: `{ ${p}_mode = static_cast<uint8_t>(${o.mode}); ${p}_ready = false; ${p}_init(); }` };
|
|
76
|
-
case 'spi.set_bit_order': {
|
|
77
|
-
// The HAL payload `order` is a string ("lsb" | "msb"), per SpiSetBitOrderOp.
|
|
78
|
-
// Normalize here in TS so we emit a boolean literal, not the raw string
|
|
79
|
-
// (which would be an undeclared C++ identifier).
|
|
80
|
-
const key = String(o.order).replace(/^["']|["']$/g, '').toLowerCase();
|
|
81
|
-
const lsb = (key === 'lsb' || key === 'lsbfirst') ? 'true' : 'false';
|
|
82
|
-
return { code: `{ ${p}_lsb = ${lsb}; ${p}_ready = false; ${p}_init(); }` };
|
|
83
|
-
}
|
|
84
|
-
case 'spi.transfer': {
|
|
85
|
-
// Single-byte full-duplex, returns the received byte (GCC stmt-expr).
|
|
107
|
+
case 'spi.transceive': {
|
|
108
|
+
const v = spiTargetNames(o.bus, o.cs).varName;
|
|
109
|
+
const tx = spiTxBuffers(o.tx, 't');
|
|
110
|
+
const rxName = String(o.rx ?? '');
|
|
111
|
+
const rx = rxName
|
|
112
|
+
? `struct spi_buf __rb = { .buf = const_cast<void*>(static_cast<const void*>(${rxName})), .len = sizeof(${rxName}) }; const struct spi_buf_set __rbs = { .buffers = &__rb, .count = 1 };`
|
|
113
|
+
: `const struct spi_buf_set __rbs = { .buffers = NULL, .count = 0 };`;
|
|
86
114
|
return {
|
|
87
|
-
|
|
115
|
+
code: `{ ${tx.decl} ${rx} (void)spi_transceive_dt(&${v}, &${tx.set}, &__rbs); }`,
|
|
88
116
|
};
|
|
89
117
|
}
|
|
90
|
-
case 'spi.
|
|
91
|
-
|
|
92
|
-
const
|
|
118
|
+
case 'spi.dev_write': {
|
|
119
|
+
const v = spiTargetNames(o.bus, o.cs).varName;
|
|
120
|
+
const tx = spiTxBuffers(o.tx, 'w');
|
|
93
121
|
return {
|
|
94
|
-
code: `{
|
|
122
|
+
code: `{ ${tx.decl} (void)spi_write_dt(&${v}, &${tx.set}); }`,
|
|
95
123
|
};
|
|
96
124
|
}
|
|
97
|
-
case 'spi.
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
// on the right node.
|
|
103
|
-
const val = op.operation === 'spi.cs_low' ? 0 : 1;
|
|
104
|
-
const gpioController = controllerNodelabelForPin(chip, o.pin);
|
|
125
|
+
case 'spi.reg_read': {
|
|
126
|
+
// One-byte register read against an INTERNAL buffer — no caller array
|
|
127
|
+
// (sidesteps the file-scope Uint8Array promotion issue; also just the
|
|
128
|
+
// right shape for ID/status registers).
|
|
129
|
+
const v = spiTargetNames(o.bus, o.cs).varName;
|
|
105
130
|
return {
|
|
106
|
-
|
|
131
|
+
expression: `({ uint8_t __txr = static_cast<uint8_t>(${o.reg}); uint8_t __rxr = 0; struct spi_buf __tbr = { .buf = &__txr, .len = 1 }; const struct spi_buf_set __txsr = { .buffers = &__tbr, .count = 1 }; struct spi_buf __rbr = { .buf = &__rxr, .len = 1 }; const struct spi_buf_set __rxsr = { .buffers = &__rbr, .count = 1 }; (void)spi_transceive_dt(&${v}, &__txsr, &__rxsr); __rxr; })`,
|
|
107
132
|
};
|
|
108
133
|
}
|
|
109
134
|
default:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Emit the per-thread state. Called from shimLines for each distinct thread
|
|
4
|
+
* the program starts.
|
|
5
|
+
*/
|
|
6
|
+
export declare function threadStateLines(instance: number, stackBytes: number): string[];
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a HAL thread.* op to Zephyr C++.
|
|
9
|
+
* Returns `{ code }` for statement ops.
|
|
10
|
+
*/
|
|
11
|
+
export declare function lowerThread(op: HALOpIR): {
|
|
12
|
+
code?: string;
|
|
13
|
+
expression?: string;
|
|
14
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Thread lowering — Zephyr kernel threads (k_thread_create / k_thread_join)
|
|
3
|
+
//
|
|
4
|
+
// One state block per thread identity slot the program STARTS: a static
|
|
5
|
+
// K_THREAD_STACK_DEFINE (sized by the construction stackKb), the k_thread
|
|
6
|
+
// struct, and an entry trampoline bridging Zephyr's (void*, void*, void*)
|
|
7
|
+
// entry signature to the no-argument registered callback. start() creates
|
|
8
|
+
// and schedules (K_NO_WAIT); join() blocks (K_FOREVER).
|
|
9
|
+
//
|
|
10
|
+
// No devicetree, no Kconfig — kernel.h (always included) is the whole
|
|
11
|
+
// dependency. State emission is keyed on thread.start ops only, so a
|
|
12
|
+
// join-only program emits nothing (and its join guards on the same slot).
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
/** Per-instance symbol stem. */
|
|
15
|
+
function stem(instance) {
|
|
16
|
+
return `__tc_thrd${instance}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Emit the per-thread state. Called from shimLines for each distinct thread
|
|
20
|
+
* the program starts.
|
|
21
|
+
*/
|
|
22
|
+
export function threadStateLines(instance, stackBytes) {
|
|
23
|
+
const p = stem(instance);
|
|
24
|
+
return [
|
|
25
|
+
'// CUTTLEFISH_THREAD_BEGIN',
|
|
26
|
+
`K_THREAD_STACK_DEFINE(${p}_stack, ${stackBytes});`,
|
|
27
|
+
`static struct k_thread ${p}_thread;`,
|
|
28
|
+
`static void (*${p}_fn)(void) = NULL;`,
|
|
29
|
+
`static void ${p}_tramp(void* a, void* b, void* c) { (void)a; (void)b; (void)c; if (${p}_fn) { ${p}_fn(); } }`,
|
|
30
|
+
'// CUTTLEFISH_THREAD_END',
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a HAL thread.* op to Zephyr C++.
|
|
35
|
+
* Returns `{ code }` for statement ops.
|
|
36
|
+
*/
|
|
37
|
+
export function lowerThread(op) {
|
|
38
|
+
const o = op;
|
|
39
|
+
const instance = typeof o.instance === 'number' ? o.instance : 0;
|
|
40
|
+
const p = stem(instance);
|
|
41
|
+
switch (op.operation) {
|
|
42
|
+
case 'thread.start': {
|
|
43
|
+
// Store the entry, create + schedule immediately. The stack size comes
|
|
44
|
+
// from the state block (the construction fact), via K_THREAD_STACK_SIZEOF.
|
|
45
|
+
return {
|
|
46
|
+
code: `${p}_fn = (${o.handler}); (void)k_thread_create(&${p}_thread, ${p}_stack, K_THREAD_STACK_SIZEOF(${p}_stack), ${p}_tramp, NULL, NULL, NULL, ${o.priority ?? 5}, 0, K_NO_WAIT);`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
case 'thread.join':
|
|
50
|
+
return { code: `(void)k_thread_join(&${p}_thread, K_FOREVER);` };
|
|
51
|
+
default:
|
|
52
|
+
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
53
|
+
`Open an issue or use rawCpp() to emit it manually.`);
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/lowering/timing.js
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// Timing lowering — Zephyr kernel timing
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// status in the manifest — the validator skips the resolver probe for these
|
|
8
|
-
// (they legitimately return polyfill-helper calls, not direct lowering).
|
|
9
|
-
// free_heap has no portable Zephyr query without CONFIG_SYS_HEAP_RUNTIME_STATS;
|
|
10
|
-
// it returns 0 with a comment (honest limitation).
|
|
4
|
+
// sleep/now/now_us/busy_wait_us lower directly to kernel calls. Periodic or
|
|
5
|
+
// deferred work is NOT a timing op: it is a Thread (k_thread) or a Counter
|
|
6
|
+
// (hardware timer), each with its own lowering.
|
|
11
7
|
// ---------------------------------------------------------------------------
|
|
12
8
|
/**
|
|
13
9
|
* Resolve a HAL timing.* op to Zephyr C++.
|
|
@@ -16,46 +12,24 @@
|
|
|
16
12
|
export function lowerTiming(op) {
|
|
17
13
|
const o = op;
|
|
18
14
|
switch (op.operation) {
|
|
19
|
-
case 'timing.
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
15
|
+
case 'timing.sleep':
|
|
16
|
+
// Time.sleep — yielding ms sleep (k_msleep), delay()'s replacement.
|
|
17
|
+
return { code: `k_msleep(${o.ms});` };
|
|
18
|
+
case 'timing.now':
|
|
19
|
+
// Time.now — ms since boot as a double (k_uptime_get is int64_t ms).
|
|
20
|
+
return { expression: 'static_cast<double>(k_uptime_get())' };
|
|
21
|
+
case 'timing.now_us':
|
|
22
|
+
// Time.nowUs — µs since boot, uptime-derived for EVERY board: the
|
|
23
|
+
// cycle-counter form (k_cyc_to_us_floor64(k_cycle_get_64())) reads a
|
|
24
|
+
// constant on SoCs without a free-running 64-bit counter, so the one
|
|
25
|
+
// uniform expression that is monotonic and advancing everywhere is
|
|
26
|
+
// the kernel uptime scaled to µs. Resolution is therefore the uptime
|
|
27
|
+
// tick (millisecond), uniformly. A double holds µs exactly for ~285
|
|
28
|
+
// years.
|
|
29
|
+
return { expression: 'static_cast<double>(k_uptime_get() * 1000)' };
|
|
30
|
+
case 'timing.busy_wait_us':
|
|
31
|
+
// Time.busyWaitUs — spin, no yield; delayMicroseconds's honest name.
|
|
28
32
|
return { code: `k_busy_wait(${o.us});` };
|
|
29
|
-
}
|
|
30
|
-
case 'timing.millis':
|
|
31
|
-
// k_uptime_get_32() returns int64_t milliseconds since boot. Cast to the
|
|
32
|
-
// Arduino-millis() return type (uint32_t) so overflow wraps identically.
|
|
33
|
-
return { expression: 'static_cast<uint32_t>(k_uptime_get_32())' };
|
|
34
|
-
case 'timing.micros': {
|
|
35
|
-
// Convert hardware cycles to microseconds. k_cycle_get_32 + the cycles/sec
|
|
36
|
-
// macro (note: sys_clock_hw_cycles_per_sec is a MACRO — needs parens).
|
|
37
|
-
return {
|
|
38
|
-
expression: '(uint32_t)(((uint64_t)k_cycle_get_32() * 1000000ULL) / sys_clock_hw_cycles_per_sec())',
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
case 'timing.free_heap':
|
|
42
|
-
// No portable free-heap query without CONFIG_SYS_HEAP_RUNTIME_STATS.
|
|
43
|
-
// Return 0 with a comment so callers don't get a link error.
|
|
44
|
-
return { expression: '(0 /* free_heap: enable CONFIG_SYS_HEAP_RUNTIME_STATS for real value */)' };
|
|
45
|
-
case 'timing.set_interval':
|
|
46
|
-
// Backed by the timer_methods polyfill (k_timer + k_work). The handler is
|
|
47
|
-
// the resolved C++ callback name; timeout is the repeat period (ms).
|
|
48
|
-
// NOTE: these ops are declared 'polyfill' in the manifest, so setInterval
|
|
49
|
-
// is rewritten to __tc_setInterval before reaching the lowering — these
|
|
50
|
-
// cases are a fallback/defense and use the real op fields (handler/timeout).
|
|
51
|
-
return { expression: `__tc_setInterval(${o.handler}, ${o.timeout})` };
|
|
52
|
-
case 'timing.set_timeout':
|
|
53
|
-
// One-shot: k_timer with K_FOREVER period.
|
|
54
|
-
return { expression: `__tc_setTimeout(${o.handler}, ${o.timeout})` };
|
|
55
|
-
case 'timing.clear_interval':
|
|
56
|
-
return { code: `__tc_clearInterval(${o.id});` };
|
|
57
|
-
case 'timing.clear_timeout':
|
|
58
|
-
return { code: `__tc_clearTimeout(${o.id});` };
|
|
59
33
|
default:
|
|
60
34
|
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
61
35
|
`Open an issue or use rawCpp() to emit it manually.`);
|
package/dist/lowering/tone.js
CHANGED
|
@@ -11,7 +11,16 @@
|
|
|
11
11
|
// matching the synchronous Arduino tone() semantics. For non-blocking tone,
|
|
12
12
|
// a workqueue would be needed (deferred).
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
|
-
import { pwmDtAliasToken } from './pwm.js';
|
|
14
|
+
import { findPwmSpec, pwmDtAliasToken } from './pwm.js';
|
|
15
|
+
/**
|
|
16
|
+
* The PWM spec a tone op drives: the op's own pin when it maps to a spec
|
|
17
|
+
* (matrix chips — ESP32 LEDC — resolve any driven pad), else the board's
|
|
18
|
+
* first spec (the aliased LED channel, e.g. pwm-led0 on the XIAO).
|
|
19
|
+
*/
|
|
20
|
+
function toneSpec(chip, pin) {
|
|
21
|
+
return (typeof pin === 'number' ? findPwmSpec(chip, pin) : undefined)
|
|
22
|
+
?? chip.pwm?.specs[0];
|
|
23
|
+
}
|
|
15
24
|
/**
|
|
16
25
|
* Resolve a HAL tone.* op to Zephyr C++ via PWM.
|
|
17
26
|
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
|
@@ -23,14 +32,17 @@ export function lowerTone(op, chip) {
|
|
|
23
32
|
// period_ns = 1e9 / freq; pulse_ns = period / 2 (50% duty).
|
|
24
33
|
// Build a fake pwm.write would need the spec var directly; emit the
|
|
25
34
|
// pwm_set_dt call against the same spec var the PWM lowering uses.
|
|
26
|
-
const spec = chip.
|
|
35
|
+
const spec = toneSpec(chip, o.pin);
|
|
27
36
|
if (!spec) {
|
|
28
37
|
return { code: `/* tone.play(${o.frequency}): no PWM spec in chip descriptor */` };
|
|
29
38
|
}
|
|
30
39
|
const v = `__tc_pwm_${pwmDtAliasToken(spec)}`;
|
|
31
40
|
const freq = o.frequency;
|
|
32
41
|
const duration = o.duration;
|
|
33
|
-
|
|
42
|
+
// Braced: two tone ops in one statement-scope (e.g. tone(440).for(50)
|
|
43
|
+
// lowers tone.play + tone.play_for back-to-back) would otherwise
|
|
44
|
+
// redeclare __period.
|
|
45
|
+
const setTone = `{ uint32_t __period = (${freq} > 0) ? (1000000000ULL / static_cast<uint64_t>(${freq})) : 0; pwm_set_dt(&${v}, __period, __period / 2); }`;
|
|
34
46
|
if (duration !== undefined) {
|
|
35
47
|
// Blocking tone for the requested duration, then stop.
|
|
36
48
|
return { code: `${setTone} k_msleep(${duration}); pwm_set_pulse_dt(&${v}, 0);` };
|
|
@@ -38,7 +50,7 @@ export function lowerTone(op, chip) {
|
|
|
38
50
|
return { code: setTone };
|
|
39
51
|
}
|
|
40
52
|
case 'tone.stop': {
|
|
41
|
-
const spec = chip.
|
|
53
|
+
const spec = toneSpec(chip, o.pin);
|
|
42
54
|
if (!spec)
|
|
43
55
|
return { code: `/* tone.stop: no PWM spec */` };
|
|
44
56
|
const v = `__tc_pwm_${pwmDtAliasToken(spec)}`;
|
package/dist/lowering/uart.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
|
5
5
|
* when the program uses UART.
|
|
6
6
|
*/
|
|
7
7
|
export declare function uartInitLines(chip: ZephyrChipDescriptor, controllerIndex: number): string[];
|
|
8
|
+
/** Render a string/number/boolean value to a per-byte poll_out loop via the
|
|
9
|
+
* __tc_dev_put overloads (the const char* overload streams a string; the
|
|
10
|
+
* double overload formats a number with __tc_fmt_num_buf). The value passes
|
|
11
|
+
* through verbatim — C++ overload resolution picks the right overload for a
|
|
12
|
+
* string literal, a number literal, or a runtime variable of either type. */
|
|
13
|
+
export declare function renderWrite(dev: string, value: string, newline: boolean): string;
|
|
8
14
|
/**
|
|
9
15
|
* Resolve a HAL uart.* op to Zephyr C++.
|
|
10
16
|
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
|
@@ -13,3 +19,10 @@ export declare function lowerUart(op: HALOpIR): {
|
|
|
13
19
|
code?: string;
|
|
14
20
|
expression?: string;
|
|
15
21
|
};
|
|
22
|
+
/** The per-port RX ring state: a construction-sized static buffer, free-running
|
|
23
|
+
* head/tail counters (available = head - tail, no wrap ambiguity), and the
|
|
24
|
+
* ISR that drains the FIFO. A byte arriving with a full ring is dropped —
|
|
25
|
+
* the embedded-honest answer (no unbounded buffering). Emitted only for
|
|
26
|
+
* ports whose uart.rx_* ops were emitted (collectUartRings), so an unused
|
|
27
|
+
* ISR never trips -Wunused-function. */
|
|
28
|
+
export declare function uartRingStateLines(index: number, ring: number): string[];
|
package/dist/lowering/uart.js
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// UART lowering — uart_poll_out / uart_poll_in per-byte
|
|
3
3
|
//
|
|
4
4
|
// Zephyr's UART API is byte-oriented (uart_poll_out / uart_poll_in). The HAL's
|
|
5
|
-
// uart.print/println/write lower to per-byte poll_out loops
|
|
6
|
-
//
|
|
7
|
-
// for a specific UART port (the XIAO exposes uart0 on D6/D7).
|
|
5
|
+
// uart.print/println/write lower to per-byte poll_out loops; these uart.* ops
|
|
6
|
+
// are for a specific UART port (the XIAO exposes uart0 on D6/D7).
|
|
8
7
|
//
|
|
9
8
|
// The device resolves at compile time via DEVICE_DT_GET(DT_NODELABEL(uart0)).
|
|
10
9
|
// uart.begin configures the baud via uart_configure.
|
|
@@ -33,16 +32,14 @@ export function uartInitLines(chip, controllerIndex) {
|
|
|
33
32
|
'// CUTTLEFISH_UART_END',
|
|
34
33
|
];
|
|
35
34
|
}
|
|
36
|
-
/** Render a string
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
const body = `for (const char* __s = (const char*)(${value}); *__s; __s++) { uart_poll_out(${dev}, *__s); }`;
|
|
45
|
-
return newline ? `${body} uart_poll_out(${dev}, '\\n');` : body;
|
|
35
|
+
/** Render a string/number/boolean value to a per-byte poll_out loop via the
|
|
36
|
+
* __tc_dev_put overloads (the const char* overload streams a string; the
|
|
37
|
+
* double overload formats a number with __tc_fmt_num_buf). The value passes
|
|
38
|
+
* through verbatim — C++ overload resolution picks the right overload for a
|
|
39
|
+
* string literal, a number literal, or a runtime variable of either type. */
|
|
40
|
+
export function renderWrite(dev, value, newline) {
|
|
41
|
+
const put = `__tc_dev_put(${dev}, ${value});`;
|
|
42
|
+
return newline ? `${put} uart_poll_out(${dev}, '\\n');` : put;
|
|
46
43
|
}
|
|
47
44
|
/**
|
|
48
45
|
* Resolve a HAL uart.* op to Zephyr C++.
|
|
@@ -54,49 +51,73 @@ export function lowerUart(op) {
|
|
|
54
51
|
const p = prefix(idx);
|
|
55
52
|
const dev = `${p}_dev`;
|
|
56
53
|
switch (op.operation) {
|
|
57
|
-
case 'uart.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
case 'uart.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
const
|
|
54
|
+
case 'uart.poll_write': {
|
|
55
|
+
const pre = uartBaudGuard(p, o.baud);
|
|
56
|
+
return { code: `${pre} ${renderWrite(dev, o.data, false)}` };
|
|
57
|
+
}
|
|
58
|
+
// ── Thin UART RX (hal/uart-port.ts) — interrupt-drained ring ──────────
|
|
59
|
+
// The first receive call arms the driver IRQ (guarded); the ISR drains
|
|
60
|
+
// the FIFO into the construction-sized static ring (uartRingStateLines).
|
|
61
|
+
// available/peek/read are then pure ring arithmetic — the calls the poll
|
|
62
|
+
// API honestly could not support.
|
|
63
|
+
case 'uart.rx_arm':
|
|
64
|
+
case 'uart.rx_available':
|
|
65
|
+
case 'uart.rx_peek':
|
|
66
|
+
case 'uart.rx_read': {
|
|
67
|
+
const r = `__tc_uartrx${idx}`;
|
|
68
|
+
const arm = `{ static bool ${r}_armed = false; if (!${r}_armed) { uart_irq_callback_user_data_set(${dev}, ${r}_isr, NULL); uart_irq_rx_enable(${dev}); ${r}_armed = true; } }`;
|
|
69
|
+
if (op.operation === 'uart.rx_arm')
|
|
70
|
+
return { code: arm };
|
|
71
|
+
if (op.operation === 'uart.rx_available') {
|
|
72
|
+
return { expression: `({ ${arm} (${r}_head - ${r}_tail); })` };
|
|
73
|
+
}
|
|
74
|
+
if (op.operation === 'uart.rx_peek') {
|
|
75
|
+
return { expression: `({ ${arm} (${r}_tail < ${r}_head ? ${r}_buf[${r}_tail % ${o.ring}] : -1); })` };
|
|
76
|
+
}
|
|
72
77
|
return {
|
|
73
|
-
|
|
78
|
+
expression: `({ ${arm} (${r}_tail < ${r}_head ? ${r}_buf[(${r}_tail)++ % ${o.ring}] : -1); })`,
|
|
74
79
|
};
|
|
75
80
|
}
|
|
76
|
-
case 'uart.read':
|
|
77
|
-
// Non-blocking poll; returns the byte or -1 if none available.
|
|
78
|
-
return { expression: `({ unsigned char __b = 0; (uart_poll_in(${dev}, &__b) == 0) ? (int)__b : -1; })` };
|
|
79
|
-
case 'uart.peek':
|
|
80
|
-
// The poll API has no buffered-byte store, so there is no true peek.
|
|
81
|
-
// Return -1 (the Arduino "no data" sentinel) rather than blocking. This
|
|
82
|
-
// is an honest limitation of the byte-level poll driver; an interrupt- or
|
|
83
|
-
// DMA-backed UART driver would be needed for real peek semantics.
|
|
84
|
-
return { expression: '(-1)' };
|
|
85
|
-
case 'uart.available':
|
|
86
|
-
// The poll API reports only "at least one byte ready" via uart_poll_in's
|
|
87
|
-
// return code — it has no buffered-byte count, and probing with poll_in
|
|
88
|
-
// would DRAIN the very byte the caller next wants to read. So we cannot
|
|
89
|
-
// honestly report availability. Return 0 (Arduino's "no data" value)
|
|
90
|
-
// rather than the old truthy -1, so `if (uart.available())` loops don't
|
|
91
|
-
// spin forever on a false-positive. Callers should instead just call
|
|
92
|
-
// uart.read() directly (it returns -1 when no byte is ready). For true
|
|
93
|
-
// buffered availability, use an interrupt/DMA-backed UART driver.
|
|
94
|
-
return { expression: '(0)' };
|
|
95
|
-
case 'uart.flush':
|
|
96
|
-
// poll_out is synchronous (blocking until sent); flush is a no-op.
|
|
97
|
-
return { code: `(void)${dev};` };
|
|
98
81
|
default:
|
|
99
82
|
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
100
83
|
`Open an issue or use rawCpp() to emit it manually.`);
|
|
101
84
|
}
|
|
102
85
|
}
|
|
86
|
+
/** The thin-port baud preamble: apply the construction baud once via the
|
|
87
|
+
* shim's `_init` helper (uart_configure). Guarded per controller. */
|
|
88
|
+
function uartBaudGuard(p, baud) {
|
|
89
|
+
const n = typeof baud === 'number' ? baud : parseInt(String(baud ?? 0), 10);
|
|
90
|
+
if (!n || isNaN(n))
|
|
91
|
+
return '';
|
|
92
|
+
const done = `${p}_baud_done`;
|
|
93
|
+
return `{ static bool ${done} = false; if (!${done}) { ${p}_init(static_cast<uint32_t>(${n})); ${done} = true; } } `;
|
|
94
|
+
}
|
|
95
|
+
/** The per-port RX ring state: a construction-sized static buffer, free-running
|
|
96
|
+
* head/tail counters (available = head - tail, no wrap ambiguity), and the
|
|
97
|
+
* ISR that drains the FIFO. A byte arriving with a full ring is dropped —
|
|
98
|
+
* the embedded-honest answer (no unbounded buffering). Emitted only for
|
|
99
|
+
* ports whose uart.rx_* ops were emitted (collectUartRings), so an unused
|
|
100
|
+
* ISR never trips -Wunused-function. */
|
|
101
|
+
export function uartRingStateLines(index, ring) {
|
|
102
|
+
const r = `__tc_uartrx${index}`;
|
|
103
|
+
const dev = `__tc_uart${index}_dev`;
|
|
104
|
+
return [
|
|
105
|
+
'// CUTTLEFISH_UARTRX_BEGIN',
|
|
106
|
+
`static uint8_t ${r}_buf[${ring}];`,
|
|
107
|
+
`static volatile uint32_t ${r}_head = 0;`,
|
|
108
|
+
`static volatile uint32_t ${r}_tail = 0;`,
|
|
109
|
+
`static void ${r}_isr(const struct device* dev, void* user_data) {`,
|
|
110
|
+
` (void)user_data;`,
|
|
111
|
+
` uart_irq_update(dev);`,
|
|
112
|
+
` while (uart_irq_rx_ready(dev)) {`,
|
|
113
|
+
` uint8_t __c = 0;`,
|
|
114
|
+
` (void)uart_fifo_read(dev, &__c, 1);`,
|
|
115
|
+
` if ((${r}_head - ${r}_tail) < ${ring}) {`,
|
|
116
|
+
` ${r}_buf[${r}_head % ${ring}] = __c;`,
|
|
117
|
+
` ${r}_head++;`,
|
|
118
|
+
` }`,
|
|
119
|
+
` }`,
|
|
120
|
+
`}`,
|
|
121
|
+
'// CUTTLEFISH_UARTRX_END',
|
|
122
|
+
];
|
|
123
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Emit the shared USB device context (USBD_DEVICE_DEFINE + string/config
|
|
5
|
+
* descriptors + configuration + class registration + usbd_enable). Emitted
|
|
6
|
+
* ONCE per program when the chip declares `usb` — the next stack has one
|
|
7
|
+
* device context on the UDC controller, regardless of how many CDC instances
|
|
8
|
+
* hang off it.
|
|
9
|
+
*
|
|
10
|
+
* VID/PID default to the Zephyr-project testing IDs (0x2fe3/0x0001); a board
|
|
11
|
+
* overrides via `zephyr.usb.vid`/`zephyr.usb.pid`. Full-speed only — the
|
|
12
|
+
* boards this framework targets (STM32 OTG_FS, nRF USBD) are FS controllers,
|
|
13
|
+
* and the HS path would need a second USBD_CONFIGURATION_DEFINE.
|
|
14
|
+
*
|
|
15
|
+
* Symbol/API shapes verified against Zephyr 4.3:
|
|
16
|
+
* subsys/usb/device_next/Kconfig, include/zephyr/usb/usbd.h, and
|
|
17
|
+
* samples/subsys/usb/common/sample_usbd_init.c.
|
|
18
|
+
*/
|
|
19
|
+
export declare function usbdDeviceLines(chip: ZephyrChipDescriptor): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Emit the per-instance CDC device + init helper. Called from shimLines when
|
|
22
|
+
* the program uses USB and the chip declares `usb`; must be paired with one
|
|
23
|
+
* usbdDeviceLines() block. The init starts the device stack (guarded) and
|
|
24
|
+
* applies the line coding.
|
|
25
|
+
*/
|
|
26
|
+
export declare function usbInitLines(chip: ZephyrChipDescriptor, instanceIndex: number): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a HAL usb.* op to Zephyr C++.
|
|
29
|
+
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
|
30
|
+
*/
|
|
31
|
+
export declare function lowerUsb(op: HALOpIR, chip: ZephyrChipDescriptor): {
|
|
32
|
+
code?: string;
|
|
33
|
+
expression?: string;
|
|
34
|
+
};
|