Skip to content

Commit 6931f3a

Browse files
committed
[WIP] Defmt print support
Signed-off-by: paulober <[email protected]>
1 parent 2143185 commit 6931f3a

File tree

12 files changed

+281
-16
lines changed

12 files changed

+281
-16
lines changed

.vscodeignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ tmp.py
3636
!scripts/Pico.code-profile
3737
!scripts/raspberrypi-swd.cfg
3838
!data/**
39+
!scripts/rttDecoder.mjs
40+
!scripts/rttDecoder.js
3941
scripts/*.ps1
4042
scripts/fix_windows_reg.py
4143
scripts/vscodeUninstaller.mjs

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@
220220
"title": "Flash Pico Project (SWD)",
221221
"category": "Raspberry Pi Pico",
222222
"enablement": "raspberry-pi-pico.isPicoProject && !raspberry-pi-pico.isRustProject"
223+
},
224+
{
225+
"command": "raspberry-pi-pico.getRTTDecoderPath",
226+
"title": "Get RTT Decoder module path",
227+
"category": "Raspberry Pi Pico",
228+
"enablement": "false"
223229
}
224230
],
225231
"configuration": {

scripts/rttDecoder.cjs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
const { spawn } = require('child_process');
2+
3+
class DefmtDecoder {
4+
constructor() {
5+
this.process = null;
6+
this.elfPath = null;
7+
this.displayOutput = null;
8+
this.graphData = null;
9+
this.ports = [];
10+
}
11+
12+
init(config, displayOutput, graphData) {
13+
// Store the callbacks and elfPath from the config
14+
this.elfPath = config.elfPath;
15+
this.displayOutput = displayOutput;
16+
this.graphData = graphData;
17+
this.ports = config.ports;
18+
19+
const defmtPrintPath = `${process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME}/.cargo/bin/defmt-print${process.platform === "win32" ? ".exe" : ""}`;
20+
21+
// Spawn the defmt-print process with the provided ELF path
22+
this.process = spawn(defmtPrintPath, ['-e', this.elfPath, "stdin"]);
23+
24+
// Handle data from defmt-print stdout and relay it to the displayOutput callback
25+
this.process.stdout.on('data', (data) => {
26+
if (this.displayOutput) {
27+
this.displayOutput(data.toString());
28+
}
29+
});
30+
31+
// Handle errors from defmt-print stderr
32+
this.process.stderr.on('data', (data) => {
33+
if (this.displayOutput) {
34+
this.displayOutput(data.toString());
35+
}
36+
});
37+
38+
// Handle when the process closes
39+
this.process.on('close', (code) => {
40+
if (this.displayOutput) {
41+
this.displayOutput(`Decoding process exited with code: ${code}`);
42+
}
43+
});
44+
}
45+
46+
sendData(input) {
47+
// Write input data to defmt-print's stdin
48+
try {
49+
if (this.process && this.process.stdin.writable) {
50+
this.process.stdin.write(input);
51+
return;
52+
}
53+
} catch { }
54+
55+
throw new Error('Process stdin is not writable.');
56+
}
57+
58+
// Expected methods from the SWODecoder API conforming to the AdvancedDecoder interface
59+
60+
typeName() {
61+
return 'DefmtDecoder';
62+
}
63+
64+
outputLabel() {
65+
return 'RPi Pico';
66+
}
67+
68+
softwareEvent(port, data) {
69+
if (this.ports.indexOf(port) !== -1) {
70+
// Handle the software event, potentially by sending data to defmt-print stdin
71+
this.sendData(data);
72+
}
73+
}
74+
75+
synchronized() {
76+
// Handle the synchronized event
77+
if (this.displayOutput) {
78+
this.displayOutput('Synchronized');
79+
}
80+
}
81+
82+
lostSynchronization() {
83+
// Handle the lost synchronization event
84+
if (this.displayOutput) {
85+
this.displayOutput('Lost synchronization');
86+
}
87+
}
88+
89+
dispose() {
90+
// Clean up the process
91+
if (this.process) {
92+
this.process.kill();
93+
this.process = null;
94+
}
95+
}
96+
}
97+
98+
module.exports = exports = DefmtDecoder;

scripts/rttDecoder.mjs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { spawn } from 'child_process';
2+
import EventEmitter from 'events';
3+
4+
/*
5+
interface AdvancedDecoder {
6+
init(
7+
config: SWOAdvancedDecoderConfig,
8+
outputData: (output: string) => void,
9+
graphData: (data: number, id: string) => void
10+
): void;
11+
typeName(): string;
12+
outputLabel(): string;
13+
softwareEvent(port: number, data: Buffer): void;
14+
synchronized(): void;
15+
lostSynchronization(): void;
16+
}*/
17+
18+
class DefmtDecoder extends EventEmitter {
19+
constructor() {
20+
this.process = null;
21+
this.elfPath = null;
22+
this.displayOutput = null;
23+
this.graphData = null;
24+
}
25+
26+
init(config, displayOutput, graphData) {
27+
// Store the callbacks and elfPath from the config
28+
this.elfPath = config.config.elfPath;
29+
this.displayOutput = displayOutput;
30+
this.graphData = graphData;
31+
32+
const defmtPrintPath = `${process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME}/.cargo/bin/defmt-print${process.platform === "win32" ? ".exe" : ""}`;
33+
34+
// Spawn the defmt-print process with the provided ELF path
35+
this.process = spawn(defmtPrintPath, ['-e', this.elfPath, "stdin"]);
36+
37+
// Handle data from defmt-print stdout and relay it to the displayOutput callback
38+
this.process.stdout.on('data', (data) => {
39+
if (this.displayOutput) {
40+
this.displayOutput(data.toString());
41+
}
42+
});
43+
44+
// Handle errors from defmt-print stderr
45+
this.process.stderr.on('data', (data) => {
46+
if (this.displayOutput) {
47+
//this.displayOutput(`Error: ${data.toString()}`);
48+
this.displayOutput(data.toString());
49+
}
50+
});
51+
52+
// Handle when the process closes
53+
this.process.on('close', (code) => {
54+
if (this.displayOutput) {
55+
this.displayOutput(`Decoding process exited with code: ${code}`);
56+
}
57+
});
58+
}
59+
60+
//sendData(input: Buffer): void;
61+
sendData(input) {
62+
// Write input data to defmt-print's stdin
63+
try {
64+
if (this.process && this.process.stdin.writable) {
65+
this.process.stdin.write(input);
66+
return;
67+
}
68+
} catch { }
69+
70+
throw new Error('Process stdin is not writable.');
71+
}
72+
73+
// Expected methods from the SWODecoder API conforming to the AdvancedDecoder interface
74+
75+
//typeName(): string;
76+
typeName() {
77+
return 'DefmtDecoder';
78+
}
79+
80+
//outputLabel(): string;
81+
outputLabel() {
82+
return 'RPi Pico';
83+
}
84+
85+
//softwareEvent(port: number, data: Buffer): void;
86+
softwareEvent(port, data) {
87+
if (this.ports.indexOf(port) !== -1) {
88+
// Handle the software event, potentially by sending data to defmt-print stdin
89+
this.sendData(data);
90+
}
91+
}
92+
93+
//synchronized(): void;
94+
synchronized() {
95+
// Handle the synchronized event
96+
if (this.displayOutput) {
97+
this.displayOutput('Synchronized');
98+
}
99+
}
100+
101+
//lostSynchronization(): void;
102+
lostSynchronization() {
103+
// Handle the lost synchronization event
104+
if (this.displayOutput) {
105+
this.displayOutput('Lost synchronization');
106+
}
107+
}
108+
109+
// own dispose method
110+
111+
dispose() {
112+
// Clean up the process
113+
if (this.process) {
114+
this.process.kill();
115+
this.process = null;
116+
}
117+
this.emit('dispose');
118+
}
119+
}
120+
121+
export default DefmtDecoder;

src/commands/getPaths.mts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import Settings, { SettingsKey } from "../settings.mjs";
2020
import which from "which";
2121
import { execSync } from "child_process";
2222
import { getPicotoolReleases } from "../utils/githubREST.mjs";
23-
import { openOCDVersion } from "../webview/newProjectPanel.mjs";
2423
import State from "../state.mjs";
2524
import VersionBundlesLoader from "../utils/versionBundles.mjs";
2625
import { getSupportedToolchains } from "../utils/toolchainUtil.mjs";
2726
import Logger from "../logger.mjs";
2827
import { rustProjectGetSelectedChip } from "../utils/rustUtil.mjs";
28+
import { OPENOCD_VERSION } from "../utils/sharedConstants.mjs";
2929

3030
export class GetPythonPathCommand extends CommandWithResult<string> {
3131
constructor() {
@@ -394,7 +394,7 @@ export class GetOpenOCDRootCommand extends CommandWithResult<
394394
this.running = true;
395395

396396
// check if it is installed if not install it
397-
const result = await downloadAndInstallOpenOCD(openOCDVersion);
397+
const result = await downloadAndInstallOpenOCD(OPENOCD_VERSION);
398398

399399
if (result === null || !result) {
400400
this.running = false;
@@ -404,7 +404,7 @@ export class GetOpenOCDRootCommand extends CommandWithResult<
404404

405405
this.running = false;
406406

407-
return buildOpenOCDPath(openOCDVersion);
407+
return buildOpenOCDPath(OPENOCD_VERSION);
408408
}
409409
}
410410

src/commands/rttDecoder.mts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Logger from "../logger.mjs";
2+
import { CommandWithResult } from "./command.mjs";
3+
import { Uri } from "vscode";
4+
5+
export default class GetRTTDecoderPathCommand extends CommandWithResult<string> {
6+
private readonly _logger = new Logger("GetRTTDecoderPathCommand");
7+
8+
public static readonly id = "getRTTDecoderPath";
9+
10+
constructor(private readonly _extensionUri: Uri) {
11+
super(GetRTTDecoderPathCommand.id);
12+
}
13+
14+
execute(): string {
15+
this._logger.debug("Retrieving RTT decoder path");
16+
17+
return Uri.joinPath(this._extensionUri, "scripts", "rttDecoder.cjs").fsPath;
18+
}
19+
}

src/extension.mts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ import { getSupportedToolchains } from "./utils/toolchainUtil.mjs";
6161
import {
6262
NewProjectPanel,
6363
getWebviewOptions,
64-
openOCDVersion,
6564
} from "./webview/newProjectPanel.mjs";
6665
import GithubApiCache from "./utils/githubApiCache.mjs";
6766
import ClearGithubApiCacheCommand from "./commands/clearGithubApiCache.mjs";
@@ -87,6 +86,8 @@ import {
8786
} from "./utils/rustUtil.mjs";
8887
import State from "./state.mjs";
8988
import { NewRustProjectPanel } from "./webview/newRustProjectPanel.mjs";
89+
import GetRTTDecoderPathCommand from "./commands/rttDecoder.mjs";
90+
import { OPENOCD_VERSION } from "./utils/sharedConstants.mjs";
9091

9192
export async function activate(context: ExtensionContext): Promise<void> {
9293
Logger.info(LoggerSource.extension, "Extension activation triggered");
@@ -135,6 +136,7 @@ export async function activate(context: ExtensionContext): Promise<void> {
135136
new ImportProjectCommand(context.extensionUri),
136137
new NewExampleProjectCommand(context.extensionUri),
137138
new UninstallPicoSDKCommand(),
139+
new GetRTTDecoderPathCommand(context.extensionUri),
138140
];
139141

140142
// register all command handlers
@@ -537,7 +539,7 @@ export async function activate(context: ExtensionContext): Promise<void> {
537539
},
538540
async progress => {
539541
const result = await downloadAndInstallOpenOCD(
540-
openOCDVersion,
542+
OPENOCD_VERSION,
541543
(prog: GotProgress) => {
542544
const percent = prog.percent * 100;
543545
progress.report({

src/utils/download.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ import { got, type Progress } from "got";
4747
import { pipeline as streamPipeline } from "node:stream/promises";
4848
import {
4949
CURRENT_PYTHON_VERSION,
50+
OPENOCD_VERSION,
5051
WINDOWS_ARM64_PYTHON_DOWNLOAD_URL,
5152
WINDOWS_X86_PYTHON_DOWNLOAD_URL,
5253
} from "./sharedConstants.mjs";
5354
import VersionBundlesLoader from "./versionBundles.mjs";
54-
import { openOCDVersion } from "../webview/newProjectPanel.mjs";
5555

5656
/// Translate nodejs platform names to ninja platform names
5757
const NINJA_PLATFORMS: { [key: string]: string } = {
@@ -1390,7 +1390,7 @@ export async function installLatestRustRequirements(
13901390
async progress => {
13911391
let progressState = 0;
13921392

1393-
return downloadAndInstallOpenOCD(openOCDVersion, (prog: Progress) => {
1393+
return downloadAndInstallOpenOCD(OPENOCD_VERSION, (prog: Progress) => {
13941394
const percent = prog.percent * 100;
13951395
progress.report({ increment: percent - progressState });
13961396
progressState = percent;

src/utils/projectGeneration/projectRust.mts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async function generateVSCodeConfig(projectRoot: string): Promise<boolean> {
7070
openOCDLaunchCommands: ["adapter speed 5000"],
7171
preLaunchTask: "Compile Project (debug)",
7272
// TODO: does currently not work
73-
rttConfig: {
73+
/*rttConfig: {
7474
enabled: true,
7575
clearSearch: true,
7676
address: "0x2003fbc0",
@@ -86,6 +86,23 @@ async function generateVSCodeConfig(projectRoot: string): Promise<boolean> {
8686
port: 0,
8787
},
8888
],
89+
},*/
90+
rttConfig: {
91+
enabled: true,
92+
address: "auto",
93+
decoders: [
94+
{
95+
label: "RPi Pico",
96+
type: "advanced",
97+
decoder: "${command:raspberry-pi-pico.getRTTDecoderPath}",
98+
inputmode: "disabled",
99+
noprompt: true,
100+
ports: [0],
101+
config: {
102+
elfPath: "${command:raspberry-pi-pico.launchTargetPath}",
103+
},
104+
},
105+
],
89106
},
90107
},
91108
],

src/utils/rustUtil.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,8 @@ export async function downloadAndInstallRust(): Promise<boolean> {
340340
return false;
341341
}
342342

343-
// install probe-rs-tools
344-
const probeRsTools = "probe-rs-tools";
343+
// or install probe-rs-tools
344+
const probeRsTools = "defmt-print";
345345
result = await cargoInstall(probeRsTools, true);
346346
if (!result) {
347347
void window.showErrorMessage(

src/utils/sharedConstants.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export const WINDOWS_ARM64_PYTHON_DOWNLOAD_URL =
55
export const CURRENT_PYTHON_VERSION = "3.12.6";
66

77
export const CURRENT_DATA_VERSION = "0.17.0";
8+
export const OPENOCD_VERSION = "0.12.0+dev";

0 commit comments

Comments
 (0)