@typecad/framework-zephyr 1.0.0-alpha.15 → 1.0.0-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -21
- package/README.md +49 -87
- package/dist/as-built.d.ts +2 -2
- package/dist/as-built.js +2 -2
- package/dist/boardgen.d.ts +1 -9
- package/dist/boardgen.js +228 -45
- package/dist/chips/types.d.ts +1 -1
- package/dist/debug-codegen.js +1 -1
- package/dist/display/touch-adapter.js +1 -1
- package/dist/display/ui-adapter.js +549 -549
- package/dist/doctor.js +4 -4
- package/dist/dt-config/custom-board.js +2 -2
- package/dist/dt-config/kconfig.js +18 -12
- package/dist/dt-config/overlay.d.ts +2 -2
- package/dist/dt-config/overlay.js +2 -2
- package/dist/framework.manifest.d.ts +3 -3
- package/dist/framework.manifest.js +10 -7
- package/dist/index.js +5 -5
- package/dist/licenses.d.ts +2 -2
- package/dist/licenses.js +8 -8
- package/dist/lowering/fs.js +1 -1
- package/dist/lowering/gpio.js +0 -32
- package/dist/lowering/http.js +264 -32
- package/dist/lowering/i2c.js +0 -8
- package/dist/lowering/interrupts.js +6 -0
- package/dist/lowering/mqtt.js +110 -8
- package/dist/lowering/usb.js +11 -11
- package/dist/lowering/wdt.js +2 -29
- package/dist/sdk/board-catalog-sync.d.ts +1 -3
- package/dist/sdk/board-catalog-sync.js +4 -10
- package/dist/strategy.d.ts +22 -44
- package/dist/strategy.js +152 -155
- package/dist/tmp-probe.d.ts +2 -0
- package/dist/tmp-probe.js +9 -0
- package/dist/toolchain/debug-config.d.ts +50 -90
- package/dist/toolchain/debug-config.js +241 -502
- package/dist/toolchain/env-check.d.ts +1 -3
- package/dist/toolchain/env-check.js +2 -7
- package/dist/toolchain/index.d.ts +65 -2
- package/dist/toolchain/index.js +432 -55
- package/dist/toolchain/runners.d.ts +16 -0
- package/dist/toolchain/runners.js +75 -0
- package/dist/toolchain/scaffold.d.ts +1 -1
- package/dist/toolchain/scaffold.js +12 -12
- package/dist/toolchain/west-discover.d.ts +6 -0
- package/dist/toolchain/west-discover.js +36 -13
- package/dist/toolchain/west-spawn.js +8 -2
- package/installer/README.md +328 -328
- package/installer/install.sh +2 -2
- package/installer/templates/project/.typecad/activate-zephyr.ps1 +1 -1
- package/installer/templates/project/.typecad/activate-zephyr.sh +1 -1
- package/installer/templates/project/.vscode/settings.json +1 -1
- package/installer/templates/project/README.md +2 -2
- package/package.json +5 -5
- package/src/as-built.ts +206 -206
- package/src/boardgen.ts +214 -48
- package/src/chips/types.ts +567 -567
- package/src/display/touch-adapter.ts +204 -204
- package/src/display/ui-adapter.ts +781 -781
- package/src/doctor.ts +4 -4
- package/src/dt-config/custom-board.ts +2 -2
- package/src/dt-config/kconfig.ts +18 -12
- package/src/dt-config/overlay.ts +1058 -1058
- package/src/framework.manifest.ts +538 -535
- package/src/index.ts +5 -5
- package/src/licenses.ts +425 -425
- package/src/lowering/fs.ts +135 -135
- package/src/lowering/gpio.ts +0 -33
- package/src/lowering/http.ts +264 -32
- package/src/lowering/i2c.ts +0 -9
- package/src/lowering/interrupts.ts +6 -0
- package/src/lowering/mqtt.ts +109 -8
- package/src/lowering/usb.ts +221 -221
- package/src/lowering/wdt.ts +2 -25
- package/src/sdk/board-catalog-sync.ts +4 -25
- package/src/strategy.ts +2265 -2309
- package/src/toolchain/debug-config.ts +264 -514
- package/src/toolchain/env-check.ts +279 -285
- package/src/toolchain/index.ts +1703 -1286
- package/src/toolchain/runners.ts +80 -0
- package/src/toolchain/scaffold.ts +296 -296
- package/src/toolchain/west-discover.ts +35 -13
- package/src/toolchain/west-spawn.ts +174 -168
- package/dist/async/timer-polyfill.d.ts +0 -10
- package/dist/async/timer-polyfill.js +0 -95
- package/dist/chips/board-overrides.d.ts +0 -7
- package/dist/chips/board-overrides.js +0 -11
- package/dist/chips/esp32.d.ts +0 -2
- package/dist/chips/esp32.js +0 -71
- package/dist/chips/esp32s3.d.ts +0 -2
- package/dist/chips/esp32s3.js +0 -103
- package/dist/chips/soc/.d.ts +0 -2
- package/dist/chips/soc/.js +0 -129
- package/dist/chips/soc/esp32.d.ts +0 -2
- package/dist/chips/soc/esp32.js +0 -120
- package/dist/chips/soc/esp32c3.d.ts +0 -2
- package/dist/chips/soc/esp32c3.js +0 -90
- package/dist/chips/soc/esp32c6.d.ts +0 -2
- package/dist/chips/soc/esp32c6.js +0 -109
- package/dist/chips/soc/esp32s3.d.ts +0 -2
- package/dist/chips/soc/esp32s3.js +0 -189
- package/dist/chips/soc/index.d.ts +0 -2
- package/dist/chips/soc/index.js +0 -23
- package/dist/chips/soc/nrf52840.d.ts +0 -2
- package/dist/chips/soc/nrf52840.js +0 -130
- package/dist/chips/soc/rp2040.d.ts +0 -2
- package/dist/chips/soc/rp2040.js +0 -141
- package/dist/chips/soc/rp2350a.d.ts +0 -2
- package/dist/chips/soc/rp2350a.js +0 -145
- package/dist/chips/soc/samd21g18a.d.ts +0 -2
- package/dist/chips/soc/samd21g18a.js +0 -143
- package/dist/chips/soc/stm32f411xe.d.ts +0 -2
- package/dist/chips/soc/stm32f411xe.js +0 -251
- package/dist/chips/xiao-ble.d.ts +0 -2
- package/dist/chips/xiao-ble.js +0 -100
- package/dist/lowering/power.d.ts +0 -9
- package/dist/lowering/power.js +0 -60
- package/dist/lowering/pulse.d.ts +0 -7
- package/dist/lowering/pulse.js +0 -51
- package/dist/lowering/tone.d.ts +0 -10
- package/dist/lowering/tone.js +0 -63
- package/dist/lowering/worker-backing.d.ts +0 -14
- package/dist/lowering/worker-backing.js +0 -79
- package/dist/lowering/worker.d.ts +0 -6
- package/dist/lowering/worker.js +0 -14
- package/dist/sdk/board-data.generated.d.ts +0 -2
- package/dist/sdk/board-data.generated.js +0 -4
- package/dist/sdk/catalog-walker.d.ts +0 -90
- package/dist/sdk/catalog-walker.js +0 -682
- package/dist/sdk/dts-reader.d.ts +0 -83
- package/dist/sdk/dts-reader.js +0 -596
- package/src/debug-codegen.ts +0 -207
package/src/lowering/usb.ts
CHANGED
|
@@ -1,221 +1,221 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// USB CDC-ACM serial lowering — Zephyr "next" USB device stack
|
|
3
|
-
//
|
|
4
|
-
// A CDC-ACM instance (cdc_acm_uart<N>, composed in the generated overlay as a
|
|
5
|
-
// child of the UDC controller node) registers as a UART-class device, so the
|
|
6
|
-
// lowering drives it with the plain uart_* API — the same per-byte poll_out
|
|
7
|
-
// loops as lowering/uart.ts. The differences from a hardware UART:
|
|
8
|
-
// - usb.begin must start the device stack (usbdDeviceLines' one-shot
|
|
9
|
-
// __tc_usbd_start) before the port carries traffic;
|
|
10
|
-
// - "connected" is a real query (DTR line ctrl), not a constant;
|
|
11
|
-
// - baud is a line-coding hint — recorded, but CDC has no wire baud.
|
|
12
|
-
//
|
|
13
|
-
// If the chip descriptor declares no `usb`, every op throws with a clear
|
|
14
|
-
// "board does not expose USB" message (the diagnostic contract the framework
|
|
15
|
-
// manifest's board-gated support entry promises).
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
|
|
18
|
-
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
19
|
-
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
20
|
-
import { parseControllerIndex } from './util.js';
|
|
21
|
-
import { renderWrite } from './uart.js';
|
|
22
|
-
|
|
23
|
-
/** Render a HAL field: pass through (already rendered by the resolver). */
|
|
24
|
-
function s(v: unknown): string {
|
|
25
|
-
return String(v);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** The C variable prefix for a CDC instance's state. */
|
|
29
|
-
function prefix(idx: number): string {
|
|
30
|
-
return `__tc_usb${idx}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Instance N resolves to the DT nodelabel the overlay generator emits. */
|
|
34
|
-
function nodeLabel(idx: number): string {
|
|
35
|
-
return `cdc_acm_uart${idx}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Emit the shared USB device context (USBD_DEVICE_DEFINE + string/config
|
|
40
|
-
* descriptors + configuration + class registration + usbd_enable). Emitted
|
|
41
|
-
* ONCE per program when the chip declares `usb` — the next stack has one
|
|
42
|
-
* device context on the UDC controller, regardless of how many CDC instances
|
|
43
|
-
* hang off it.
|
|
44
|
-
*
|
|
45
|
-
* VID/PID default to the Zephyr-project testing IDs (0x2fe3/0x0001); a board
|
|
46
|
-
* overrides via `zephyr.usb.vid`/`zephyr.usb.pid`. Full-speed only — the
|
|
47
|
-
* boards this framework targets (STM32 OTG_FS, nRF USBD) are FS controllers,
|
|
48
|
-
* and the HS path would need a second USBD_CONFIGURATION_DEFINE.
|
|
49
|
-
*
|
|
50
|
-
* Symbol/API shapes verified against Zephyr 4.3:
|
|
51
|
-
* subsys/usb/device_next/Kconfig, include/zephyr/usb/usbd.h, and
|
|
52
|
-
* samples/subsys/usb/common/sample_usbd_init.c.
|
|
53
|
-
*/
|
|
54
|
-
export function usbdDeviceLines(chip: ZephyrChipDescriptor): string[] {
|
|
55
|
-
if (!chip.usb) return [];
|
|
56
|
-
const vid = chip.usb.vid ?? '0x2fe3';
|
|
57
|
-
const pid = chip.usb.pid ?? '0x0001';
|
|
58
|
-
// 1200-baud touch-to-reset (BOSSA-bootloader boards): a usbd message
|
|
59
|
-
// callback that reboots into the bootloader when the host sets the CDC
|
|
60
|
-
// baud rate to 1200. Zephyr's CDC-ACM class publishes
|
|
61
|
-
// USBD_MSG_CDC_ACM_LINE_CODING on every host SET_LINE_CODING (the message
|
|
62
|
-
// carries the CDC uart device; its line coding is readable via
|
|
63
|
-
// uart_cfg_get). The reboot writes the bootloader's stay-resident magic
|
|
64
|
-
// (Arduino SAMD scheme: the bootloader checks the last word of SRAM after
|
|
65
|
-
// reset) so the board waits in the bootloader instead of booting the app.
|
|
66
|
-
const touch = chip.usb.touchReset;
|
|
67
|
-
const touchLines: string[] = touch
|
|
68
|
-
? [
|
|
69
|
-
`// 1200-baud touch-to-reset: reboot into the bootloader (flag 0x${touch.magic.toString(16)} @ 0x${touch.flagAddress.toString(16)}).`,
|
|
70
|
-
'static void __tc_usbd_msg_cb(struct usbd_context* ctx, const struct usbd_msg* msg) {',
|
|
71
|
-
' (void)ctx;',
|
|
72
|
-
' if (msg->type != USBD_MSG_CDC_ACM_LINE_CODING) { return; }',
|
|
73
|
-
' struct uart_config __tc_cfg;',
|
|
74
|
-
' if (uart_config_get(msg->dev, &__tc_cfg) == 0 && __tc_cfg.baudrate == 1200) {',
|
|
75
|
-
' // Delay the reboot so the host touch (which applies 1200 as part',
|
|
76
|
-
' // of opening the port) can close its handle first — resetting under',
|
|
77
|
-
' // an open host handle can wedge the Windows usbser driver and block',
|
|
78
|
-
' // re-enumeration until a physical replug.',
|
|
79
|
-
' k_msleep(250);',
|
|
80
|
-
` *(volatile uint32_t*)0x${touch.flagAddress.toString(16)} = 0x${touch.magic.toString(16)};`,
|
|
81
|
-
' NVIC_SystemReset();',
|
|
82
|
-
' }',
|
|
83
|
-
'}',
|
|
84
|
-
]
|
|
85
|
-
: [];
|
|
86
|
-
const touchRegister: string[] = touch
|
|
87
|
-
? [' usbd_msg_register_cb(&__tc_usbd, __tc_usbd_msg_cb);']
|
|
88
|
-
: [];
|
|
89
|
-
return [
|
|
90
|
-
'// CUTTLEFISH_USBD_BEGIN',
|
|
91
|
-
'USBD_DEVICE_DEFINE(__tc_usbd,',
|
|
92
|
-
` DEVICE_DT_GET(DT_NODELABEL(${chip.usb.controller})),`,
|
|
93
|
-
` ${vid}, ${pid});`,
|
|
94
|
-
'USBD_DESC_LANG_DEFINE(__tc_usbd_lang);',
|
|
95
|
-
'USBD_DESC_MANUFACTURER_DEFINE(__tc_usbd_mfr, "typecad");',
|
|
96
|
-
'USBD_DESC_PRODUCT_DEFINE(__tc_usbd_product, "
|
|
97
|
-
// Serial number from hwinfo: Windows keys USB devnodes without a serial
|
|
98
|
-
// on the physical port, so flash-cycle re-enumerations reuse stale
|
|
99
|
-
// port-keyed nodes (the "access denied" open wedge). Identity-based
|
|
100
|
-
// instance paths are stable across ports and never hit that pool.
|
|
101
|
-
'USBD_DESC_SERIAL_NUMBER_DEFINE(__tc_usbd_sn);',
|
|
102
|
-
'USBD_DESC_CONFIG_DEFINE(__tc_usbd_cfg_desc, "
|
|
103
|
-
'USBD_CONFIGURATION_DEFINE(__tc_usbd_cfg, 0, 250, &__tc_usbd_cfg_desc);',
|
|
104
|
-
'static bool __tc_usbd_started = false;',
|
|
105
|
-
...touchLines,
|
|
106
|
-
'static void __tc_usbd_start(void) {',
|
|
107
|
-
' if (__tc_usbd_started) { return; }',
|
|
108
|
-
' __tc_usbd_started = true;',
|
|
109
|
-
' int err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_lang);',
|
|
110
|
-
' if (err != 0) { printk("
|
|
111
|
-
' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_sn);',
|
|
112
|
-
' if (err != 0) { printk("
|
|
113
|
-
' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_mfr);',
|
|
114
|
-
' if (err != 0) { printk("
|
|
115
|
-
' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_product);',
|
|
116
|
-
' if (err != 0) { printk("
|
|
117
|
-
' err = usbd_add_configuration(&__tc_usbd, USBD_SPEED_FS, &__tc_usbd_cfg);',
|
|
118
|
-
' if (err != 0) { printk("
|
|
119
|
-
' err = usbd_register_all_classes(&__tc_usbd, USBD_SPEED_FS, 1, NULL);',
|
|
120
|
-
' if (err != 0) { printk("
|
|
121
|
-
// Registered before usbd_init so no line-coding event can be missed.
|
|
122
|
-
...touchRegister,
|
|
123
|
-
// usbd_init builds the descriptor tables from the registered
|
|
124
|
-
// configuration/classes and marks the context initialized — usbd_enable
|
|
125
|
-
// refuses with -EPERM ("not initialized") without it (sample_usbd_init.c
|
|
126
|
-
// calls it between setup and enable).
|
|
127
|
-
' err = usbd_init(&__tc_usbd);',
|
|
128
|
-
' if (err != 0) { printk("
|
|
129
|
-
// Boards with VBUS detection start on the VBUS event in the Zephyr
|
|
130
|
-
// sample, via a message callback we do not register; enabling
|
|
131
|
-
// unconditionally (and reporting the error) is the sample's own path
|
|
132
|
-
// for boards without detection (STM32 OTG_FS).
|
|
133
|
-
' err = usbd_enable(&__tc_usbd);',
|
|
134
|
-
' if (err != 0) { printk("
|
|
135
|
-
' printk("
|
|
136
|
-
'}',
|
|
137
|
-
'// CUTTLEFISH_USBD_END',
|
|
138
|
-
];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Emit the per-instance CDC device + init helper. Called from shimLines when
|
|
143
|
-
* the program uses USB and the chip declares `usb`; must be paired with one
|
|
144
|
-
* usbdDeviceLines() block. The init starts the device stack (guarded) and
|
|
145
|
-
* applies the line coding.
|
|
146
|
-
*/
|
|
147
|
-
export function usbInitLines(chip: ZephyrChipDescriptor, instanceIndex: number): string[] {
|
|
148
|
-
if (!chip.usb) return [];
|
|
149
|
-
const p = prefix(instanceIndex);
|
|
150
|
-
const dev = `${p}_dev`;
|
|
151
|
-
return [
|
|
152
|
-
'// CUTTLEFISH_USB_BEGIN',
|
|
153
|
-
`static const struct device* ${dev} = DEVICE_DT_GET(DT_NODELABEL(${nodeLabel(instanceIndex)}));`,
|
|
154
|
-
`static void ${p}_init(void) {`,
|
|
155
|
-
` __tc_usbd_start(); // CDC line coding is the host's business`,
|
|
156
|
-
`}`,
|
|
157
|
-
'// CUTTLEFISH_USB_END',
|
|
158
|
-
];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** Throw the board-capability diagnostic. Centralized so every op reads identically. */
|
|
162
|
-
function requireUsb(chip: ZephyrChipDescriptor, op: string): void {
|
|
163
|
-
if (!chip.usb) {
|
|
164
|
-
throw new Error(
|
|
165
|
-
`framework-zephyr: \`${op}\` needs a USB device stack, but this board's chip data declares no \`zephyr.usb\` ` +
|
|
166
|
-
`(controller + cdcInstances). Boards whose Zephyr DTS has no enabled UDC controller node ` +
|
|
167
|
-
`(zephyr_udc0) cannot provide USB CDC serial.`,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Resolve a HAL usb.* op to Zephyr C++.
|
|
174
|
-
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
|
175
|
-
*/
|
|
176
|
-
export function lowerUsb(op: HALOpIR, chip: ZephyrChipDescriptor): { code?: string; expression?: string } {
|
|
177
|
-
requireUsb(chip, op.operation);
|
|
178
|
-
const o = op as any;
|
|
179
|
-
const idx = parseControllerIndex(o.port);
|
|
180
|
-
if (chip.usb && idx >= chip.usb.cdcInstances) {
|
|
181
|
-
throw new Error(
|
|
182
|
-
`framework-zephyr: usb port "${o.port}" (instance ${idx}) is out of range — this board composes ` +
|
|
183
|
-
`${chip.usb.cdcInstances} CDC-ACM instance(s) (USB0..USB${chip.usb.cdcInstances - 1}).`,
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
const p = prefix(idx);
|
|
187
|
-
const dev = `${p}_dev`;
|
|
188
|
-
|
|
189
|
-
switch (op.operation) {
|
|
190
|
-
case 'usb.begin':
|
|
191
|
-
return { code: `${p}_init();` };
|
|
192
|
-
case 'usb.wait_ready': {
|
|
193
|
-
// Bounded DTR poll, sliced in the shim-side expression (no user-code
|
|
194
|
-
// busy loop). timeoutMs 0 = wait forever.
|
|
195
|
-
return { expression: `({ bool __ok = false; uint32_t __t = 0U; while (true) { uint32_t __dtr = 0U; if ((uart_line_ctrl_get(${dev}, UART_LINE_CTRL_DTR, &__dtr) == 0) && (__dtr != 0U)) { __ok = true; break; } if ((${s(o.timeoutMs ?? 0)}) != 0 && __t >= (uint32_t)(${s(o.timeoutMs ?? 0)})) { break; } k_msleep(10); __t += 10U; } __ok; })` };
|
|
196
|
-
}
|
|
197
|
-
case 'usb.end':
|
|
198
|
-
// CDC has no per-port disable short of tearing down the device stack
|
|
199
|
-
// (which would drop every other instance); make end observable but safe.
|
|
200
|
-
return { code: `(void)${dev};` };
|
|
201
|
-
case 'usb.print':
|
|
202
|
-
return { code: renderWrite(dev, o.value, false) };
|
|
203
|
-
case 'usb.println':
|
|
204
|
-
return { code: renderWrite(dev, o.value, true) };
|
|
205
|
-
case 'usb.read':
|
|
206
|
-
// Same poll semantics as uart.read: the byte or -1 if none available.
|
|
207
|
-
return { expression: `({ unsigned char __b = 0; (uart_poll_in(${dev}, &__b) == 0) ? (int)__b : -1; })` };
|
|
208
|
-
case 'usb.available':
|
|
209
|
-
// uart_poll_in reports only "one byte ready" and probing would drain it;
|
|
210
|
-
// same honest limitation as uart.available.
|
|
211
|
-
return { expression: '(0)' };
|
|
212
|
-
case 'usb.connected':
|
|
213
|
-
// DTR asserted = the host actually opened the port.
|
|
214
|
-
return { expression: `({ uint32_t __dtr = 0; (uart_line_ctrl_get(${dev}, UART_LINE_CTRL_DTR, &__dtr) == 0) && (__dtr != 0); })` };
|
|
215
|
-
default:
|
|
216
|
-
throw new Error(
|
|
217
|
-
`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
218
|
-
`Open an issue or use rawCpp() to emit it manually.`,
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// USB CDC-ACM serial lowering — Zephyr "next" USB device stack
|
|
3
|
+
//
|
|
4
|
+
// A CDC-ACM instance (cdc_acm_uart<N>, composed in the generated overlay as a
|
|
5
|
+
// child of the UDC controller node) registers as a UART-class device, so the
|
|
6
|
+
// lowering drives it with the plain uart_* API — the same per-byte poll_out
|
|
7
|
+
// loops as lowering/uart.ts. The differences from a hardware UART:
|
|
8
|
+
// - usb.begin must start the device stack (usbdDeviceLines' one-shot
|
|
9
|
+
// __tc_usbd_start) before the port carries traffic;
|
|
10
|
+
// - "connected" is a real query (DTR line ctrl), not a constant;
|
|
11
|
+
// - baud is a line-coding hint — recorded, but CDC has no wire baud.
|
|
12
|
+
//
|
|
13
|
+
// If the chip descriptor declares no `usb`, every op throws with a clear
|
|
14
|
+
// "board does not expose USB" message (the diagnostic contract the framework
|
|
15
|
+
// manifest's board-gated support entry promises).
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
19
|
+
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
20
|
+
import { parseControllerIndex } from './util.js';
|
|
21
|
+
import { renderWrite } from './uart.js';
|
|
22
|
+
|
|
23
|
+
/** Render a HAL field: pass through (already rendered by the resolver). */
|
|
24
|
+
function s(v: unknown): string {
|
|
25
|
+
return String(v);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The C variable prefix for a CDC instance's state. */
|
|
29
|
+
function prefix(idx: number): string {
|
|
30
|
+
return `__tc_usb${idx}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Instance N resolves to the DT nodelabel the overlay generator emits. */
|
|
34
|
+
function nodeLabel(idx: number): string {
|
|
35
|
+
return `cdc_acm_uart${idx}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Emit the shared USB device context (USBD_DEVICE_DEFINE + string/config
|
|
40
|
+
* descriptors + configuration + class registration + usbd_enable). Emitted
|
|
41
|
+
* ONCE per program when the chip declares `usb` — the next stack has one
|
|
42
|
+
* device context on the UDC controller, regardless of how many CDC instances
|
|
43
|
+
* hang off it.
|
|
44
|
+
*
|
|
45
|
+
* VID/PID default to the Zephyr-project testing IDs (0x2fe3/0x0001); a board
|
|
46
|
+
* overrides via `zephyr.usb.vid`/`zephyr.usb.pid`. Full-speed only — the
|
|
47
|
+
* boards this framework targets (STM32 OTG_FS, nRF USBD) are FS controllers,
|
|
48
|
+
* and the HS path would need a second USBD_CONFIGURATION_DEFINE.
|
|
49
|
+
*
|
|
50
|
+
* Symbol/API shapes verified against Zephyr 4.3:
|
|
51
|
+
* subsys/usb/device_next/Kconfig, include/zephyr/usb/usbd.h, and
|
|
52
|
+
* samples/subsys/usb/common/sample_usbd_init.c.
|
|
53
|
+
*/
|
|
54
|
+
export function usbdDeviceLines(chip: ZephyrChipDescriptor): string[] {
|
|
55
|
+
if (!chip.usb) return [];
|
|
56
|
+
const vid = chip.usb.vid ?? '0x2fe3';
|
|
57
|
+
const pid = chip.usb.pid ?? '0x0001';
|
|
58
|
+
// 1200-baud touch-to-reset (BOSSA-bootloader boards): a usbd message
|
|
59
|
+
// callback that reboots into the bootloader when the host sets the CDC
|
|
60
|
+
// baud rate to 1200. Zephyr's CDC-ACM class publishes
|
|
61
|
+
// USBD_MSG_CDC_ACM_LINE_CODING on every host SET_LINE_CODING (the message
|
|
62
|
+
// carries the CDC uart device; its line coding is readable via
|
|
63
|
+
// uart_cfg_get). The reboot writes the bootloader's stay-resident magic
|
|
64
|
+
// (Arduino SAMD scheme: the bootloader checks the last word of SRAM after
|
|
65
|
+
// reset) so the board waits in the bootloader instead of booting the app.
|
|
66
|
+
const touch = chip.usb.touchReset;
|
|
67
|
+
const touchLines: string[] = touch
|
|
68
|
+
? [
|
|
69
|
+
`// 1200-baud touch-to-reset: reboot into the bootloader (flag 0x${touch.magic.toString(16)} @ 0x${touch.flagAddress.toString(16)}).`,
|
|
70
|
+
'static void __tc_usbd_msg_cb(struct usbd_context* ctx, const struct usbd_msg* msg) {',
|
|
71
|
+
' (void)ctx;',
|
|
72
|
+
' if (msg->type != USBD_MSG_CDC_ACM_LINE_CODING) { return; }',
|
|
73
|
+
' struct uart_config __tc_cfg;',
|
|
74
|
+
' if (uart_config_get(msg->dev, &__tc_cfg) == 0 && __tc_cfg.baudrate == 1200) {',
|
|
75
|
+
' // Delay the reboot so the host touch (which applies 1200 as part',
|
|
76
|
+
' // of opening the port) can close its handle first — resetting under',
|
|
77
|
+
' // an open host handle can wedge the Windows usbser driver and block',
|
|
78
|
+
' // re-enumeration until a physical replug.',
|
|
79
|
+
' k_msleep(250);',
|
|
80
|
+
` *(volatile uint32_t*)0x${touch.flagAddress.toString(16)} = 0x${touch.magic.toString(16)};`,
|
|
81
|
+
' NVIC_SystemReset();',
|
|
82
|
+
' }',
|
|
83
|
+
'}',
|
|
84
|
+
]
|
|
85
|
+
: [];
|
|
86
|
+
const touchRegister: string[] = touch
|
|
87
|
+
? [' usbd_msg_register_cb(&__tc_usbd, __tc_usbd_msg_cb);']
|
|
88
|
+
: [];
|
|
89
|
+
return [
|
|
90
|
+
'// CUTTLEFISH_USBD_BEGIN',
|
|
91
|
+
'USBD_DEVICE_DEFINE(__tc_usbd,',
|
|
92
|
+
` DEVICE_DT_GET(DT_NODELABEL(${chip.usb.controller})),`,
|
|
93
|
+
` ${vid}, ${pid});`,
|
|
94
|
+
'USBD_DESC_LANG_DEFINE(__tc_usbd_lang);',
|
|
95
|
+
'USBD_DESC_MANUFACTURER_DEFINE(__tc_usbd_mfr, "typecad");',
|
|
96
|
+
'USBD_DESC_PRODUCT_DEFINE(__tc_usbd_product, "typecad-hal app");',
|
|
97
|
+
// Serial number from hwinfo: Windows keys USB devnodes without a serial
|
|
98
|
+
// on the physical port, so flash-cycle re-enumerations reuse stale
|
|
99
|
+
// port-keyed nodes (the "access denied" open wedge). Identity-based
|
|
100
|
+
// instance paths are stable across ports and never hit that pool.
|
|
101
|
+
'USBD_DESC_SERIAL_NUMBER_DEFINE(__tc_usbd_sn);',
|
|
102
|
+
'USBD_DESC_CONFIG_DEFINE(__tc_usbd_cfg_desc, "typecad-hal");',
|
|
103
|
+
'USBD_CONFIGURATION_DEFINE(__tc_usbd_cfg, 0, 250, &__tc_usbd_cfg_desc);',
|
|
104
|
+
'static bool __tc_usbd_started = false;',
|
|
105
|
+
...touchLines,
|
|
106
|
+
'static void __tc_usbd_start(void) {',
|
|
107
|
+
' if (__tc_usbd_started) { return; }',
|
|
108
|
+
' __tc_usbd_started = true;',
|
|
109
|
+
' int err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_lang);',
|
|
110
|
+
' if (err != 0) { printk("typecad-hal usb: lang descriptor failed: %d\\n", err); return; }',
|
|
111
|
+
' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_sn);',
|
|
112
|
+
' if (err != 0) { printk("typecad-hal usb: serial descriptor failed: %d\\n", err); return; }',
|
|
113
|
+
' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_mfr);',
|
|
114
|
+
' if (err != 0) { printk("typecad-hal usb: manufacturer descriptor failed: %d\\n", err); return; }',
|
|
115
|
+
' err = usbd_add_descriptor(&__tc_usbd, &__tc_usbd_product);',
|
|
116
|
+
' if (err != 0) { printk("typecad-hal usb: product descriptor failed: %d\\n", err); return; }',
|
|
117
|
+
' err = usbd_add_configuration(&__tc_usbd, USBD_SPEED_FS, &__tc_usbd_cfg);',
|
|
118
|
+
' if (err != 0) { printk("typecad-hal usb: add configuration failed: %d\\n", err); return; }',
|
|
119
|
+
' err = usbd_register_all_classes(&__tc_usbd, USBD_SPEED_FS, 1, NULL);',
|
|
120
|
+
' if (err != 0) { printk("typecad-hal usb: register classes failed: %d\\n", err); return; }',
|
|
121
|
+
// Registered before usbd_init so no line-coding event can be missed.
|
|
122
|
+
...touchRegister,
|
|
123
|
+
// usbd_init builds the descriptor tables from the registered
|
|
124
|
+
// configuration/classes and marks the context initialized — usbd_enable
|
|
125
|
+
// refuses with -EPERM ("not initialized") without it (sample_usbd_init.c
|
|
126
|
+
// calls it between setup and enable).
|
|
127
|
+
' err = usbd_init(&__tc_usbd);',
|
|
128
|
+
' if (err != 0) { printk("typecad-hal usb: init failed: %d\\n", err); return; }',
|
|
129
|
+
// Boards with VBUS detection start on the VBUS event in the Zephyr
|
|
130
|
+
// sample, via a message callback we do not register; enabling
|
|
131
|
+
// unconditionally (and reporting the error) is the sample's own path
|
|
132
|
+
// for boards without detection (STM32 OTG_FS).
|
|
133
|
+
' err = usbd_enable(&__tc_usbd);',
|
|
134
|
+
' if (err != 0) { printk("typecad-hal usb: enable failed: %d\\n", err); return; }',
|
|
135
|
+
' printk("typecad-hal usb: device enabled\\n");',
|
|
136
|
+
'}',
|
|
137
|
+
'// CUTTLEFISH_USBD_END',
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Emit the per-instance CDC device + init helper. Called from shimLines when
|
|
143
|
+
* the program uses USB and the chip declares `usb`; must be paired with one
|
|
144
|
+
* usbdDeviceLines() block. The init starts the device stack (guarded) and
|
|
145
|
+
* applies the line coding.
|
|
146
|
+
*/
|
|
147
|
+
export function usbInitLines(chip: ZephyrChipDescriptor, instanceIndex: number): string[] {
|
|
148
|
+
if (!chip.usb) return [];
|
|
149
|
+
const p = prefix(instanceIndex);
|
|
150
|
+
const dev = `${p}_dev`;
|
|
151
|
+
return [
|
|
152
|
+
'// CUTTLEFISH_USB_BEGIN',
|
|
153
|
+
`static const struct device* ${dev} = DEVICE_DT_GET(DT_NODELABEL(${nodeLabel(instanceIndex)}));`,
|
|
154
|
+
`static void ${p}_init(void) {`,
|
|
155
|
+
` __tc_usbd_start(); // CDC line coding is the host's business`,
|
|
156
|
+
`}`,
|
|
157
|
+
'// CUTTLEFISH_USB_END',
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Throw the board-capability diagnostic. Centralized so every op reads identically. */
|
|
162
|
+
function requireUsb(chip: ZephyrChipDescriptor, op: string): void {
|
|
163
|
+
if (!chip.usb) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`framework-zephyr: \`${op}\` needs a USB device stack, but this board's chip data declares no \`zephyr.usb\` ` +
|
|
166
|
+
`(controller + cdcInstances). Boards whose Zephyr DTS has no enabled UDC controller node ` +
|
|
167
|
+
`(zephyr_udc0) cannot provide USB CDC serial.`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Resolve a HAL usb.* op to Zephyr C++.
|
|
174
|
+
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
|
175
|
+
*/
|
|
176
|
+
export function lowerUsb(op: HALOpIR, chip: ZephyrChipDescriptor): { code?: string; expression?: string } {
|
|
177
|
+
requireUsb(chip, op.operation);
|
|
178
|
+
const o = op as any;
|
|
179
|
+
const idx = parseControllerIndex(o.port);
|
|
180
|
+
if (chip.usb && idx >= chip.usb.cdcInstances) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`framework-zephyr: usb port "${o.port}" (instance ${idx}) is out of range — this board composes ` +
|
|
183
|
+
`${chip.usb.cdcInstances} CDC-ACM instance(s) (USB0..USB${chip.usb.cdcInstances - 1}).`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
const p = prefix(idx);
|
|
187
|
+
const dev = `${p}_dev`;
|
|
188
|
+
|
|
189
|
+
switch (op.operation) {
|
|
190
|
+
case 'usb.begin':
|
|
191
|
+
return { code: `${p}_init();` };
|
|
192
|
+
case 'usb.wait_ready': {
|
|
193
|
+
// Bounded DTR poll, sliced in the shim-side expression (no user-code
|
|
194
|
+
// busy loop). timeoutMs 0 = wait forever.
|
|
195
|
+
return { expression: `({ bool __ok = false; uint32_t __t = 0U; while (true) { uint32_t __dtr = 0U; if ((uart_line_ctrl_get(${dev}, UART_LINE_CTRL_DTR, &__dtr) == 0) && (__dtr != 0U)) { __ok = true; break; } if ((${s(o.timeoutMs ?? 0)}) != 0 && __t >= (uint32_t)(${s(o.timeoutMs ?? 0)})) { break; } k_msleep(10); __t += 10U; } __ok; })` };
|
|
196
|
+
}
|
|
197
|
+
case 'usb.end':
|
|
198
|
+
// CDC has no per-port disable short of tearing down the device stack
|
|
199
|
+
// (which would drop every other instance); make end observable but safe.
|
|
200
|
+
return { code: `(void)${dev};` };
|
|
201
|
+
case 'usb.print':
|
|
202
|
+
return { code: renderWrite(dev, o.value, false) };
|
|
203
|
+
case 'usb.println':
|
|
204
|
+
return { code: renderWrite(dev, o.value, true) };
|
|
205
|
+
case 'usb.read':
|
|
206
|
+
// Same poll semantics as uart.read: the byte or -1 if none available.
|
|
207
|
+
return { expression: `({ unsigned char __b = 0; (uart_poll_in(${dev}, &__b) == 0) ? (int)__b : -1; })` };
|
|
208
|
+
case 'usb.available':
|
|
209
|
+
// uart_poll_in reports only "one byte ready" and probing would drain it;
|
|
210
|
+
// same honest limitation as uart.available.
|
|
211
|
+
return { expression: '(0)' };
|
|
212
|
+
case 'usb.connected':
|
|
213
|
+
// DTR asserted = the host actually opened the port.
|
|
214
|
+
return { expression: `({ uint32_t __dtr = 0; (uart_line_ctrl_get(${dev}, UART_LINE_CTRL_DTR, &__dtr) == 0) && (__dtr != 0); })` };
|
|
215
|
+
default:
|
|
216
|
+
throw new Error(
|
|
217
|
+
`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
218
|
+
`Open an issue or use rawCpp() to emit it manually.`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
package/src/lowering/wdt.ts
CHANGED
|
@@ -6,36 +6,13 @@
|
|
|
6
6
|
// wdt_install_timeout in a static var so wdt.reset can feed it.
|
|
7
7
|
//
|
|
8
8
|
// enable(timeout): wdt_install_timeout + wdt_setup. Zephyr expects the timeout
|
|
9
|
-
// in milliseconds (wdt_window.max)
|
|
10
|
-
//
|
|
9
|
+
// in milliseconds (wdt_window.max); the thin Watchdog (hal/watchdog.ts)
|
|
10
|
+
// construction already carries a plain timeoutMs number.
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
|
|
13
13
|
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
14
14
|
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
15
15
|
|
|
16
|
-
/** Parse a HAL wdt timeout ("250ms", WDTO_2S, or a bare number) to ms.
|
|
17
|
-
* Tolerates undefined (the manifest validator's probe sends a minimal op)
|
|
18
|
-
* and quote-wrapped strings (resolveSemanticArg can hand the literal's
|
|
19
|
-
* quoted source text through). */
|
|
20
|
-
function timeoutToMs(timeout: string | number | undefined): number {
|
|
21
|
-
if (typeof timeout === 'number') return timeout;
|
|
22
|
-
if (!timeout) return 1000; // default 1s when absent (e.g. the validator probe)
|
|
23
|
-
timeout = timeout.replace(/^['"]|['"]$/g, '');
|
|
24
|
-
// Arduino WDTO_* constants.
|
|
25
|
-
const wdto: Record<string, number> = {
|
|
26
|
-
WDTO_15MS: 15, WDTO_30MS: 30, WDTO_60MS: 60, WDTO_120MS: 120,
|
|
27
|
-
WDTO_250MS: 250, WDTO_500MS: 500, WDTO_1S: 1000, WDTO_2S: 2000,
|
|
28
|
-
WDTO_4S: 4000, WDTO_8S: 8000,
|
|
29
|
-
};
|
|
30
|
-
if (wdto[timeout]) return wdto[timeout];
|
|
31
|
-
const m = timeout.match(/^(\d+)\s*ms$/i);
|
|
32
|
-
if (m) return parseInt(m[1], 10);
|
|
33
|
-
const s = timeout.match(/^(\d+)\s*s$/i);
|
|
34
|
-
if (s) return parseInt(s[1], 10) * 1000;
|
|
35
|
-
const n = parseInt(timeout, 10);
|
|
36
|
-
return isNaN(n) ? 1000 : n;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
16
|
/**
|
|
40
17
|
* Emit the WDT device + channel state. Called from shimLines when the program
|
|
41
18
|
* uses the watchdog.
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// @typecad/cuttlefish/board-catalog — the create flow needs it before any
|
|
6
6
|
// framework is installed. This module adds the one thing the framework owns
|
|
7
7
|
// — the full spawn-based west discovery cascade for an explicit
|
|
8
|
-
// `
|
|
9
|
-
//
|
|
8
|
+
// `typecad-hal board sync`. Consumers pull the rest of the store's surface
|
|
9
|
+
// from @typecad/cuttlefish/board-catalog directly.
|
|
10
10
|
// ----------------------------------------------------------------------------
|
|
11
11
|
|
|
12
12
|
import path from 'node:path';
|
|
@@ -17,21 +17,8 @@ export type BoardCatalogSyncReport = catalog.BoardCatalogSyncReport;
|
|
|
17
17
|
export type BoardCatalogEnsureResult = catalog.BoardCatalogEnsureResult;
|
|
18
18
|
export type BoardCatalogOverlay = catalog.BoardCatalogOverlay;
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
isZephyrBase,
|
|
23
|
-
locateZephyrBaseCheap,
|
|
24
|
-
overlayPathFor,
|
|
25
|
-
readBoardCatalogOverlayFile,
|
|
26
|
-
loadBoardCatalogOverlay,
|
|
27
|
-
resetBoardCatalogOverlayCache,
|
|
28
|
-
isOverlayStale,
|
|
29
|
-
diffBoardCatalogs,
|
|
30
|
-
boardRecordFingerprint,
|
|
31
|
-
findBoardInCatalog,
|
|
32
|
-
activeBoardCatalog,
|
|
33
|
-
resetActiveBoardCatalog,
|
|
34
|
-
} = catalog;
|
|
20
|
+
// Re-exported for the package index's board-sync surface.
|
|
21
|
+
export const { loadBoardCatalogOverlay, resetBoardCatalogOverlayCache } = catalog;
|
|
35
22
|
|
|
36
23
|
/** Sync with the full west discovery cascade behind it (the CLI's path):
|
|
37
24
|
* falls back through spawn-based discovery when the cheap fs probes find
|
|
@@ -52,11 +39,3 @@ export function syncBoardCatalog(opts: { zephyrBase?: string } = {}): catalog.Bo
|
|
|
52
39
|
export function ensureFreshBoardCatalog(): catalog.BoardCatalogEnsureResult {
|
|
53
40
|
return catalog.ensureFreshBoardCatalog();
|
|
54
41
|
}
|
|
55
|
-
|
|
56
|
-
/** Locate a Zephyr tree by any means (spawn cascade included). */
|
|
57
|
-
export function locateZephyrBaseFull(): string | undefined {
|
|
58
|
-
const install = discoverWest();
|
|
59
|
-
return install?.zephyrBase && catalog.isZephyrBase(install.zephyrBase)
|
|
60
|
-
? path.resolve(install.zephyrBase)
|
|
61
|
-
: undefined;
|
|
62
|
-
}
|