Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Surfer support #5

Open
wants to merge 11 commits into
base: surfer
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@
"command": "rtlDebugger.browseWaveforms",
"category": "RTL Debugger",
"title": "Browse Waveforms"
},
{
"command": "rtlDebugger.addToWaveform",
"category": "RTL Debugger",
"title": "Add to Waveform",
"icon": "$(keybindings-add)"
}
],
"viewsContainers": {
Expand Down Expand Up @@ -319,6 +325,11 @@
"command": "rtlDebugger.unWatchVariable",
"when": "view == rtlDebugger.sidebar && viewItem =~ /inWatchList/",
"group": "inline"
},
{
"command": "rtlDebugger.addToWaveform",
"when": "view == rtlDebugger.sidebar && viewItem =~ /variable/",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong, it should check if the waveform viewer is active but I can't figure out how

"group": "inline"
}
],
"rtlDebugger.setRadix": [
Expand Down
1 change: 1 addition & 0 deletions src/debug/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export class Session {
onRecv: async (serverPacket) => {},
onDone: async () => {},
};
this.secondaryLinks.push(secondaryLink);
return secondaryLink;
}

Expand Down
4 changes: 4 additions & 0 deletions src/debugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as vscode from 'vscode';
import { NodeStreamLink } from './cxxrtl/link';
import { StatusBarItem } from './ui/status';
import { Session } from './debug/session';
import { WaveformProvider } from './ui/waveform';

export enum CXXRTLSimulationStatus {
Paused = 'paused',
Expand All @@ -21,6 +22,9 @@ export class CXXRTLDebugger {
private statusBarItem: StatusBarItem;
private terminal: vscode.Terminal | null = null;
session: Session | null = null;
nextWaveformviewId: number = 0;
waveformProviders: Map<string, WaveformProvider> = new Map();
lastActiveWaveformTab: string | null = null;

// Session properties.

Expand Down
50 changes: 45 additions & 5 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ export function activate(context: vscode.ExtensionContext) {
}
}));

context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.addToWaveform', (treeItem) => {
if (rtlDebugger.lastActiveWaveformTab) {
console.log(rtlDebugger.lastActiveWaveformTab);
const target = rtlDebugger.waveformProviders.get(rtlDebugger.lastActiveWaveformTab);
if (target) {
target.addVariable(treeItem.designation.variable);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it would allow for exactly one Surfer tab to exist.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, so far I've been working under the assumption that this would be the case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing code doesn't enforce it (intentionally--I think that would be fairly limiting if it was the only thing possible...)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, makes sense. It does open a bunch of UI questions about where interactions should happen, for example which surfer should add a variable when you click +

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the VS Code UI maintains an "active tab" or "selected tab" or something like that. Barring that, simply the last one that received focus will match user expectations.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed an update to allow multiple viewers. I'm not very happy with the code since it requires maintaining my own list of tab->webview mappings that are based on the weird ViewType string but as far as I can tell, there is no other convenient way to do it...

}));

context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.setRadix.2', (treeItem) =>
globalVariableOptions.update(treeItem.designation.variable.cxxrtlIdentifier, { radix: 2 })));
context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.setRadix.8', (treeItem) =>
Expand All @@ -90,18 +100,48 @@ export function activate(context: vscode.ExtensionContext) {
globalWatchList.remove(treeItem.metadata.index)));

context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.browseWaveforms', () => {
const viewKey = `rtlDebugger.waveforms.${rtlDebugger.nextWaveformviewId++}`;
const webviewPanel = vscode.window.createWebviewPanel(
'rtlDebugger.waveforms',
'Waveforms', {
viewKey,
'Waveforms',
{
viewColumn: vscode.ViewColumn.Beside,
}, {
},
{
enableScripts: true,
retainContextWhenHidden: true,
});
}
);
console.log(`Creating web view panel with viewType ${webviewPanel.viewType}`);
const bundleRoot = vscode.Uri.joinPath(context.extensionUri, 'out/');
context.subscriptions.push(new WaveformProvider(rtlDebugger, webviewPanel, bundleRoot));
const waveformProvider = new WaveformProvider(rtlDebugger, webviewPanel, bundleRoot);
rtlDebugger.waveformProviders.set(viewKey, waveformProvider);
rtlDebugger.lastActiveWaveformTab = viewKey;
context.subscriptions.push(waveformProvider);
}));

vscode.window.tabGroups.onDidChangeTabs(event => {
const activeWaveformTab = event.changed.map((tab, _) => {
if (tab.input instanceof vscode.TabInputWebview) {
console.log(`${tab.input.viewType}`);

const key = tab.input.viewType.match(/.*(rtlDebugger.waveforms\.\d+)/);

if (key) {
return key[1];
} else {
return null;
}
} else {
return null;
}
}).find((id) => id !== null);

if (activeWaveformTab) {
rtlDebugger.lastActiveWaveformTab = activeWaveformTab;
}
});

// For an unknown reason, the `vscode.open` command (which does the exact same thing) ignores the options.
context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.openDocument',
(uri: vscode.Uri, options: vscode.TextDocumentShowOptions) => {
Expand Down
4 changes: 4 additions & 0 deletions src/model/variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export abstract class Variable {
get cxxrtlIdentifier(): string {
return this.fullName.join(' ');
}

get wcpIdentifier(): string {
return this.fullName.join(' ');
}
}

export class ScalarVariable extends Variable {
Expand Down
43 changes: 39 additions & 4 deletions src/surfer/embed.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import libsurferInit, * as libsurfer from 'libsurfer';

Check failure on line 1 in src/surfer/embed.ts

View workflow job for this annotation

GitHub Actions / build

Cannot find module 'libsurfer' or its corresponding type declarations.

import type { ExtensionToWebviewMessage, WebviewToExtensionMessage } from '../ui/waveform';
import { ClientPacketString, type ExtensionToWebviewMessage, type WebviewToExtensionMessage } from '../ui/waveform';

function libsurferInjectMessage(message: any) {
libsurfer.inject_message(JSON.stringify(message));
}


document.addEventListener('DOMContentLoaded', async () => {
const vscode = acquireVsCodeApi();
const canvas = <HTMLCanvasElement>document.getElementById('canvas');
Expand All @@ -19,17 +20,51 @@
});

const postMessage = <(message: WebviewToExtensionMessage) => void>vscode.postMessage;
window.addEventListener('message', (event: MessageEvent<ExtensionToWebviewMessage>) => {
window.addEventListener('message', async (event: MessageEvent<ExtensionToWebviewMessage>) => {
const message = event.data;
console.error('[RTL Debugger] [surferEmbed] Unhandled extension to webview message', message);
if (message.type === 'cxxrtl_sc_message') {
await libsurfer.on_cxxrtl_sc_message(message.message.inner);
} else if (message.type === 'wcp_cs_message') {
await libsurfer.handle_wcp_cs_message(message.message);
} else {
console.error('[RTL Debugger] [surferEmbed] Unhandled extension to webview message', message);
}
});

const handle_cxxrtl_cs_messages = async () => {
while (true) {
const message = await libsurfer.cxxrtl_cs_message();
if (message) {
postMessage({type: 'cxxrtl_cs_message', message: new ClientPacketString(message)});
} else {
throw Error('Got an undefined message from Surfer. Its client probably disconnected');
}
}
};

const handle_wcp_sc_messages = async () => {
while (true) {
const message = await libsurfer.next_wcp_sc_message();
if (message) {
postMessage({type: 'wcp_sc_message', message: message});
} else {
throw Error('Got an undefined message from Surfer. Its client probably disconnected');
}
}
};

try {
await libsurferInit();
await new libsurfer.WebHandle().start(canvas);

handle_cxxrtl_cs_messages();
handle_wcp_sc_messages();

await libsurfer.start_cxxrtl();
await libsurfer.start_wcp();

libsurferInjectMessage('ToggleMenu'); // turn off menu
libsurferInjectMessage('ToggleStatusBar'); // turn off status bar
libsurferInjectMessage('ToggleStatusbar'); // turn off status bar
libsurferInjectMessage('ToggleSidePanel');
libsurferInjectMessage({ SelectTheme: 'dark+' }); // pick VS Code like theme

Expand Down
4 changes: 2 additions & 2 deletions src/ui/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@ class ScopeTreeItem extends TreeItem {
for (const variable of variables) {
if (variable instanceof ScalarVariable) {
children.push(new ScalarTreeItem(this.provider, variable.designation(),
variable.width > 1 ? 'canWatch|canSetRadix' : 'canWatch'));
variable.width > 1 ? 'canWatch|canSetRadix|variable' : 'canWatch|variable'));
}
if (variable instanceof MemoryVariable) {
children.push(new ArrayTreeItem(this.provider, variable.designation(), 'canWatch|canSetRadix'));
children.push(new ArrayTreeItem(this.provider, variable.designation(), 'canWatch|canSetRadix|variable'));
}
}
return children;
Expand Down
47 changes: 47 additions & 0 deletions src/ui/waveform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,31 @@ import * as vscode from 'vscode';
import { CXXRTLDebugger } from '../debugger';
// @ts-ignore
import embedHtml from '../surfer/embed.html';
import { ILink, Packet } from '../cxxrtl/link';
import { ClientPacket } from '../cxxrtl/proto';
import { Variable } from '../model/variable';

export class ClientPacketString {
constructor(public inner: string) { }
}
export class ServerPacketString {
constructor(public inner: string) { }
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These classes don't seem like they're doing anything useful?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added them because I wanted type safety when passing around different kinds of strings



export type ExtensionToWebviewMessage =
| { type: 'restore', state: any }
// TODO: Proper type here
| { type: 'cxxrtl_sc_message', message: ServerPacketString }
| { type: 'wcp_cs_message', message: string }
;

export type WebviewToExtensionMessage =
| { type: 'ready' }
| { type: 'crash', error: any }
// TODO: Proper type here
| { type: 'cxxrtl_cs_message', message: ClientPacketString }
| { type: 'wcp_sc_message', message: string }
;

export class WaveformProvider {
Expand All @@ -23,6 +40,18 @@ export class WaveformProvider {
this.webview.asWebviewUri(bundleRoot).toString());
this.webview.onDidReceiveMessage(this.processMessage.bind(this));
this.webview.html = webviewHtml;
const debuggerLink = rtlDebugger.session?.createSecondaryLink();

// TODO: Correct way to handle errors?
if (debuggerLink) {
this.debuggerLink = debuggerLink;
this.debuggerLink.onRecv = async (message) => {
// console.log("Running on recv for ", message)
await this.sendMessage({ type: 'cxxrtl_sc_message', message: new ServerPacketString(message.asString()) });
};
} else {
throw new Error('Failed to create secondary debugger link');
}
}

dispose() {
Expand All @@ -45,8 +74,26 @@ export class WaveformProvider {
console.log('[RTL Debugger] [WaveformProvider] Ready');
} else if (message.type === 'crash') {
console.log('[RTL Debugger] [WaveformProvider] Crash:', message.error);
} else if (message.type === 'cxxrtl_cs_message') {
console.log('[RTL Debugger] [WaveformProvider] Got CSMessage', message.message);
const packet: Packet<ClientPacket> = Packet.fromString(message.message.inner);
await this.debuggerLink.send(packet);
} else if (message.type === 'wcp_sc_message') {
console.log('[RTL Debugger] [WaveformProvider] Got WCP SC message', message.message);
} else {
console.error('[RTL Debugger] [WaveformProvider] Unhandled webview to extension message:', message);
}
}

async addVariable(variable: Variable) {
// TODO: How should we handle the callbacks here?
const message = JSON.stringify({
type: 'command',
command: 'add_variables',
names: [variable.wcpIdentifier]
});
this.sendMessage({type: 'wcp_cs_message', message});
}

private debuggerLink: ILink;
}
2 changes: 1 addition & 1 deletion vendor/surfer
Submodule surfer updated from d79976 to 3dd32c
Loading