Skip to content

Initial Surfer support #5

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

Open
wants to merge 11 commits into
base: surfer
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
3 changes: 3 additions & 0 deletions example/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"${workspaceFolder}/design_sim"
],
"rtlDebugger.watchList": [
{
"id": "top data"
},
{
"id": "top data"
}
Expand Down
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
2 changes: 2 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,7 @@ export class CXXRTLDebugger {
private statusBarItem: StatusBarItem;
private terminal: vscode.Terminal | null = null;
session: Session | null = null;
waveformProvider: WaveformProvider | null = null;

// Session properties.

Expand Down
14 changes: 13 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { WaveformProvider } from './ui/waveform';
export function activate(context: vscode.ExtensionContext) {
const rtlDebugger = new CXXRTLDebugger();

console.log('Attached');

const sidebarTreeDataProvider = new sidebar.TreeDataProvider(rtlDebugger);
const sidebarTreeView = vscode.window.createTreeView('rtlDebugger.sidebar', {
treeDataProvider: sidebarTreeDataProvider
Expand Down Expand Up @@ -75,6 +77,12 @@ export function activate(context: vscode.ExtensionContext) {
}
}));

context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.addToWaveform', (treeItem) => {
if (rtlDebugger.waveformProvider) {
rtlDebugger.waveformProvider.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 @@ -89,7 +97,10 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.unWatchVariable', (treeItem) =>
globalWatchList.remove(treeItem.metadata.index)));


console.log('Registering rtlDebugger.browseWaveforms');
context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.browseWaveforms', () => {
console.log('Running browseWaveforms');
const webviewPanel = vscode.window.createWebviewPanel(
'rtlDebugger.waveforms',
'Waveforms', {
Expand All @@ -99,7 +110,8 @@ export function activate(context: vscode.ExtensionContext) {
retainContextWhenHidden: true,
});
const bundleRoot = vscode.Uri.joinPath(context.extensionUri, 'out/');
context.subscriptions.push(new WaveformProvider(rtlDebugger, webviewPanel, bundleRoot));
rtlDebugger.waveformProvider = new WaveformProvider(rtlDebugger, webviewPanel, bundleRoot)
context.subscriptions.push(rtlDebugger.waveformProvider);
}));

// For an unknown reason, the `vscode.open` command (which does the exact same thing) ignores the options.
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
44 changes: 40 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';

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,52 @@ document.addEventListener('DOMContentLoaded', async () => {
});

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_scmessage') {
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_csmessage', 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) {
console.log("[WCP] Receiving wcp sc message: {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
58 changes: 53 additions & 5 deletions src/ui/waveform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,32 @@
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 }
;
| { type: 'restore', state: any }
// TODO: Proper type here
| { type: 'cxxrtl_scmessage', message: ServerPacketString }
| { type: 'wcp_cs_message', message: string }
;

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

export class WaveformProvider {
constructor(
Expand All @@ -23,6 +40,19 @@
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("Receving scmessage ", message.asString());
// console.log("Running on recv for ", message)
await this.sendMessage({ type: "cxxrtl_scmessage", message: new ServerPacketString(message.asString()) })
};
} else {
throw new Error('Failed to create secondary debugger link');
}
}

dispose() {
Expand All @@ -45,8 +75,26 @@
console.log('[RTL Debugger] [WaveformProvider] Ready');
} else if (message.type === 'crash') {
console.log('[RTL Debugger] [WaveformProvider] Crash:', message.error);
} else if (message.type == 'cxxrtl_csmessage') {

Check failure on line 78 in src/ui/waveform.ts

View workflow job for this annotation

GitHub Actions / build

Expected '===' and instead saw '=='
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') {

Check failure on line 82 in src/ui/waveform.ts

View workflow job for this annotation

GitHub Actions / build

Expected '===' and instead saw '=='
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 ddb282
Loading