笔者曾经使用 Digital IDE 在 VSCode 中进行文件编辑并使用 Vivado 进行综合、布线和生成比特流。近来笔者苦于 Vivado 编译过程过慢而希望寻找速度更快的(开源)解决方案。在 Gemini 和 Claude 的帮助下成功部署。笔者使用的是基于 Xilinx xc7a35tfgg484-2 芯片的开发板。

需求清单

部署

对于 xc7a35tfgg484-2 型号的 Xilinx 开发板,目前有完善的全开源解决方案:

  1. 综合与布线:Yosys + nextpnr-xilinx
  2. 比特流生成:nextpnr-xilinx
  3. 烧录:openFPGALoader

其中第一步与第二步暂无 Windows 下的解决方案,但有完善的 docker 镜像 regymm/openxc7。openFPGALoader 可以通过 MSYS2 在 Windows 上安装。MSYS2 是一个在 Windows 上模拟 Unix 命令行和环境的模拟器。烧录时,默认的下载器驱动可能无法被 openFPGALoader 识别,需要使用 Zadig 换一下驱动。

环境准备

安装 Docker

安装 MSYS2

MSYS2 是一个在 Windows 上模拟 Unix 命令行和环境的模拟器,openFPGALoader 依赖于它来提供类 Unix 的环境和工具链。

推荐使用 Scoop 安装

1
scoop install msys2

或者直接从 MSYS2 官网 下载安装包,安装后将 msys2.exe 所在目录加入环境变量 PATH

安装 openFPGALoader

安装完 MSYS2 后,进入 MSYS2 终端

1
msys2 -mingw64

首次进入时 MSYS2 可能会更新它的包管理器。待它更新完成后,安装 openFPGALoader

1
pacman -S mingw-w64-x86_64-openFPGALoader

安装 Zadig

Zadig 是一个 Windows 下的 USB 驱动安装工具,可以用来替换默认的 USB 驱动,使 openFPGALoader 能够识别开发板。

推荐使用 Scoop 安装

1
2
scoop bucket add nonportable
scoop install zadig

或者直接从 Zadig 官网下载。下载的 exe 文件可以直接运行,无需安装。

编译步骤

笔者的项目结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
./
├── build/
│ └── # 编译输出文件
├── docker/
│ ├── compile.sh
│ └── docker-compose.yml
└── user/
├── data/
│ └── top.xdc
├── sim/
│ └── # Testbench 文件
└── src/
└── # Verilog 文件

启动 Docker 容器

docker/docker-compose.yml 定义了一个基于 regymm/openxc7 镜像的服务,挂载了 docker/ 的父目录(即项目根目录)到容器内的 /mnt/workspace 目录。文件内容如下

1
2
3
4
5
6
7
8
9
services:
openxc7-dev:
image: regymm/openxc7
container_name: fpga_openxc7_env
privileged: true
volumes:
- ..:/mnt/workspace
working_dir: /mnt/workspace
command: tail -f /dev/null

在项目根目录下运行以下命令启动 Docker 容器

1
2
docker compose -f docker/docker-compose.yml up -d
# 若报错“找不到命令”,尝试将 docker compose 换成 docker-compose

在所有工作完毕后,可以运行以下命令停止 Docker 容器

1
2
docker compose -f .\docker\docker-compose.yml down
# 若报错“找不到命令”,尝试将 docker compose 换成 docker-compose

编译

编译脚本 docker/compile.sh 定义了综合、布线和比特流生成的步骤。脚本内容如下

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
#!/bin/bash

# 这个脚本在 Docker 容器内执行,完成 Xilinx xc7a35tfgg484-2 类 FPGA 项目的编译流程,不包含烧录步骤
# 把下面的变量按情况修改成你自己的项目结构和文件名
# 先 docker-compose up -d 启动容器
# 然后 docker exec -it fpga_openxc7_env "docker/compile.sh" 即可执行编译
# 最终生成的设计文件会保存在 $BUILD_DIR/$PROJ_NAME/design.bit 中,之后可以使用 openFPGALoader 烧录到 FPGA 上

set -e

# 一些文件
# workdir 是 docker/ 目录的父目录,也就是项目根目录
PROJ_NAME="my_fpga_project"
TOP_MODULE="top"
SRC_FILE="user/src/*.v"
XDC_FILE="user/data/top.xdc"
BUILD_DIR="build"

# 建立构建目录
PROJ_BUILD_DIR="$BUILD_DIR/$PROJ_NAME"
mkdir -p $PROJ_BUILD_DIR

# 1. 逻辑综合
yosys -p "synth_xilinx -flatten -abc9 -arch xc7 -top $TOP_MODULE; write_json $PROJ_BUILD_DIR/design.json" $SRC_FILE
# 2. 生成芯片架构数据库 chipdb
if [ ! -f "$PROJ_BUILD_DIR/xc7a35t.bin" ]; then
echo "Generating chipdb for xc7a35tfgg484-2..."
# 提取器件数据并生成 BBA 文本文件
pypy3 /nextpnr-xilinx/xilinx/python/bbaexport.py --device xc7a35tfgg484-2 --bba $PROJ_BUILD_DIR/xc7a35t.bba
# 将 BBA 文本编译为 nextpnr 可读的 BIN 二进制文件
bbasm -l $PROJ_BUILD_DIR/xc7a35t.bba "$PROJ_BUILD_DIR/xc7a35t.bin"
# 删除庞大的临时中间文件以节省空间
rm -f $PROJ_BUILD_DIR/xc7a35t.bba
fi
# 3. 物理布局布线
nextpnr-xilinx --chipdb "$PROJ_BUILD_DIR/xc7a35t.bin" --xdc $XDC_FILE --json $PROJ_BUILD_DIR/design.json --write $PROJ_BUILD_DIR/design_routed.json --fasm $PROJ_BUILD_DIR/design.fasm
# 4. 生成比特流
/prjxray/env/bin/fasm2frames --part xc7a35tfgg484-2 --db-root /nextpnr-xilinx/xilinx/external/prjxray-db/artix7 $PROJ_BUILD_DIR/design.fasm > $PROJ_BUILD_DIR/design.frames
xc7frames2bit --part_file /nextpnr-xilinx/xilinx/external/prjxray-db/artix7/xc7a35tfgg484-2/part.yaml --part_name xc7a35tfgg484-2 --frm_file $PROJ_BUILD_DIR/design.frames --output_file $PROJ_BUILD_DIR/design.bit
echo "FPGA design compiled successfully! The bitstream is located at: $PROJ_BUILD_DIR/design.bit"

脚本前部定义了一些变量,可以根据自己的项目结构和文件名进行修改:

  • PROJ_NAME:项目名称,编译输出文件将保存在 $BUILD_DIR/$PROJ_NAME/ 目录下
  • TOP_MODULE:顶层模块名称
  • SRC_FILE:Verilog 源文件路径,可以使用通配符
  • XDC_FILE:约束文件路径
  • BUILD_DIR:编译输出的根目录

需要注意的是,脚本硬编码了 Xilinx xc7a35tfgg484-2 这个型号的芯片,用户如果使用其他型号的芯片,需要修改脚本中的相关参数和路径。

脚本修改完成后,执行以下命令在 Docker 容器内运行编译脚本。第一次编译时会生成一个芯片架构数据库文件 xc7a35t.bin,这个过程可能会比较慢,但后续编译会直接使用这个文件,不需要重复生成。

1
docker exec -it fpga_openxc7_env "docker/compile.sh"

最终生成的比特流文件 design.bit 将保存在 $BUILD_DIR/$PROJ_NAME/ 目录下,可以使用 openFPGALoader 烧录到 FPGA 上。

烧录

将开发板连接到电脑。首先检查 openFPGALoader 是否能够识别开发板

1
msys2 -mingw64 -c "openFPGALoader --detect"

正常输出应类似

1
2
3
4
5
6
7
8
9
empty
No cable or board specified: using direct ft2232 interface
Jtag frequency : requested 6.00MHz -> real 6.00MHz
index 0:
idcode 0x362d093
manufacturer xilinx
family artix a7 35t
model xc7a35
irlength 6

如果输出显示没有找到设备,需要使用 Zadig 替换默认的 USB 驱动。打开 Zadig,菜单栏 Option 中选择 List All Devices,然后在设备列表中找到对应的设备(通常是一个 FTDI 设备,名称通常是 Logic_CPU),它具有 Interface 0 和 Interface 1 两个接口。一般 Interface 0 是 JTAG 接口,用于烧录,Interface 1 是 UART 接口,用于串口通信。选择 Interface 0,点击 Replace Driver 按钮,将它的驱动替换为 WinUSB 即可。Interface 1 的驱动保持不变。驱动替换后,在 Windows 设备管理器中对应的接口会从“端口”变成“通用串形总线设备”。如后续希望将驱动改回默认的 FTDI 驱动,在 Windows 设备管理器中找到对应的接口,右键选择卸载设备,并勾选“尝试删除此设备的驱动程序”,卸载后重新插拔开发板即可。

更换驱动后再次运行 msys2 -mingw64 -c "openFPGALoader --detect",即可正确识别开发板。然后运行以下命令烧录比特流到 FPGA 上

1
msys2 -mingw64 -c "openFPGALoader <path_to_your_bitstream>"

其中 <path_to_your_bitstream> 替换成实际的比特流文件路径,按照笔者的项目结构即为 build/my_fpga_project/design.bit

局限

  1. 目前的开源时序数据库大多是基于逆向工程估算的,它在极限高频时序驱动优化上的精度暂时还比不上商业版的 Vivado。如果设计的系统运行时钟频率非常高(超过百兆赫兹),在开源的 nextpnr 阶段可能会面临稍大的时序闭合挑战。但对于大部分常规设计,这套开源工具链组合已经可以提供无缝端到端体验。
  2. 这套开源工具链还不能支持大多数 Vivado IP 核。