Skip to content

Commit d3320b3

Browse files
committed
feat: vcd export for logic analyzer data
1 parent 151d62f commit d3320b3

File tree

7 files changed

+82
-2
lines changed

7 files changed

+82
-2
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ cd esp-idf-hello-world
3636
wokwi-cli .
3737
```
3838

39+
### Logic Analyzer VCD Export
40+
41+
If your diagram includes a [logic analyzer](https://docs.wokwi.com/parts/wokwi-logic-analyzer), you can export the captured signals to a VCD file:
42+
43+
```bash
44+
wokwi-cli . --vcd-file logic.vcd
45+
```
46+
3947
## Configuration Wizard
4048

4149
To generate a `wokwi.toml` and a default `diagram.json` files for your project, run:

packages/cli/src/commands/simulate.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ interface SimulateOptions {
4646
screenshotFile?: string;
4747
timeoutExitCode?: string;
4848
quiet?: boolean;
49+
vcdFile?: string;
4950
}
5051

5152
export function simulateCommand(program: Command): void {
@@ -64,6 +65,7 @@ export function simulateCommand(program: Command): void {
6465
.option('--screenshot-file <path>', 'Screenshot output file', 'screenshot.png')
6566
.option('--timeout-exit-code <code>', 'Exit code on timeout', '42')
6667
.option('-q, --quiet', 'Suppress status messages')
68+
.option('--vcd-file <path>', 'Output path for VCD (logic analyzer) file')
6769
.action((projectPath: string, options: SimulateOptions, command: Command) => {
6870
return runSimulation(projectPath, options, command);
6971
});
@@ -96,6 +98,7 @@ async function runSimulation(projectPath: string, options: SimulateOptions, comm
9698
const screenshotFile = options.screenshotFile ?? 'screenshot.png';
9799
const timeoutExitCode = parseInt(options.timeoutExitCode ?? '42', 10);
98100
const timeoutNanos = timeout * millis;
101+
const vcdFile = options.vcdFile;
99102

100103
const token = requireToken();
101104

@@ -351,6 +354,23 @@ async function runSimulation(projectPath: string, options: SimulateOptions, comm
351354
// wait for the screenshot to be saved, if any
352355
await screenshotPromise;
353356
} finally {
357+
// Export VCD if requested
358+
if (vcdFile) {
359+
try {
360+
const result = await client.readVCD();
361+
if (result.sampleCount > 0) {
362+
writeFileSync(vcdFile, result.vcd);
363+
if (!quiet) {
364+
console.log(`VCD file written to: ${vcdFile}`);
365+
}
366+
} else if (!quiet) {
367+
console.log('No logic analyzer data to export');
368+
}
369+
} catch (err) {
370+
console.error('Error exporting VCD:', (err as Error).message);
371+
}
372+
}
373+
354374
client.close();
355375
}
356376
}

packages/cli/src/mcp/SimulationManager.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { APIClient, SerialMonitorDataPayload, type APIEvent } from '@wokwi/client';
2-
import { existsSync, readFileSync } from 'fs';
1+
import { APIClient, SerialMonitorDataPayload, type APIEvent, type VCDReadResponse } from '@wokwi/client';
2+
import { existsSync, readFileSync, writeFileSync } from 'fs';
33
import path from 'path';
44
import { parseConfig } from '../config.js';
55
import { DEFAULT_SERVER } from '../constants.js';
@@ -209,6 +209,18 @@ export class SimulationManager {
209209
return result.png;
210210
}
211211

212+
async exportVCD(outputPath: string): Promise<VCDReadResponse> {
213+
if (!this.client) {
214+
throw new Error('Not connected to simulation');
215+
}
216+
217+
const result = await this.client.readVCD();
218+
if (result.sampleCount > 0) {
219+
writeFileSync(outputPath, result.vcd);
220+
}
221+
return result;
222+
}
223+
212224
async cleanup(): Promise<void> {
213225
if (this.client) {
214226
this.client.close();

packages/cli/src/mcp/WokwiMCPTools.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,20 @@ export class WokwiMCPTools {
133133
required: ['partId'],
134134
},
135135
},
136+
{
137+
name: 'wokwi_export_vcd',
138+
description: 'Export logic analyzer data as VCD (Value Change Dump) file',
139+
inputSchema: {
140+
type: 'object',
141+
properties: {
142+
outputPath: {
143+
type: 'string',
144+
description: 'Path where VCD file will be saved',
145+
},
146+
},
147+
required: ['outputPath'],
148+
},
149+
},
136150
];
137151
}
138152

@@ -229,6 +243,20 @@ export class WokwiMCPTools {
229243
};
230244
}
231245

246+
case 'wokwi_export_vcd': {
247+
const result = await this.simulationManager.exportVCD(args.outputPath);
248+
return {
249+
content: [
250+
{
251+
type: 'text',
252+
text: result.sampleCount > 0
253+
? `VCD exported to: ${args.outputPath} (${result.sampleCount} samples, ${result.channelCount} channels)`
254+
: 'No logic analyzer data to export',
255+
},
256+
],
257+
};
258+
}
259+
232260
default:
233261
throw new Error(`Unknown tool: ${name}`);
234262
}

packages/client/src/APIClient.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
APIResultError,
88
APISimStartParams,
99
PinReadResponse,
10+
VCDReadResponse,
1011
} from './APITypes.js';
1112
import { base64ToByteArray, byteArrayToBase64 } from './base64.js';
1213
import { PausePoint, type PausePointParams } from './PausePoint.js';
@@ -131,6 +132,10 @@ export class APIClient {
131132
});
132133
}
133134

135+
async readVCD() {
136+
return await this.sendCommand<VCDReadResponse>('sim:read-vcd');
137+
}
138+
134139
async addPausePoint(params: PausePointParams, resume = false) {
135140
const id = `pp${this.lastPausePointId++}_${params.type}`;
136141
const commands = [this.sendCommand('pause-point:add', { id, ...params })];

packages/client/src/APITypes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,9 @@ export interface PinReadResponse {
5959
value: number;
6060
voltage: number;
6161
}
62+
63+
export interface VCDReadResponse {
64+
vcd: string;
65+
channelCount: number;
66+
sampleCount: number;
67+
}

packages/client/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export type {
2626
ChipsLogPayload,
2727
PinReadResponse,
2828
SerialMonitorDataPayload,
29+
VCDReadResponse,
2930
} from './APITypes.js';
3031

3132
// Utilities

0 commit comments

Comments
 (0)