Skip to content
Merged
32 changes: 23 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@ The following extensions are included in this extension pack:
## pyOCD Debug Setup

- Install `GCC compiler for ARM CPUs` with the `Arm Tools Environment Manager` to get access to a GDB (`arm-none-eabi-gdb`).

- (Temporary, should become obsolete with full `*.cbuild-run.yml` support in pyOCD)<br>
- **Temporary** - should become obsolete with full `*.cbuild-run.yml` support in pyOCD:<br>
Make sure to set up your CMSIS Pack installation root folder by one of the following methods:
- Set your system environment variable `CMSIS_PACK_ROOT`.
- Add the following to your debug launch configuration
```
"environment": {
"CMSIS_PACK_ROOT": "</path/to/your/pack/cache>"
}

```

## Segger J-Link Debug Setup

- Install the latest [J-Link Software and Documentation Pack](https://www.segger.com/downloads/jlink/#J-LinkSoftwareAndDocumentationPack) from [Segger](https://www.segger.com/). Ensure all required drivers and host platform specific settings are done.
- Ensure the installation folder is added to your system's `PATH` environment variable. Alternatively, you can add an absolute path to your installation in the debug launch configuration.

## Additional Extension Functionality

This extension contributes additional functionality to more seamlessly integrate the included extensions:
Expand All @@ -48,15 +51,26 @@ This section describes the contributed pseudo debugger types and their support t

#### CMSIS Debugger (pyOCD) - `cmsis-debug-pyocd`

TODO:
- Assumed setup and where to find things.
- Automatically filled in values on debug start.
The `cmsis-debug-pyocd` debugger type allows to add default debug configurations to the workspace's `launch.json` file to debug via GDB and pyOCD. The actually used debugger type is `gdbtarget`.

In addition this extension contributes a debug configuration resolver which automatically fills the following gaps during debug launch:
- If option `target`.`server` is set to `pyocd`, then it expands this option to the absolute path of the built-in pyOCD distribution.
- Adds/extends the `target`.`serverParameters` list of `pyocd` command line arguments:
- Prepends `gdbserver` if not present.
- Appends `--port` and the corresponding `port` value if `target`.`port` is set.
- Appends `--cbuild-run` and the corresponding `cbuildRunFile` path if `cmsis`.`cbuildRunFile` is set.

**Note**: The built-in version of pyOCD supports the command line option `--cbuild-run`. However, this is a new option which isn't contained yet in releases outside this extension.

#### CMSIS Debugger (J-Link) - `cmsis-debug-jlink`

TODO:
- Assumed setup and where to find things.
- Automatically filled in values on debug start.
The `cmsis-debug-jlink` debugger type allows to add default debug configurations to the workspace's `launch.json` file to debug via GDB and the Segger J-Link GDB server. The actually used debugger type is `gdbtarget`.

**Note**: The generated default debug configuration uses `JLinkGDBServer` as `target`.`server` setting. The executable with this name has slightly differing behavior depending on your host platform. It launches a GUI-less server on Linux and MacOS. Whereas a GDB server with GUI is launched on Windows. Please change the value to `JLinkGDBServerCL` to suppress the GUI on Windows.

In addition this extension contributes a debug configuration resolver which automatically fills the following gaps during debug launch:
- Adds/extends the `target`.`serverParameters` list of `JLinkGDBServer`/`JLinkGDBServerCL` command line arguments:
- Appends `--port` and the corresponding `port` value if `target`.`port` is set.

## Trademarks
Visual Studio is a trademark of the Microsoft group of companies.
Expand Down
12 changes: 2 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@
"target": {
"server": "JLinkGDBServer",
"serverParameters": [
"-select",
"USB",
"-device",
"${command:cmsis-csolution.getDeviceName}",
"-endian",
Expand All @@ -130,11 +128,9 @@
"-speed",
"auto",
"-noir",
"-LocalhostOnly",
"-vd",
"-nogui",
"-port",
"3333"
"-localhostonly"
],
"port": "3333"
}
Expand All @@ -160,8 +156,6 @@
"target": {
"server": "JLinkGDBServer",
"serverParameters": [
"-select",
"USB",
"-device",
"^\"\\${command:cmsis-csolution.getDeviceName}\"",
"-endian",
Expand All @@ -171,11 +165,9 @@
"-speed",
"auto",
"-noir",
"-LocalhostOnly",
"-vd",
"-nogui",
"-port",
"3333"
"-localhostonly"
],
"port": "3333"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright 2025 Arm Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as vscode from 'vscode';
import { GDBTargetConfiguration } from '../gdbtarget-configuration';

export abstract class BaseConfigurationProvider implements vscode.DebugConfigurationProvider {

protected async commandExists(commandName: string): Promise<boolean> {
const commands = await vscode.commands.getCommands();
return !!commands.find(command => command === commandName);
};

protected parameterExists(name: string, params: string[]): boolean {
return !!params.find(param => param.trim() === name);
}

protected async shouldAppendParameter(params: string[], paramName: string, commandName?: string): Promise<boolean> {
return !this.parameterExists(paramName, params) && (!commandName || await this.commandExists(commandName));
}

protected abstract resolveServerParameters(debugConfiguration: GDBTargetConfiguration): Promise<GDBTargetConfiguration>;

public resolveDebugConfigurationWithSubstitutedVariables(
_folder: vscode.WorkspaceFolder | undefined,
debugConfiguration: vscode.DebugConfiguration,
_token?: vscode.CancellationToken
): Promise<vscode.DebugConfiguration | null | undefined> {
return this.resolveServerParameters(debugConfiguration);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@
* limitations under the License.
*/

import * as vscode from 'vscode';
import { logger } from '../../logger';
import { BaseConfigurationProvider } from './base-configuration-provider';
import { GDBTargetConfiguration } from '../gdbtarget-configuration';

export const JLINK_SERVER_TYPE_REGEXP = /.*JLinkGDBServer(|.exe)\s*$/i;
export const JLINK_SERVER_TYPE_REGEXP = /.*JLinkGDBServer(|CL)(|.exe|Exe)\s*$/i;

export class JlinkConfigurationProvider implements vscode.DebugConfigurationProvider {
const JLINK_CLI_ARG_PORT = '-port';

public async resolveDebugConfigurationWithSubstitutedVariables(
_folder: vscode.WorkspaceFolder | undefined,
debugConfiguration: vscode.DebugConfiguration,
_token?: vscode.CancellationToken
): Promise<vscode.DebugConfiguration | null | undefined> {
logger.debug('Resolving J-Link configuration');
export class JlinkConfigurationProvider extends BaseConfigurationProvider {

protected async resolveServerParameters(debugConfiguration: GDBTargetConfiguration): Promise<GDBTargetConfiguration> {
logger.debug('Resolving J-Link GDB server parameters');
if (!debugConfiguration.target) {
return debugConfiguration;
}
// serverParameters
debugConfiguration.target.serverParameters ??= [];
const parameters = debugConfiguration.target.serverParameters;
// port (use value defined in 'port' outside 'serverParamters')
const port = debugConfiguration.target?.port;
if (port && await this.shouldAppendParameter(parameters, JLINK_CLI_ARG_PORT)) {
parameters.push(JLINK_CLI_ARG_PORT, `${port}`);
}
return debugConfiguration;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,76 +14,57 @@
* limitations under the License.
*/

import * as vscode from 'vscode';
import { logger } from '../../logger';
import { GDBTargetConfiguration, TargetConfiguration } from '../gdbtarget-configuration';
import { BuiltinToolPath } from '../../desktop/builtin-tool-path';
import { BaseConfigurationProvider } from './base-configuration-provider';

const PYOCD_BUILTIN_PATH = 'tools/pyocd/pyocd';
export const PYOCD_EXECUTABLE_ONLY_REGEXP = /^\s*pyocd(|.exe)\s*$/i;
const PYOCD_EXECUTABLE_ONLY_REGEXP = /^\s*pyocd(|.exe)\s*$/i;
export const PYOCD_SERVER_TYPE_REGEXP = /.*pyocd(|.exe)\s*$/i;

export class PyocdConfigurationProvider implements vscode.DebugConfigurationProvider {
protected builtinPyocd = new BuiltinToolPath(PYOCD_BUILTIN_PATH);

protected async hasCommand(commandName: string): Promise<boolean> {
const commands = await vscode.commands.getCommands();
return !!commands.find(command => command === commandName);
};

protected hasParam(name: string, params: string[]): boolean {
return !!params.find(param => param.trim() === name);
}
const PYOCD_CLI_ARG_GDBSERVER = 'gdbserver';
const PYOCD_CLI_ARG_PORT = '--port';
const PYOCD_CLI_ARG_CBUILDRUN = '--cbuild-run';

protected async shouldAppendParam(params: string[], paramName: string, commandName?: string): Promise<boolean> {
return !this.hasParam(paramName, params) && (!commandName || await this.hasCommand(commandName));
}
export class PyocdConfigurationProvider extends BaseConfigurationProvider {
protected builtinPyocd = new BuiltinToolPath(PYOCD_BUILTIN_PATH);

protected resolveServerPath(target: TargetConfiguration): void {
const targetServer = target.server;
const useBuiltin = !targetServer || PYOCD_EXECUTABLE_ONLY_REGEXP.test(targetServer);
const builtinUri = useBuiltin ? this.builtinPyocd.getAbsolutePath() : undefined;
if (builtinUri) {
target.server = builtinUri.fsPath;
const updateUri = useBuiltin ? this.builtinPyocd.getAbsolutePath() : undefined;
if (updateUri) {
target.server = updateUri.fsPath;
}
}

protected async resolveServerParameters(debugConfiguration: GDBTargetConfiguration): Promise<GDBTargetConfiguration> {
logger.debug('Resolving pyOCD server parameters');
if (!debugConfiguration.target) {
return debugConfiguration;
}
// server
this.resolveServerPath(debugConfiguration.target);
// serverParameters
const parameters = debugConfiguration.target.serverParameters ??= [];
debugConfiguration.target.serverParameters ??= [];
const parameters = debugConfiguration.target.serverParameters;
// gdbserver
if (await this.shouldAppendParam(parameters, 'gdbserver')) {
if (await this.shouldAppendParameter(parameters, PYOCD_CLI_ARG_GDBSERVER)) {
// Prepend, it must be the first argument
parameters.unshift('gdbserver');
parameters.unshift(PYOCD_CLI_ARG_GDBSERVER);
}
// port (use value defined in 'port' outside 'serverParamters')
const port = debugConfiguration.target?.port;
if (port && await this.shouldAppendParam(parameters, '--port')) {
parameters.push('--port');
parameters.push(`${port}`);
if (port && await this.shouldAppendParameter(parameters, PYOCD_CLI_ARG_PORT)) {
parameters.push(PYOCD_CLI_ARG_PORT, `${port}`);
}
// cbuild-run
const cbuildRunFile = debugConfiguration.cmsis?.cbuildRunFile;
if (cbuildRunFile && await this.shouldAppendParam(parameters, '--cbuild-run')) {
parameters.push('--cbuild-run');
parameters.push(`${cbuildRunFile}`);
if (cbuildRunFile && await this.shouldAppendParameter(parameters, PYOCD_CLI_ARG_CBUILDRUN)) {
parameters.push(PYOCD_CLI_ARG_CBUILDRUN, `${cbuildRunFile}`);
}
return debugConfiguration;
}

public async resolveDebugConfigurationWithSubstitutedVariables(
_folder: vscode.WorkspaceFolder | undefined,
debugConfiguration: vscode.DebugConfiguration,
_token?: vscode.CancellationToken
): Promise<vscode.DebugConfiguration | null | undefined> {
logger.debug('Resolving pyOCD configuration');
const resolvedConfig = await this.resolveServerParameters(debugConfiguration);
return resolvedConfig;
}

}