147 lines
3.8 KiB
TypeScript
147 lines
3.8 KiB
TypeScript
/**
|
||
* 波形追踪工具
|
||
* 调用 PyInstaller 打包的 waveform_trace 可执行文件
|
||
*/
|
||
import { spawn } from 'child_process';
|
||
import * as path from 'path';
|
||
import * as fs from 'fs';
|
||
import * as vscode from 'vscode';
|
||
|
||
/**
|
||
* 波形追踪参数
|
||
*/
|
||
export interface WaveformTraceArgs {
|
||
/** Verilog 源文件路径(相对于项目根目录) */
|
||
verilogPath: string;
|
||
/** VCD 波形文件路径(相对于项目根目录) */
|
||
vcdPath: string;
|
||
/** 仿真工具的输出字符串(包含 mismatch 信息) */
|
||
simOutput: string;
|
||
/** BFS 回溯层数,默认 2 */
|
||
traceLevel?: number;
|
||
}
|
||
|
||
/**
|
||
* 执行波形追踪
|
||
* @param args 追踪参数
|
||
* @param context 执行上下文
|
||
* @returns 追踪结果字符串
|
||
*/
|
||
export async function executeWaveformTrace(
|
||
args: WaveformTraceArgs,
|
||
context: { extensionPath: string }
|
||
): Promise<string> {
|
||
// 获取可执行文件路径
|
||
const tracerPath = getWaveformTracerPath(context.extensionPath);
|
||
|
||
// 检查可执行文件是否存在
|
||
if (!fs.existsSync(tracerPath)) {
|
||
throw new Error(
|
||
`waveform_trace 工具未安装: ${tracerPath}\n` +
|
||
'请确保插件包含 tools/waveform_trace/bin/ 目录'
|
||
);
|
||
}
|
||
|
||
// 获取工作区路径
|
||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||
if (!workspaceFolders || workspaceFolders.length === 0) {
|
||
throw new Error('请先打开一个工作区');
|
||
}
|
||
const workspacePath = workspaceFolders[0].uri.fsPath;
|
||
|
||
// 解析路径(支持相对路径)
|
||
const verilogAbsPath = path.isAbsolute(args.verilogPath)
|
||
? args.verilogPath
|
||
: path.join(workspacePath, args.verilogPath);
|
||
const vcdAbsPath = path.isAbsolute(args.vcdPath)
|
||
? args.vcdPath
|
||
: path.join(workspacePath, args.vcdPath);
|
||
|
||
// 验证文件存在
|
||
if (!fs.existsSync(verilogAbsPath)) {
|
||
throw new Error(`Verilog 文件不存在: ${args.verilogPath}`);
|
||
}
|
||
if (!fs.existsSync(vcdAbsPath)) {
|
||
throw new Error(`VCD 文件不存在: ${args.vcdPath}`);
|
||
}
|
||
|
||
// 调用可执行文件
|
||
return new Promise((resolve, reject) => {
|
||
const child = spawn(tracerPath, [
|
||
'--verilog', verilogAbsPath,
|
||
'--vcd', vcdAbsPath,
|
||
'--sim-output', args.simOutput,
|
||
'--trace-level', String(args.traceLevel || 2),
|
||
'--output-format', 'text'
|
||
], {
|
||
windowsHide: true,
|
||
cwd: workspacePath,
|
||
shell: false
|
||
});
|
||
|
||
let stdout = '';
|
||
let stderr = '';
|
||
|
||
child.stdout.on('data', (data: Buffer) => {
|
||
stdout += data.toString();
|
||
});
|
||
|
||
child.stderr.on('data', (data: Buffer) => {
|
||
stderr += data.toString();
|
||
});
|
||
|
||
child.on('close', (code: number | null) => {
|
||
if (code === 0) {
|
||
// 成功时返回 stdout,忽略 stderr 中的进度信息
|
||
resolve(stdout || stderr);
|
||
} else {
|
||
reject(new Error(
|
||
`waveform_trace 执行失败 (code=${code}):\n${stderr || stdout}`
|
||
));
|
||
}
|
||
});
|
||
|
||
child.on('error', (error: Error) => {
|
||
reject(new Error(`waveform_trace 启动失败: ${error.message}`));
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取 waveform_trace 可执行文件路径
|
||
*/
|
||
function getWaveformTracerPath(extensionPath: string): string {
|
||
const platform = process.platform;
|
||
let binName = 'waveform_trace';
|
||
|
||
if (platform === 'win32') {
|
||
binName = 'waveform_trace.exe';
|
||
}
|
||
|
||
return path.join(extensionPath, 'tools', 'waveform_trace', 'bin', binName);
|
||
}
|
||
|
||
/**
|
||
* 检查 waveform_trace 工具是否可用
|
||
*/
|
||
export function checkWaveformTraceAvailable(extensionPath: string): {
|
||
available: boolean;
|
||
message: string;
|
||
path?: string;
|
||
} {
|
||
const tracerPath = getWaveformTracerPath(extensionPath);
|
||
|
||
if (fs.existsSync(tracerPath)) {
|
||
return {
|
||
available: true,
|
||
message: 'waveform_trace 工具可用',
|
||
path: tracerPath
|
||
};
|
||
} else {
|
||
return {
|
||
available: false,
|
||
message: `waveform_trace 工具未找到: ${tracerPath}`
|
||
};
|
||
}
|
||
}
|