Skip to content

Commit c5e1239

Browse files
committed
Implement the Continue Forward command.
1 parent 0f1bbdb commit c5e1239

File tree

5 files changed

+133
-16
lines changed

5 files changed

+133
-16
lines changed

example/design_sim.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ int main(int argc, char **argv) {
2020

2121
agent.step();
2222

23-
size_t steps = 0;
23+
size_t cycles = 0;
2424
do {
2525
agent.advance(1_ns);
2626
top.p_clk.set(false);
@@ -30,9 +30,9 @@ int main(int argc, char **argv) {
3030
top.p_clk.set(true);
3131
agent.step();
3232

33-
if (steps == 3)
33+
if (cycles == 3)
3434
agent.breakpoint(CXXRTL_LOCATION);
35-
} while (steps++ < 1000);
35+
} while (cycles++ < 1000);
3636

3737
return 0;
3838
}

package.json

+16-7
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@
102102
{
103103
"command": "rtlDebugger.runSimulation",
104104
"category": "RTL Debugger",
105-
"title": "Run Simulation",
106-
"icon": "$(debug-continue)"
105+
"title": "Run Simulation"
107106
},
108107
{
109108
"command": "rtlDebugger.runSimulationUntil",
@@ -116,6 +115,12 @@
116115
"title": "Pause Simulation",
117116
"icon": "$(debug-pause)"
118117
},
118+
{
119+
"command": "rtlDebugger.continueForward",
120+
"category": "RTL Debugger",
121+
"title": "Continue Forward",
122+
"icon": "$(debug-continue)"
123+
},
119124
{
120125
"command": "rtlDebugger.stepForward",
121126
"category": "RTL Debugger",
@@ -217,6 +222,10 @@
217222
"command": "rtlDebugger.pauseSimulation",
218223
"when": "rtlDebugger.sessionStatus == running && rtlDebugger.simulationStatus == running"
219224
},
225+
{
226+
"command": "rtlDebugger.continueForward",
227+
"when": "rtlDebugger.sessionStatus == running"
228+
},
220229
{
221230
"command": "rtlDebugger.stepForward",
222231
"when": "rtlDebugger.sessionStatus == running"
@@ -242,24 +251,24 @@
242251
"group": "navigation@2"
243252
},
244253
{
245-
"command": "rtlDebugger.runSimulation",
246-
"when": "view == rtlDebugger.sidebar && rtlDebugger.sessionStatus == running && rtlDebugger.simulationStatus == paused",
254+
"command": "rtlDebugger.continueForward",
255+
"when": "view == rtlDebugger.sidebar && rtlDebugger.sessionStatus == running && rtlDebugger.simulationStatus != running",
247256
"group": "navigation@3"
248257
},
249258
{
250259
"command": "rtlDebugger.pauseSimulation",
251260
"when": "view == rtlDebugger.sidebar && rtlDebugger.sessionStatus == running && rtlDebugger.simulationStatus == running",
252-
"group": "navigation@3"
261+
"group": "navigation@4"
253262
},
254263
{
255264
"command": "rtlDebugger.startSession",
256265
"when": "view == rtlDebugger.sidebar && rtlDebugger.sessionStatus == absent",
257-
"group": "navigation@4"
266+
"group": "navigation@10"
258267
},
259268
{
260269
"command": "rtlDebugger.stopSession",
261270
"when": "view == rtlDebugger.sidebar && rtlDebugger.sessionStatus == running",
262-
"group": "navigation@5"
271+
"group": "navigation@11"
263272
}
264273
],
265274
"view/item/context": [

src/debug/session.ts

+109-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as proto from '../cxxrtl/proto';
44
import { ILink } from '../cxxrtl/link';
55
import { Connection } from '../cxxrtl/client';
66
import { TimeInterval, TimePoint } from '../model/time';
7-
import { Diagnostic, Reference, Sample, UnboundReference } from '../model/sample';
7+
import { Diagnostic, DiagnosticType, Reference, Sample, UnboundReference } from '../model/sample';
88
import { Variable } from '../model/variable';
99
import { Scope } from '../model/scope';
1010
import { Location } from '../model/source';
@@ -32,6 +32,11 @@ export interface ISimulationStatus {
3232
nextSampleTime?: TimePoint;
3333
}
3434

35+
export enum SimulationPauseReason {
36+
TimeReached,
37+
DiagnosticsReached,
38+
}
39+
3540
export class Session {
3641
private connection: Connection;
3742

@@ -52,8 +57,10 @@ export class Session {
5257
for (const secondaryLink of this.secondaryLinks) {
5358
secondaryLink.onRecv(event);
5459
}
55-
if (event.event === 'simulation_paused' || event.event === 'simulation_finished') {
56-
await this.querySimulationStatus();
60+
if (event.event === 'simulation_paused') {
61+
await this.handleSimulationPausedEvent(event.cause);
62+
} else if (event.event === 'simulation_finished') {
63+
await this.handleSimulationFinishedEvent();
5764
}
5865
};
5966
this.querySimulationStatus(); // populate nextSampleTime
@@ -313,9 +320,18 @@ export class Session {
313320

314321
private simulationStatusTimeout: NodeJS.Timeout | null = null;
315322

316-
private _onDidChangeSimulationStatus: vscode.EventEmitter<ISimulationStatus> = new vscode.EventEmitter<ISimulationStatus>();
323+
private _onDidChangeSimulationStatus: vscode.EventEmitter<ISimulationStatus> = new vscode.EventEmitter();
317324
readonly onDidChangeSimulationStatus: vscode.Event<ISimulationStatus> = this._onDidChangeSimulationStatus.event;
318325

326+
private _onDidRunSimulation: vscode.EventEmitter<void> = new vscode.EventEmitter();
327+
readonly onDidRunSimulation: vscode.Event<void> = this._onDidRunSimulation.event;
328+
329+
private _onDidPauseSimulation: vscode.EventEmitter<SimulationPauseReason> = new vscode.EventEmitter();
330+
readonly onDidPauseSimulation: vscode.Event<SimulationPauseReason> = this._onDidPauseSimulation.event;
331+
332+
private _onDidFinishSimulation: vscode.EventEmitter<void> = new vscode.EventEmitter();
333+
readonly onDidFinishSimulation: vscode.Event<void> = this._onDidFinishSimulation.event;
334+
319335
private _simulationStatus: ISimulationStatus = {
320336
status: 'paused',
321337
latestTime: TimePoint.ZERO,
@@ -354,23 +370,62 @@ export class Session {
354370
}
355371
}
356372

357-
async runSimulation(options: { untilTime?: TimePoint } = {}): Promise<void> {
373+
async runSimulation(options: {
374+
untilTime?: TimePoint,
375+
untilDiagnostics?: DiagnosticType[]
376+
} = {}): Promise<void> {
358377
await this.connection.runSimulation({
359378
type: 'command',
360379
command: 'run_simulation',
361380
until_time: options.untilTime?.toCXXRTL() ?? null,
362-
until_diagnostics: [],
381+
until_diagnostics: options.untilDiagnostics?.map((type) => <DiagnosticType>type) ?? [],
363382
sample_item_values: true
364383
});
365384
await this.querySimulationStatus();
385+
this._onDidRunSimulation.fire();
366386
}
367387

368388
async pauseSimulation(): Promise<void> {
369389
await this.connection.pauseSimulation({
370390
type: 'command',
371391
command: 'pause_simulation'
372392
});
393+
}
394+
395+
private async handleSimulationPausedEvent(cause: proto.PauseCause): Promise<void> {
396+
if (cause === 'until_time') {
397+
await this.querySimulationStatus();
398+
this._onDidPauseSimulation.fire(SimulationPauseReason.TimeReached);
399+
} else if (cause === 'until_diagnostics') {
400+
// The `until_diagnostics` cause is a little cursed. For `always @(posedge clk)`
401+
// assertions, the pause time will be two steps ahead, and for `always @(*)` ones,
402+
// it will usually be one step ahead. This is because of several fencepost issues with
403+
// both the storage and the retrieval of diagnostics, which are baked into the CXXRTL
404+
// execution and replay model. (Diagnostics recorded from C++ are fine.)
405+
//
406+
// To handle this, rather than relying on the event time, we scan the database for any
407+
// diagnostics since the last time the simulation state was updated. (A diagnostic that
408+
// caused the simulation to be paused must be somewhere between that and the latest
409+
// sample in the database at the time of pausing.) This avoids the need to simulate
410+
// the entire interval twice, as would happen if querying the interval between the "Run
411+
// Simulation Until Diagnostics" command and the time of pausing.
412+
const latestTimeBeforePause = this.simulationStatus.latestTime;
413+
await this.querySimulationStatus();
414+
const latestTimeAfterPause = this.simulationStatus.latestTime;
415+
const diagnosticAt = await this.searchIntervalForDiagnostics(
416+
new TimeInterval(latestTimeBeforePause, latestTimeAfterPause));
417+
if (diagnosticAt === null) {
418+
console.error('[CXXRTL] Paused on diagnostic but no such diagnostics found');
419+
return;
420+
}
421+
this.timeCursor = diagnosticAt;
422+
this._onDidPauseSimulation.fire(SimulationPauseReason.DiagnosticsReached);
423+
}
424+
}
425+
426+
private async handleSimulationFinishedEvent(): Promise<void> {
373427
await this.querySimulationStatus();
428+
this._onDidFinishSimulation.fire();
374429
}
375430

376431
get isSimulationRunning(): boolean {
@@ -456,4 +511,52 @@ export class Session {
456511
}
457512
return this.timeCursor;
458513
}
514+
515+
async continueForward(): Promise<void> {
516+
if (this.timeCursor.lessThan(this.simulationStatus.latestTime)) {
517+
const diagnosticAt = await this.searchIntervalForDiagnostics(
518+
new TimeInterval(this.timeCursor, this.simulationStatus.latestTime));
519+
if (diagnosticAt !== null) {
520+
this.timeCursor = diagnosticAt;
521+
return;
522+
}
523+
}
524+
// No diagnostics between time cursor and end of database; run the simulation.
525+
if (this.simulationStatus.status === 'paused') {
526+
// The pause handler will run `searchIntervalForDiagnostics`.
527+
await this.runSimulation({
528+
untilDiagnostics: [
529+
DiagnosticType.Assert,
530+
DiagnosticType.Assume,
531+
DiagnosticType.Break
532+
]
533+
});
534+
} else if (this.simulationStatus.status === 'finished') {
535+
this.timeCursor = this.simulationStatus.latestTime;
536+
}
537+
}
538+
539+
private async searchIntervalForDiagnostics(interval: TimeInterval): Promise<TimePoint | null> {
540+
const response = await this.connection.queryInterval({
541+
type: 'command',
542+
command: 'query_interval',
543+
interval: interval.toCXXRTL(),
544+
collapse: true,
545+
items: null,
546+
item_values_encoding: null,
547+
diagnostics: true
548+
});
549+
for (const sample of response.samples) {
550+
const sampleTime = TimePoint.fromCXXRTL(sample.time);
551+
if (!sampleTime.greaterThan(interval.begin)) {
552+
continue;
553+
}
554+
for (const diagnostic of sample.diagnostics!) {
555+
if (['break', 'assert', 'assume'].includes(diagnostic.type)) {
556+
return sampleTime;
557+
}
558+
}
559+
}
560+
return null;
561+
}
459562
}

src/debugger.ts

+3
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ export class CXXRTLDebugger {
8383
this.session.onDidChangeSimulationStatus((status) => {
8484
this.setSimulationStatus(status.status as CXXRTLSimulationStatus);
8585
});
86+
this.session.onDidFinishSimulation(() => {
87+
vscode.window.showInformationMessage('Simulation has finished.');
88+
});
8689
this.setSessionStatus(CXXRTLSessionStatus.Running);
8790
this._onDidChangeSession.fire(this.session);
8891
this.setSimulationStatus(

src/extension.ts

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export function activate(context: vscode.ExtensionContext) {
6161
rtlDebugger.session!.stepBackward()));
6262
context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.stepForward', () =>
6363
rtlDebugger.session!.stepForward()));
64+
context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.continueForward', () =>
65+
rtlDebugger.session!.continueForward()));
6466
context.subscriptions.push(vscode.commands.registerCommand('rtlDebugger.goToTime', async () => {
6567
const goToTime = await inputTime({ prompt: 'Enter the time to examine the state at.' });
6668
if (goToTime !== undefined) {

0 commit comments

Comments
 (0)