← Back to Blog
FPGAZynqXilinxVivadoIP IntegratorAXI硬件平台Block DesignXDC

Zynq 实战 07|硬件平台设计与验证:IP Integrator 全流程,从搭 Block Design 到导出 .xsa

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

Zynq 实战 07|硬件平台设计与验证

这是《Zynq FPGA 嵌入式系统设计实战》系列的第 7 篇。 板子:Pynq-Z2(XC7Z020-1CLG400C)。工具链:Vivado / Vitis 2023.2。 上一篇:《Zynq 实战 06|自定义 AXI IP 核开发》


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

前几篇我们把 AXI 协议、IP 打包流程都走过一遍了。这一篇是硬件设计的汇合点——把所有零件组装成一个完整的 Zynq 系统,生成比特流,导出给 Vitis 开始写软件。

具体我们要搭这样一个系统:

  • ZYNQ7 Processing System:PS 核心,使能 M_AXI_GP0 + IRQ_F2P
  • AXI GPIO:控制 4 个 LED + 读 2 个按键
  • AXI Timer:32-bit 可编程定时器,产生定时中断
  • 自定义 PWM IP(来自第 06 篇):输出 PWM 信号,产生中断
  • Processor System Reset:统一管理 PL 侧复位
  • Concat IP:把多路中断汇聚成一根 IRQ_F2P

本文不会讲:Vitis 软件编写(那是下一篇)、PetaLinux 集成(第 10 篇往后)。


1. 创建工程,进入 IP Integrator

假设你已经创建了 Vivado 工程,目标器件 xc7z020clg400-1(Pynq-Z2)。打开工程后,在 Flow Navigator → IP INTEGRATOR → Create Block Design,弹出对话框填 design_1,点 OK。

Diagram 窗口打开后是空的。在空白区右键选 Add IP(或按快捷键 P),搜索添加第一个 IP:ZYNQ7 Processing System

拖进来之后你会看到一条绿色提示横幅:“Run Block Automation”。先别急着点,我们先来看看这个操作实际做什么。

Run Block Automation 内部发生了什么

Block Automation 是 Vivado 针对 ZYNQ7 PS IP 的特殊处理,只对 PS 有效(不是所有 IP 都有这个选项)。点击后它会做:

  1. 读取 Vivado 里配置的 Board Preset(Pynq-Z2 的 .xboard 文件)
  2. 自动配置 PS 的 DDR 引脚(PS7_DDR_*)和固定 IO(PS7_FIXED_IO)的约束
  3. 把 PS 的 DDR 接口和 Fixed IO 在 Diagram 中折叠成两个外部端口(你会看到 DDRFIXED_IO 引脚出现在 PS block 外部)

如果你没有加载 Board Preset(比如用的非官方板子),Block Automation 还是能跑,但它会提示”No preset found”,你需要手动配置 DDR 参数。

点击 Run Block Automation,勾选 Apply Board Preset,确认后 PS block 多出了 DDR 和 FIXED_IO 两个外部接口。

接下来双击 PS block,进入配置界面,做两件事:

① 确认时钟设置(Page: Clock Configuration):

  • FCLK_CLK0 保持默认 100 MHz(来自 IOPLL,对应 UG585 第 25 章 Clock Management)
  • 如果你需要第二个不同频率的时钟,在这里打开 FCLK_CLK1 并设置目标频率(比如 200 MHz 给高速路径)

② 打开 IRQ_F2P 输入(Page: Interrupts):

  • 找到 Fabric Interrupts → PL-PS Interrupt Ports → IRQ_F2P,勾上 [0:0](先只用第 0 号,后面用 Concat 扩展)

2. 添加 PL 端 IP 并 Run Connection Automation

依次添加以下 IP(全部用 P 键搜索):

  1. AXI GPIO × 1(控制 LED 和按键)
  2. AXI Timer × 1
  3. 你在第 06 篇打包的 pwm_v1_0(搜索不到就先加入 User Repository:Tools → Settings → IP → Repository)
  4. Processor System Reset × 1
  5. Concat × 1

添加完这 5 个 IP 后,Diagram 顶部会再次出现绿色提示:“Run Connection Automation”。这次我们来看它做的事更多:

Run Connection Automation 内部发生了什么

Connection Automation 是通用的(对所有 IP 生效),它的逻辑是:

  1. 扫描 Diagram 里所有未连接的接口引脚
  2. 对符合特定模式的接口(AXI4-Lite slave ↔ PS GP master;aclk ↔ FCLK;aresetn ↔ 复位)自动完成连线
  3. 如果有多个 AXI slave,自动插入一个 AXI Interconnect(或 SmartConnect),并分配地址

勾选所有项,点击 OK。Vivado 会:

  • AXI Interconnect(4 slave 端口)连接 PS 的 M_AXI_GP0 到 GPIO/Timer/PWM 的 AXI slave 接口
  • 把每个 IP 的 s_axi_aclk 连到 FCLK_CLK0(100 MHz)
  • Processor System Reset 的输入时钟也连到 FCLK_CLK0
  • 把各 IP 的 s_axi_aresetn 连到 proc_sys_reset_0peripheral_aresetn[0:0]

完成后整个 Diagram 已经有 80% 的连线了。

🚧 避坑:Connection Automation 不会自动连中断线。中断必须你手动处理——这正是下一节的核心。很多初学者跑完 Automation 就直接 Validate,然后发现 IRQ_F2P 是空的,在 Vitis 里永远等不到中断。


3. 时钟分发:FCLK_CLK0 怎么喂给多个 IP

当前设计里所有 IP 都跑在 FCLK_CLK0(100 MHz)。实际上这已经够了——AXI GPIO 和 AXI Timer 在 100 MHz 都没问题,PWM IP 的 counter 在 100 MHz 跑也能产生足够精度(1 μs 分辨率)。

如果你需要让某个 IP 跑在不同频率,有两种方案:

方案 A:在 PS 里再打开 FCLK_CLK1

进 PS 配置 → Clock Configuration,打开 FCLK_CLK1,设置频率(比如 200 MHz)。然后把需要高频的 IP(比如一个视频处理模块)的 aclk 连到 FCLK_CLK1。

这是最简单的方案。PS 的 IOPLL 可以同时输出最多 4 个独立频率(FCLK_CLK0~3),每个都可以独立设置。精度取决于 IOPLL 的分频系数,不是每个频率都能精确打到——Vivado 会显示你配 200 MHz 实际拿到 199.998 MHz 之类的,正常。

方案 B:接 Clock Wizard IP

从 FCLK_CLK0 出来,进一个 Clocking Wizard,在里面配输出时钟数量和频率。适合需要精确相位关系非整数分频的场景,比如 LCD PCLK 需要和行场信号严格对齐。

🚧 避坑:MMCM/PLL(Clock Wizard 内部用的)有锁定延迟,上电后要等 locked 信号拉高才能撤复位。如果你用了 Clock Wizard,Processor System Resetdcm_locked 输入要连 Clock Wizard 的 locked,不然 PL 复位会在时钟还没稳定时就释放,导致初始化出错。


4. Processor System Reset 的作用

Processor System Reset(PG164)这个 IP 几乎每个 Block Design 都需要,但经常被当成”黑盒”随手连上。它做的事:

  1. 监控多个复位源:PS 发来的 ps7_0/FCLK_RESET0_N(低有效)、来自 MMCM 的 dcm_locked 信号、外部 ext_reset_in
  2. 同步化复位:把异步复位信号同步到本 IP 的工作时钟域,消除亚稳态
  3. 输出多路复位interconnect_aresetn(给 AXI Interconnect 用)和 peripheral_aresetn[0:N](给各个外设 IP 用)

复位时序:只有当所有有效的复位源都撤销之后,Processor System Reset 才会释放 peripheral_aresetn。顺序是:PS 复位撤销 → MMCM locked → 等若干个时钟周期 → 才拉高 aresetn

当前设计里,proc_sys_reset_0 的连线应该是:

  • slowest_sync_clkFCLK_CLK0
  • ext_reset_in → PS 的 FCLK_RESET0_N(Connection Automation 已经自动连了)
  • dcm_locked:如果没用 Clock Wizard,接 VCC(常量 1,表示”时钟永远锁定”)

5. 中断信号汇聚:Concat IP 是唯一正确方式

这是本篇最重要的细节,也是原始教材里写得最含糊的地方。

ZYNQ7 PS 的 IRQ_F2P 端口接受最多 16 个中断输入([15:0])。但这不意味着你可以直接把 3 个 IP 的中断线各连一根到 IRQ_F2P 的不同位——你在 Vivado 的 Block Design 里做不到这个(Vivado 不允许把一个 bus 口拆开接线)。

正确做法:用 Concat IP 把多路 1-bit 中断合并成一根 N-bit 总线,再接到 IRQ_F2P。

配置 Concat IP:双击 → 设置 Number of Ports = 3(对应 GPIO_interrupt + Timer_interrupt + PWM_interrupt)。

连线:

AXI GPIO    ip2intc_irpt  ──→ Concat In0
AXI Timer   interrupt     ──→ Concat In1  
pwm_v1_0    irq_out       ──→ Concat In2

                              dout[2:0] ──→ ZYNQ7 PS IRQ_F2P[2:0]

为什么不能绕过 Concat 直接连?

IRQ_F2P 中断号分配是按位置固定的:你接到 IRQ_F2P[0] 的 IP,在软件里对应的中断 ID 是 XPS_FPGA0_INT_ID = 61(UG585 Table 7-4);[1] 对应 62,以此类推。如果你因为某种原因(比如手动在 HDL 里拼接)让中断顺序乱了,软件里注册的 ISR 就会被错误的 IP 触发——这种 bug 极难排查。

用 Concat IP 的好处:Vivado 会在生成 .xsa 时,把 Concat 的接线关系写进硬件描述,Vitis/XSCT 可以直接查到 IRQ_F2P 每一位对应的 IP,自动分配中断 ID,无需手算。

Block Design 系统架构图(Pynq-Z2) ZYNQ7 PS Cortex-A9 × 2 DDR3 Controller FCLK_CLK0 100 MHz M_AXI_GP0 (32-bit) IRQ_F2P [2:0] FCLK_RESET0_N DDR ↑ FIXED_IO ↑ (PS MIO → 芯片管脚) clk:H16 (125MHz) AXI Interconnect 1M × 3S AXI GPIO Channel 1: LED [3:0] Channel 2: BTN [1:0] ip2intc_irpt → AXI Timer 32-bit 计数器 interrupt → pwm_v1_0 自定义 PWM IP irq_out → Concat In0 ← In1 ← In2 ← dout[2:0] → Processor System Reset → peripheral_aresetn 100MHz 外部 IO 端口 led[3:0] / btn[1:0] / pwm_out AXI 数据 中断信号 IRQ_F2P 时钟分发 复位信号
图 1. 完整 Block Design 结构图(Pynq-Z2,ZYNQ7 PS + AXI GPIO + Timer + 自定义 PWM + Concat 中断汇聚)

6. Address Editor:地址段分配规则

点击 Diagram 顶部的 Address Editor 标签页。你应该看到类似这样的表:

IPInterfaceMasterBase AddressRangeHigh Address
axi_gpio_0S_AXI/ps7_0/M_AXI_GP00x4120_000064K0x4120_FFFF
axi_timer_0S_AXI/ps7_0/M_AXI_GP00x4280_000064K0x4280_FFFF
pwm_v1_0S_AXI/ps7_0/M_AXI_GP00x43C0_000064K0x43C0_FFFF

地址分配规则(来自 UG585 第 4.3 节 Address Map):

  • Zynq 的 M_AXI_GP0 可访问地址范围:0x4000_0000 ~ 0x7FFF_FFFF(共 1GB)
  • 每个从机的地址块默认分配 64K,基地址必须 64K 对齐(即低 16 位必须是 0)
  • Connection Automation 会自动分配不重叠地址,但如果你手动添加 IP 或改了范围,需要检查有无重叠

如何修改地址:在 Address Editor 里双击 Base Address 直接改数字。改完之后做一次 Validate Design(见下一节),它会检查有无地址重叠。

🚧 避坑:如果你用了 AXI Interconnect 而非 SmartConnect,且超过 16 个从机,地址段会快速占用完 GP0 的 1GB 空间。超过限制时 Validate 会报”address decode capacity exceeded”。解决方案:切换到 SmartConnect(它用 LUTRAM 实现更大的地址解码表),或者使用 HP 端口。


7. Validate Design:读懂红色和橙色警告

按快捷键 F6 或点击菜单 Tools → Validate Design

如果设计是正确的,会弹出:“Validation successful. There are no errors or critical warnings in this design.” ← 这才是你的目标。

以下是最常见的错误类型和真实含义:

🔴 红色 Error(必须修复)

  • "IP clock is not connected" → 某个 IP 的 aclk 引脚是悬空的。检查 Diagram,找到那个缺 clock 连线的 IP,连到 FCLK_CLK0
  • "Interface not connected: /axi_gpio_0/S_AXI" → IP 的 AXI 接口没接到 Interconnect 上。右键点那个 IP 的 S_AXI 端口,选 Make Connection

🟠 Critical Warning(不阻止生成,但会导致功能错误)

  • "CRITICAL WARNING: [BD 41-759] ... IRQ_F2P has no connections" → 你打开了 IRQ_F2P 但没连任何东西。如果不用中断,就在 PS 配置里关掉 IRQ_F2P;如果用,就连好 Concat
  • "CRITICAL WARNING: [BD 41-935] ... Clock domain crossing" → 你把一个在 Clock A 下的接口连到了 Clock B 下的接口,没有 CDC(Clock Domain Crossing)处理。这不会报 Error,但会导致实际电路出现采样错误,极难调试

🚧 避坑:Vivado 的”Critical Warning”命名非常误导——它不是警告,它代表着你的设计在这个点上很可能功能错误,只是 Vivado 无法 100% 确定而不把它升级为 Error。每一条 Critical Warning 都要仔细读,不要无脑忽略。


8. Generate Output Products → Create HDL Wrapper

Validate 通过后,按照以下顺序操作:

Step 1: Generate Output Products

在 Sources 面板里右键点击 design_1.bd,选 Generate Output Products,会弹出一个设置窗口。关键选项:

Synthesis Options:

  • Out of context per IP(默认):每个 IP 单独综合成子 DCP 文件(.dcp),缓存在 ip_user_files/ 目录。下次 IP 参数没改时复用,大幅加速整体综合。推荐用这个
  • Global Synthesis:把所有 IP 和你的顶层逻辑放在一起综合。缺点是每次都要全量综合,慢;好处是跨模块优化更彻底,极少数情况下能拿到更好的时序

实际工程建议:开发阶段用 Out of context,最终流片前可以跑一次 Global 做最终优化

Generate,等待完成(几十秒)。

Step 2: Create HDL Wrapper

右键 design_1.bdCreate HDL Wrapper,弹出对话框选:

Let Vivado manage wrapper and auto-update

这样每次你修改 Block Design 后,顶层 Wrapper 会自动重新生成。选 Copy generated wrapper to allow user edits 的话你自己管,改 BD 后要手动重新 Create 才更新。

生成的顶层文件是 design_1_wrapper.v(默认在 Sources 面板的顶层)。它的结构大概长这样:

// design_1_wrapper.v — 由 Vivado 自动生成,不要手动改
// (只包含了 Block Design 导出的外部端口)
module design_1_wrapper (
  // PS 侧的 DDR 和 Fixed IO——这些直接连到芯片封装管脚
  inout  [14:0] DDR_addr,
  inout  [2:0]  DDR_ba,
  inout         DDR_cas_n,
  inout         DDR_ck_n,
  inout         DDR_ck_p,
  inout         DDR_cke,
  inout         DDR_cs_n,
  inout  [3:0]  DDR_dm,
  inout  [31:0] DDR_dq,
  inout  [3:0]  DDR_dqs_n,
  inout  [3:0]  DDR_dqs_p,
  inout         DDR_odt,
  inout         DDR_ras_n,
  inout         DDR_reset_n,
  inout         DDR_we_n,
  inout         FIXED_IO_ddr_vrn,
  inout         FIXED_IO_ddr_vrp,
  inout  [53:0] FIXED_IO_mio,
  inout         FIXED_IO_ps_clk,
  inout         FIXED_IO_ps_porb,
  inout         FIXED_IO_ps_srstb,
  // PL 侧的自定义 IO
  output [3:0]  led_tri_o,
  input  [1:0]  btn_tri_i,
  output        pwm_out
);

  design_1 design_1_i (
    .DDR_addr        (DDR_addr),
    // ...所有端口穿透
    .led_tri_o       (led_tri_o),
    .btn_tri_i       (btn_tri_i),
    .pwm_out         (pwm_out)
  );
endmodule

注意:DDR 和 FIXED_IO 的端口都是 inout,不需要你在 XDC 里约束引脚位置——这些引脚位置已经固化在 PS7 原语里,由 Board Preset 自动处理。你在 XDC 里只需要处理 PL 侧的外部 IO(LED、按键、PWM 等)。


9. 编写 .xdc 约束文件(Pynq-Z2 真实引脚)

在 Sources 面板右键 → Add Sources → Add or create constraints,新建 pynq_z2.xdc

以下是 Pynq-Z2 的真实引脚约束(来源:Pynq-Z2 原理图 v1.0,TUL Embedded,主板上丝印位置和 schematic 对应):

# ============================================================
# Pynq-Z2 约束文件 | Vivado 2023.2 | XC7Z020-1CLG400C
# 注意:DDR/FIXED_IO 引脚不需要在这里约束(PS7 原语自动处理)
# ============================================================

# ——— LED(Active High,4 个) ———
set_property PACKAGE_PIN R14 [get_ports {led_tri_o[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[0]}]

set_property PACKAGE_PIN P14 [get_ports {led_tri_o[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[1]}]

set_property PACKAGE_PIN N16 [get_ports {led_tri_o[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[2]}]

set_property PACKAGE_PIN M14 [get_ports {led_tri_o[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[3]}]

# ——— 按键(Active High,2 个)———
# BTN0 = D19(板子左下侧靠近 USB 的按键)
set_property PACKAGE_PIN D19 [get_ports {btn_tri_i[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {btn_tri_i[0]}]

# BTN1 = D20
set_property PACKAGE_PIN D20 [get_ports {btn_tri_i[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {btn_tri_i[1]}]

# ——— 拨码开关(Active High)———
# SW0 = M20
set_property PACKAGE_PIN M20 [get_ports {sw_tri_i[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[0]}]

# SW1 = M19
set_property PACKAGE_PIN M19 [get_ports {sw_tri_i[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sw_tri_i[1]}]

# ——— PWM 输出(以 PMOD JA 的 JA1 引脚 Y18 为例)———
set_property PACKAGE_PIN Y18 [get_ports pwm_out]
set_property IOSTANDARD LVCMOS33 [get_ports pwm_out]

# ——— 时钟约束 ———
# FCLK_CLK0 是 PS 内部时钟,不是外部端口,不需要 create_clock
# 如果 PL 有外部直接驱动的时钟端口(非 Block Design 内部 FCLK),才需要:
# set_property PACKAGE_PIN H16 [get_ports sys_clock]
# set_property IOSTANDARD LVCMOS33 [get_ports sys_clock]
# create_clock -period 8.000 -name sys_clk_pin [get_ports sys_clock]
# (H16 是 Pynq-Z2 板上 125 MHz 外部晶振直连 PL 的引脚)

# ——— 误报抑制(可选) ———
# 对于 Zynq 设计,PS7 内部有很多跨时钟路径,Vivado 会产生大量
# false path 警告。如果你的设计纯 FCLK 驱动,可以加:
# set_false_path -from [get_cells -hierarchical -filter {NAME =~ *ps7_0*}]

关于 create_clock 的说明

很多教程说”Zynq 需要 create_clock -period 10.000”。这对于完全由 Block Design 内部 FCLK 驱动的设计是错的——FCLK_CLK0 是 PS7 原语内部产生的,Vivado 在综合时会自动识别这个时钟,不需要(也不应该)在 XDC 里手动 create_clock

你需要 create_clock 的情况:在顶层 HDL Wrapper 里(或 Block Design 里)有来自板卡管脚的外部时钟输入端口,比如 sys_clock 直接连到某个 PL 时钟 buffer 的输入。


10. Generate Bitstream → 导出 .xsa 给 Vitis

一切就绪,Flow Navigator 底部点 Generate Bitstream。Vivado 会依次执行:

  1. Synthesis:把 HDL Wrapper + 所有子模块(包括 BD 生成的网表)转换成门级网表,典型时间 1-5 分钟
  2. Implementation:布局(Placer)→ 布线(Router)→ 时序优化,典型时间 3-15 分钟
  3. Bitstream Generation:把布线结果转成 Zynq 配置格式,输出 .bit 文件,1-2 分钟

完成后输出文件在:

<project_name>/<project_name>.runs/impl_1/
├── design_1_wrapper.bit       ← 比特流文件
└── design_1_wrapper_routed.dcp ← 布线结果(用于增量重编译)

导出硬件平台(.xsa)

菜单 File → Export → Export Hardware

  1. Platform type:选 Fixed(固定硬件平台,适合 Vitis Baremetal / FreeRTOS)
  2. Include bitstream:✅ 勾上(这样 Vitis 可以直接下载到板子)
  3. Output file:选择一个路径,比如 ~/Projects/pynq_z2_hw/pynq_z2.xsa

生成的 .xsa 文件是一个 ZIP 档(你可以用 unzip 看它里面有什么):

pynq_z2.xsa
├── design_1_wrapper.bit       ← 比特流
├── design_1.bda               ← Block Design 记录
├── pynq_z2_bd.tcl             ← BD 重建脚本
├── psynth.tcl                 ← PS 配置
└── xsa.xml                    ← 硬件元数据(IP 地址映射、中断编号等)

xsa.xml 里包含了所有 IP 的基地址、中断 ID 分配——这是 Vitis 用来生成 BSP(Board Support Package)的关键。所以地址和中断连对了,Vitis 里的驱动才能自动初始化正确

在 Vitis 里:File → New → Platform Project,选择 Browse for XSA,指向刚才导出的 .xsa,后面的事就是下一篇的内容了。


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

  • Run Block Automation(只对 PS)和 Run Connection Automation(对所有 IP)解决的问题不同,不要混用
  • FCLK_CLK0 是 PS 内部时钟,不需要 .xdc 里的 create_clock;外部 PL 时钟端口才需要
  • IRQ_F2P 多路中断必须经过 Concat IP 汇聚,否则中断号分配会错乱,软件端 ISR 注册到错误 IP
  • Processor System Resetdcm_locked 如果没用 Clock Wizard 则接 VCC,不能悬空
  • XDC 里 Pynq-Z2 按键 BTN0 = D19,开关 SW0 = M20;DDR/FIXED_IO 引脚不需要手动约束
  • Validate Design 的 Critical Warning ≠ 普通警告,每条都要看
  • Out-of-context 综合比 Global 快,开发期用;需要极限时序时最后跑一次 Global

12. 下一篇预告

下一篇 《Zynq 实战 08|Vitis Baremetal 开发:从 .xsa 到第一个能跑的中断驱动程序》,我们会:

  • 在 Vitis 里导入 .xsa,创建 Platform + Application Project
  • 用 XIicPs / XGpioPs / XTmrCtr 驱动访问刚才设计的 AXI IP
  • 用 Xil_ExceptionRegisterHandler 注册 Concat 接进来的三路中断
  • 用 XSCT 命令行下载比特流 + 调试

参考资料

文档号名称用途
UG585Zynq-7000 SoC TRM第 4.3 节 Address Map;第 7 章 Interrupts(IRQ_F2P 中断 ID 表 7-4)
UG994Vivado Design Suite: Designing IP Subsystems using IP IntegratorBlock Automation / Connection Automation 官方手册
PG164Processor System Reset Product GuidePSR IP 复位时序细节
PG144AXI GPIO Product Guideip2intc_irpt 行为;双通道配置
PG079AXI Timer Product Guideinterrupt 信号极性;compare 寄存器
UG912Vivado Design Suite Properties Reference Manualset_property / create_clock 约束语法
UG904Vivado Design Suite User Guide: Implementationout-of-context synthesis 说明;增量实现
Pynq-Z2 Schematic v1.0TUL Embedded引脚约束来源(BTN0=D19, SW0=M20, LD0=R14 等)

这是《Zynq FPGA 嵌入式系统设计实战》系列第 7 篇。 如果你在某一步卡住了,最有效的调试方式是:打开 Tcl Console,把 Vivado 报的完整错误信息粘到搜索引擎里——通常 BD 编号(如 [BD 41-759])就能精确定位到对应 Xilinx 论坛帖子。