AI编程(二) - 基于OpenSkills的FPGA开发自动化

AI编程(二) - 基于OpenSkills的FPGA开发自动化

前置阅读AI编程(一) - 从datasheet到Verilog原型设计 - 从datasheet到verilog原型设计/)

本文代码示例基于Windows + Vivado 2024.1环境,理论上适用于Linux版Vivado。

1. 背景

FPGA开发是一个高度流程化的过程,涉及RTL代码编写、约束编写、仿真、综合、实现、比特流生成等多个环节。每个环节都需要手动操作大量的EDA工具命令。这种工作模式存在以下痛点:

  1. Vivado TCL脚本编写繁琐:Vivado的工程管理几乎完全依赖TCL脚本,参数众多,学习曲线陡峭
  2. 约束文件编写枯燥且易错:XDC约束中的时序路径、IO位置、Clock约束等需要精确对应FPGA器件资源
  3. 仿真环境搭建重复:每个新项目都需要手动创建仿真目录、配置波形文件
  4. 综合报告解读困难:Vivado的综合实现报告结构复杂,人工阅读效率低

本文介绍如何基于Cursor和OpenSkills,将FPGA开发的完整流程封装为可复用的Skills,实现从RTL到Bitstream的全流程自动化

2. FPGA开发全流程梳理

在开始自动化之前,先梳理FPGA开发的完整流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
┌─────────────────────────────────────────────────────────────┐
│ RTL代码编写 │
│ (Verilog/SystemVerilog/VHDL) │
└─────────────────────────┬───────────────────────────────────┘
│ .v/.sv/.vhd

┌─────────────────────────────────────────────────────────────┐
│ 仿真验证 │
│ (Vivado Simulator / ModelSim) │
│ RTL仿真 → 功能验证 → 覆盖率统计 → 回归测试 │
└─────────────────────────┬───────────────────────────────────┘
│ 综合通过

┌─────────────────────────────────────────────────────────────┐
│ 综合 (Synthesis) │
│ VivadoSynthesis │
│ 时序约束(XDC) → 综合网表 → 时序报告 │
└─────────────────────────┬───────────────────────────────────┘
│ 综合通过

┌─────────────────────────────────────────────────────────────┐
│ 实现 (Implementation) │
│ VivadoImplementation │
│ 布局布線 → DRC检查 → 功耗分析 → 时序收敛 │
└─────────────────────────┬───────────────────────────────────┘
│ 实现通过

┌─────────────────────────────────────────────────────────────┐
│ 比特流生成 (Bitstream) │
│ write_bitstream │
└─────────────────────────┬───────────────────────────────────┘
│ .bit / .bit.gz

下载到FPGA板卡

这五个环节中,前三个(RTL编写、仿真、综合)是最适合AI辅助的;后两个(实现、比特流)由于高度依赖器件资源和时序收敛,AI介入的程度有限。

3. 创建FPGA开发Skill

3.1 Skill目录结构

首先创建Skill的基础目录结构:

1
2
3
4
5
6
7
8
fpga-vivado-development/
├── SKILL.md # 核心技能定义
├── references/
│ ├── VIVADO_TCL_REFERENCE.md # Vivado TCL命令速查
│ ├── XDC_CONSTRAINT_GUIDE.md # XDC约束编写指南
│ └── VERILOG_STYLE.md # Verilog编码规范
└── scripts/
└── run_simulation.tcl # 仿真脚本模板

3.2 SKILL.md核心定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
---
name: fpga-vivado-development
description: >
将FPGA开发中的Vivado操作流程封装为可复用的Skills,适用于:
- Vivado TCL脚本自动生成
- XDC时序/IO约束编写
- Verilog编码规范检查
- 综合/实现/比特流生成流程自动化
- 综合报告自动解读

当用户提到以下内容时触发:
- Vivado, FPGA, Xilinx, 综合, 实现, bitstream, TCL脚本
- XDC约束, 时序约束, IO约束
- FPGA仿真, Verilog, SystemVerilog
- Project Navigator, HLS
---

# FPGA Vivado开发自动化技能

## 核心能力

本Skill将FPGA开发中的以下任务自动化:

1. **TCL脚本生成**:综合、实现、比特流的TCL脚本
2. **XDC约束生成**:Clock约束、IO约束、时序约束
3. **代码规范检查**:Verilog/SV编码风格检查
4. **仿真环境搭建**:仿真脚本+波形配置文件
5. **报告解读**:综合/实现Timing/Synthesis报告分析

---

## 一、TCL脚本自动化

### 1.1 综合脚本模板

Vivado综合是FPGA开发的第一步。以下模板覆盖了常用的综合配置:

```tcl
# synthesis.tcl - Vivado综合脚本模板
# 用法:vivado -mode batch -source synthesis.tcl

# ==================== 项目配置 ====================
set project_name "fpga_top"
set part_name "xc7k325tffg900-2" ;# 根据实际器件修改
set src_dir "./rtl"
set constr_dir "./constr"
set build_dir "./build/synthesis"

# ==================== 综合配置 ====================
create_project $project_name $build_dir -part $part_name -force

# 添加源文件
add_files [glob $src_dir/*.v]
add_files [glob $src_dir/*.sv]
add_files [glob $src_dir/*.vhd]

# 设置综合策略
set_property strategy Flow_PerfOptimized_high [get_runs synth_1]

# 添加约束文件
add_files -fileset constrs_1 [glob $constr_dir/*.xdc]
set_property used_in_synthesis true [get_files [glob $constr_dir/*.xdc]]

# ==================== 综合执行 ====================
launch_runs synth_1 -jobs 4
wait_on_run synth_1

# ==================== 报告 ====================
open_run synth_1 -name synth_1
report_utilization -file "./reports/utilization_synth.rpt"
report_timing_summary -file "./reports/timing_synth.rpt"

puts "Synthesis completed successfully"

1.2 AI生成TCL的关键要点

让AI生成Vivado TCL脚本时,需要提供以下信息:

1
2
3
4
5
6
7
8
请为以下规格生成Vivado综合TCL脚本:
- 器件: xc7k325tffg900-2
- 源文件目录: ./rtl (包含*.v和*.sv文件)
- 约束目录: ./constr (包含timing.xdc和pins.xdc)
- 综合策略: timing_driven
- 综合并行任务数: 4

请在综合完成后自动生成utilization和timing_summary报告

1.3 实现脚本模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# implementation.tcl - Vivado实现脚本
# 用法: vivado -mode batch -source implementation.tcl

set project_name "fpga_top"
set build_dir "./build/synthesis"
set constr_dir "./constr"
set impl_dir "./build/implementation"

# 打开综合结果
open_run synth_1

# 设置实现策略
set_property strategy Performance_ExtraTimingOpt [get_runs impl_1]

# 添加物理约束
add_files -fileset constrs_1 [glob $constr_dir/pins_physical.xdc]

# 执行实现
launch_runs impl_1 -jobs 4
wait_on_run impl_1

# ==================== 报告 ====================
open_run impl_1
report_utilization -file "./reports/utilization_impl.rpt"
report_timing_summary -file "./reports/timing_impl.rpt"
report_power -file "./reports/power_impl.rpt"
report_drc -file "./reports/drc_impl.rpt"

puts "Implementation completed successfully"

1.4 比特流生成脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# bitstream.tcl - 比特流生成脚本
# 用法: vivado -mode batch -source bitstream.tcl

set project_name "fpga_top"

# 打开实现结果
open_run impl_1

# 设置比特流配置
set_property BITSTREAM.GENERAL.COMPRESS TRUE [get_runs impl_1]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [get_runs impl_1]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [get_runs impl_1]

# 生成比特流
launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1

puts "Bitstream generated: $project_name.bit"

二、XDC约束自动化

XDC(Xilinx Design Constraints)是Vivado中约束文件的标准格式,包含时序约束和物理约束两大部分。

2.1 Clock约束

Clock是所有时序约束的基础,必须优先定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# clocks.xdc - 时钟约束
# 系统时钟: 100MHz, 来自FPGA引脚
create_clock -period 10.000 -name sys_clk [get_ports sys_clk_p]
set_property PACKAGE_PIN K17 [get_ports sys_clk_p]
set_property IOSTANDARD DIFF_HSTL_I_18 [get_ports sys_clk_p]

# GT参考时钟: 125MHz
create_clock -period 8.000 -name gt_ref_clk [get_pins i_gtp0/GTREFCLK0]

# 虚拟时钟(用于IO约束)
create_clock -period 10.000 -name virt_clk

# 生成时钟(MMCM/PLL输出)
create_generated_clock -name clk_200m -source [get_ports sys_clk_p] \
-divide_by 2 -multiply_by 20 [get_pins i_mmcm/CLKOUT0]

AI辅助生成Clock约束的prompt示例

1
2
3
4
5
6
请为以下时钟系统生成XDC Clock约束:
- 输入参考时钟: 25MHz, 引脚G9, LVCMOS33, 差分正端
- MMCM输出:
- CLKOUT0: 100MHz (给DDR3控制器)
- CLKOUT1: 200MHz (给高速串行收发器)
- CLKOUT2: 50MHz (给逻辑模块)

2.2 IO约束

IO约束定义引脚位置和电气标准:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# pins.xdc - IO物理约束

# DDR3接口
set_property PACKAGE_PIN H5 [get_ports {ddr3_dq[0]}]
set_property IOSTANDARD SSTL135 [get_ports {ddr3_dq[*]}]
set_property PACKAGE_PIN J6 [get_ports ddr3_addr[0]]
set_property PACKAGE_PIN K7 [get_ports ddr3_we_n]
set_property PACKAGE_PIN L8 [get_ports ddr3_ras_n]

# GPIO LED
set_property PACKAGE_PIN T14 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}]

# 高速串行收发器
set_property PACKAGE_PIN D5 [get_pins i_gtp0/TXP]
set_property PACKAGE_PIN C5 [get_pins i_gtp0/TXN]
set_property PACKAGE_PIN F6 [get_pins i_gtp0/RXP]
set_property PACKAGE_PIN E6 [get_pins i_gtp0/RXN]
set_property IOSTANDARD LVDS_25 [get_pins i_gtp0/TXP]

AI辅助生成IO约束的prompt示例

1
2
3
4
5
6
7
8
9
10
11
12
13
请为以下接口生成XDC IO约束(器件: xc7k325tffg900-2):

1. FMC HPC接口 (High Pin Count)
- 24个LVDS差分对, IOSTANDARD = LVDS_25
- FMC_LA00_N/P 到 FMC_LA23_N/P
- 参考器件手册Table 4-7: FMC Connector Pinout

2. DDR3接口
- 16位数据总线 DQ[0:15]
- 13位地址 ADDR[0:12]
- 控制信号: CAS_N, RAS_N, WE_N, CS_N, CKE, ODT
- 时钟: DQS_P/N
- IOSTANDARD = SSTL135

2.3 时序约束

时序约束是最复杂的部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# timing.xdc - 时序约束

# 输入延迟约束(相对于virt_clk)
set_input_delay -clock virt_clk -max 2.5 [get_ports {pcie_rx[*]}]
set_input_delay -clock virt_clk -min 0.5 [get_ports {pcie_rx[*]}]
set_input_delay -clock virt_clk -max 2.5 [get_ports {pcie_rx[*]}] -clock_fall

# 输出延迟约束
set_output_delay -clock virt_clk -max 1.5 [get_ports {pcie_tx[*]}]
set_output_delay -clock virt_clk -min 0.0 [get_ports {pcie_tx[*]}]

# 伪路径约束(异步复位、时钟多路复用等)
set_false_path -from [get_ports async_reset_n]
set_false_path -from [get_clocks clk_200m] -to [get_clocks gt_ref_clk]

# 最大延迟约束(跨时钟域)
set_max_delay -from [get_clocks sys_clk] -to [get_clocks clk_200m] 8.000

三、仿真环境自动化

3.1 仿真脚本模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# sim.tcl - Vivado仿真脚本
# 用法: vivado -mode batch -source sim.tcl

set project_name "fpga_top"
set sim_top "tb_fpga_top"
set src_dir "./rtl"
set tb_dir "./tb"
set wave_dir "./sim/waves"

# 创建项目
create_project -in_memory -part xc7k325tffg900-2

# 添加源文件
add_files [glob $src_dir/*.v]
add_files [glob $tb_dir/*.v]

# 设置仿真顶层
set_property top $sim_top [get_filesets sim_1]

# 设置仿真精度
set_property time_precision 1ps [get_filesets sim_1]

# 运行仿真
launch_simulation
run 1ms
close_sim

3.2 Verilog测试台模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// tb_fpga_top.v - 基础测试台模板
`timescale 1ns / 1ps

module tb_fpga_top;

// ==================== 时钟和复位 ====================
logic sys_clk;
logic sys_rst_n;

// 100MHz时钟
initial begin
sys_clk = 0;
forever #5ns sys_clk = ~sys_clk;
end

// 异步复位
initial begin
sys_rst_n = 0;
#100ns;
sys_rst_n = 1;
end

// ==================== DUT实例化 ====================
fpga_top u_dut (
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.led (led)
);

// ==================== 激励生成 ====================
initial begin
$display("[%0t] Testbench started", $time);

wait (sys_rst_n === 1'b1);
@(posedge sys_clk);

// 测试用例1: 上电初始化
$display("[%0t] Test 1: Initialization", $time);
#1us;

// 测试用例2: 数据传输
$display("[%0t] Test 2: Data transfer", $time);

$display("[%0t] Testbench finished", $time);
$finish;
end

// ==================== 输出监视 ====================
always @(posedge sys_clk) begin
if (led !== 2'b00)
$display("[%0t] LED status: %b", $time, led);
end

// ==================== 覆盖率收集 ====================
covergroup cg_led @(posedge sys_clk);
option.per_instance = 1;
coverpoint led {
bins all_off = {2'b00};
bins all_on = {2'b11};
bins mixed = {[2'b01:2'b10]};
}
endgroup

cg_led cg_inst = new();

endmodule

四、综合报告自动解读

Vivado综合会生成大量报告,AI可以帮助自动解读关键信息:

4.1 关键报告项

报告类型 关键关注点
utilization LUT/FF使用率、BRAM、DSP、IO利用率
timing_summary WNS/TNS、最差路径、时钟域穿越
power 动态功耗、静态功耗、时钟网络功耗
drc 设计规则检查错误

4.2 AI解读报告的prompt模板

1
2
3
4
5
6
7
请分析以下Vivado综合报告,重点关注:
1. 资源利用率是否超过80%(如果超过,给出优化建议)
2. 是否有未约束的时钟或跨时钟域路径
3. 最差时序路径位于哪个模块
4. 是否有高扇出的信号导致布线困难

以下是utilization报告内容(粘贴报告全文)

4.3 时序违例修复策略

1
2
3
4
5
6
7
8
9
10
11
12
13
根据以下时序违例报告,给出修复优先级排序:

Timing Report摘要:
- WNS: -0.842ns
- TNS: -45.321ns
- 最差路径: dut/i_axi_arb/i_priority_encoder -> dut/o_crossbar/mux_0

分析以下可能的优化策略,并按优先级排序:
1. 流水线寄存器插入
2. 重定时(Retiming)
3. 扇出优化(Carry-Structure)
4. 物理约束(Block Placement)
5. 综合策略调整

五、完整工作流脚本

将以上所有脚本整合为一个一键执行的工作流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# run_full_flow.tcl - FPGA开发一键执行脚本
# 执行顺序: 综合 -> 实现 -> 比特流

set project_name "fpga_top"
set part_name "xc7k325tffg900-2"
set src_dir "./rtl"
set constr_dir "./constr"
set build_dir "./build"
set rtl_dir "./rtl"

# ==================== 步骤1: 综合 ====================
puts "\n==== Step 1: Synthesis ===="
create_project -force $project_name/synth $build_dir/synth -part $part_name
add_files [glob $rtl_dir/*.v]
add_files [glob $constr_dir/*.xdc]
set_property used_in_synthesis true [get_files [glob $constr_dir/*.xdc]]
launch_runs synth_1 -jobs 4
wait_on_run synth_1

if {[get_property PROGRESS [get_runs synth_1]] != "100%"} {
error "Synthesis failed. Check logs."
}

# ==================== 步骤2: 实现 ====================
puts "\n==== Step 2: Implementation ===="
open_run synth_1
launch_runs impl_1 -jobs 4
wait_on_run impl_1

if {[get_property PROGRESS [get_runs impl_1]] != "100%"} {
error "Implementation failed. Check logs."
}

# ==================== 步骤3: 比特流 ====================
puts "\n==== Step 3: Bitstream ===="
open_run impl_1
set_property BITSTREAM.GENERAL.COMPRESS TRUE [get_runs impl_1]
launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1

puts "\n==== Full flow completed ===="
puts "Bitstream: $build_dir/impl_1/$project_name.bit"
puts "Reports: $build_dir/"

使用方法

1
2
cd D:\fpga\project
vivado -mode batch -source run_full_flow.tcl

六、自定义Skill的工程实践

6.1 项目AGENTS.md配置

在FPGA项目的根目录创建AGENTS.md,让AI了解项目硬件规格:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# AGENTS.md - FPGA项目配置

## 项目信息
- 器件: xc7k325tffg900-2
- 综合工具: Vivado 2024.1
- 仿真器: Vivado Simulator

## 时钟规格
- 系统时钟: 100MHz (引脚K17)
- DDR3参考: 200MHz
- PCIe参考: 125MHz

## 资源预算
- LUT使用率目标: < 70%
- FF使用率目标: < 60%
- BRAM使用率目标: < 50%

## 编码规范
- 使用SystemVerilog进行接口定义
- 时钟复位统一使用: input clk, input rst_n
- 状态机使用one-hot编码
- 异步FIFO深度: 16级以上

6.2 自定义Skill脚本

将常用的TCL操作封装为Python脚本,供AI调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/env python3
"""
fpga_flow.py - FPGA开发流程辅助脚本
"""
import subprocess, os, sys
from pathlib import Path

class VivadoFlow:
def __init__(self, project_dir):
self.project_dir = Path(project_dir)
self.vivado = "vivado"

def run_tcl(self, tcl_script):
"""执行TCL脚本"""
cmd = [self.vivado, "-mode", "batch", "-source", str(tcl_script)]
result = subprocess.run(cmd, capture_output=True, text=True)
return result

def read_report(self, report_path):
"""读取Vivado报告"""
if not Path(report_path).exists():
return None
with open(report_path, "r") as f:
return f.read()

def check_timing(self, build_dir):
"""检查时序收敛"""
report = self.read_report(f"{build_dir}/impl_1/timing_summary.rpt")
if not report:
return {"status": "unknown"}
wns = self._extract_wns(report)
return {"status": "pass" if wns >= 0 else "fail", "wns": wns}

def _extract_wns(self, report):
"""从报告提取WNS"""
for line in report.split("\n"):
if "WNS" in line and "ns" in line:
parts = line.split()
for i, p in enumerate(parts):
if "WNS" in p and i+1 < len(parts):
return float(parts[i+1].rstrip("ns"))
return 0.0

七、常见问题与解决

7.1 综合报告中LUT使用率过高

原因:缺乏流水线、状态机未优化、高扇出信号。

解决

  1. 在关键路径插入流水线寄存器
  2. 使用max_fanout属性限制信号扇出
  3. 状态机使用one-hot编码
1
2
3
4
5
# 限制扇出
set_property MAX_FANOUT 16 [get_nets -hier -filter {NAME =~ "ctrl_en*"}]

# one-hot状态机
set_property fsm_encoding one_hot [get_cells -hier -filter {FSM_ENCODING}]

7.2 跨时钟域路径违例

原因:两个时钟域之间缺少CDC约束。

解决

  1. 使用set_false_path标记异步CDC路径
  2. 使用异步FIFO隔离时钟域
  3. 添加set_max_delay约束
1
2
3
# 异步CDC路径
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
set_false_path -from [get_clocks clk_b] -to [get_clocks clk_a]

7.3 DRC检查错误

常见错误

  • IO bank电压冲突
  • 引脚被占用
  • DCI级联不匹配
1
2
# 检查IO bank电压
report_property -all [get_iobanks]

7.4 Vivado License获取失败

1
2
# 设置license环境变量
$env:XILINXD_LICENSE_FILE = "2100@license-server"

八、总结

本文介绍了基于OpenSkills的FPGA开发自动化工作流:

开发环节 AI辅助价值 自动化程度
RTL代码编写 ⭐⭐⭐⭐⭐ 高(见AI编程(一))
XDC约束生成 ⭐⭐⭐⭐
TCL脚本生成 ⭐⭐⭐⭐
仿真环境 ⭐⭐⭐
综合报告解读 ⭐⭐⭐⭐
实现/比特流 ⭐⭐ 低(时序收敛需人工)

核心经验:

  1. 约束先行:在开始RTL编写之前,先用AI生成完整的XDC约束模板,可以避免后期大量的时序修复工作
  2. 脚本版本化:将所有TCL脚本纳入Git管理,便于追踪变更和回退
  3. 报告自动化解读:让AI负责解读Vivado生成的各类报告,人工只需关注AI标注的重点
  4. 参数化设计:RTL代码中充分使用parameterlocalparam,让同一套代码适配不同器件

参考资料

  1. Xilinx Vivado Design Suite User Guide
  2. Vivado Design Suite TCL Documentation
  3. XDC约束指南 (UG903)
  4. AI编程(一) - 从datasheet到Verilog原型设计 - 从datasheet到verilog原型设计/)
  5. Anthropic Claude Code Skills