@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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// debug session is self-contained. cortex-debug starts OpenOCD as a child
|
|
9
9
|
// process via `servertype: "openocd"`; the preLaunch task handles only the
|
|
10
10
|
// build + flash step. After attach we issue `monitor reset init`, set a
|
|
11
|
-
// temporary hardware breakpoint at
|
|
11
|
+
// temporary hardware breakpoint at main(), and continue — this ensures the
|
|
12
12
|
// breakpoint is deferred until the bootloader maps the app flash region.
|
|
13
13
|
//
|
|
14
14
|
// All generators are deterministic + idempotent so toggling --debug does not
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
18
18
|
import { join, resolve, dirname, relative } from 'node:path';
|
|
19
19
|
import { ZephyrStrategy } from '../strategy.js';
|
|
20
|
+
import { resolveChipFromBoard } from '../chips/resolve.js';
|
|
21
|
+
import { resolveProbeMethod } from './index.js';
|
|
22
|
+
import { loadCuttlefishConfig } from '@typecad/cuttlefish/config-loader';
|
|
20
23
|
/**
|
|
21
24
|
* Resolve the GDB binary path for the target from the build cache, falling
|
|
22
25
|
* back to a filesystem scan of known Zephyr SDK locations when no build
|
|
@@ -29,14 +32,13 @@ import { ZephyrStrategy } from '../strategy.js';
|
|
|
29
32
|
* omits gdbPath and relies on Cortex-Debug's default resolution).
|
|
30
33
|
*/
|
|
31
34
|
export function resolveGdbPath(buildDir, target) {
|
|
32
|
-
void target; // toolchain dir is esp32s3-specific today; see gdbPathFromSdkRoot
|
|
33
35
|
const cachePath = join(buildDir, 'CMakeCache.txt');
|
|
34
36
|
if (existsSync(cachePath)) {
|
|
35
37
|
try {
|
|
36
38
|
const cache = readFileSync(cachePath, 'utf-8');
|
|
37
39
|
const m = cache.match(/^ZEPHYR_SDK_INSTALL_DIR:PATH=(.+)$/m);
|
|
38
40
|
if (m) {
|
|
39
|
-
const fromCache = gdbPathFromSdkRoot(m[1].trim());
|
|
41
|
+
const fromCache = gdbPathFromSdkRoot(m[1].trim(), target);
|
|
40
42
|
if (fromCache)
|
|
41
43
|
return fromCache;
|
|
42
44
|
}
|
|
@@ -47,17 +49,23 @@ export function resolveGdbPath(buildDir, target) {
|
|
|
47
49
|
}
|
|
48
50
|
// No build dir yet (project just created): probe known SDK locations.
|
|
49
51
|
for (const sdkRoot of discoverZephyrSdkRoots()) {
|
|
50
|
-
const p = gdbPathFromSdkRoot(sdkRoot);
|
|
52
|
+
const p = gdbPathFromSdkRoot(sdkRoot, target);
|
|
51
53
|
if (p)
|
|
52
54
|
return p;
|
|
53
55
|
}
|
|
54
56
|
return undefined;
|
|
55
57
|
}
|
|
56
|
-
/** The
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
/** The GDB location inside a Zephyr SDK root, per target architecture:
|
|
59
|
+
* ARM boards use the arm-zephyr-eabi toolchain (verified against
|
|
60
|
+
* zephyr-sdk-0.17.4), Espressif the xtensa-espressif_esp32s3_zephyr-elf one.
|
|
61
|
+
* Returns a forward-slash absolute path or undefined. */
|
|
62
|
+
export function gdbPathFromSdkRoot(sdkRoot, target) {
|
|
63
|
+
// No target given (legacy callers/tests): the historical Espresif default.
|
|
64
|
+
const boardId = (target ?? '').split('/')[0];
|
|
65
|
+
const isArm = boardId.length > 0 && !boardId.startsWith('esp32');
|
|
66
|
+
const toolchainDir = isArm ? 'arm-zephyr-eabi' : 'xtensa-espressif_esp32s3_zephyr-elf';
|
|
67
|
+
const gdbName = isArm ? 'arm-zephyr-eabi-gdb.exe' : 'xtensa-espressif_esp32s3_zephyr-elf-gdb.exe';
|
|
68
|
+
const gdbPath = join(sdkRoot, toolchainDir, 'bin', gdbName);
|
|
61
69
|
return existsSync(gdbPath) ? gdbPath.replace(/\\/g, '/') : undefined;
|
|
62
70
|
}
|
|
63
71
|
/** Compare two dotted version strings numerically (0.17.10 > 0.17.4). */
|
|
@@ -76,7 +84,7 @@ function compareSdkVersions(a, b) {
|
|
|
76
84
|
* Probe the well-known Zephyr SDK install locations, newest version first:
|
|
77
85
|
* 1. $ZEPHYR_SDK_INSTALL_DIR (the var board.cmake reads)
|
|
78
86
|
* 2. <MAMBA_ROOT_PREFIX | ~/micromamba>/zephyr-sdk/zephyr-sdk-<ver> — the
|
|
79
|
-
*
|
|
87
|
+
* the bundled Zephyr installer layout
|
|
80
88
|
* 3. ~/zephyr-sdk-<ver> — the standalone download layout
|
|
81
89
|
*
|
|
82
90
|
* Only roots that actually contain the esp32s3 GDB are useful to callers;
|
|
@@ -116,44 +124,59 @@ export function discoverZephyrSdkRoots(opts) {
|
|
|
116
124
|
return roots.filter((r, i) => roots.indexOf(r) === i);
|
|
117
125
|
}
|
|
118
126
|
/**
|
|
119
|
-
* Resolve the
|
|
120
|
-
*
|
|
127
|
+
* Resolve the OpenOCD binary path. The esp32s3 needs the Espressif OpenOCD
|
|
128
|
+
* fork (openocd-esp32) — not the Zephyr SDK's openocd and not a
|
|
121
129
|
* generic/GDB-stub build — because only it carries the Xtensa + esp_usb_jtag
|
|
122
|
-
* support.
|
|
123
|
-
*
|
|
130
|
+
* support. ARM targets prefer the Zephyr SDK's own openocd (it ships the
|
|
131
|
+
* interface/target cfgs boards reference); the Espressif fork is only
|
|
132
|
+
* consulted for esp32 targets.
|
|
124
133
|
*
|
|
125
134
|
* Discovery order:
|
|
126
|
-
* 1. ESPRESSIF_TOOLCHAIN_PATH env (the var board.cmake reads) —
|
|
127
|
-
* openocd-esp32/bin/openocd.exe.
|
|
135
|
+
* 1. ESPRESSIF_TOOLCHAIN_PATH env (the var board.cmake reads) — esp32
|
|
136
|
+
* targets; its openocd-esp32/bin/openocd.exe.
|
|
128
137
|
* 2. The standard ESP-IDF install layout: ~/.espressif/tools/openocd-esp32/
|
|
129
|
-
* <version>/openocd-esp32/bin/openocd.exe
|
|
138
|
+
* <version>/openocd-esp32/bin/openocd.exe (esp32 targets; newest first).
|
|
139
|
+
* 3. A Zephyr SDK root (discoverZephyrSdkRoots) — its
|
|
140
|
+
* tools/opt/openocd/bin/openocd(.exe), any target.
|
|
130
141
|
* Returns undefined if not found (the launch.json then omits openOCDPath and
|
|
131
142
|
* Cortex-Debug falls back to PATH / its openocdPath setting).
|
|
132
143
|
*/
|
|
133
|
-
export function resolveOpenOcdPath() {
|
|
144
|
+
export function resolveOpenOcdPath(target) {
|
|
145
|
+
const isEsp32Target = (target ?? '').split('/')[0].startsWith('esp32');
|
|
134
146
|
const candidates = [];
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// 2. ~/.espressif/tools/openocd-esp32/<version>/openocd-esp32/bin/openocd.exe
|
|
141
|
-
const home = process.env.USERPROFILE || process.env.HOME;
|
|
142
|
-
if (home) {
|
|
143
|
-
const base = join(home, '.espressif', 'tools', 'openocd-esp32');
|
|
144
|
-
let versions = [];
|
|
145
|
-
try {
|
|
146
|
-
versions = readdirSync(base).filter((v) => existsSync(join(base, v, 'openocd-esp32', 'bin', 'openocd.exe')));
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
// dir absent
|
|
147
|
+
if (isEsp32Target) {
|
|
148
|
+
// 1. ESPRESSIF_TOOLCHAIN_PATH env
|
|
149
|
+
const envPath = process.env.ESPRESSIF_TOOLCHAIN_PATH;
|
|
150
|
+
if (envPath) {
|
|
151
|
+
candidates.push(join(envPath, 'openocd-esp32', 'bin', 'openocd.exe'));
|
|
150
152
|
}
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
// 2. ~/.espressif/tools/openocd-esp32/<version>/openocd-esp32/bin/openocd.exe
|
|
154
|
+
const home = process.env.USERPROFILE || process.env.HOME;
|
|
155
|
+
if (home) {
|
|
156
|
+
const base = join(home, '.espressif', 'tools', 'openocd-esp32');
|
|
157
|
+
let versions = [];
|
|
158
|
+
try {
|
|
159
|
+
versions = readdirSync(base).filter((v) => existsSync(join(base, v, 'openocd-esp32', 'bin', 'openocd.exe')));
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// dir absent
|
|
163
|
+
}
|
|
164
|
+
// newest version last — sort then reverse so the highest wins on match.
|
|
165
|
+
versions.sort().reverse();
|
|
166
|
+
for (const v of versions) {
|
|
167
|
+
candidates.push(join(base, v, 'openocd-esp32', 'bin', 'openocd.exe'));
|
|
168
|
+
}
|
|
155
169
|
}
|
|
156
170
|
}
|
|
171
|
+
// 3. The Zephyr SDK's openocd (ARM targets' interface/target cfgs ship
|
|
172
|
+
// with it — interface/stlink-dap.cfg, target/stm32f4x.cfg, ...). Layout
|
|
173
|
+
// differs by SDK generation: 1.0.x hosttools/openocd, 0.17.x
|
|
174
|
+
// tools/opt/openocd (when present).
|
|
175
|
+
const exe = process.platform === 'win32' ? 'openocd.exe' : 'openocd';
|
|
176
|
+
for (const sdkRoot of discoverZephyrSdkRoots()) {
|
|
177
|
+
candidates.push(join(sdkRoot, 'hosttools', 'openocd', 'bin', exe));
|
|
178
|
+
candidates.push(join(sdkRoot, 'tools', 'opt', 'openocd', 'bin', exe));
|
|
179
|
+
}
|
|
157
180
|
for (const c of candidates) {
|
|
158
181
|
if (existsSync(c))
|
|
159
182
|
return resolve(c).replace(/\\/g, '/');
|
|
@@ -173,8 +196,8 @@ export function resolveOpenOcdPath() {
|
|
|
173
196
|
* THAT as the workspace root. This makes F5 work when a user opens the project
|
|
174
197
|
* folder directly, and keeps launch.json paths relative to it.
|
|
175
198
|
*
|
|
176
|
-
* Returns { workspaceRoot,
|
|
177
|
-
* project root (the `.vscode/` target) and
|
|
199
|
+
* Returns { workspaceRoot, appRel } where workspaceRoot is the cuttlefish
|
|
200
|
+
* project root (the `.vscode/` target) and appRel is the Zephyr app dir
|
|
178
201
|
* (`projectRoot`) relative to it (e.g. 'src/out').
|
|
179
202
|
*/
|
|
180
203
|
export function resolveDebugLocations(projectRoot) {
|
|
@@ -193,8 +216,8 @@ export function resolveDebugLocations(projectRoot) {
|
|
|
193
216
|
break;
|
|
194
217
|
dir = parent;
|
|
195
218
|
}
|
|
196
|
-
const
|
|
197
|
-
return { workspaceRoot,
|
|
219
|
+
const appRel = relative(workspaceRoot, resolve(projectRoot)).replace(/\\/g, '/');
|
|
220
|
+
return { workspaceRoot, appRel };
|
|
198
221
|
}
|
|
199
222
|
/** Read-merge-write a JSON file, adding/replacing a single config by a key. */
|
|
200
223
|
function mergeJsonArrayEntry(filePath, arrayKey, matchKey, entry) {
|
|
@@ -277,10 +300,10 @@ export function generateGdbScript(sourceMapPath) {
|
|
|
277
300
|
* `openOcdCfgRel` is the workspace-relative path to the generated
|
|
278
301
|
* .cuttlefish/openocd.cfg, passed to cortex-debug's configFiles.
|
|
279
302
|
*/
|
|
280
|
-
function buildLaunchConfig(o, gdbScriptRel, openOcdCfgRel) {
|
|
303
|
+
function buildLaunchConfig(o, gdbScriptRel, openOcdCfgRel, method) {
|
|
281
304
|
// The ELF is at <projectRoot>/build/zephyr/zephyr.elf (Zephyr's standard
|
|
282
305
|
// build output). cortex-debug uses `executable` (not `program`).
|
|
283
|
-
const executable = `\${workspaceFolder}/${o.
|
|
306
|
+
const executable = `\${workspaceFolder}/${o.appRel}/build/zephyr/zephyr.elf`;
|
|
284
307
|
// OpenOCD cfg relative to the workspace root so cortex-debug can pass it
|
|
285
308
|
// via the -f flag. Must be a list; cortex-debug prepends -f per entry.
|
|
286
309
|
const configFiles = [`\${workspaceFolder}/${openOcdCfgRel}`];
|
|
@@ -289,7 +312,7 @@ function buildLaunchConfig(o, gdbScriptRel, openOcdCfgRel) {
|
|
|
289
312
|
// OpenOCD binary path — the Espressif fork (openocd-esp32) is required for
|
|
290
313
|
// the esp_usb_jtag adapter. cortex-debug's `serverpath` tells it where to
|
|
291
314
|
// find the binary (not on PATH by default).
|
|
292
|
-
const openocdPath = resolveOpenOcdPath();
|
|
315
|
+
const openocdPath = resolveOpenOcdPath(o.target);
|
|
293
316
|
// Post-attach commands executed after GDB connects to the OpenOCD gdbserver.
|
|
294
317
|
// set mem inaccessible-by-default off — suppresses "Cannot access memory"
|
|
295
318
|
// errors caused by overlapping Xtensa memory regions (flash-mapped
|
|
@@ -298,41 +321,72 @@ function buildLaunchConfig(o, gdbScriptRel, openOcdCfgRel) {
|
|
|
298
321
|
// read-only so -break-insert uses hw breakpoints, not sw breakpoints
|
|
299
322
|
// (which would fail with "Cannot access memory at 0x4200xxxx").
|
|
300
323
|
// monitor reset init — reset target + halt (bootloader maps flash)
|
|
301
|
-
// thb
|
|
302
|
-
// c — continue; bootloader maps flash, breaks at
|
|
324
|
+
// thb main — temporary HW breakpoint at main()
|
|
325
|
+
// c — continue; bootloader maps flash, breaks at main()
|
|
303
326
|
//
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
|
|
327
|
+
// The trailing `c` is ESP32-only. On instant-reset ARM targets the
|
|
328
|
+
// thb-main stop lands within milliseconds — WHILE cortex-debug is still
|
|
329
|
+
// chewing through this command list — and a stop event mid-initialization
|
|
330
|
+
// leaves the session half-started (toolbar never enables, user breakpoints
|
|
331
|
+
// never bind). The ESP32's bootloader takes hundreds of milliseconds, so
|
|
332
|
+
// its stop safely arrives after init completes. ARM keeps the pending
|
|
333
|
+
// thb (the first user Continue stops at main()) but hands the run/stop
|
|
334
|
+
// transition to cortex-debug.
|
|
335
|
+
//
|
|
336
|
+
// Paths in GDB commands must be RELATIVE (resolved against GDB's own
|
|
337
|
+
// working directory), never ${workspaceFolder} literals or absolute paths:
|
|
338
|
+
// - cortex-debug passes command strings through verbatim — no variable
|
|
339
|
+
// expansion in postAttachCommands (verified against its gdb.ts) — so a
|
|
340
|
+
// ${workspaceFolder} would reach GDB as a literal.
|
|
341
|
+
// - Even where VS Code expands it, on Windows the expansion carries
|
|
342
|
+
// backslashes that GDB reads as escape sequences (\t → tab, etc.).
|
|
343
|
+
// - An absolute path bakes the generating machine's checkout location
|
|
344
|
+
// into launch.json and breaks on every other clone/checkout.
|
|
345
|
+
// cortex-debug spawns GDB with cwd from the config's `cwd` field
|
|
346
|
+
// (${workspaceFolder} above), so a relative path IS the workspace root —
|
|
347
|
+
// `set directories .` adds exactly the directory the old absolute form did,
|
|
348
|
+
// and `source <rel>` finds the frame-filter script.
|
|
349
|
+
// The Xtensa flash-mapping commands are ESP32-specific (app flash isn't
|
|
350
|
+
// readable until the bootloader maps it); ARM targets drop them.
|
|
351
|
+
const isEsp32Target = o.target.split('/')[0].startsWith('esp32');
|
|
307
352
|
const postAttachCommands = [
|
|
308
|
-
|
|
353
|
+
'set directories .',
|
|
309
354
|
'set remote hardware-watchpoint-limit 2',
|
|
310
355
|
'set remote hardware-breakpoint-limit 2',
|
|
311
|
-
|
|
312
|
-
|
|
356
|
+
...(isEsp32Target ? [
|
|
357
|
+
'set mem inaccessible-by-default off',
|
|
358
|
+
'mem 0x42000000 0x44000000 ro cache',
|
|
359
|
+
] : []),
|
|
313
360
|
'monitor reset init',
|
|
314
|
-
'thb
|
|
315
|
-
'c',
|
|
361
|
+
'thb main',
|
|
362
|
+
...(isEsp32Target ? ['c'] : []),
|
|
316
363
|
];
|
|
317
364
|
if (gdbScriptRel) {
|
|
318
|
-
postAttachCommands.splice(1, 0, `source ${
|
|
365
|
+
postAttachCommands.splice(1, 0, `source ${gdbScriptRel}`);
|
|
319
366
|
}
|
|
367
|
+
// Probe-method-driven server shape: jlink-runner methods use cortex-debug's
|
|
368
|
+
// jlink server (needs the device name from the board table); openocd-runner
|
|
369
|
+
// methods keep the openocd server with the generated cfg file. Without a
|
|
370
|
+
// method (ESP32 builtin-JTAG targets), the historical default applies.
|
|
371
|
+
const servertype = method?.runner === 'jlink' ? 'jlink' : 'openocd';
|
|
372
|
+
const isEsp32 = o.target.split('/')[0].startsWith('esp32');
|
|
320
373
|
const cfg = {
|
|
321
|
-
name:
|
|
374
|
+
name: `TypeCAD Debug (Zephyr, ${o.target.split('/')[0]})`,
|
|
322
375
|
type: 'cortex-debug',
|
|
323
376
|
// Attach mode: no download (the ELF is already flashed). The server
|
|
324
377
|
// controller's attachCommands() just halts the target, then our
|
|
325
|
-
// postAttachCommands reset it, set a HW breakpoint at
|
|
378
|
+
// postAttachCommands reset it, set a HW breakpoint at main(), and
|
|
326
379
|
// continue. HW breakpoints use debug registers and work before the
|
|
327
380
|
// bootloader maps the app flash region.
|
|
328
381
|
request: 'attach',
|
|
329
382
|
cwd: '${workspaceFolder}',
|
|
330
383
|
executable,
|
|
331
|
-
servertype
|
|
332
|
-
configFiles,
|
|
333
|
-
|
|
384
|
+
servertype,
|
|
385
|
+
...(servertype === 'openocd' ? { configFiles } : {}),
|
|
386
|
+
...(method?.debugDevice ? { device: method.debugDevice } : {}),
|
|
387
|
+
interface: method?.debugInterface ?? (isEsp32 ? 'jtag' : 'swd'),
|
|
334
388
|
...(gdbPath ? { gdbPath } : {}),
|
|
335
|
-
...(openocdPath ? { serverpath: openocdPath } : {}),
|
|
389
|
+
...(servertype === 'openocd' && openocdPath ? { serverpath: openocdPath } : {}),
|
|
336
390
|
postAttachCommands,
|
|
337
391
|
preLaunchTask: 'cuttlefish: build + flash (debug)',
|
|
338
392
|
};
|
|
@@ -351,7 +405,7 @@ function buildTask(o) {
|
|
|
351
405
|
label: 'cuttlefish: build + flash (debug)',
|
|
352
406
|
type: 'shell',
|
|
353
407
|
command: 'npx cuttlefish build --compile --upload --debug',
|
|
354
|
-
options: { cwd: `\${workspaceFolder}/${o.
|
|
408
|
+
options: { cwd: `\${workspaceFolder}/${o.appRel}` },
|
|
355
409
|
group: { kind: 'build', isDefault: false },
|
|
356
410
|
problemMatcher: [],
|
|
357
411
|
};
|
|
@@ -378,13 +432,77 @@ const OPENOCD_ADAPTER_SPEED = 4000;
|
|
|
378
432
|
* helper/RTOS tcl, so the override can land before the driver exists or be
|
|
379
433
|
* re-defaulted). Putting it in the cfg, after the source, is deterministic.
|
|
380
434
|
*/
|
|
381
|
-
|
|
382
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Drop the gdb-attach/gdb-detach target-event blocks from a cfg line list.
|
|
437
|
+
* Board cfgs use them for standalone openocd sessions (reset on attach,
|
|
438
|
+
* resume on detach), but under VS Code they fire DURING cortex-debug's
|
|
439
|
+
* initialization — the unexpected stop event aborts the session setup
|
|
440
|
+
* ("Program stopped, probably due to a reset and/or halt issued by
|
|
441
|
+
* debugger") and the toolbar never enables. The IDE owns the lifecycle.
|
|
442
|
+
*/
|
|
443
|
+
function stripGdbEventBlocks(lines) {
|
|
444
|
+
const out = [];
|
|
445
|
+
let skipping = false;
|
|
446
|
+
let depth = 0;
|
|
447
|
+
for (const line of lines) {
|
|
448
|
+
if (!skipping && /configure\s+-event\s+gdb-(attach|detach)/.test(line)) {
|
|
449
|
+
skipping = true;
|
|
450
|
+
depth = 0;
|
|
451
|
+
}
|
|
452
|
+
if (skipping) {
|
|
453
|
+
depth += (line.match(/\{/g) ?? []).length - (line.match(/\}/g) ?? []).length;
|
|
454
|
+
if (depth <= 0 && (line.includes('}') || !line.includes('{')))
|
|
455
|
+
skipping = false;
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
out.push(line);
|
|
459
|
+
}
|
|
460
|
+
return out;
|
|
461
|
+
}
|
|
462
|
+
function buildOpenOcdCfg(method, target) {
|
|
463
|
+
const header = [
|
|
383
464
|
'# Auto-generated by @typecad/framework-zephyr. Do not edit — regenerate',
|
|
384
|
-
'# with `cuttlefish build --debug`.
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
465
|
+
'# with `cuttlefish build --debug`.',
|
|
466
|
+
];
|
|
467
|
+
// Zephyr RTOS awareness (thread names in the call stack) — appended after
|
|
468
|
+
// the target source on ARM targets; the ESP32 board cfgs already carry
|
|
469
|
+
// ESP_RTOS Zephyr.
|
|
470
|
+
const isEsp32 = (target ?? '').split('/')[0].startsWith('esp32');
|
|
471
|
+
const rtosLine = isEsp32 ? [] : ['$_TARGETNAME configure -rtos Zephyr'];
|
|
472
|
+
if (method?.debugCfgSource && method.debugCfgSource.length > 0) {
|
|
473
|
+
// Probe-method-driven config: the board package's verified interface +
|
|
474
|
+
// target sources, then the method's quirk lines (e.g. reset_config for an
|
|
475
|
+
// unwired SRST) — the cfg-file form of the west runner args.
|
|
476
|
+
return [
|
|
477
|
+
...header,
|
|
478
|
+
`# Probe method '${method.id}' — from the board package's probeMethods table.`,
|
|
479
|
+
...method.debugCfgSource.map((src) => `source [find ${src}]`),
|
|
480
|
+
...stripGdbEventBlocks(method.debugCfg ?? []),
|
|
481
|
+
...rtosLine,
|
|
482
|
+
'',
|
|
483
|
+
].join('\n');
|
|
484
|
+
}
|
|
485
|
+
// Pack-derived methods (the board's own support/openocd.cfg, extracted by
|
|
486
|
+
// the board data pack) carry complete cfg lines — including their
|
|
487
|
+
// `source [find ...]` directives — verbatim in file order, minus the
|
|
488
|
+
// gdb-attach/detach events (IDE-managed lifecycle; see stripGdbEventBlocks).
|
|
489
|
+
if (method?.debugCfg && method.debugCfg.length > 0) {
|
|
490
|
+
return [
|
|
491
|
+
...header,
|
|
492
|
+
`# Probe method '${method.id}' — the board's own support/openocd.cfg,`,
|
|
493
|
+
`# extracted by the board data pack (gdb-attach/detach events removed —`,
|
|
494
|
+
`# the IDE owns the session lifecycle).`,
|
|
495
|
+
...stripGdbEventBlocks(method.debugCfg),
|
|
496
|
+
...rtosLine,
|
|
497
|
+
'',
|
|
498
|
+
].join('\n');
|
|
499
|
+
}
|
|
500
|
+
return [
|
|
501
|
+
...header,
|
|
502
|
+
'# Sources the board cfg (which loads the esp_usb_jtag adapter driver +',
|
|
503
|
+
'# esp32s3 target + ESP_RTOS Zephyr) then overrides the adapter speed to a',
|
|
504
|
+
'# USB-JTAG-stable value AFTER the driver is loaded. See debug-config.ts',
|
|
505
|
+
'# for the rationale.',
|
|
388
506
|
'source [find board/esp32s3-builtin.cfg]',
|
|
389
507
|
`adapter speed ${OPENOCD_ADAPTER_SPEED}`,
|
|
390
508
|
'',
|
|
@@ -400,25 +518,57 @@ function buildOpenOcdCfg() {
|
|
|
400
518
|
* <projectRoot>/.cuttlefish/openocd.cfg (OpenOCD cfg, adapter speed override)
|
|
401
519
|
* <projectRoot>/.cuttlefish/.cuttlefish-gdb.py (lambda frame filter, conditional)
|
|
402
520
|
*/
|
|
521
|
+
/**
|
|
522
|
+
* Resolve the debug-side probe method for a project: the board constants the
|
|
523
|
+
* transpile persists + the workspace config's zephyr.probe selection.
|
|
524
|
+
* Returns the method object (for cfg/servertype data) or undefined — ESP32
|
|
525
|
+
* targets and boards without tables fall back to the framework's default
|
|
526
|
+
* (esp_usb_jtag openocd) behavior.
|
|
527
|
+
*/
|
|
528
|
+
function resolveDebugProbeMethod(projectRoot, workspaceRoot) {
|
|
529
|
+
try {
|
|
530
|
+
const bcPath = [
|
|
531
|
+
join(projectRoot, 'src', 'board-constants.json'),
|
|
532
|
+
join(projectRoot, 'board-constants.json'),
|
|
533
|
+
].find((p) => existsSync(p));
|
|
534
|
+
if (!bcPath)
|
|
535
|
+
return undefined;
|
|
536
|
+
const raw = JSON.parse(readFileSync(bcPath, 'utf8'));
|
|
537
|
+
const chip = resolveChipFromBoard(new Map(Object.entries(raw)));
|
|
538
|
+
if (!chip)
|
|
539
|
+
return undefined;
|
|
540
|
+
const zc = loadCuttlefishConfig(workspaceRoot)?.zephyrConfig;
|
|
541
|
+
const probe = resolveProbeMethod(zc, chip, 'debug');
|
|
542
|
+
if (!probe.ok)
|
|
543
|
+
return undefined;
|
|
544
|
+
return chip.probeMethods?.find((m) => m.id === zc?.probe);
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
return undefined;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
403
550
|
export function writeDebugConfig(o) {
|
|
404
551
|
const vscodeDir = join(o.workspaceRoot, '.vscode');
|
|
405
552
|
const cuttlefishDir = join(o.projectRoot, '.cuttlefish');
|
|
406
553
|
mkdirSync(cuttlefishDir, { recursive: true });
|
|
554
|
+
// The selected probe method (if any) drives the debug server shape:
|
|
555
|
+
// openocd cfg source/quirks, cortex-debug servertype, wire interface.
|
|
556
|
+
const method = resolveDebugProbeMethod(o.projectRoot, o.workspaceRoot);
|
|
407
557
|
// openocd.cfg — written first so its relative path can be wired into the
|
|
408
558
|
// cortex-debug configFiles. cortex-debug starts OpenOCD as a child process
|
|
409
559
|
// and passes this file via -f.
|
|
410
560
|
const openOcdCfgPath = join(cuttlefishDir, 'openocd.cfg');
|
|
411
|
-
writeFileSync(openOcdCfgPath, buildOpenOcdCfg(), 'utf-8');
|
|
412
|
-
const openOcdCfgRel = `${o.
|
|
561
|
+
writeFileSync(openOcdCfgPath, buildOpenOcdCfg(method, o.target), 'utf-8');
|
|
562
|
+
const openOcdCfgRel = `${o.appRel}/.cuttlefish/openocd.cfg`;
|
|
413
563
|
// launch.json — merge the cortex-debug config by name.
|
|
414
564
|
const gdbScript = generateGdbScript(o.sourceMapPath);
|
|
415
565
|
let gdbScriptRel;
|
|
416
566
|
if (gdbScript) {
|
|
417
567
|
const scriptPath = join(cuttlefishDir, '.cuttlefish-gdb.py');
|
|
418
568
|
writeFileSync(scriptPath, gdbScript, 'utf-8');
|
|
419
|
-
gdbScriptRel = `${o.
|
|
569
|
+
gdbScriptRel = `${o.appRel}/.cuttlefish/.cuttlefish-gdb.py`;
|
|
420
570
|
}
|
|
421
|
-
const launchConfig = buildLaunchConfig(o, gdbScriptRel, openOcdCfgRel);
|
|
571
|
+
const launchConfig = buildLaunchConfig(o, gdbScriptRel, openOcdCfgRel, method);
|
|
422
572
|
mergeJsonArrayEntry(join(vscodeDir, 'launch.json'), 'configurations', 'name', launchConfig);
|
|
423
573
|
// tasks.json — merge the build+flash task by label. OpenOCD is managed by
|
|
424
574
|
// cortex-debug so the task is a simple synchronous build step.
|
|
@@ -433,7 +583,7 @@ export function writeDebugConfig(o) {
|
|
|
433
583
|
* debug artifacts — always lands at `src/out`. Keep in sync with
|
|
434
584
|
* generateProjectConfig in @typecad/cuttlefish create/templates.ts.
|
|
435
585
|
*/
|
|
436
|
-
const
|
|
586
|
+
const STARTER_APP_REL = 'src/out';
|
|
437
587
|
/**
|
|
438
588
|
* Create-time starter debug artifacts. Called by the cuttlefish `create` flow
|
|
439
589
|
* (via the package's `writeProjectDebugArtifacts` export) so a fresh project
|
|
@@ -453,11 +603,11 @@ export function writeProjectDebugArtifacts(o) {
|
|
|
453
603
|
if (new ZephyrStrategy().debugMode(o.buildTarget) !== 'gdb')
|
|
454
604
|
return [];
|
|
455
605
|
const workspaceRoot = resolve(o.workspaceRoot);
|
|
456
|
-
const projectRoot = join(workspaceRoot,
|
|
606
|
+
const projectRoot = join(workspaceRoot, STARTER_APP_REL);
|
|
457
607
|
writeDebugConfig({
|
|
458
608
|
projectRoot,
|
|
459
609
|
workspaceRoot,
|
|
460
|
-
|
|
610
|
+
appRel: STARTER_APP_REL,
|
|
461
611
|
target: o.buildTarget ?? '',
|
|
462
612
|
// No build dir exists yet — resolveGdbPath falls back to probing known
|
|
463
613
|
// Zephyr SDK locations so gdbPath is still filled in when possible.
|
|
@@ -466,6 +616,6 @@ export function writeProjectDebugArtifacts(o) {
|
|
|
466
616
|
return [
|
|
467
617
|
'.vscode/launch.json',
|
|
468
618
|
'.vscode/tasks.json',
|
|
469
|
-
`${
|
|
619
|
+
`${STARTER_APP_REL}/.cuttlefish/openocd.cfg`,
|
|
470
620
|
];
|
|
471
621
|
}
|
|
@@ -2,7 +2,7 @@ import { type WestInstall } from './west-discover.js';
|
|
|
2
2
|
import { type CompatStatus } from './compat.js';
|
|
3
3
|
/**
|
|
4
4
|
* Raw west facts gathered from discovery + a `west --version` probe. Mirrors
|
|
5
|
-
*
|
|
5
|
+
* probe data: `westFound` is true when a usable west install was
|
|
6
6
|
* discovered (discovery itself probes responsiveness).
|
|
7
7
|
*/
|
|
8
8
|
export interface WestProbeData {
|
|
@@ -16,8 +16,8 @@ export interface WestProbeData {
|
|
|
16
16
|
zephyrBase: string | undefined;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Test-injection seam for checkZephyrEnv. Mirrors
|
|
20
|
-
*
|
|
19
|
+
* Test-injection seam for checkZephyrEnv. Mirrors the
|
|
20
|
+
* fakeProbe option so tests never spawn a real west/python.
|
|
21
21
|
*/
|
|
22
22
|
export interface CheckZephyrEnvOptions {
|
|
23
23
|
/** FOR TESTS ONLY: skip the real probe and use this data directly. */
|
|
@@ -84,7 +84,7 @@ export declare function boardExistsInCheckout(boardId: string, zephyrBase: strin
|
|
|
84
84
|
* checkout. Reports what (if anything) is wrong.
|
|
85
85
|
*
|
|
86
86
|
* - If `buildTarget` is undefined/empty, the board check is skipped (not a
|
|
87
|
-
* failure), mirroring
|
|
87
|
+
* failure), mirroring the no-build-target path.
|
|
88
88
|
* - Never installs anything. Never mutates the user environment.
|
|
89
89
|
* - Never throws — always returns a result. Callers decide how to react.
|
|
90
90
|
*
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// Zephyr environment check — the shared detection behind `cuttlefish doctor`.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
4
|
+
// Zephyr environment check: gather the impure
|
|
5
5
|
// environment facts once (west presence + version, Zephyr version, board
|
|
6
6
|
// existence), then reduce them to a structured result the doctor (and, later,
|
|
7
7
|
// the build/test gates) can present uniformly. The check is side-effect-free
|
|
8
8
|
// and never throws — it never installs or mutates anything.
|
|
9
9
|
//
|
|
10
|
-
// Two
|
|
10
|
+
// Two checks (mirroring the doctor contract):
|
|
11
11
|
// 1. west (the Zephyr build tool) is discoverable + responsive — the direct
|
|
12
|
-
//
|
|
12
|
+
// toolchain presence. discoverWest() already confirms
|
|
13
13
|
// responsiveness via `west --version`; we additionally capture the version
|
|
14
14
|
// string to report it.
|
|
15
15
|
// 2. the configured board target exists in the Zephyr checkout
|
|
@@ -117,7 +117,7 @@ export function boardExistsInCheckout(boardId, zephyrBase) {
|
|
|
117
117
|
* checkout. Reports what (if anything) is wrong.
|
|
118
118
|
*
|
|
119
119
|
* - If `buildTarget` is undefined/empty, the board check is skipped (not a
|
|
120
|
-
* failure), mirroring
|
|
120
|
+
* failure), mirroring the no-build-target path.
|
|
121
121
|
* - Never installs anything. Never mutates the user environment.
|
|
122
122
|
* - Never throws — always returns a result. Callers decide how to react.
|
|
123
123
|
*
|
|
@@ -165,7 +165,7 @@ export function checkZephyrEnv(buildTarget, options) {
|
|
|
165
165
|
check,
|
|
166
166
|
messages: [
|
|
167
167
|
`Zephyr ${zephyrVersion} is outside the supported range (${compat.range}) for @typecad/framework-zephyr.`,
|
|
168
|
-
" Set ZEPHYR_BASE to a compatible Zephyr checkout, or
|
|
168
|
+
" Set ZEPHYR_BASE to a compatible Zephyr checkout, or or run the bundled Zephyr installer (npx --package @typecad/framework-zephyr zephyr-installer).",
|
|
169
169
|
],
|
|
170
170
|
fixCommand: undefined,
|
|
171
171
|
};
|