← Back to Blog
FPGAZynqXilinxVivadoIP IntegratorXDCTcl嵌入式系统

Zynq 实战 03|Vivado 基础设计流程:从空工程到点亮 LED 全流程手册

This article was written in Chinese and auto-translated via Google Translate.
View Chinese Original →

Zynq 实战 03|Vivado 基础设计流程:从空工程到点亮 LED 全流程手册

这是《Zynq FPGA 嵌入式系统设计实战》系列的第 3 篇。 板子:Pynq-Z2(XC7Z020-1CLG400C)。工具链:Vivado / Vitis 2023.2。 上一篇:Zynq 实战 02|开发环境搭建:Vivado / Vitis / PetaLinux 2023.2 安装避坑指南


0. 这一篇要解决什么问题

在前两篇搞清楚架构、装好工具链之后,现在要做第一件真正的事:跑通一个完整的 Vivado 设计流程

目标非常具体:在 Pynq-Z2 的 PL 端,用 IP Integrator 搭一个 ZYNQ7 PS + AXI GPIO 的最小系统,让 PS 能通过 AXI 控制 4 个板载 LED(LD0~LD3)。

本文会讲什么:工程创建 → 板子文件配置 → IPI Block Design → XDC 约束 → 综合/实现 → 比特流生成 → Hardware Manager 烧录 → Tcl 重建脚本。

本文不会讲:Vitis 软件侧(写 C 代码控制 GPIO)放在下一篇,本篇只把硬件设计做到能下载进板子。


1. 工程类型先想清楚:别一上来就点 RTL Project

Vivado 新建工程向导第三步会问你 Project Type,新手一般直接选默认的 RTL Project,不会想太多。但实际上这里有个日后会反复影响你工作流程的决定。

主要三种类型对比:

类型是否生成工程文件夹综合方式适合场景
RTL Project✅ 生成 .xpr + 完整文件夹GUI 驱动日常开发、有 IPI Block Design
Post-synthesis Project导入已综合网表拿到第三方 netlist 继续实现
Project-less (Tcl)❌ 只有脚本Tcl 脚本驱动全流程CI/CD、可重现 build

推荐的工程策略:用 RTL Project 做 GUI 开发,但同时维护一个 Tcl 重建脚本(本文最后一节讲怎么生成)。这样:

  • 开发时有完整 GUI(方便 IP Integrator、时序分析、调试)
  • .xpr 文件和 IP 缓存不用进 git(几百 MB 的编译中间产物没必要 commit)
  • 团队成员只需要 source rebuild_project.tcl 就能从头重建整个工程

🚧 避坑:新手最常犯的错误是把整个 Vivado 工程文件夹 git add .,结果 repo 里多出几百 MB 的 .runs/ 缓存。正确做法:.gitignore 里排除 .Xil/*.jou*.log.runs/.cache/只 track 源文件、约束、IP 配置和 Tcl 重建脚本


2. 创建 Pynq-Z2 工程(含 board file 正确安装姿势)

2.1 先装 board file,否则板子选不到

Vivado 默认不自带 Pynq-Z2 的板级描述文件。要在 Create Project 向导里的”Board”标签页选到它,你需要先安装。

方法一:Xhub 在线安装(推荐,2023.2 已内置)

Vivado → Tools → Vivado Store → Boards
搜索 "pynq-z2" → 点 Install

安装完成后,板子文件路径:

~/.Xilinx/Vivado/2023.2/xhub/board_store/XilinxBoardStore/boards/Xilinx/pynq-z2/

方法二:手动克隆安装

# 板子文件官方仓库
git clone https://github.com/Xilinx/XilinxBoardStore.git

# 把 pynq-z2 复制到 Vivado 搜索路径
cp -r XilinxBoardStore/boards/Xilinx/pynq-z2 \
  ~/.Xilinx/Vivado/2023.2/xhub/board_store/XilinxBoardStore/boards/Xilinx/

# 重启 Vivado 后,Create Project → Board 标签即可搜到

🚧 避坑:Vivado 2023.2 的 Xhub 在某些网络环境下(尤其日本国内代理)会超时。如果 Vivado Store 加载很慢,直接用方法二手动克隆。仓库地址:github.com/Xilinx/XilinxBoardStore

2.2 新建工程步骤

1. Vivado → Create Project → Next
2. Project name: led_gpio_demo
   Project location: ~/Projects/zynq/
   □ Create project subdirectory(勾上)
3. Project Type: RTL Project
   □ Do not specify sources at this time(勾上,Sources 后面再加)
4. Default Part → Board 标签页
   搜索 "pynq-z2"
   选 "PYNQ-Z2" (TUL Embedded,版本 1.0)
5. Finish

选了 board 之后,Vivado 会自动知道:

  • 目标器件是 xc7z020clg400-1
  • PS 有 DDR3 配置、MIO 映射、时钟设定
  • 后面 Block Automation 的默认配置会贴合 Pynq-Z2 的实际硬件

3. IP Integrator:搭 ZYNQ7 PS + AXI GPIO 最小系统

这是本篇最核心的一节。我们要在 IPI 里建一个最小系统:PS 通过 AXI GP0 总线控制一个 AXI GPIO IP,GPIO 的输出接到 4 个 LED 引脚。

IPI Block Design — 最小 LED 系统 ZYNQ7 Processing System DDR3 / 512MB FCLK_CLK0 = 100 MHz FCLK_RESET0_N M_AXI_GP0 → AXI Interconnect AXI GPIO GPIO Width = 4 All Output (LED) gpio_rtl[3:0] → LD0~LD3 AXI GPIO 默认地址:0x4120_0000(AXI GP0 地址空间)
图 1. IPI 最小 LED 系统:PS → AXI Interconnect → AXI GPIO → LD0~LD3

3.1 创建 Block Design

Flow Navigator → IP INTEGRATOR → Create Block Design
Design name: system
→ OK

空白 Diagram 出现后,按 Ctrl+I(或点工具栏的 + 按钮)搜索并添加 IP:

Step 1:添加 ZYNQ7 PS

搜索 zynq,双击 ZYNQ7 Processing System,画布出现 PS7 块。

Step 2:运行 Block Automation

IP 添加后顶部会出现绿色提示条:

Run Block Automation

点击 → 选 All AutomationOK

这一步做了什么:Vivado 读取你之前选的 board preset(pynq-z2.tcl),自动配置 PS7 的:

  • DDR3 接口(512MB,400MHz)
  • 外部 DDR 引脚(MIO 绑定)
  • FCLK_CLK0 = 100MHz
  • UART0 / ENET0 / USB0 / SD0 的 MIO 映射

这就是选 board 而不是直接选 part 的好处——不需要手动配 DDR timing。

Step 3:添加 AXI GPIO

再按 Ctrl+I,搜索 axi gpio,双击添加。双击 GPIO 块打开配置:

GPIO Width: 4
All Outputs: ✅(勾选,LED 是输出)
Channel 2: 不勾(我们只用 Channel 1)
→ OK

Step 4:运行 Connection Automation

顶部再次出现绿色提示:

Run Connection Automation

点击 → 全选 → OK。

Vivado 会自动:

  • 在 PS7 和 AXI GPIO 之间插入 AXI Interconnect
  • 连接 FCLK_CLK0 → AXI 时钟
  • 连接 FCLK_RESET0_N → 复位
  • 分配 AXI 地址(GPIO 默认 0x4120_0000,4KB 空间)

Step 5:把 GPIO 的 IO 端口引出来

找到 AXI GPIO 块上的 GPIO 端口(绿色),右键 → Make External

这会在 BD 边界创建一个叫 gpio_rtl_0 的外部端口,后面 XDC 里要引用这个名字。

Step 6:验证设计

Diagram 工具栏 → Validate Design(√ 图标)

如果出现 Critical Warning 说 gpio_rtl_0 没有约束,先忽略,XDC 里会补上。其他 error 才需要修。

Step 7:生成 HDL Wrapper

Sources 面板 → Design Sources → system.bd
右键 → Create HDL Wrapper
→ Let Vivado manage wrapper and auto-update → OK

这会生成 system_wrapper.v,作为顶层模块被 Vivado 综合。

🚧 避坑:不要手动编辑 system_wrapper.v——Vivado 每次修改 BD 后会重新生成它,你的改动会被覆盖。如果需要在 wrapper 层加逻辑,创建一个新的顶层文件,把 system_wrapper 例化进去。


4. XDC 约束:Pynq-Z2 真实引脚定义

XDC(Xilinx Design Constraints)是 Vivado 的约束格式,语法基于 Tcl,但文件名以 .xdc 结尾。

4.1 添加约束文件

Flow Navigator → PROJECT MANAGER → Add Sources
→ Add or create constraints → Create File
File name: pynq_z2_top.xdc
→ OK → Finish

4.2 Pynq-Z2 完整 LED 引脚 XDC

以下是基于 Pynq-Z2 官方原理图(Rev C)的真实引脚定义。Board 上 LD0~LD3 连接的 PL 引脚:

LEDPACKAGE_PINIOSTANDARDBank
LD0R14LVCMOS33Bank 35
LD1P14LVCMOS33Bank 35
LD2N16LVCMOS33Bank 35
LD3M14LVCMOS33Bank 35
## pynq_z2_top.xdc
## Pynq-Z2 (XC7Z020-1CLG400C) — PL User LEDs
## 参考:Pynq-Z2 Rev C Schematic, Sheet 6 (LED Connections)

## LD0 - PL_LED0
set_property PACKAGE_PIN R14 [get_ports {gpio_rtl_0_tri_o[0]}]
set_property IOSTANDARD  LVCMOS33 [get_ports {gpio_rtl_0_tri_o[0]}]

## LD1 - PL_LED1
set_property PACKAGE_PIN P14 [get_ports {gpio_rtl_0_tri_o[1]}]
set_property IOSTANDARD  LVCMOS33 [get_ports {gpio_rtl_0_tri_o[1]}]

## LD2 - PL_LED2
set_property PACKAGE_PIN N16 [get_ports {gpio_rtl_0_tri_o[2]}]
set_property IOSTANDARD  LVCMOS33 [get_ports {gpio_rtl_0_tri_o[2]}]

## LD3 - PL_LED3
set_property PACKAGE_PIN M14 [get_ports {gpio_rtl_0_tri_o[3]}]
set_property IOSTANDARD  LVCMOS33 [get_ports {gpio_rtl_0_tri_o[3]}]

端口名说明gpio_rtl_0_tri_o 是 AXI GPIO IP 在 IPI 里生成的 tristate 端口名称。如果你 Make External 后 Vivado 给它起的名字不同(比如 gpio_rtl_0),打开 system_wrapper.v 查一下实际的顶层端口名称,改成对应的。

4.3 关于 create_clock 的说明

在纯 IPI 设计里,FCLK_CLK0 不需要在 XDC 里写 create_clock。原因:PS7 IP 的约束(.xdc 文件随 IP 一起打包)已经包含了对所有 FCLK 端口的时序约束声明,Vivado 在综合/实现时会自动读取它们。

如果你在做纯 PL 的 RTL 设计(不用 PS,用外部晶振),则需要:

## 仅在纯 PL 设计中使用外部时钟引脚时才写这行
## Pynq-Z2 没有直接接到 PL 的独立晶振(所有 PL 时钟来自 PS FCLK)
## 下面的例子是假设你有一个外部 125MHz 时钟输入
create_clock -name sys_clk -period 8.000 [get_ports sys_clk_p]

🚧 避坑:原资料里那个 XDC 例子直接写 set_property PACKAGE_PIN Y11create_clock -name clk_fpga——Y11 在 7Z020 CLG400 封装里根本不是 clock-capable 引脚,综合时会报 Critical Warning,时序分析结果不可信。用 IPI + PS7 的设计,FCLK 时钟约束交给 Vivado 的 IP 约束机制,别自己乱写。


5. Synthesis 与 Implementation:流程差异与 Log 解读

Vivado 综合与实现流程 RTL 设计 .v / .sv / BD 综合 Synthesis RTL → 门级网表 synth_design .dcp 检查点 (综合后) 实现 Implementation Opt Design 逻辑优化 Place Design 物理布局 Phys Opt 物理优化(可选) Route Design 走线 Timing Check report_timing 综合:RTL 语义 → 逻辑门(与目标器件无关的抽象层) 实现:把逻辑门放进 7Z020 具体的 LUT/FF/BRAM 里,并完成物理走线 write_bitstream → .bit 文件
图 2. Vivado 综合与实现完整流程

5.1 综合(Synthesis)做什么

综合是把你写的 RTL(寄存器传输级描述)翻译成逻辑门级网表的过程。这一步和具体器件的物理位置无关——输出的 .dcp(Design Checkpoint)文件里是抽象的 LUT、FF、CARRY4 等原语,还没有分配到 XC7Z020 里任何具体的物理坐标。

综合后必看的两个报告

  1. Utilization Reportreport_utilization):看 LUT 使用了多少、FF 多少、BRAM 多少。如果这里显示资源超标(100%+),后面实现一定失败。

  2. Timing Report(综合后):此时时序分析是估算(基于理想走线),不精确。但如果综合后 WNS(Worst Negative Slack)已经是 -5ns,实现后也很难过。

5.2 实现(Implementation)做什么

实现把综合网表映射到器件物理资源上,分四个子阶段:

子阶段命令做什么
Opt Designopt_design逻辑级优化(去除冗余逻辑、合并等)
Place Designplace_design把每个 LUT/FF 分配到具体的 SLICE 坐标
Phys Opt Designphys_opt_design基于真实物理位置做时序驱动优化
Route Designroute_design用芯片内部互联资源走线,完成所有连接

实现后必看的报告

# Timing summary — 最重要的
report_timing_summary -file timing_summary.rpt

# 看 WNS (Worst Negative Slack):
#   WNS ≥ 0:时序通过,可以生成比特流
#   WNS < 0:时序违规,需要优化(降频 / 改设计 / 加 pipelie)

# Utilization — 资源使用
report_utilization -file utilization.rpt

# Power estimate — 功耗预估
report_power -file power.rpt

Log 里你会反复看到的关键词

  • TIMING-18:提示有时序违规的 INFO
  • CRITICAL WARNING [Drc 23-20]:DRC 设计规则检查警告,很多是可忽略的,但 ERROR 必须修
  • Slack (MET):时序已满足
  • Slack (VIOLATED):时序未满足,后面跟着具体的路径

🚧 避坑:不要轻易忽略 TIMING-18。很多新手的习惯是”综合通过了就生成比特流”——但综合后的时序报告是估算,实现后的才是真实的。有过 WNS 在综合后 +1.2ns,实现后变成 -0.8ns 的情况,下载进板子后逻辑时不时出错,极难调试。养成习惯:实现完看一次 report_timing_summary,WNS ≥ 0 才生成比特流。


6. 生成比特流与 Hardware Manager 烧录

6.1 生成比特流

Flow Navigator → PROGRAM AND DEBUG → Generate Bitstream

或者在 Tcl Console:

launch_runs impl_1 -to_step write_bitstream -jobs 4
wait_on_run impl_1

比特流文件路径:

led_gpio_demo/led_gpio_demo.runs/impl_1/system_wrapper.bit

6.2 连接板子 + 烧录

Pynq-Z2 用 micro USB 连到 PC 的 PROG/UART 口(板子上标 “PROG”,不是 “UART”),跳线帽拨到 JTAG 位置(否则会从 SD 卡或 QSPI 启动,Hardware Manager 找不到 TAP)。

Open Hardware Manager → Open Target → Auto Connect

# 找到 xc7z020_1
右键 → Program Device
Bitstream file: .../impl_1/system_wrapper.bit
→ Program

或者 Tcl:

open_hw_manager
connect_hw_server
open_hw_target
set_property PROGRAM.FILE {./impl_1/system_wrapper.bit} [get_hw_devices xc7z020_1]
program_hw_devices [get_hw_devices xc7z020_1]
close_hw_target

烧录成功后,Pynq-Z2 上 LD0~LD3 的状态由 AXI GPIO 寄存器控制。此时 LED 可能全亮、全灭或随机状态——因为我们还没有从 PS 侧写软件,GPIO 的输出寄存器初始值是 0x0 或未定义。这是正常的,下一篇 Vitis 软件篇里会写 C 代码来控制它们。

🚧 避坑:烧录比特流后,你烧的是 PL 配置,不是板子的 Flash。断电后比特流会丢失,下次上电需要重新烧。要做到上电自动加载,需要把比特流和 FSBL 打包写入 SD 卡或 QSPI Flash——那是 boot 流程的内容,后面篇章讲。


7. Tcl 重建脚本:write_project_tcl 的妙用

这是整个流程里最容易被跳过、但长期来看最重要的一步。

Vivado 工程文件夹里有大量编译中间产物(.runs/.cache/.srcs/bd/.gen/),如果这些都进了 git,仓库会迅速膨胀到几百 MB,而且在不同机器上有路径问题。

正确做法:让 git 只 track 源文件和重建脚本,用脚本重建工程。

7.1 生成重建脚本

在工程跑通之后,在 Vivado Tcl Console 里执行:

# 切换到工程根目录
cd ~/Projects/zynq/led_gpio_demo

# 生成重建脚本,排除编译产物
write_project_tcl -force \
  -no_copy_sources \
  ./scripts/rebuild_project.tcl

参数说明:

  • -force:覆盖已有文件
  • -no_copy_sources:不把源文件复制到脚本,只记录引用路径(源文件已在 git 里)

7.2 用脚本重建工程

在另一台机器(或删掉 .xpr 文件后)恢复工程:

# Vivado batch 模式运行 Tcl 脚本
vivado -mode batch -source ~/Projects/zynq/led_gpio_demo/scripts/rebuild_project.tcl

# 或者在 Vivado GUI Tcl Console 里:
# source ~/Projects/zynq/led_gpio_demo/scripts/rebuild_project.tcl

脚本会重新创建 .xpr、重新关联所有源文件、重建 Block Design,恢复到和你当时一模一样的状态。

7.3 推荐的 .gitignore

# Vivado generated files
*.jou
*.log
*.str
*.xpr
.Xil/
.cache/
.runs/
.sim/
.ip_user_files/
ip_user_files/

# 保留这些 ↓
# scripts/rebuild_project.tcl   ← 重建脚本
# src/                           ← 自己写的 RTL
# constrs/                       ← XDC 约束

🚧 避坑write_project_tcl 默认生成的脚本里会有绝对路径(/home/kevin/Projects/...)。如果你要在团队里共享,在生成后检查脚本里的路径,把绝对路径改成相对路径,或者加一行 set origin_dir [file dirname [info script]] 作为基准路径。


8. 本篇你应该带走的几个判断

  • 创建工程时选了 board 而不是 part,能享受自动 preset 配置,值得
  • Board file 手动克隆路径:~/.Xilinx/Vivado/2023.2/xhub/board_store/XilinxBoardStore/boards/Xilinx/pynq-z2/
  • IPI 设计中 PS FCLK 时钟不需要在 XDC 里写 create_clock,PS7 IP 自带约束
  • Pynq-Z2 板载 LD0~LD3 真实引脚:R14 / P14 / N16 / M14,LVCMOS33,Bank 35
  • 综合后时序是估算,实现后才是真实的——养成实现后看 report_timing_summary 的习惯
  • write_project_tcl 是工程可重现、可版本控制的关键——每次修完 BD 就更新一次

9. 下一篇预告

下一篇 《Zynq 实战 04|Vitis 嵌入式软件入门:从 Hello World 到 AXI GPIO 控制 LED》,我们会:

  • 在 Vitis 2023.2 里从本篇的硬件平台(.xsa 文件)创建 Platform + Application
  • 理解 BSP(Board Support Package)里的地址映射是怎么来的
  • 写 C 代码通过 XGpio_DiscreteWrite() 控制 LED 流水灯
  • 通过 UART 打印调试信息,用 SDK Terminal 看输出
  • JTAG 调试:断点、单步、寄存器查看

参考资料

文档号名称用途
UG585Zynq-7000 SoC Technical Reference ManualPS7 寄存器定义、AXI GP/HP 端口细节
UG994Vivado Design Suite User Guide: Designing IP Subsystems Using IP IntegratorIP Integrator 完整操作参考
UG895Vivado Design Suite User Guide: System-Level Design EntryBlock Design、HDL Wrapper 创建
UG896Vivado Design Suite User Guide: Designing with IPIP 核配置、约束管理
UG901Vivado Design Suite User Guide: Synthesis综合策略、synth_design 参数
UG904Vivado Design Suite User Guide: Implementation实现子流程、place_design / route_design
UG835Vivado Design Suite Tcl Command Reference Guidewrite_project_tcl 等 Tcl 命令完整参考
PG144AXI GPIO Product GuideAXI GPIO IP 寄存器映射、配置选项
XilinxBoardStoregithub.com/Xilinx/XilinxBoardStorePynq-Z2 board file 官方仓库

这是《Zynq FPGA 嵌入式系统设计实战》系列第 3 篇。 本系列覆盖从架构、开发环境、AXI 设计、PetaLinux 到完整项目实战的全流程。 欢迎留言交流,踩过的坑越多,后面的坑越容易绕开。