diff --git a/docs/EDA联动功能需求文档.md b/docs/EDA联动功能需求文档.md new file mode 100644 index 0000000..50ed7c7 --- /dev/null +++ b/docs/EDA联动功能需求文档.md @@ -0,0 +1,368 @@ +# Vivado 联动功能需求文档 + +## 1. 项目背景 + +### 1.1 当前状态 + +IC Coder Plugin 目前支持: + +- iverilog 仿真(内置 Windows 版本) +- VCD 波形查看 +- Verilog 代码生成和文件操作 + +### 1.2 需求来源 + +用户需要在 VS Code 中直接调用本地 Vivado 工具,并将产出文件自动导入到项目中,完成从仿真到 FPGA 部署的完整流程。 + +### 1.3 Vivado 是什么? + +**Vivado** 是 Xilinx(现 AMD)的 FPGA 开发工具,用于将 Verilog 代码部署到 FPGA 硬件: + +- **综合(Synthesis)**:将 RTL 代码转换为门级网表 +- **实现(Implementation)**:布局布线,映射到具体 FPGA 芯片 +- **生成比特流(Bitstream)**:生成 .bit 配置文件用于烧录 + +**与 iverilog 的区别**: + +- iverilog:只做**仿真验证**(软件层面验证逻辑) +- Vivado:做**综合+实现+生成配置文件**(真正部署到硬件) + +**典型开发流程**: + +``` +编写 Verilog → iverilog 仿真验证 → Vivado 综合 → Vivado 实现 → 生成 .bit 文件 → 烧录到 FPGA +``` + +## 2. 功能目标 + +### 2.1 核心目标 + +- **前端工具封装**:在插件前端实现 Vivado 调用的完整逻辑 +- **后端简化调用**:后端只需调用一个工具接口 +- **文件自动导入**:Vivado 执行完成后,自动将产出文件导入到项目 +- **流程可视化**:执行进度、日志实时显示 + +### 2.2 非功能目标 + +- 配置简单,用户友好 +- 执行过程可视化(进度、日志) +- 错误处理完善,提示清晰 + +## 3. 功能详细需求 + +### 3.1 Vivado 支持的操作 + +#### 3.1.1 综合(Synthesis) + +- **输入**:Verilog/VHDL 源文件、约束文件(.xdc) +- **输出**:设计检查点(.dcp)、综合报告(.rpt) +- **用途**:将 RTL 代码转换为门级网表,检查资源使用情况 + +#### 3.1.2 实现(Implementation) + +- **输入**:综合后的 .dcp 文件 +- **输出**:实现后的 .dcp 文件、时序报告、布局布线报告 +- **用途**:完成布局布线,检查时序是否满足要求 + +#### 3.1.3 生成比特流(Generate Bitstream) + +- **输入**:实现后的 .dcp 文件 +- **输出**:比特流文件(.bit) +- **用途**:生成可烧录到 FPGA 的配置文件 + +### 3.2 配置管理 + +#### 3.2.1 配置项 + +```json +{ + "vivado": { + "enabled": true, + "executablePath": "C:/Xilinx/Vivado/2023.1/bin/vivado.bat", + "workingDir": "${workspaceFolder}/vivado_project", + "part": "xc7a35tcpg236-1", // FPGA 型号 + "commands": { + "synthesis": "vivado -mode batch -source synth.tcl", + "implementation": "vivado -mode batch -source impl.tcl", + "bitstream": "vivado -mode batch -source bitstream.tcl" + }, + "outputFiles": { + "synthesis": ["*.dcp", "*_synth.rpt"], + "implementation": ["*.dcp", "*_timing.rpt", "*_utilization.rpt"], + "bitstream": ["*.bit"] + } + } +} +``` + +#### 3.2.2 存储位置 + +- 全局配置:VS Code Settings(`settings.json`) +- 项目配置:`.vscode/ic-coder-vivado.json`(优先级更高) + +### 3.3 工具调用接口 + +#### 3.3.1 接口定义 + +```typescript +interface VivadoToolRequest { + command: string; // 命令类型:synthesis | implementation | bitstream + parameters?: { + topModule?: string; // 顶层模块名 + files?: string[]; // 输入文件列表 + part?: string; // FPGA 型号(可选,使用配置中的默认值) + constraints?: string; // 约束文件路径(.xdc) + outputDir?: string; // 输出目录 + }; + importOutput?: { + enabled: boolean; // 是否自动导入 + targetDir: string; // 目标目录 + }; +} + +interface VivadoToolResponse { + success: boolean; + command: string; + executionTime: number; // 执行时间(毫秒) + output: string; // 标准输出 + error?: string; // 错误信息 + importedFiles?: string[]; // 已导入的文件列表 + reports?: { + // 报告摘要 + resources?: string; // 资源使用情况 + timing?: string; // 时序信息 + }; +} +``` + +### 3.4 执行流程 + +#### 3.4.1 参数验证 + +- 检查 Vivado 是否已配置 +- 检查可执行文件是否存在 +- 检查输入文件是否存在 +- 检查工作目录是否存在 + +#### 3.4.2 TCL 脚本生成 + +根据命令类型自动生成 TCL 脚本: + +**综合脚本示例(synth.tcl)**: + +```tcl +# 读取源文件 +read_verilog counter.v +read_xdc constraints.xdc + +# 设置顶层模块 +set_property top counter [current_fileset] + +# 综合 +synth_design -part xc7a35tcpg236-1 -top counter + +# 生成报告 +report_utilization -file utilization_synth.rpt +report_timing -file timing_synth.rpt + +# 保存检查点 +write_checkpoint -force counter_synth.dcp +``` + +#### 3.4.3 命令执行 + +- 启动子进程执行 Vivado 命令 +- 实时捕获标准输出和错误输出 +- 向前端推送进度信息(解析日志中的进度标记) + +#### 3.4.4 结果处理 + +- 检查执行结果(退出码) +- 解析报告文件,提取关键信息(资源使用、时序) +- 查找产出文件 + +#### 3.4.5 文件导入 + +- 根据配置的文件模式查找产出文件 +- 复制文件到目标目录 +- 通知用户导入结果 + +### 3.5 UI 交互 + +#### 3.5.1 配置界面 + +- 在设置页面添加 "Vivado 配置" 选项 +- 支持配置 Vivado 路径、FPGA 型号 +- 支持测试 Vivado 可用性(点击按钮测试) + +#### 3.5.2 调用界面 + +- 在聊天面板中,AI 可以建议使用 Vivado +- 用户确认后,显示执行进度对话框 +- 实时显示日志输出(可折叠) +- 显示执行状态:准备中 → 执行中 → 完成/失败 + +#### 3.5.3 结果展示 + +- 执行成功:显示执行时间、资源使用、时序信息 +- 执行失败:显示错误信息、建议解决方案 +- 导入文件:高亮显示已导入的文件,支持点击打开报告 + +### 3.6 后端集成 + +#### 3.6.1 工具定义 + +后端在工具列表中添加 Vivado 工具: + +```json +{ + "name": "runVivado", + "description": "调用 Vivado 执行综合、实现或生成比特流", + "parameters": { + "command": "命令类型(synthesis/implementation/bitstream)", + "topModule": "顶层模块名", + "files": "输入文件列表", + "constraints": "约束文件路径(可选)" + } +} +``` + +#### 3.6.2 调用示例 + +``` +用户:帮我用 Vivado 综合一下 counter.v +AI:好的,我将使用 Vivado 进行综合。 + [调用工具] runVivado + 参数: + - command: synthesis + - topModule: counter + - files: ["counter.v"] + - constraints: "constraints.xdc" + + [执行中...] + Vivado 综合完成! + - 执行时间:45 秒 + - 资源使用:LUT: 32/20800 (0.15%), FF: 8/41600 (0.02%) + - 产出文件:counter_synth.dcp, utilization_synth.rpt + - 已自动导入到:vivado_project/output/ +``` + +## 4. 用户场景 + +### 4.1 场景一:单步综合 + +1. 用户编写完 Verilog 代码 +2. 在聊天中输入:"用 Vivado 综合一下 counter.v" +3. AI 调用 `runVivado` 工具 +4. 插件执行 Vivado 综合 +5. 综合完成后,显示资源使用情况,自动导入报告文件 + +### 4.2 场景二:完整流程 + +1. 用户输入:"用 Vivado 跑完整个流程" +2. AI 依次调用: + - 综合(Synthesis) + - 实现(Implementation) + - 生成比特流(Bitstream) +3. 每个步骤完成后显示结果 +4. 最终生成 .bit 文件,用户可以烧录到 FPGA + +### 4.3 场景三:查看报告 + +1. Vivado 执行完成后 +2. 用户点击导入的报告文件 +3. 在编辑器中查看资源使用、时序分析等信息 + +## 5. 技术约束 + +### 5.1 平台兼容性 + +- Windows:支持 `.bat` 可执行文件 +- Linux:支持 shell 脚本 +- 路径分隔符自动适配 + +### 5.2 性能要求 + +- 命令执行不阻塞 UI +- 综合时间可能较长(分钟级),需要进度提示 +- 日志输出实时更新,限制缓冲区大小 + +### 5.3 安全性 + +- 工作目录限制在项目范围内 +- 许可证路径不记录到日志 + +## 6. 验收标准 + +### 6.1 功能验收 + +- [ ] 用户可以配置 Vivado 路径和 FPGA 型号 +- [ ] AI 可以通过工具调用成功执行 Vivado 综合 +- [ ] 产出文件自动导入到指定目录 +- [ ] 执行过程有清晰的进度提示 +- [ ] 报告文件可以正常打开查看 + +### 6.2 性能验收 + +- [ ] 小型项目综合时间 < 1 分钟 +- [ ] UI 响应流畅,不卡顿 +- [ ] 日志输出实时更新(延迟 < 500ms) + +### 6.3 用户体验验收 + +- [ ] 配置界面直观易用 +- [ ] 首次使用有引导提示 +- [ ] 错误提示清晰,有解决建议 +- [ ] 导入的文件可以直接打开查看 + +## 7. 风险和依赖 + +### 7.1 风险 + +- **Vivado 版本差异**:不同版本的命令行参数可能不同 +- **许可证问题**:Vivado 需要许可证才能运行 +- **路径问题**:Windows 路径中的空格和特殊字符 +- **执行时间长**:大型项目可能需要数十分钟 + +### 7.2 依赖 + +- 用户需要自行安装 Vivado +- 用户需要配置正确的 Vivado 路径 +- 需要设置环境变量(如 `XILINX_VIVADO`) +- 需要有效的 Vivado 许可证 + +## 8. 后续扩展 + +### 8.1 短期扩展 + +- 支持自定义 TCL 脚本模板 +- 支持批量处理多个设计 +- 支持时序约束编辑器 + +### 8.2 长期扩展 + +- 支持其他 FPGA 工具(Quartus) +- 云端 Vivado 服务集成 +- 结果对比和版本管理 +- 性能分析和优化建议 + +--- + +## 附录 + +### A. Vivado 命令行参考 + +- 官方文档:https://docs.xilinx.com/ +- TCL 命令参考:UG835 +- 设计流程参考:UG892 + +### B. 术语表 + +- **RTL**:Register Transfer Level,寄存器传输级 +- **综合**:Synthesis,将 RTL 代码转换为门级网表 +- **实现**:Implementation,布局布线 +- **比特流**:Bitstream,FPGA 配置文件 +- **DCP**:Design Checkpoint,Vivado 设计检查点文件 +- **XDC**:Xilinx Design Constraints,约束文件 +- **LUT**:Look-Up Table,查找表(FPGA 基本逻辑单元) +- **FF**:Flip-Flop,触发器 diff --git a/docs/Vivado联动前后端对接文档.md b/docs/Vivado联动前后端对接文档.md new file mode 100644 index 0000000..c4f15f8 --- /dev/null +++ b/docs/Vivado联动前后端对接文档.md @@ -0,0 +1,413 @@ +# Vivado 联动前后端对接文档 + +## 1. 概述 + +本文档描述后端 AI 服务如何调用前端的 Vivado 工具,以及前端如何响应和返回结果。 + +### 1.1 调用流程 + +``` +后端 AI 服务 + ↓ (1) 发送工具调用请求 +前端 Extension (MessageHandler) + ↓ (2) 解析请求,调用 VivadoRunner +VivadoRunner + ↓ (3) 执行 Vivado,实时推送进度 +前端 Webview + ↓ (4) 显示进度和结果 +前端 Extension + ↓ (5) 返回执行结果给后端 +后端 AI 服务 +``` + +## 2. 工具定义(后端) + +### 2.1 工具注册 + +后端需要在工具列表中注册 `runVivado` 工具: + +```json +{ + "name": "runVivado", + "description": "调用本地 Vivado 工具执行 FPGA 综合、实现或生成比特流。用于将 Verilog 代码部署到 FPGA 硬件。", + "inputSchema": { + "type": "object", + "properties": { + "command": { + "type": "string", + "enum": ["synthesis", "implementation", "bitstream"], + "description": "要执行的命令类型:synthesis(综合)、implementation(实现)、bitstream(生成比特流)" + }, + "topModule": { + "type": "string", + "description": "顶层模块名称" + }, + "files": { + "type": "array", + "items": { "type": "string" }, + "description": "输入的 Verilog 文件路径列表" + }, + "constraints": { + "type": "string", + "description": "约束文件路径(.xdc 文件),可选" + }, + "part": { + "type": "string", + "description": "FPGA 型号,可选,默认使用配置中的型号" + } + }, + "required": ["command", "topModule", "files"] + } +} +``` + +### 2.2 调用示例 + +#### 示例 1:综合单个文件 + +```json +{ + "tool": "runVivado", + "parameters": { + "command": "synthesis", + "topModule": "counter", + "files": ["counter.v"] + } +} +``` + +#### 示例 2:综合带约束文件 + +```json +{ + "tool": "runVivado", + "parameters": { + "command": "synthesis", + "topModule": "uart_top", + "files": ["uart_tx.v", "uart_rx.v", "uart_top.v"], + "constraints": "constraints.xdc" + } +} +``` + +#### 示例 3:实现(需要先综合) + +```json +{ + "tool": "runVivado", + "parameters": { + "command": "implementation", + "topModule": "counter" + } +} +``` + +#### 示例 4:生成比特流(需要先实现) + +```json +{ + "tool": "runVivado", + "parameters": { + "command": "bitstream", + "topModule": "counter" + } +} +``` + +## 3. 前端接收和处理 + +### 3.1 MessageHandler 处理逻辑 + +前端在 `messageHandler.ts` 中添加工具处理: + +```typescript +// src/utils/messageHandler.ts + +export async function handleToolExecution( + panel: vscode.WebviewPanel, + toolName: string, + parameters: any +): Promise { + + if (toolName === 'runVivado') { + return await handleVivadoTool(panel, parameters); + } + + // 其他工具处理... +} + +async function handleVivadoTool( + panel: vscode.WebviewPanel, + parameters: any +): Promise { + + const { command, topModule, files, constraints, part } = parameters; + + // 构建请求 + const request: VivadoToolRequest = { + command, + parameters: { + topModule, + files, + constraints, + part + }, + importOutput: { + enabled: true, + targetDir: path.join( + vscode.workspace.workspaceFolders![0].uri.fsPath, + 'vivado_output' + ) + } + }; + + // 向前端发送开始消息 + panel.webview.postMessage({ + type: 'vivado-start', + command + }); + + // 执行 Vivado + const response = await runVivado(request, (progress) => { + // 实时推送进度到前端 + panel.webview.postMessage({ + type: 'vivado-progress', + progress + }); + }); + + // 向前端发送完成消息 + panel.webview.postMessage({ + type: 'vivado-complete', + response + }); + + // 返回结果给后端 + return response; +} +``` + +## 4. 响应格式 + +### 4.1 成功响应 + +```json +{ + "success": true, + "command": "synthesis", + "executionTime": 45230, + "output": "Vivado 执行日志...", + "importedFiles": [ + "/path/to/vivado_output/counter_synth.dcp", + "/path/to/vivado_output/counter_utilization_synth.rpt" + ], + "reports": { + "resources": "LUT: 32/20800 (0.15%)\nFF: 8/41600 (0.02%)", + "timing": "WNS: 5.234ns, TNS: 0.000ns" + } +} +``` + +### 4.2 失败响应 + +```json +{ + "success": false, + "command": "synthesis", + "executionTime": 1250, + "output": "部分执行日志...", + "error": "ERROR: [Synth 8-439] module 'counter' not found" +} +``` + +## 5. 后端使用指南 + +### 5.1 AI 对话流程 + +``` +用户:帮我用 Vivado 综合一下 counter.v + +AI 分析: +1. 用户想要综合 Verilog 文件 +2. 需要调用 runVivado 工具 +3. 命令类型是 synthesis +4. 顶层模块名从文件名推断为 counter +5. 输入文件是 counter.v + +AI 调用工具: +{ + "tool": "runVivado", + "parameters": { + "command": "synthesis", + "topModule": "counter", + "files": ["counter.v"] + } +} + +前端执行并返回结果 + +AI 回复用户: +"Vivado 综合完成! +- 执行时间:45.2 秒 +- 资源使用:LUT: 32/20800 (0.15%), FF: 8/41600 (0.02%) +- 产出文件已导入到 vivado_output 目录" +``` + +### 5.2 完整流程示例 + +``` +用户:用 Vivado 跑完整个流程 + +AI:好的,我将依次执行综合、实现和生成比特流。 + +步骤 1:综合 +[调用] runVivado { command: "synthesis", ... } +[结果] 综合成功,耗时 45s + +步骤 2:实现 +[调用] runVivado { command: "implementation", ... } +[结果] 实现成功,耗时 120s,时序满足要求 + +步骤 3:生成比特流 +[调用] runVivado { command: "bitstream", ... } +[结果] 比特流生成成功,文件:counter.bit + +完成!所有文件已导入到 vivado_output 目录。 +``` + +## 6. 错误处理 + +### 6.1 常见错误 + +#### 错误 1:Vivado 未配置 + +```json +{ + "success": false, + "error": "Vivado 未配置,请在设置中配置 Vivado 路径" +} +``` + +**AI 应该回复**: +"Vivado 尚未配置,请先在插件设置中配置 Vivado 的安装路径。" + +#### 错误 2:文件不存在 + +```json +{ + "success": false, + "error": "输入文件不存在: counter.v" +} +``` + +**AI 应该回复**: +"找不到文件 counter.v,请确认文件路径是否正确。" + +#### 错误 3:综合失败 + +```json +{ + "success": false, + "error": "ERROR: [Synth 8-439] module 'counter' not found", + "output": "详细日志..." +} +``` + +**AI 应该回复**: +"综合失败,错误信息:找不到模块 'counter'。请检查: +1. 模块名是否正确 +2. 文件中是否定义了该模块 +3. 是否有语法错误" + +### 6.2 错误处理建议 + +后端收到 `success: false` 时: +1. 提取 `error` 字段中的错误信息 +2. 分析错误类型(配置问题、文件问题、语法问题等) +3. 给用户提供具体的解决建议 +4. 必要时可以查看 `output` 字段获取详细日志 + +## 7. 进度推送(可选) + +前端会实时推送进度信息到 Webview,后端无需处理,但可以了解进度格式: + +```json +{ + "type": "vivado-progress", + "progress": { + "stage": "synthesis", + "percentage": 45, + "message": "正在综合模块 counter..." + } +} +``` + +## 8. 测试建议 + +### 8.1 后端测试用例 + +```javascript +// 测试用例 1:基本综合 +test('综合单个文件', async () => { + const result = await callTool('runVivado', { + command: 'synthesis', + topModule: 'counter', + files: ['counter.v'] + }); + + expect(result.success).toBe(true); + expect(result.importedFiles.length).toBeGreaterThan(0); +}); + +// 测试用例 2:错误处理 +test('文件不存在', async () => { + const result = await callTool('runVivado', { + command: 'synthesis', + topModule: 'test', + files: ['not_exist.v'] + }); + + expect(result.success).toBe(false); + expect(result.error).toContain('不存在'); +}); +``` + +## 9. 注意事项 + +### 9.1 执行时间 +- 综合:小型设计 30s-2min,大型设计 5-30min +- 实现:通常是综合时间的 2-3 倍 +- 生成比特流:通常 10-30s + +后端应该设置合理的超时时间(建议 10 分钟)。 + +### 9.2 依赖关系 +- `implementation` 需要先执行 `synthesis` +- `bitstream` 需要先执行 `implementation` + +后端 AI 应该理解这个依赖关系,按顺序调用。 + +### 9.3 文件路径 +- 所有文件路径都是相对于工作区根目录 +- 前端会自动解析为绝对路径 +- 支持相对路径和绝对路径 + +## 10. 快速集成清单 + +后端开发者需要做的事情: + +- [ ] 在工具列表中注册 `runVivado` 工具 +- [ ] 实现工具调用逻辑(发送请求到前端) +- [ ] 处理返回结果(success/error) +- [ ] 实现错误处理和用户提示 +- [ ] 理解三个命令的依赖关系 +- [ ] 设置合理的超时时间 +- [ ] 编写测试用例 + +前端开发者需要做的事情: + +- [ ] 实现 `handleVivadoTool` 函数 +- [ ] 集成 VivadoRunner +- [ ] 实现进度推送 +- [ ] 实现结果展示 +- [ ] 处理各种错误情况 diff --git a/docs/Vivado联动功能技术设计文档.md b/docs/Vivado联动功能技术设计文档.md new file mode 100644 index 0000000..b91c4be --- /dev/null +++ b/docs/Vivado联动功能技术设计文档.md @@ -0,0 +1,864 @@ +# Vivado 联动功能技术设计文档 + +## 1. 架构设计 + +### 1.1 整体架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 后端 AI 服务 │ +│ (调用 runVivado 工具) │ +└────────────────────────────┬────────────────────────────────┘ + │ 工具调用请求 + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ VS Code Extension │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ ICHelperPanel (Webview) │ │ +│ │ - 接收后端工具调用 │ │ +│ │ - 显示执行进度和日志 │ │ +│ │ - 展示执行结果 │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ ↓ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ VivadoRunner (utils/vivadoRunner.ts) │ │ +│ │ - 配置管理 │ │ +│ │ - TCL 脚本生成 │ │ +│ │ - 命令执行 │ │ +│ │ - 进度监控 │ │ +│ │ - 结果解析 │ │ +│ └──────────────────────────────────────────────────────┘ │ +│ ↓ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ FileImporter (utils/fileImporter.ts) │ │ +│ │ - 查找产出文件 │ │ +│ │ - 复制文件到目标目录 │ │ +│ │ - 通知文件变更 │ │ +│ └──────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ 本地 Vivado 工具 │ +│ (通过子进程执行 TCL 脚本) │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 1.2 模块职责 + +#### 1.2.1 VivadoRunner +- 读取和验证 Vivado 配置 +- 根据命令类型生成 TCL 脚本 +- 启动子进程执行 Vivado +- 实时捕获输出并解析进度 +- 返回执行结果 + +#### 1.2.2 FileImporter +- 根据文件模式查找产出文件 +- 复制文件到指定目录 +- 返回已导入的文件列表 + +#### 1.2.3 MessageHandler +- 接收后端的 `runVivado` 工具调用 +- 调用 VivadoRunner 执行 +- 向 Webview 推送进度和结果 + +## 2. 数据结构设计 + +### 2.1 配置结构 + +```typescript +/** + * Vivado 配置 + */ +interface VivadoConfig { + enabled: boolean; + executablePath: string; + workingDir: string; + part: string; + commands: { + synthesis: string; + implementation: string; + bitstream: string; + }; + outputFiles: { + synthesis: string[]; + implementation: string[]; + bitstream: string[]; + }; +} +``` + +### 2.2 请求和响应结构 + +```typescript +/** + * Vivado 工具请求 + */ +interface VivadoToolRequest { + command: 'synthesis' | 'implementation' | 'bitstream'; + parameters?: { + topModule?: string; + files?: string[]; + part?: string; + constraints?: string; + outputDir?: string; + }; + importOutput?: { + enabled: boolean; + targetDir: string; + }; +} + +/** + * Vivado 工具响应 + */ +interface VivadoToolResponse { + success: boolean; + command: string; + executionTime: number; + output: string; + error?: string; + importedFiles?: string[]; + reports?: { + resources?: string; + timing?: string; + }; +} + +/** + * 执行进度 + */ +interface VivadoProgress { + stage: string; + percentage: number; + message: string; +} +``` + +## 3. 核心模块实现 + +### 3.1 配置管理 + +#### 3.1.1 配置读取 +```typescript +// src/utils/vivadoConfig.ts + +import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; + +export function getVivadoConfig(): VivadoConfig | null { + // 优先读取项目配置 + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (workspaceFolder) { + const projectConfigPath = path.join( + workspaceFolder.uri.fsPath, + '.vscode', + 'ic-coder-vivado.json' + ); + if (fs.existsSync(projectConfigPath)) { + const content = fs.readFileSync(projectConfigPath, 'utf-8'); + return JSON.parse(content).vivado; + } + } + + // 读取全局配置 + const config = vscode.workspace.getConfiguration('ic-coder'); + return config.get('vivado') || null; +} + +export function validateConfig(config: VivadoConfig): string | null { + if (!config.enabled) { + return 'Vivado 未启用'; + } + if (!fs.existsSync(config.executablePath)) { + return `Vivado 可执行文件不存在: ${config.executablePath}`; + } + return null; +} +``` + +### 3.2 TCL 脚本生成 + +#### 3.2.1 脚本生成器 +```typescript +// src/utils/tclGenerator.ts + +export function generateSynthesisTcl( + topModule: string, + files: string[], + part: string, + constraints?: string, + outputDir?: string +): string { + const output = outputDir || '.'; + let tcl = `# Vivado 综合脚本\n\n`; + + // 读取源文件 + files.forEach(file => { + tcl += `read_verilog ${file}\n`; + }); + + // 读取约束文件 + if (constraints) { + tcl += `read_xdc ${constraints}\n`; + } + + tcl += `\n# 综合\n`; + tcl += `synth_design -part ${part} -top ${topModule}\n\n`; + + // 生成报告 + tcl += `# 生成报告\n`; + tcl += `report_utilization -file ${output}/${topModule}_utilization_synth.rpt\n`; + tcl += `report_timing -file ${output}/${topModule}_timing_synth.rpt\n\n`; + + // 保存检查点 + tcl += `# 保存检查点\n`; + tcl += `write_checkpoint -force ${output}/${topModule}_synth.dcp\n`; + + return tcl; +} + +export function generateImplementationTcl( + dcpFile: string, + outputDir?: string +): string { + const output = outputDir || '.'; + const baseName = path.basename(dcpFile, '.dcp').replace('_synth', ''); + + let tcl = `# Vivado 实现脚本\n\n`; + tcl += `open_checkpoint ${dcpFile}\n\n`; + + tcl += `# 优化\n`; + tcl += `opt_design\n`; + tcl += `place_design\n`; + tcl += `route_design\n\n`; + + tcl += `# 生成报告\n`; + tcl += `report_utilization -file ${output}/${baseName}_utilization_impl.rpt\n`; + tcl += `report_timing_summary -file ${output}/${baseName}_timing_impl.rpt\n\n`; + + tcl += `# 保存检查点\n`; + tcl += `write_checkpoint -force ${output}/${baseName}_impl.dcp\n`; + + return tcl; +} + +export function generateBitstreamTcl( + dcpFile: string, + outputDir?: string +): string { + const output = outputDir || '.'; + const baseName = path.basename(dcpFile, '.dcp').replace('_impl', ''); + + let tcl = `# Vivado 比特流生成脚本\n\n`; + tcl += `open_checkpoint ${dcpFile}\n\n`; + + tcl += `# 生成比特流\n`; + tcl += `write_bitstream -force ${output}/${baseName}.bit\n`; + + return tcl; +} +``` + +### 3.3 VivadoRunner 实现 + +```typescript +// src/utils/vivadoRunner.ts + +import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; +import { spawn } from 'child_process'; +import { getVivadoConfig, validateConfig } from './vivadoConfig'; +import { generateSynthesisTcl, generateImplementationTcl, generateBitstreamTcl } from './tclGenerator'; + +export async function runVivado( + request: VivadoToolRequest, + progressCallback?: (progress: VivadoProgress) => void +): Promise { + const startTime = Date.now(); + + // 读取配置 + const config = getVivadoConfig(); + if (!config) { + return { + success: false, + command: request.command, + executionTime: 0, + output: '', + error: 'Vivado 未配置' + }; + } + + // 验证配置 + const configError = validateConfig(config); + if (configError) { + return { + success: false, + command: request.command, + executionTime: 0, + output: '', + error: configError + }; + } + + // 准备工作目录 + const workingDir = resolveWorkingDir(config.workingDir); + if (!fs.existsSync(workingDir)) { + fs.mkdirSync(workingDir, { recursive: true }); + } + + // 生成 TCL 脚本 + const tclScript = generateTclScript(request, config, workingDir); + const tclPath = path.join(workingDir, `${request.command}.tcl`); + fs.writeFileSync(tclPath, tclScript); + + // 执行 Vivado + const result = await executeVivado( + config.executablePath, + tclPath, + workingDir, + progressCallback + ); + + const executionTime = Date.now() - startTime; + + // 解析报告 + const reports = parseReports(request.command, workingDir, request.parameters?.topModule); + + // 导入文件 + let importedFiles: string[] = []; + if (request.importOutput?.enabled && result.success) { + importedFiles = await importOutputFiles( + request.command, + config, + workingDir, + request.importOutput.targetDir + ); + } + + return { + success: result.success, + command: request.command, + executionTime, + output: result.output, + error: result.error, + importedFiles, + reports + }; +} + +function generateTclScript( + request: VivadoToolRequest, + config: VivadoConfig, + workingDir: string +): string { + const { command, parameters } = request; + const part = parameters?.part || config.part; + + switch (command) { + case 'synthesis': + return generateSynthesisTcl( + parameters?.topModule || 'top', + parameters?.files || [], + part, + parameters?.constraints, + parameters?.outputDir + ); + case 'implementation': + const synthDcp = path.join(workingDir, `${parameters?.topModule}_synth.dcp`); + return generateImplementationTcl(synthDcp, parameters?.outputDir); + case 'bitstream': + const implDcp = path.join(workingDir, `${parameters?.topModule}_impl.dcp`); + return generateBitstreamTcl(implDcp, parameters?.outputDir); + default: + throw new Error(`未知命令: ${command}`); + } +} + +async function executeVivado( + executablePath: string, + tclPath: string, + workingDir: string, + progressCallback?: (progress: VivadoProgress) => void +): Promise<{ success: boolean; output: string; error?: string }> { + return new Promise((resolve) => { + let output = ''; + let errorOutput = ''; + + const process = spawn(executablePath, ['-mode', 'batch', '-source', tclPath], { + cwd: workingDir, + shell: true + }); + + process.stdout.on('data', (data) => { + const text = data.toString(); + output += text; + + // 解析进度 + if (progressCallback) { + const progress = parseProgress(text); + if (progress) { + progressCallback(progress); + } + } + }); + + process.stderr.on('data', (data) => { + errorOutput += data.toString(); + }); + + process.on('close', (code) => { + if (code === 0) { + resolve({ success: true, output }); + } else { + resolve({ success: false, output, error: errorOutput || '执行失败' }); + } + }); + }); +} + +function parseProgress(logText: string): VivadoProgress | null { + // 解析 Vivado 日志中的进度信息 + if (logText.includes('Starting synthesis')) { + return { stage: 'synthesis', percentage: 10, message: '开始综合' }; + } + if (logText.includes('Finished synthesis')) { + return { stage: 'synthesis', percentage: 100, message: '综合完成' }; + } + // 更多进度解析... + return null; +} + +function parseReports( + command: string, + workingDir: string, + topModule?: string +): { resources?: string; timing?: string } { + const reports: { resources?: string; timing?: string } = {}; + + if (command === 'synthesis' || command === 'implementation') { + const utilizationFile = path.join( + workingDir, + `${topModule}_utilization_${command === 'synthesis' ? 'synth' : 'impl'}.rpt` + ); + if (fs.existsSync(utilizationFile)) { + const content = fs.readFileSync(utilizationFile, 'utf-8'); + reports.resources = extractResourceSummary(content); + } + + const timingFile = path.join( + workingDir, + `${topModule}_timing_${command === 'synthesis' ? 'synth' : 'impl'}.rpt` + ); + if (fs.existsSync(timingFile)) { + const content = fs.readFileSync(timingFile, 'utf-8'); + reports.timing = extractTimingSummary(content); + } + } + + return reports; +} + +function extractResourceSummary(reportContent: string): string { + // 提取资源使用摘要 + const lines = reportContent.split('\n'); + const summary: string[] = []; + + for (const line of lines) { + if (line.includes('LUT') || line.includes('FF') || line.includes('BRAM')) { + summary.push(line.trim()); + } + } + + return summary.join('\n'); +} + +function extractTimingSummary(reportContent: string): string { + // 提取时序摘要 + const lines = reportContent.split('\n'); + for (const line of lines) { + if (line.includes('WNS') || line.includes('TNS')) { + return line.trim(); + } + } + return ''; +} + +function resolveWorkingDir(workingDir: string): string { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (workspaceFolder) { + return workingDir.replace('${workspaceFolder}', workspaceFolder.uri.fsPath); + } + return workingDir; +} +``` + +### 3.4 文件导入实现 + +```typescript +// src/utils/fileImporter.ts + +import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as glob from 'glob'; + +export async function importOutputFiles( + command: string, + config: VivadoConfig, + sourceDir: string, + targetDir: string +): Promise { + const patterns = config.outputFiles[command] || []; + const importedFiles: string[] = []; + + for (const pattern of patterns) { + const files = glob.sync(pattern, { cwd: sourceDir }); + + for (const file of files) { + const sourcePath = path.join(sourceDir, file); + const targetPath = path.join(targetDir, file); + + // 确保目标目录存在 + const targetDirPath = path.dirname(targetPath); + if (!fs.existsSync(targetDirPath)) { + fs.mkdirSync(targetDirPath, { recursive: true }); + } + + // 复制文件 + fs.copyFileSync(sourcePath, targetPath); + importedFiles.push(targetPath); + } + } + + return importedFiles; +} +``` + +### 3.5 MessageHandler 集成 + +```typescript +// src/utils/messageHandler.ts (新增部分) + +import { runVivado } from './vivadoRunner'; + +// 在 handleUserMessage 中添加 Vivado 工具处理 +export async function handleVivadoTool( + panel: vscode.WebviewPanel, + toolCall: any +): Promise { + const { command, topModule, files, constraints } = toolCall.parameters; + + // 构建请求 + const request: VivadoToolRequest = { + command, + parameters: { + topModule, + files, + constraints + }, + importOutput: { + enabled: true, + targetDir: path.join(vscode.workspace.workspaceFolders![0].uri.fsPath, 'vivado_output') + } + }; + + // 向前端发送开始消息 + panel.webview.postMessage({ + type: 'vivado-start', + command + }); + + // 执行 Vivado + const response = await runVivado(request, (progress) => { + // 推送进度 + panel.webview.postMessage({ + type: 'vivado-progress', + progress + }); + }); + + // 向前端发送结果 + panel.webview.postMessage({ + type: 'vivado-complete', + response + }); +} +``` + +## 4. 前端 UI 实现 + +### 4.1 进度显示组件 + +```typescript +// src/views/vivadoProgress.ts + +export function renderVivadoProgress(progress: VivadoProgress): string { + return ` +
+
+ ${progress.stage} + ${progress.percentage}% +
+
+
+
+
${progress.message}
+
+ `; +} +``` + +### 4.2 结果展示组件 + +```typescript +// src/views/vivadoResult.ts + +export function renderVivadoResult(response: VivadoToolResponse): string { + if (!response.success) { + return ` +
+

❌ 执行失败

+
${response.error}
+
+ `; + } + + return ` +
+

✅ 执行成功

+
+

命令: ${response.command}

+

执行时间: ${(response.executionTime / 1000).toFixed(2)}s

+
+ + ${response.reports?.resources ? ` +
+
资源使用
+
${response.reports.resources}
+
+ ` : ''} + + ${response.reports?.timing ? ` +
+
时序信息
+
${response.reports.timing}
+
+ ` : ''} + + ${response.importedFiles && response.importedFiles.length > 0 ? ` +
+
已导入文件
+
    + ${response.importedFiles.map(f => `
  • ${f}
  • `).join('')} +
+
+ ` : ''} +
+ `; +} +``` + +## 5. 配置界面实现 + +### 5.1 设置页面扩展 + +```typescript +// src/views/vivadoSettings.ts + +export function renderVivadoSettings(config: VivadoConfig | null): string { + return ` +
+

Vivado 配置

+ +
+ + +
+ +
+ + + +
+ +
+ + +
+ +
+ + +
+ + +
+ `; +} +``` + +## 6. 测试方案 + +### 6.1 单元测试 + +```typescript +// src/test/vivadoRunner.test.ts + +import * as assert from 'assert'; +import { generateSynthesisTcl } from '../utils/tclGenerator'; + +suite('Vivado TCL Generator', () => { + test('生成综合脚本', () => { + const tcl = generateSynthesisTcl( + 'counter', + ['counter.v'], + 'xc7a35tcpg236-1' + ); + + assert.ok(tcl.includes('read_verilog counter.v')); + assert.ok(tcl.includes('synth_design')); + assert.ok(tcl.includes('write_checkpoint')); + }); +}); +``` + +### 6.2 集成测试 + +```typescript +// src/test/vivadoIntegration.test.ts + +suite('Vivado Integration', () => { + test('完整综合流程', async () => { + const request: VivadoToolRequest = { + command: 'synthesis', + parameters: { + topModule: 'counter', + files: ['test/fixtures/counter.v'] + } + }; + + const response = await runVivado(request); + assert.ok(response.success); + assert.ok(response.executionTime > 0); + }); +}); +``` + +## 7. 部署和发布 + +### 7.1 文件清单 + +新增文件: +- `src/utils/vivadoConfig.ts` - 配置管理 +- `src/utils/tclGenerator.ts` - TCL 脚本生成 +- `src/utils/vivadoRunner.ts` - Vivado 执行器 +- `src/utils/fileImporter.ts` - 文件导入 +- `src/views/vivadoProgress.ts` - 进度显示 +- `src/views/vivadoResult.ts` - 结果展示 +- `src/views/vivadoSettings.ts` - 设置界面 + +修改文件: +- `src/utils/messageHandler.ts` - 添加 Vivado 工具处理 +- `src/views/settingsComponent.ts` - 添加 Vivado 设置页面 + +### 7.2 配置文件更新 + +```json +// package.json (新增配置项) +{ + "contributes": { + "configuration": { + "properties": { + "ic-coder.vivado.enabled": { + "type": "boolean", + "default": false, + "description": "启用 Vivado 集成" + }, + "ic-coder.vivado.executablePath": { + "type": "string", + "default": "", + "description": "Vivado 可执行文件路径" + }, + "ic-coder.vivado.workingDir": { + "type": "string", + "default": "${workspaceFolder}/vivado_project", + "description": "Vivado 工作目录" + }, + "ic-coder.vivado.part": { + "type": "string", + "default": "xc7a35tcpg236-1", + "description": "默认 FPGA 型号" + } + } + } + } +} +``` + +## 8. 常见问题和解决方案 + +### 8.1 Vivado 许可证问题 + +**问题**:执行时提示许可证错误 + +**解决方案**: +1. 检查环境变量 `XILINX_VIVADO` 是否设置 +2. 确认许可证服务器可访问 +3. 在配置中添加许可证路径 + +### 8.2 路径问题 + +**问题**:Windows 路径包含空格导致执行失败 + +**解决方案**: +```typescript +function escapeWindowsPath(p: string): string { + return p.includes(' ') ? `"${p}"` : p; +} +``` + +### 8.3 执行超时 + +**问题**:大型项目综合时间过长 + +**解决方案**: +- 增加超时时间配置 +- 添加取消执行功能 +- 显示详细进度信息 + +## 9. 性能优化 + +### 9.1 日志缓冲 + +限制日志输出大小,避免内存溢出: + +```typescript +const MAX_LOG_SIZE = 1024 * 1024; // 1MB +let logBuffer = ''; + +process.stdout.on('data', (data) => { + logBuffer += data.toString(); + if (logBuffer.length > MAX_LOG_SIZE) { + logBuffer = logBuffer.slice(-MAX_LOG_SIZE / 2); + } +}); +``` + +### 9.2 增量构建 + +支持增量综合,只重新综合修改的模块。 + +## 10. 后续优化方向 + +1. **并行执行**:支持多个设计同时综合 +2. **缓存机制**:缓存未修改模块的综合结果 +3. **云端集成**:支持云端 Vivado 服务 +4. **可视化报告**:图形化展示资源使用和时序 +5. **自动约束生成**:根据设计自动生成 XDC 约束文件 +