@typecad/framework-zephyr 1.0.0-alpha.18 → 1.0.0-alpha.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit.d.ts +111 -0
- package/dist/audit.js +416 -0
- package/dist/boardgen.js +63 -5
- package/dist/chips/resolve.js +20 -0
- package/dist/chips/types.d.ts +21 -0
- package/dist/display/bindings.d.ts +55 -0
- package/dist/display/bindings.js +316 -0
- package/dist/display/gfx.d.ts +2 -3
- package/dist/display/gfx.js +166 -154
- package/dist/display/index.js +20 -1
- package/dist/display/mipi-dbi-host.d.ts +9 -0
- package/dist/display/mipi-dbi-host.js +174 -0
- package/dist/display/profiles.d.ts +109 -4
- package/dist/display/profiles.js +270 -7
- package/dist/display/touch-adapter.js +118 -48
- package/dist/display/ui-adapter-eink.d.ts +2 -0
- package/dist/display/ui-adapter-eink.js +4 -0
- package/dist/display/ui-adapter-gray.d.ts +8 -0
- package/dist/display/ui-adapter-gray.js +170 -0
- package/dist/display/ui-adapter-mono.d.ts +13 -0
- package/dist/display/ui-adapter-mono.js +230 -0
- package/dist/display/ui-adapter-native.d.ts +10 -0
- package/dist/display/ui-adapter-native.js +295 -0
- package/dist/display/ui-adapter-shared.d.ts +11 -0
- package/dist/display/ui-adapter-shared.js +122 -0
- package/dist/display/ui-adapter.js +53 -101
- package/dist/dt-config/kconfig.d.ts +62 -1
- package/dist/dt-config/kconfig.js +123 -26
- package/dist/dt-config/overlay.d.ts +13 -0
- package/dist/dt-config/overlay.js +431 -16
- package/dist/framework.manifest.d.ts +9 -3
- package/dist/framework.manifest.js +123 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -1
- package/dist/licenses.js +5 -84
- package/dist/lowering/can.d.ts +25 -0
- package/dist/lowering/can.js +97 -0
- package/dist/lowering/clock.d.ts +17 -0
- package/dist/lowering/clock.js +58 -0
- package/dist/lowering/hid.d.ts +27 -0
- package/dist/lowering/hid.js +244 -0
- package/dist/lowering/i2c.d.ts +8 -0
- package/dist/lowering/i2c.js +140 -0
- package/dist/lowering/i2s.d.ts +27 -0
- package/dist/lowering/i2s.js +98 -0
- package/dist/lowering/index.d.ts +9 -1
- package/dist/lowering/index.js +25 -1
- package/dist/lowering/matrix.d.ts +15 -0
- package/dist/lowering/matrix.js +63 -0
- package/dist/lowering/power.d.ts +10 -0
- package/dist/lowering/power.js +35 -0
- package/dist/lowering/pwm.js +25 -0
- package/dist/lowering/sensor.d.ts +2 -2
- package/dist/lowering/sensor.js +8 -4
- package/dist/lowering/strip.d.ts +16 -0
- package/dist/lowering/strip.js +70 -0
- package/dist/lowering/thread.js +5 -1
- package/dist/lowering/trace.d.ts +44 -0
- package/dist/lowering/trace.js +239 -0
- package/dist/lowering/uart.js +6 -1
- package/dist/lowering/usb.d.ts +3 -1
- package/dist/lowering/usb.js +6 -3
- package/dist/sbom.d.ts +181 -0
- package/dist/sbom.js +901 -0
- package/dist/strategy.d.ts +60 -2
- package/dist/strategy.js +485 -25
- package/dist/toolchain/index.d.ts +12 -1
- package/dist/toolchain/index.js +115 -27
- package/dist/toolchain/scaffold.d.ts +4 -1
- package/dist/toolchain/scaffold.js +67 -3
- package/dist/west-inventory.d.ts +25 -0
- package/dist/west-inventory.js +97 -0
- package/package.json +3 -3
- package/src/audit.ts +529 -0
- package/src/boardgen.ts +54 -5
- package/src/chips/resolve.ts +21 -0
- package/src/chips/types.ts +576 -567
- package/src/display/bindings.ts +347 -0
- package/src/display/gfx.ts +318 -306
- package/src/display/index.ts +87 -70
- package/src/display/mipi-dbi-host.ts +183 -0
- package/src/display/profiles.ts +458 -139
- package/src/display/touch-adapter.ts +274 -204
- package/src/display/ui-adapter-eink.ts +13 -0
- package/src/display/ui-adapter-gray.ts +178 -0
- package/src/display/ui-adapter-mono.ts +238 -0
- package/src/display/ui-adapter-native.ts +304 -0
- package/src/display/ui-adapter-shared.ts +125 -0
- package/src/display/ui-adapter.ts +51 -100
- package/src/dt-config/kconfig.ts +647 -511
- package/src/dt-config/overlay.ts +433 -16
- package/src/framework.manifest.ts +131 -10
- package/src/index.ts +11 -1
- package/src/licenses.ts +5 -84
- package/src/lowering/can.ts +140 -0
- package/src/lowering/clock.ts +91 -0
- package/src/lowering/hid.ts +261 -0
- package/src/lowering/i2c.ts +146 -0
- package/src/lowering/i2s.ts +143 -0
- package/src/lowering/index.ts +18 -1
- package/src/lowering/matrix.ts +70 -0
- package/src/lowering/power.ts +41 -0
- package/src/lowering/pwm.ts +192 -167
- package/src/lowering/sensor.ts +159 -155
- package/src/lowering/strip.ts +81 -0
- package/src/lowering/thread.ts +5 -1
- package/src/lowering/trace.ts +270 -0
- package/src/lowering/uart.ts +6 -1
- package/src/lowering/usb.ts +224 -221
- package/src/sbom.ts +1117 -0
- package/src/strategy.ts +440 -25
- package/src/toolchain/index.ts +117 -28
- package/src/toolchain/scaffold.ts +62 -3
- package/src/west-inventory.ts +102 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// HID lowering — USB keyboard/mouse over the Zephyr "next" device stack
|
|
3
|
+
//
|
|
4
|
+
// One HID interface per program (Zephyr's zephyr,hid-device node — a v1
|
|
5
|
+
// ceiling like the nRF PWM matrix: Keyboard OR Mouse, not both). The shim
|
|
6
|
+
// block declares the interface device + the boot report buffer + the report
|
|
7
|
+
// descriptor macro; the overlay generator synthesizes the DT node with the
|
|
8
|
+
// protocol the program uses (key verbs → keyboard, mouse verbs → mouse).
|
|
9
|
+
// Key/button arguments arrive as KEY.*/MOUSE.* token text (or runtime
|
|
10
|
+
// expressions) — the token map below translates name-for-name onto Zephyr's
|
|
11
|
+
// HID_KEY_* / button macros, with a build error naming the valid spellings.
|
|
12
|
+
// ----------------------------------------------------------------------------
|
|
13
|
+
/** KEY.* token → Zephyr usage macro. Digits and the four modifiers are the
|
|
14
|
+
* spellings that differ; everything else maps name-for-name. */
|
|
15
|
+
const KEY_TOKENS = {
|
|
16
|
+
A: 'HID_KEY_A', B: 'HID_KEY_B', C: 'HID_KEY_C', D: 'HID_KEY_D', E: 'HID_KEY_E',
|
|
17
|
+
F: 'HID_KEY_F', G: 'HID_KEY_G', H: 'HID_KEY_H', I: 'HID_KEY_I', J: 'HID_KEY_J',
|
|
18
|
+
K: 'HID_KEY_K', L: 'HID_KEY_L', M: 'HID_KEY_M', N: 'HID_KEY_N', O: 'HID_KEY_O',
|
|
19
|
+
P: 'HID_KEY_P', Q: 'HID_KEY_Q', R: 'HID_KEY_R', S: 'HID_KEY_S', T: 'HID_KEY_T',
|
|
20
|
+
U: 'HID_KEY_U', V: 'HID_KEY_V', W: 'HID_KEY_W', X: 'HID_KEY_X', Y: 'HID_KEY_Y',
|
|
21
|
+
Z: 'HID_KEY_Z',
|
|
22
|
+
N1: 'HID_KEY_1', N2: 'HID_KEY_2', N3: 'HID_KEY_3', N4: 'HID_KEY_4', N5: 'HID_KEY_5',
|
|
23
|
+
N6: 'HID_KEY_6', N7: 'HID_KEY_7', N8: 'HID_KEY_8', N9: 'HID_KEY_9', N0: 'HID_KEY_0',
|
|
24
|
+
ENTER: 'HID_KEY_ENTER', ESC: 'HID_KEY_ESC', BACKSPACE: 'HID_KEY_BACKSPACE',
|
|
25
|
+
TAB: 'HID_KEY_TAB', SPACE: 'HID_KEY_SPACE',
|
|
26
|
+
MINUS: 'HID_KEY_MINUS', EQUAL: 'HID_KEY_EQUAL',
|
|
27
|
+
LEFTBRACE: 'HID_KEY_LEFTBRACE', RIGHTBRACE: 'HID_KEY_RIGHTBRACE',
|
|
28
|
+
BACKSLASH: 'HID_KEY_BACKSLASH', SEMICOLON: 'HID_KEY_SEMICOLON',
|
|
29
|
+
APOSTROPHE: 'HID_KEY_APOSTROPHE', GRAVE: 'HID_KEY_GRAVE',
|
|
30
|
+
COMMA: 'HID_KEY_COMMA', DOT: 'HID_KEY_DOT', SLASH: 'HID_KEY_SLASH',
|
|
31
|
+
CAPSLOCK: 'HID_KEY_CAPSLOCK',
|
|
32
|
+
F1: 'HID_KEY_F1', F2: 'HID_KEY_F2', F3: 'HID_KEY_F3', F4: 'HID_KEY_F4',
|
|
33
|
+
F5: 'HID_KEY_F5', F6: 'HID_KEY_F6', F7: 'HID_KEY_F7', F8: 'HID_KEY_F8',
|
|
34
|
+
F9: 'HID_KEY_F9', F10: 'HID_KEY_F10', F11: 'HID_KEY_F11', F12: 'HID_KEY_F12',
|
|
35
|
+
// Zephyr names usage 70 HID_KEY_SYSRQ (the /* PRINTSCREEN */ comment is
|
|
36
|
+
// its only mention of the name) — the HAL token keeps the datasheet word.
|
|
37
|
+
PRINTSCREEN: 'HID_KEY_SYSRQ', SCROLLLOCK: 'HID_KEY_SCROLLLOCK',
|
|
38
|
+
PAUSE: 'HID_KEY_PAUSE', INSERT: 'HID_KEY_INSERT', HOME: 'HID_KEY_HOME',
|
|
39
|
+
PAGEUP: 'HID_KEY_PAGEUP', DEL: 'HID_KEY_DELETE', END: 'HID_KEY_END',
|
|
40
|
+
PAGEDOWN: 'HID_KEY_PAGEDOWN', RIGHT: 'HID_KEY_RIGHT', LEFT: 'HID_KEY_LEFT',
|
|
41
|
+
DOWN: 'HID_KEY_DOWN', UP: 'HID_KEY_UP',
|
|
42
|
+
};
|
|
43
|
+
/** Modifier tokens — a separate bitmask enum in Zephyr (usage codes start
|
|
44
|
+
* at 4, which collides with modifier bits, so the two kinds CANNOT share
|
|
45
|
+
* one runtime branch: the lowering emits different code per kind).
|
|
46
|
+
* Exported for the hid.h drift-guard test (every mapped macro must exist
|
|
47
|
+
* in Zephyr's header). */
|
|
48
|
+
export const KEY_MODIFIER_TOKENS = {
|
|
49
|
+
CTRL: 'HID_KBD_MODIFIER_LEFT_CTRL',
|
|
50
|
+
SHIFT: 'HID_KBD_MODIFIER_LEFT_SHIFT',
|
|
51
|
+
ALT: 'HID_KBD_MODIFIER_LEFT_ALT',
|
|
52
|
+
// HID usage tables call the Windows/Apple/GUI key "UI" — Zephyr follows.
|
|
53
|
+
GUI: 'HID_KBD_MODIFIER_LEFT_UI',
|
|
54
|
+
};
|
|
55
|
+
/** Is this KEY.* token a modifier (bitmask) rather than a usage code? */
|
|
56
|
+
export function isHidModifierToken(token) {
|
|
57
|
+
return token.startsWith('KEY.') && KEY_MODIFIER_TOKENS[token.slice(4)] !== undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Map a modifier token to its Zephyr bitmask macro. */
|
|
60
|
+
function hidModifierMacro(token) {
|
|
61
|
+
return KEY_MODIFIER_TOKENS[token.slice(4)];
|
|
62
|
+
}
|
|
63
|
+
const MOUSE_TOKENS = {
|
|
64
|
+
LEFT: 'BIT(0)', RIGHT: 'BIT(1)', MIDDLE: 'BIT(2)',
|
|
65
|
+
};
|
|
66
|
+
/** Map a KEY or MOUSE token (or pass a runtime expression through) to C. */
|
|
67
|
+
export function hidTokenToMacro(token, kind) {
|
|
68
|
+
const prefix = kind === 'key' ? 'KEY.' : 'MOUSE.';
|
|
69
|
+
if (!token.startsWith(prefix))
|
|
70
|
+
return token; // runtime expression
|
|
71
|
+
const name = token.slice(prefix.length);
|
|
72
|
+
const table = kind === 'key' ? KEY_TOKENS : MOUSE_TOKENS;
|
|
73
|
+
const mapped = table[name];
|
|
74
|
+
if (!mapped) {
|
|
75
|
+
const valid = Object.keys(table).map((k) => prefix + k).join(', ');
|
|
76
|
+
throw new Error(`Unknown ${kind} token '${token}'. Valid spellings: ${valid} (or a runtime number expression).`);
|
|
77
|
+
}
|
|
78
|
+
return mapped;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Emit the HID interface state for one protocol. Called from shimLines when
|
|
82
|
+
* the program uses hid.* — one device handle, one report buffer, the boot
|
|
83
|
+
* report descriptor, and the guarded register helper that also starts the
|
|
84
|
+
* shared usbd context (the same one CDC rides).
|
|
85
|
+
*/
|
|
86
|
+
export function hidInitLines(protocol) {
|
|
87
|
+
// The boot descriptors: keyboard takes no args; mouse takes its button
|
|
88
|
+
// count (3 — the MOUSE.LEFT/RIGHT/MIDDLE surface).
|
|
89
|
+
const desc = protocol === 'keyboard' ? 'HID_KEYBOARD_REPORT_DESC()' : 'HID_MOUSE_REPORT_DESC(3)';
|
|
90
|
+
// Driver-contract buffers: USB_STATIC_BUF_DEFINE gives pointer alignment
|
|
91
|
+
// (the DWC2 driver rejects unaligned IN buffers with "Buffer not aligned"
|
|
92
|
+
// and the completion still fires — every submit silently failed before
|
|
93
|
+
// this) and the nocache section on builds that require it.
|
|
94
|
+
const buf = protocol === 'keyboard'
|
|
95
|
+
? 'USB_STATIC_BUF_DEFINE(__tc_kb_report, 8);'
|
|
96
|
+
: 'USB_STATIC_BUF_DEFINE(__tc_mouse_report, 4);';
|
|
97
|
+
const pragmaWrap = protocol === 'mouse'
|
|
98
|
+
? [
|
|
99
|
+
'#pragma GCC diagnostic push',
|
|
100
|
+
'#pragma GCC diagnostic ignored "-Wnarrowing"',
|
|
101
|
+
]
|
|
102
|
+
: [];
|
|
103
|
+
const pragmaUnwrap = protocol === 'mouse' ? ['#pragma GCC diagnostic pop'] : [];
|
|
104
|
+
return [
|
|
105
|
+
'// CUTTLEFISH_HID_BEGIN',
|
|
106
|
+
'static const struct device* __tc_hid_dev = DEVICE_DT_GET_ONE(zephyr_hid_device);',
|
|
107
|
+
...pragmaWrap,
|
|
108
|
+
`static const uint8_t __tc_hid_desc[] = ${desc};`,
|
|
109
|
+
...pragmaUnwrap,
|
|
110
|
+
buf,
|
|
111
|
+
'static bool __tc_hid_registered = false;',
|
|
112
|
+
// The API contract: get_report is required for every HID device type
|
|
113
|
+
// (hid_dev_register rejects NULL ops with -EINVAL), set_protocol is
|
|
114
|
+
// required for boot-subclass devices (both descriptors here), and
|
|
115
|
+
// set_report when an output report is declared. Minimal stubs — the
|
|
116
|
+
// verbs never read host->device state in v1.
|
|
117
|
+
'static int __tc_hid_get_report(const struct device* dev, const uint8_t type, const uint8_t id, const uint16_t len, uint8_t* const buf) {',
|
|
118
|
+
' (void)dev; (void)type; (void)id; (void)len; (void)buf;',
|
|
119
|
+
' return -ENOTSUP;',
|
|
120
|
+
'}',
|
|
121
|
+
'static int __tc_hid_set_report(const struct device* dev, const uint8_t type, const uint8_t id, const uint16_t len, const uint8_t* const buf) {',
|
|
122
|
+
' (void)dev; (void)type; (void)id; (void)len; (void)buf;',
|
|
123
|
+
' return 0;',
|
|
124
|
+
'}',
|
|
125
|
+
'static void __tc_hid_set_protocol(const struct device* dev, const uint8_t protocol) {',
|
|
126
|
+
' (void)dev; (void)protocol;',
|
|
127
|
+
'}',
|
|
128
|
+
'static bool __tc_hid_ready = false;',
|
|
129
|
+
'static void __tc_hid_iface_ready(const struct device* dev, const bool ready) {',
|
|
130
|
+
' (void)dev;',
|
|
131
|
+
' __tc_hid_ready = ready;',
|
|
132
|
+
'}',
|
|
133
|
+
'static uint32_t __tc_hid_idle = 0;',
|
|
134
|
+
'static void __tc_hid_set_idle(const struct device* dev, const uint8_t id, const uint32_t duration) {',
|
|
135
|
+
' (void)dev; (void)id;',
|
|
136
|
+
' __tc_hid_idle = duration;',
|
|
137
|
+
'}',
|
|
138
|
+
'static uint32_t __tc_hid_get_idle(const struct device* dev, const uint8_t id) {',
|
|
139
|
+
' (void)dev; (void)id;',
|
|
140
|
+
' return __tc_hid_idle;',
|
|
141
|
+
'}',
|
|
142
|
+
'static void __tc_hid_output_report(const struct device* dev, const uint16_t len, const uint8_t* const buf) {',
|
|
143
|
+
' (void)dev; (void)len; (void)buf;',
|
|
144
|
+
'}',
|
|
145
|
+
'static const struct hid_device_ops __tc_hid_ops = {',
|
|
146
|
+
' .iface_ready = __tc_hid_iface_ready,',
|
|
147
|
+
' .get_report = __tc_hid_get_report,',
|
|
148
|
+
' .set_report = __tc_hid_set_report,',
|
|
149
|
+
' .set_idle = __tc_hid_set_idle,',
|
|
150
|
+
' .get_idle = __tc_hid_get_idle,',
|
|
151
|
+
' .set_protocol = __tc_hid_set_protocol,',
|
|
152
|
+
' .output_report = __tc_hid_output_report,',
|
|
153
|
+
'};',
|
|
154
|
+
'static void __tc_hid_register(void) {',
|
|
155
|
+
' if (__tc_hid_registered) { return; }',
|
|
156
|
+
' __tc_hid_registered = true;',
|
|
157
|
+
' int err = hid_device_register(__tc_hid_dev, __tc_hid_desc, sizeof(__tc_hid_desc), &__tc_hid_ops);',
|
|
158
|
+
' if (err != 0) { printk("typecad-hal hid: register failed: %d\\n", err); return; }',
|
|
159
|
+
' __tc_usbd_start();',
|
|
160
|
+
'}',
|
|
161
|
+
'static void __tc_hid_submit(const uint8_t* rep, uint16_t len) {',
|
|
162
|
+
' int err = hid_device_submit_report(__tc_hid_dev, len, rep);',
|
|
163
|
+
' // -EACCES is the normal pre-enumeration race (submit before the',
|
|
164
|
+
' // host configures the interface) — not worth a printk; every other',
|
|
165
|
+
' // errno (e.g. the alignment error that silently ate reports once)',
|
|
166
|
+
' // deserves to be loud.',
|
|
167
|
+
' if (err != 0 && err != -EACCES) { printk("typecad-hal hid: submit failed: %d\\n", err); }',
|
|
168
|
+
'}',
|
|
169
|
+
'// CUTTLEFISH_HID_END',
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Resolve a HAL hid.* op to Zephyr C++.
|
|
174
|
+
* Returns `{ code }` for statement ops.
|
|
175
|
+
*/
|
|
176
|
+
export function lowerHid(op, chip) {
|
|
177
|
+
if (!chip.usb) {
|
|
178
|
+
throw new Error(`framework-zephyr: \`${op.operation}\` needs a USB device stack, but this board's chip data declares no \`zephyr.usb\` ` +
|
|
179
|
+
`(controller + cdcInstances). Boards whose Zephyr DTS has no enabled UDC controller node ` +
|
|
180
|
+
`(zephyr_udc0) cannot provide USB HID.`);
|
|
181
|
+
}
|
|
182
|
+
const o = op;
|
|
183
|
+
switch (op.operation) {
|
|
184
|
+
case 'hid.kb_begin':
|
|
185
|
+
return { code: '__tc_hid_register();' };
|
|
186
|
+
case 'hid.kb_press': {
|
|
187
|
+
// Modifier tokens are compile-time bitmasks — a different emit than
|
|
188
|
+
// the usage-code slot logic (runtime expressions take the key path).
|
|
189
|
+
if (isHidModifierToken(String(o.key))) {
|
|
190
|
+
const mod = hidModifierMacro(String(o.key));
|
|
191
|
+
return {
|
|
192
|
+
code: `{ __tc_hid_register(); __tc_kb_report[0] = static_cast<uint8_t>(__tc_kb_report[0] | ${mod}); __tc_hid_submit(__tc_kb_report, 8U); }`,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const key = hidTokenToMacro(String(o.key), 'key');
|
|
196
|
+
return {
|
|
197
|
+
code: `{ __tc_hid_register(); uint8_t __tc_k = static_cast<uint8_t>(${key}); for (uint32_t __tc_slot = 2U; __tc_slot < 8U; ++__tc_slot) { if (__tc_kb_report[__tc_slot] == 0U) { __tc_kb_report[__tc_slot] = __tc_k; break; } } __tc_hid_submit(__tc_kb_report, 8U); }`,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
case 'hid.kb_release': {
|
|
201
|
+
if (isHidModifierToken(String(o.key))) {
|
|
202
|
+
const mod = hidModifierMacro(String(o.key));
|
|
203
|
+
return {
|
|
204
|
+
code: `{ __tc_hid_register(); __tc_kb_report[0] = static_cast<uint8_t>(__tc_kb_report[0] & static_cast<uint8_t>(~${mod})); __tc_hid_submit(__tc_kb_report, 8U); }`,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
const key = hidTokenToMacro(String(o.key), 'key');
|
|
208
|
+
return {
|
|
209
|
+
code: `{ __tc_hid_register(); uint8_t __tc_k = static_cast<uint8_t>(${key}); for (uint32_t __tc_slot = 2U; __tc_slot < 8U; ++__tc_slot) { if (__tc_kb_report[__tc_slot] == __tc_k) { __tc_kb_report[__tc_slot] = 0U; } } __tc_hid_submit(__tc_kb_report, 8U); }`,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
case 'hid.kb_release_all':
|
|
213
|
+
return {
|
|
214
|
+
code: `{ __tc_hid_register(); for (uint32_t __tc_slot = 0U; __tc_slot < 8U; ++__tc_slot) { __tc_kb_report[__tc_slot] = 0U; } __tc_hid_submit(__tc_kb_report, 8U); }`,
|
|
215
|
+
};
|
|
216
|
+
case 'hid.mouse_begin':
|
|
217
|
+
return { code: '__tc_hid_register();' };
|
|
218
|
+
case 'hid.mouse_move': {
|
|
219
|
+
return {
|
|
220
|
+
code: `{ __tc_hid_register(); int32_t __tc_mx = static_cast<int32_t>(${o.dx}); int32_t __tc_my = static_cast<int32_t>(${o.dy}); int32_t __tc_mw = static_cast<int32_t>(${o.wheel}); if (__tc_mx > 127) { __tc_mx = 127; } if (__tc_mx < -127) { __tc_mx = -127; } if (__tc_my > 127) { __tc_my = 127; } if (__tc_my < -127) { __tc_my = -127; } if (__tc_mw > 127) { __tc_mw = 127; } if (__tc_mw < -127) { __tc_mw = -127; } __tc_mouse_report[1] = static_cast<uint8_t>(__tc_mx); __tc_mouse_report[2] = static_cast<uint8_t>(__tc_my); __tc_mouse_report[3] = static_cast<uint8_t>(__tc_mw); __tc_hid_submit(__tc_mouse_report, 4U); }`,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
case 'hid.mouse_press':
|
|
224
|
+
case 'hid.mouse_release': {
|
|
225
|
+
const btn = hidTokenToMacro(String(o.button), 'button');
|
|
226
|
+
const opText = op.operation === 'hid.mouse_press' ? '|' : '&';
|
|
227
|
+
const mask = op.operation === 'hid.mouse_press'
|
|
228
|
+
? btn
|
|
229
|
+
: `static_cast<uint8_t>(~${btn})`;
|
|
230
|
+
return {
|
|
231
|
+
code: `{ __tc_hid_register(); __tc_mouse_report[0] = static_cast<uint8_t>(__tc_mouse_report[0] ${opText} ${mask}); __tc_hid_submit(__tc_mouse_report, 4U); }`,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
case 'hid.mouse_click': {
|
|
235
|
+
const btn = hidTokenToMacro(String(o.button), 'button');
|
|
236
|
+
return {
|
|
237
|
+
code: `{ __tc_hid_register(); __tc_mouse_report[0] = static_cast<uint8_t>(__tc_mouse_report[0] | ${btn}); __tc_hid_submit(__tc_mouse_report, 4U); __tc_mouse_report[0] = static_cast<uint8_t>(__tc_mouse_report[0] & static_cast<uint8_t>(~${btn})); __tc_hid_submit(__tc_mouse_report, 4U); }`,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
default:
|
|
241
|
+
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
242
|
+
`Open an issue or use rawCpp() to emit it manually.`);
|
|
243
|
+
}
|
|
244
|
+
}
|
package/dist/lowering/i2c.d.ts
CHANGED
|
@@ -5,6 +5,14 @@ import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
|
5
5
|
* controller the program uses (gated on ctx.analysis.usesI2C).
|
|
6
6
|
*/
|
|
7
7
|
export declare function i2cInitLines(chip: ZephyrChipDescriptor, controllerIndex: number): string[];
|
|
8
|
+
/**
|
|
9
|
+
* Emit one responder's state: receive ring (UART discipline — free-running
|
|
10
|
+
* head/tail, overflow-drop), response buffer with its served position, the
|
|
11
|
+
* two handler slots, the five i2c_target callbacks, and the config the ops'
|
|
12
|
+
* registration guard passes to i2c_target_register. Called from shimLines
|
|
13
|
+
* for each constructed responder (collectI2cResponders).
|
|
14
|
+
*/
|
|
15
|
+
export declare function i2cResponderStateLines(chip: ZephyrChipDescriptor, controllerIndex: number, address: number, rx: number, tx: number): string[];
|
|
8
16
|
/**
|
|
9
17
|
* Resolve a HAL i2c.* op to Zephyr C++.
|
|
10
18
|
* Returns `{ code }` for statement ops, `{ expression }` for value-returning ops.
|
package/dist/lowering/i2c.js
CHANGED
|
@@ -40,6 +40,106 @@ function speedForHz(hz) {
|
|
|
40
40
|
return 'I2C_SPEED_FAST_PLUS';
|
|
41
41
|
return 'I2C_SPEED_HIGH';
|
|
42
42
|
}
|
|
43
|
+
// ── I2C responder (hal/i2c-responder.ts) — this board as an I2C target ────
|
|
44
|
+
//
|
|
45
|
+
// One state block per constructed responder (bus + address), the i2c_target
|
|
46
|
+
// vocabulary verbatim: the five driver callbacks behind two user-facing ones
|
|
47
|
+
// (onReceive fires at STOP with the receive ring's new byte count; onRequest
|
|
48
|
+
// fires at read start — the write() refill point). Registration
|
|
49
|
+
// (i2c_target_register) is once-guarded at the first op, against the
|
|
50
|
+
// controller handle the master-side init block emitted (__tc_i2c<N>_dev).
|
|
51
|
+
/** The C symbol prefix for one responder's state. */
|
|
52
|
+
function respPrefix(idx, address) {
|
|
53
|
+
return `__tc_i2cresp${idx}_a${address.toString(16)}`;
|
|
54
|
+
}
|
|
55
|
+
/** The once-guarded registration every responder op carries — the UART arm
|
|
56
|
+
* discipline: the first emitted call registers the address, so the
|
|
57
|
+
* callbacks installed early in setup make it answer from boot. An
|
|
58
|
+
* unsupported controller prints loudly (the CAN policy: -ENOSYS from a
|
|
59
|
+
* driver with no target mode must not be a silent empty responder). */
|
|
60
|
+
function respRegisterGuard(p, idx) {
|
|
61
|
+
return `{ if (!${p}_registered) { ${p}_registered = true; int __tc_err = i2c_target_register(__tc_i2c${idx}_dev, &${p}_cfg); if (__tc_err != 0) { printk("typecad-hal i2c responder: target_register failed: %d\\n", __tc_err); } } } `;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Emit one responder's state: receive ring (UART discipline — free-running
|
|
65
|
+
* head/tail, overflow-drop), response buffer with its served position, the
|
|
66
|
+
* two handler slots, the five i2c_target callbacks, and the config the ops'
|
|
67
|
+
* registration guard passes to i2c_target_register. Called from shimLines
|
|
68
|
+
* for each constructed responder (collectI2cResponders).
|
|
69
|
+
*/
|
|
70
|
+
export function i2cResponderStateLines(chip, controllerIndex, address, rx, tx) {
|
|
71
|
+
const ctrl = chip.i2c?.controllers[controllerIndex];
|
|
72
|
+
if (!ctrl)
|
|
73
|
+
return [];
|
|
74
|
+
const p = respPrefix(controllerIndex, address);
|
|
75
|
+
return [
|
|
76
|
+
'// CUTTLEFISH_I2CRESP_BEGIN',
|
|
77
|
+
`static uint8_t ${p}_rx[${rx}];`,
|
|
78
|
+
`static volatile uint32_t ${p}_head = 0;`,
|
|
79
|
+
`static volatile uint32_t ${p}_tail = 0;`,
|
|
80
|
+
`static uint8_t ${p}_tx[${tx}];`,
|
|
81
|
+
`static volatile uint32_t ${p}_txlen = 0;`,
|
|
82
|
+
`static volatile uint32_t ${p}_txpos = 0;`,
|
|
83
|
+
`static void (*${p}_on_rx)(double) = NULL;`,
|
|
84
|
+
`static void (*${p}_on_rq)(void) = NULL;`,
|
|
85
|
+
// A controller write transaction: reset the ring, then take each byte.
|
|
86
|
+
// A byte arriving with a full ring is dropped — the embedded-honest
|
|
87
|
+
// answer (no unbounded buffering), same as the UART RX ring.
|
|
88
|
+
`static int ${p}_write_requested(struct i2c_target_config* cfg) {`,
|
|
89
|
+
' (void)cfg;',
|
|
90
|
+
` ${p}_head = 0;`,
|
|
91
|
+
` ${p}_tail = 0;`,
|
|
92
|
+
' return 0;',
|
|
93
|
+
'}',
|
|
94
|
+
`static int ${p}_write_received(struct i2c_target_config* cfg, uint8_t val) {`,
|
|
95
|
+
' (void)cfg;',
|
|
96
|
+
` if ((${p}_head - ${p}_tail) < ${rx}U) {`,
|
|
97
|
+
` ${p}_rx[${p}_head % ${rx}U] = val;`,
|
|
98
|
+
` ${p}_head++;`,
|
|
99
|
+
' }',
|
|
100
|
+
' return 0;',
|
|
101
|
+
'}',
|
|
102
|
+
// A controller read transaction: the user's refill point first, then the
|
|
103
|
+
// first byte. Past the buffer's end the controller reads 0xFF (the bus
|
|
104
|
+
// has no NACK for a too-long read; the value is the honest filler).
|
|
105
|
+
`static int ${p}_read_requested(struct i2c_target_config* cfg, uint8_t* val) {`,
|
|
106
|
+
' (void)cfg;',
|
|
107
|
+
` if (${p}_on_rq != NULL) { ${p}_on_rq(); }`,
|
|
108
|
+
` ${p}_txpos = 1U;`,
|
|
109
|
+
` *val = (${p}_txlen > 0U) ? ${p}_tx[0] : 0xFFU;`,
|
|
110
|
+
' return 0;',
|
|
111
|
+
'}',
|
|
112
|
+
`static int ${p}_read_processed(struct i2c_target_config* cfg, uint8_t* val) {`,
|
|
113
|
+
' (void)cfg;',
|
|
114
|
+
` *val = (${p}_txpos < ${p}_txlen) ? ${p}_tx[${p}_txpos] : 0xFFU;`,
|
|
115
|
+
` ${p}_txpos++;`,
|
|
116
|
+
' return 0;',
|
|
117
|
+
'}',
|
|
118
|
+
// End of transaction: announce a completed controller write with the
|
|
119
|
+
// count now waiting in the ring (the Wire onReceive(len) semantic).
|
|
120
|
+
`static int ${p}_stop(struct i2c_target_config* cfg) {`,
|
|
121
|
+
' (void)cfg;',
|
|
122
|
+
` if ((${p}_on_rx != NULL) && (${p}_head > ${p}_tail)) {`,
|
|
123
|
+
` ${p}_on_rx(static_cast<double>(${p}_head - ${p}_tail));`,
|
|
124
|
+
' }',
|
|
125
|
+
' return 0;',
|
|
126
|
+
'}',
|
|
127
|
+
`static const struct i2c_target_callbacks ${p}_cbs = {`,
|
|
128
|
+
` .write_requested = ${p}_write_requested,`,
|
|
129
|
+
` .read_requested = ${p}_read_requested,`,
|
|
130
|
+
` .write_received = ${p}_write_received,`,
|
|
131
|
+
` .read_processed = ${p}_read_processed,`,
|
|
132
|
+
` .stop = ${p}_stop,`,
|
|
133
|
+
'};',
|
|
134
|
+
`static struct i2c_target_config ${p}_cfg = {`,
|
|
135
|
+
' .flags = 0U,',
|
|
136
|
+
` .address = ${address}U,`,
|
|
137
|
+
` .callbacks = &${p}_cbs,`,
|
|
138
|
+
'};',
|
|
139
|
+
`static bool ${p}_registered = false;`,
|
|
140
|
+
'// CUTTLEFISH_I2CRESP_END',
|
|
141
|
+
];
|
|
142
|
+
}
|
|
43
143
|
/** The thin-device bus-speed preamble: apply the construction hz once
|
|
44
144
|
* (i2c_configure with Zephyr's I2C_SPEED_SET tier mapping), or '' when the
|
|
45
145
|
* target constructed without hz. Guarded per controller, so the cost after
|
|
@@ -91,6 +191,46 @@ export function lowerI2c(op, chip) {
|
|
|
91
191
|
const arr = `static const uint8_t __tc_i2cw[] = { ${bytes.join(', ')} };`;
|
|
92
192
|
return { code: `${pre} ${arr} (void)i2c_write(${p}_dev, __tc_i2cw, sizeof(__tc_i2cw), static_cast<uint16_t>(${o.address}));` };
|
|
93
193
|
}
|
|
194
|
+
// ── I2C responder (hal/i2c-responder.ts) — target-mode ops ───────────
|
|
195
|
+
// Every op carries the once-guarded registration; the state it touches
|
|
196
|
+
// is the per-responder block i2cResponderStateLines emitted.
|
|
197
|
+
case 'i2c.resp_on_receive': {
|
|
198
|
+
const rp = respPrefix(idx, o.address);
|
|
199
|
+
return { code: `${respRegisterGuard(rp, idx)} ${rp}_on_rx = ${o.handler};` };
|
|
200
|
+
}
|
|
201
|
+
case 'i2c.resp_on_request': {
|
|
202
|
+
const rp = respPrefix(idx, o.address);
|
|
203
|
+
return { code: `${respRegisterGuard(rp, idx)} ${rp}_on_rq = ${o.handler};` };
|
|
204
|
+
}
|
|
205
|
+
case 'i2c.resp_available': {
|
|
206
|
+
const rp = respPrefix(idx, o.address);
|
|
207
|
+
return { expression: `({ ${respRegisterGuard(rp, idx)} (${rp}_head - ${rp}_tail); })` };
|
|
208
|
+
}
|
|
209
|
+
case 'i2c.resp_read': {
|
|
210
|
+
const rp = respPrefix(idx, o.address);
|
|
211
|
+
return { expression: `({ ${respRegisterGuard(rp, idx)} (${rp}_tail < ${rp}_head ? ${rp}_rx[(${rp}_tail)++ % ${o.rx}U] : -1); })` };
|
|
212
|
+
}
|
|
213
|
+
case 'i2c.resp_write': {
|
|
214
|
+
const rp = respPrefix(idx, o.address);
|
|
215
|
+
const bytes = o.bytes ?? [];
|
|
216
|
+
const isBuffer = bytes.length === 1 && typeof bytes[0] === 'string' && /^[A-Za-z_][A-Za-z0-9_]*$/.test(bytes[0]);
|
|
217
|
+
if (isBuffer) {
|
|
218
|
+
const name = bytes[0];
|
|
219
|
+
// A named user buffer: copy its storage into the response buffer,
|
|
220
|
+
// clamped to the constructed size; the length lands last so an
|
|
221
|
+
// in-flight read never sees a torn buffer.
|
|
222
|
+
return {
|
|
223
|
+
code: `${respRegisterGuard(rp, idx)} { ${rp}_txlen = 0U; for (uint32_t __tc_i = 0U; (__tc_i < sizeof(${name})) && (__tc_i < ${o.tx}U); ++__tc_i) { ${rp}_tx[__tc_i] = ${name}[__tc_i]; } ${rp}_txlen = (sizeof(${name}) < ${o.tx}U) ? static_cast<uint32_t>(sizeof(${name})) : ${o.tx}U; }`,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (bytes.length > Number(o.tx)) {
|
|
227
|
+
throw new Error(`I2C responder write: the response buffer holds ${o.tx} bytes — got ${bytes.length}. Construct with a larger txBufferBytes.`);
|
|
228
|
+
}
|
|
229
|
+
const assigns = bytes
|
|
230
|
+
.map((b, i) => `${rp}_tx[${i}] = static_cast<uint8_t>(${b});`)
|
|
231
|
+
.join(' ');
|
|
232
|
+
return { code: `${respRegisterGuard(rp, idx)} { ${rp}_txlen = 0U; ${assigns} ${rp}_txlen = ${bytes.length}U; }` };
|
|
233
|
+
}
|
|
94
234
|
default:
|
|
95
235
|
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
96
236
|
`Open an issue or use rawCpp() to emit it manually.`);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
import type { ZephyrChipDescriptor } from '../chips/types.js';
|
|
3
|
+
/** Which i2s.* verbs the program actually uses on a controller — the init
|
|
4
|
+
* block emits only the state those verbs reference (a write-only program
|
|
5
|
+
* must not carry an unused rx buffer: -Werror fails the build). */
|
|
6
|
+
export interface I2sUsage {
|
|
7
|
+
write: boolean;
|
|
8
|
+
read: boolean;
|
|
9
|
+
readAt: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Emit the per-controller I2S state: device handle, mem slab, TX/RX block
|
|
13
|
+
* buffers, and the lazy per-direction init helpers. Called from shimLines
|
|
14
|
+
* when the program uses i2s.* on this controller and the chip declares it.
|
|
15
|
+
* The once-flags live HERE (shim scope), not in the lowered call sites — a
|
|
16
|
+
* second write() statement must not reconfigure/re-start a running stream
|
|
17
|
+
* (i2s_configure is legal only before the first trigger).
|
|
18
|
+
*/
|
|
19
|
+
export declare function i2sInitLines(chip: ZephyrChipDescriptor, controllerIndex: number, blockBytes: number, usage: I2sUsage): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Resolve a HAL i2s.* op to Zephyr C++.
|
|
22
|
+
* Returns `{ code }` for write, `{ expression }` for read/readAt.
|
|
23
|
+
*/
|
|
24
|
+
export declare function lowerI2s(op: HALOpIR, chip: ZephyrChipDescriptor): {
|
|
25
|
+
code?: string;
|
|
26
|
+
expression?: string;
|
|
27
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// I2S lowering — Zephyr's i2s API over the harvested controller node
|
|
3
|
+
//
|
|
4
|
+
// The controller is addressed by its DT nodelabel (chip.i2s, the harvested
|
|
5
|
+
// i2s@ node — ESP32 I2S0/1). Each direction lazy-inits on first use (the
|
|
6
|
+
// PWM discipline): configure + trigger START once, then every write/read is
|
|
7
|
+
// one block. The shim owns the mem slab (2 blocks), the TX packing buffer,
|
|
8
|
+
// and the RX receive buffer; blockAt/readAt index the last received block.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
/** The C variable names for an I2S controller's state. */
|
|
11
|
+
function prefix(idx) {
|
|
12
|
+
return `__tc_i2s${idx}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Emit the per-controller I2S state: device handle, mem slab, TX/RX block
|
|
16
|
+
* buffers, and the lazy per-direction init helpers. Called from shimLines
|
|
17
|
+
* when the program uses i2s.* on this controller and the chip declares it.
|
|
18
|
+
* The once-flags live HERE (shim scope), not in the lowered call sites — a
|
|
19
|
+
* second write() statement must not reconfigure/re-start a running stream
|
|
20
|
+
* (i2s_configure is legal only before the first trigger).
|
|
21
|
+
*/
|
|
22
|
+
export function i2sInitLines(chip, controllerIndex, blockBytes, usage) {
|
|
23
|
+
const ctrl = chip.i2s?.controllers[controllerIndex];
|
|
24
|
+
if (!ctrl)
|
|
25
|
+
return [];
|
|
26
|
+
const p = prefix(controllerIndex);
|
|
27
|
+
// readAt rides the RX engine too (its lowering lazily starts RX like read
|
|
28
|
+
// does) — a readAt-only program still references the whole engine state.
|
|
29
|
+
const needsEngine = usage.write || usage.read || usage.readAt;
|
|
30
|
+
const lines = [
|
|
31
|
+
'// CUTTLEFISH_I2S_BEGIN',
|
|
32
|
+
`static const struct device* ${p}_dev = DEVICE_DT_GET(DT_NODELABEL(${ctrl.nodeLabel}));`,
|
|
33
|
+
];
|
|
34
|
+
if (needsEngine) {
|
|
35
|
+
lines.push(`K_MEM_SLAB_DEFINE_STATIC(${p}_slab, ${blockBytes}, 2, 4);`, `K_MEM_SLAB_DEFINE_STATIC(${p}_rxslab, ${blockBytes}, 2, 4);`, `static void ${p}_init(uint32_t hz, uint8_t channels, uint8_t bits, size_t block_bytes) {`, ' struct i2s_config cfg = { 0 };', ' cfg.word_size = bits;', ' cfg.channels = channels;', ' cfg.format = I2S_FMT_DATA_FORMAT_I2S;', ' cfg.options = 0;', ' cfg.frame_clk_freq = hz;', ` cfg.mem_slab = &${p}_slab;`, ' cfg.block_size = block_bytes;', ' cfg.timeout = 200;', ` int err = i2s_configure(${p}_dev, I2S_DIR_TX, &cfg);`, ' if (err != 0) { printk("typecad-hal i2s: tx configure failed: %d\\n", err); }', ` cfg.mem_slab = &${p}_rxslab;`, ` err = i2s_configure(${p}_dev, I2S_DIR_RX, &cfg);`, ' if (err != 0) { printk("typecad-hal i2s: rx configure failed: %d\\n", err); }', '}', `static bool ${p}_init_done = false;`);
|
|
36
|
+
}
|
|
37
|
+
if (usage.write) {
|
|
38
|
+
lines.push(`static int16_t ${p}_txbuf[${blockBytes / 2}];`, `static bool ${p}_tx_started = false;`);
|
|
39
|
+
}
|
|
40
|
+
if (usage.read || usage.readAt) {
|
|
41
|
+
lines.push(`static int16_t ${p}_rxbuf[${blockBytes / 2}];`);
|
|
42
|
+
lines.push(`static bool ${p}_rx_started = false;`);
|
|
43
|
+
}
|
|
44
|
+
lines.push('// CUTTLEFISH_I2S_END');
|
|
45
|
+
return lines;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a HAL i2s.* op to Zephyr C++.
|
|
49
|
+
* Returns `{ code }` for write, `{ expression }` for read/readAt.
|
|
50
|
+
*/
|
|
51
|
+
export function lowerI2s(op, chip) {
|
|
52
|
+
const o = op;
|
|
53
|
+
// DAC discipline: no harvested i2s@ node lowers to the honest comment.
|
|
54
|
+
if (!chip.i2s || chip.i2s.controllers.length === 0) {
|
|
55
|
+
return { code: `/* ${op.operation}: this board's chip data declares no I2S controller (no harvested i2s@ node) */` };
|
|
56
|
+
}
|
|
57
|
+
const idx = Math.min(Number(o.instance ?? 0), chip.i2s.controllers.length - 1);
|
|
58
|
+
const p = prefix(idx);
|
|
59
|
+
const hz = Number(o.hz ?? 16000);
|
|
60
|
+
const channels = Number(o.channels ?? 2);
|
|
61
|
+
const bits = Number(o.bits ?? 16);
|
|
62
|
+
const blockFrames = Number(o.blockFrames ?? 64);
|
|
63
|
+
const blockBytes = blockFrames * channels * (bits / 8);
|
|
64
|
+
switch (op.operation) {
|
|
65
|
+
case 'i2s.write': {
|
|
66
|
+
const samples = (o.samples ?? []);
|
|
67
|
+
const cap = blockFrames * channels;
|
|
68
|
+
if (samples.length > cap) {
|
|
69
|
+
throw new Error(`I2S write: one block carries at most ${cap} samples (${blockFrames} frames × ${channels} channels) — got ${samples.length}.`);
|
|
70
|
+
}
|
|
71
|
+
// Zero-pad to the full block; samples are 16-bit.
|
|
72
|
+
const writes = [];
|
|
73
|
+
for (let i = 0; i < cap; i++) {
|
|
74
|
+
const v = i < samples.length ? samples[i] : 0;
|
|
75
|
+
writes.push(`${p}_txbuf[${i}] = static_cast<int16_t>(${v});`);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
code: `{ if (!${p}_init_done) { ${p}_init_done = true; ${p}_init(${hz}U, ${channels}U, ${bits}U, ${blockBytes}U); } ${writes.join(' ')} int err = i2s_buf_write(${p}_dev, ${p}_txbuf, ${blockBytes}U); if (err != 0) { printk("typecad-hal i2s: write failed: %d\\n", err); } else { if (!${p}_tx_started) { ${p}_tx_started = true; err = i2s_trigger(${p}_dev, I2S_DIR_TX, I2S_TRIGGER_START); if (err != 0) { printk("typecad-hal i2s: tx start failed: %d\\n", err); } } } }`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
case 'i2s.read': {
|
|
82
|
+
return {
|
|
83
|
+
expression: `({ if (!${p}_init_done) { ${p}_init_done = true; ${p}_init(${hz}U, ${channels}U, ${bits}U, ${blockBytes}U); } if (!${p}_rx_started) { ${p}_rx_started = true; int err = i2s_trigger(${p}_dev, I2S_DIR_RX, I2S_TRIGGER_START); if (err != 0) { printk("typecad-hal i2s: rx start failed: %d\\n", err); } } size_t got = 0U; int err = i2s_buf_read(${p}_dev, ${p}_rxbuf, &got); if (err != 0) { printk("typecad-hal i2s: read failed: %d\\n", err); } static_cast<double>(${p}_rxbuf[0]); })`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
case 'i2s.read_at': {
|
|
87
|
+
// Same lazy RX start as read(): readAt indexes the last RECEIVED
|
|
88
|
+
// block, so the stream must be running — and a readAt-only program
|
|
89
|
+
// references (and therefore compiles in) the whole engine state.
|
|
90
|
+
return {
|
|
91
|
+
expression: `({ if (!${p}_init_done) { ${p}_init_done = true; ${p}_init(${hz}U, ${channels}U, ${bits}U, ${blockBytes}U); } if (!${p}_rx_started) { ${p}_rx_started = true; int err = i2s_trigger(${p}_dev, I2S_DIR_RX, I2S_TRIGGER_START); if (err != 0) { printk("typecad-hal i2s: rx start failed: %d\\n", err); } } int32_t at = static_cast<int32_t>(${o.index}); double v = 0.0; if (at >= 0 && static_cast<uint32_t>(at) < (sizeof(${p}_rxbuf) / sizeof(${p}_rxbuf[0]))) { v = static_cast<double>(${p}_rxbuf[at]); } v; })`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
throw new Error(`framework-zephyr does not yet support HAL op \`${op.operation}\`. ` +
|
|
96
|
+
`Open an issue or use rawCpp() to emit it manually.`);
|
|
97
|
+
}
|
|
98
|
+
}
|
package/dist/lowering/index.d.ts
CHANGED
|
@@ -21,7 +21,15 @@ import { lowerFs } from './fs.js';
|
|
|
21
21
|
import { lowerSensor } from './sensor.js';
|
|
22
22
|
import { lowerHwtimer, lowerCounter } from './hwtimer.js';
|
|
23
23
|
import { lowerThread } from './thread.js';
|
|
24
|
-
|
|
24
|
+
import { lowerTrace } from './trace.js';
|
|
25
|
+
import { lowerStrip } from './strip.js';
|
|
26
|
+
import { lowerHid } from './hid.js';
|
|
27
|
+
import { lowerMatrix } from './matrix.js';
|
|
28
|
+
import { lowerPower } from './power.js';
|
|
29
|
+
import { lowerClock } from './clock.js';
|
|
30
|
+
import { lowerCan } from './can.js';
|
|
31
|
+
import { lowerI2s } from './i2s.js';
|
|
32
|
+
export { lowerGpio, lowerTiming, lowerAdc, lowerPwm, lowerI2c, lowerSpi, lowerUart, lowerUsb, lowerInterrupt, lowerWdt, lowerBle, lowerWifi, lowerHttp, lowerMqtt, lowerPreferences, lowerBoard, lowerRandom, lowerDac, lowerFs, lowerHwtimer, lowerCounter, lowerSensor, lowerThread, lowerTrace, lowerStrip, lowerHid, lowerMatrix, lowerPower, lowerClock, lowerCan, lowerI2s, };
|
|
25
33
|
/**
|
|
26
34
|
* Lower a HAL op to Zephyr C++. Returns undefined for unsupported categories
|
|
27
35
|
* (mirrors lowerHalOp in framework-esp32/src/lowering/index.ts).
|
package/dist/lowering/index.js
CHANGED
|
@@ -38,7 +38,15 @@ import { lowerFs } from './fs.js';
|
|
|
38
38
|
import { lowerSensor } from './sensor.js';
|
|
39
39
|
import { lowerHwtimer, lowerCounter } from './hwtimer.js';
|
|
40
40
|
import { lowerThread } from './thread.js';
|
|
41
|
-
|
|
41
|
+
import { lowerTrace } from './trace.js';
|
|
42
|
+
import { lowerStrip } from './strip.js';
|
|
43
|
+
import { lowerHid } from './hid.js';
|
|
44
|
+
import { lowerMatrix } from './matrix.js';
|
|
45
|
+
import { lowerPower } from './power.js';
|
|
46
|
+
import { lowerClock } from './clock.js';
|
|
47
|
+
import { lowerCan } from './can.js';
|
|
48
|
+
import { lowerI2s } from './i2s.js';
|
|
49
|
+
export { lowerGpio, lowerTiming, lowerAdc, lowerPwm, lowerI2c, lowerSpi, lowerUart, lowerUsb, lowerInterrupt, lowerWdt, lowerBle, lowerWifi, lowerHttp, lowerMqtt, lowerPreferences, lowerBoard, lowerRandom, lowerDac, lowerFs, lowerHwtimer, lowerCounter, lowerSensor, lowerThread, lowerTrace, lowerStrip, lowerHid, lowerMatrix, lowerPower, lowerClock, lowerCan, lowerI2s, };
|
|
42
50
|
/**
|
|
43
51
|
* Lower a HAL op to Zephyr C++. Returns undefined for unsupported categories
|
|
44
52
|
* (mirrors lowerHalOp in framework-esp32/src/lowering/index.ts).
|
|
@@ -94,6 +102,22 @@ export function lowerHalOp(op) {
|
|
|
94
102
|
return lowerSensor(op);
|
|
95
103
|
if (op.operation.startsWith('thread.'))
|
|
96
104
|
return lowerThread(op);
|
|
105
|
+
if (op.operation.startsWith('trace.'))
|
|
106
|
+
return lowerTrace(op);
|
|
107
|
+
if (op.operation.startsWith('strip.'))
|
|
108
|
+
return lowerStrip(op, chip);
|
|
109
|
+
if (op.operation.startsWith('hid.'))
|
|
110
|
+
return lowerHid(op, chip);
|
|
111
|
+
if (op.operation.startsWith('matrix.'))
|
|
112
|
+
return lowerMatrix(op);
|
|
113
|
+
if (op.operation.startsWith('power.'))
|
|
114
|
+
return lowerPower(op, chip);
|
|
115
|
+
if (op.operation.startsWith('clock.'))
|
|
116
|
+
return lowerClock(op);
|
|
117
|
+
if (op.operation.startsWith('can.'))
|
|
118
|
+
return lowerCan(op, chip);
|
|
119
|
+
if (op.operation.startsWith('i2s.'))
|
|
120
|
+
return lowerI2s(op, chip);
|
|
97
121
|
// raw / snprintf.emit / display.* / ... — not lowered by this
|
|
98
122
|
// framework. Return undefined so the transpiler falls back and the manifest
|
|
99
123
|
// validator confirms the unsupported declaration.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HALOpIR } from '@typecad/cuttlefish/api/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Emit the matrix trampoline state. Called from shimLines when the program
|
|
4
|
+
* uses matrix.* — the listener targets the synthesized tc_matrix node, the
|
|
5
|
+
* handler pointer is assigned by the on_key lowering.
|
|
6
|
+
*/
|
|
7
|
+
export declare function matrixInitLines(): string[];
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a HAL matrix.* op to Zephyr C++.
|
|
10
|
+
* Returns `{ code }` for statement ops.
|
|
11
|
+
*/
|
|
12
|
+
export declare function lowerMatrix(op: HALOpIR): {
|
|
13
|
+
code?: string;
|
|
14
|
+
expression?: string;
|
|
15
|
+
};
|