Skip to content

Commit d866799

Browse files
committed
[WIP] Defmt print support
Signed-off-by: paulober <[email protected]>
1 parent 33b8ce3 commit d866799

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
@@ -35,6 +35,8 @@ node_modules/**
3535
!scripts/Pico.code-profile
3636
!scripts/raspberrypi-swd.cfg
3737
!data/**
38+
!scripts/rttDecoder.mjs
39+
!scripts/rttDecoder.js
3840
scripts/*.ps1
3941
scripts/fix_windows_reg.py
4042
scripts/vscodeUninstaller.mjs

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,12 @@
238238
"title": "Clean CMake",
239239
"category": "Raspberry Pi Pico",
240240
"enablement": "raspberry-pi-pico.isPicoProject && !raspberry-pi-pico.isRustProject"
241+
},
242+
{
243+
"command": "raspberry-pi-pico.getRTTDecoderPath",
244+
"title": "Get RTT Decoder module path",
245+
"category": "Raspberry Pi Pico",
246+
"enablement": "false"
241247
}
242248
],
243249
"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() {
@@ -432,7 +432,7 @@ export class GetOpenOCDRootCommand extends CommandWithResult<
432432
this.running = true;
433433

434434
// check if it is installed if not install it
435-
const result = await downloadAndInstallOpenOCD(openOCDVersion);
435+
const result = await downloadAndInstallOpenOCD(OPENOCD_VERSION);
436436

437437
if (result === null || !result) {
438438
this.running = false;
@@ -442,7 +442,7 @@ export class GetOpenOCDRootCommand extends CommandWithResult<
442442

443443
this.running = false;
444444

445-
return buildOpenOCDPath(openOCDVersion);
445+
return buildOpenOCDPath(OPENOCD_VERSION);
446446
}
447447
}
448448

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
@@ -64,7 +64,6 @@ import { getSupportedToolchains } from "./utils/toolchainUtil.mjs";
6464
import {
6565
NewProjectPanel,
6666
getWebviewOptions,
67-
openOCDVersion,
6867
} from "./webview/newProjectPanel.mjs";
6968
import GithubApiCache from "./utils/githubApiCache.mjs";
7069
import ClearGithubApiCacheCommand from "./commands/clearGithubApiCache.mjs";
@@ -93,6 +92,8 @@ import {
9392
} from "./utils/rustUtil.mjs";
9493
import State from "./state.mjs";
9594
import { NewRustProjectPanel } from "./webview/newRustProjectPanel.mjs";
95+
import GetRTTDecoderPathCommand from "./commands/rttDecoder.mjs";
96+
import { OPENOCD_VERSION } from "./utils/sharedConstants.mjs";
9697

9798
export async function activate(context: ExtensionContext): Promise<void> {
9899
Logger.info(LoggerSource.extension, "Extension activation triggered");
@@ -144,6 +145,7 @@ export async function activate(context: ExtensionContext): Promise<void> {
144145
new NewExampleProjectCommand(context.extensionUri),
145146
new UninstallPicoSDKCommand(),
146147
new CleanCMakeCommand(ui),
148+
new GetRTTDecoderPathCommand(context.extensionUri),
147149
];
148150

149151
// register all command handlers
@@ -566,7 +568,7 @@ export async function activate(context: ExtensionContext): Promise<void> {
566568
},
567569
async progress => {
568570
const result = await downloadAndInstallOpenOCD(
569-
openOCDVersion,
571+
OPENOCD_VERSION,
570572
(prog: GotProgress) => {
571573
const percent = prog.percent * 100;
572574
progress.report({

src/utils/download.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ import { got, type Progress } from "got";
4646
import { pipeline as streamPipeline } from "node:stream/promises";
4747
import {
4848
CURRENT_PYTHON_VERSION,
49+
OPENOCD_VERSION,
4950
WINDOWS_ARM64_PYTHON_DOWNLOAD_URL,
5051
WINDOWS_X86_PYTHON_DOWNLOAD_URL,
5152
} from "./sharedConstants.mjs";
5253
import { compareGe } from "./semverUtil.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 } = {
@@ -1429,7 +1429,7 @@ export async function installLatestRustRequirements(
14291429
async progress => {
14301430
let progressState = 0;
14311431

1432-
return downloadAndInstallOpenOCD(openOCDVersion, (prog: Progress) => {
1432+
return downloadAndInstallOpenOCD(OPENOCD_VERSION, (prog: Progress) => {
14331433
const percent = prog.percent * 100;
14341434
progress.report({ increment: percent - progressState });
14351435
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
@@ -551,8 +551,8 @@ export async function downloadAndInstallRust(): Promise<boolean> {
551551
rmSync(latestPath, { recursive: true, force: true });
552552
}
553553

554-
// install probe-rs-tools
555-
const probeRsTools = "probe-rs-tools";
554+
// or install probe-rs-tools
555+
const probeRsTools = "defmt-print";
556556
result = await cargoInstall(probeRsTools, true);
557557
if (!result) {
558558
void window.showErrorMessage(

0 commit comments

Comments
 (0)