2. Vivado工程搭建与IP集成:创建Block Design、添加Zynq PS核、配置M_AXI_GP接口、连接AXI Interconnect

好,咱们正式开始动手。上一章聊了AXI总线的理论基础,这一章咱们直接进Vivado,把硬件工程搭起来。我个人习惯,做Zynq项目从来不会跳过Block Design这一步——它就像画电路板原理图,画清楚了,后面写代码才不慌。

2.1 创建Vivado工程与Block Design

打开Vivado 2020.1以上版本(我用的是2021.1,差别不大)。先创建一个RTL工程:

  1. Create Project → 选 RTL Project,勾上 Do not specify sources at this time(后面再添加)
  2. Target Language 选 Verilog,Simulator 默认
  3. Board Part 选你手头的板子,比如ZedBoard、ZCU104或者自定义的XSA文件

工程建好后,在 Flow Navigator 里点 Create Block Design。名字我一般用 system,简单明了。

小技巧: 我习惯把Block Design的层级和顶层模块名保持一致,这样后期做约束文件时不容易搞混。曾经有个项目,BD叫top_bd,顶层叫top,结果综合时一堆连线找不到,排查了半天。

2.2 添加Zynq PS核

在Diagram窗口右键 → Add IP,搜索 Zynq7 Processing System(如果是Zynq UltraScale+就搜 Zynq UltraScale+ MPSoC)。双击添加。

添加后,Vivado会自动弹出 Run Block Automation 的提示。这里要注意:

  • 如果选 Apply Board Preset,Vivado会根据你选的板子自动配置DDR、时钟、MIO等参数
  • 如果选 Customize,就手动配置

我建议第一次做时选 Apply Board Preset,然后进去微调。为什么呢?因为板级预设已经帮你配好了DDR控制器和时钟树,省去很多麻烦。但预设不一定完全符合你的需求,比如MIO分配、EMIO使能这些,还得自己改。

核心操作: 双击Zynq PS核,进入 PS-PL Configuration 页面,展开 General → Enable Clock Resets,确保 FCLK_CLK0 和 FCLK_RESET0_N 是使能的。这是PL侧的主时钟和复位,没有它们,你的逻辑跑不起来。

2.3 配置M_AXI_GP接口

接下来是关键——配置PS到PL的AXI接口。在 PS-PL Configuration 页面,找到 AXI Non Secure Enablement 下的 GP Master AXI Interface。

默认情况下,M_AXI_GP0 是勾选的。我建议至少勾选 M_AXI_GP0 和 M_AXI_GP1,原因后面会讲。

接口名称 数据位宽 最大频率 典型用途
M_AXI_GP0 32位 ~150 MHz 控制寄存器、小数据量传输
M_AXI_GP1 32位 ~150 MHz 状态读取、配置下发
M_AXI_HP0~3 64位 ~200 MHz 高速数据搬运(DMA、VDMA)
M_AXI_ACP 64位 ~200 MHz 缓存一致性访问

配置完成后,在 Clock Configuration 页面确认 FCLK_CLK0 的频率。我一般设100 MHz,够用且时序容易收敛。如果你要跑高速接口,可以提到150 MHz,但要注意布线延迟。

注意: M_AXI_GP接口是32位数据总线,最大只支持32位地址空间(4GB)。如果你需要访问PL侧的大容量DDR或BRAM,必须用HP接口或ACP接口。我曾经有个项目,用GP接口去读一个16MB的FIFO,结果地址空间不够,折腾了两天才发现是接口选错了。

2.4 添加AXI Interconnect

现在PS核有了,但它的M_AXI_GP接口不能直接连到PL侧的IP上——需要一个 AXI Interconnect 做桥接。说白了,它就是个AXI总线交换机,负责地址译码、数据路由、时钟域转换。

在Diagram窗口右键 → Add IP,搜索 AXI Interconnect,添加。

双击配置:

  • Number of Master Interfaces:设成1(目前只连一个PL IP)
  • Number of Slave Interfaces:设成1(来自PS的M_AXI_GP0)
  • Enable Advanced Features:不勾(默认即可)

然后连线:

  1. 把 M_AXI_GP0 拖到 S00_AXI 上
  2. 把 FCLK_CLK0 拖到 s00_axi_aclk 和 m00_axi_aclk 上
  3. 把 FCLK_RESET0_N 拖到 s00_axi_aresetn 和 m00_axi_aresetn 上

连完后的结构大概是这样:

+----------------+      +-------------------+      +----------------+
|   Zynq PS      |      |  AXI Interconnect |      |  PL IP (如AXI  |
|  M_AXI_GP0 ----|----> |  S00_AXI → M00_AXI|----> |  BRAM Ctrl)    |
|  FCLK_CLK0 ----|----> |  s00_axi_aclk     |      |                |
|  FCLK_RST_N ---|----> |  s00_axi_aresetn  |      |                |
+----------------+      +-------------------+      +----------------+
经验之谈: 如果你后续要加多个PL IP,可以把 Number of Master Interfaces 设成对应的数量。比如加两个AXI BRAM控制器,就设成2。每个Master接口会分配一段地址空间,PS通过地址来区分访问哪个IP。

2.5 地址分配与验证

连线完成后,点 Address Editor 标签页。你会看到Vivado自动给每个Master接口分配了一段地址范围。默认是 0x40000000 ~ 0x4000FFFF(64KB)。

我个人习惯把地址范围改大一点,比如 0x40000000 ~ 0x400FFFFF(1MB),方便以后扩展。改完后点 Assign Address。

最后,点 Validate Design(绿色勾)。如果没报错,恭喜你,Block Design搭建完成。

常见错误: 如果Validate时报 Unconnected pins,检查一下时钟和复位是否都连上了。特别是 s00_axi_aresetn 和 m00_axi_aresetn,很容易漏掉。我刚开始做时,漏连了复位,结果仿真时AXI总线一直处于复位状态,数据死活写不进去。

2.6 本章核心结构图

下面这张图展示了本章搭建的完整系统结构:

Zynq PS与PL AXI互联结构图 Zynq PS与PL AXI互联结构图 Zynq PS ARM Cortex-A9 M_AXI_GP0 (32位) FCLK_CLK0 (100MHz) FCLK_RESET0_N AXI Interconnect 地址译码 / 时钟域转换 S00_AXI (从口) M00_AXI (主口) 地址范围: 0x40000000 PL 用户逻辑 AXI BRAM / 自定义IP AXI从口 说明: 1. PS通过M_AXI_GP0发出AXI请求,Interconnect译码后路由到PL IP 2. 时钟和复位必须从PS引出,保证PL逻辑与PS同步

2.7 本章小结

到这一步,你已经完成了:

  • 创建Vivado工程和Block Design
  • 添加并配置Zynq PS核(时钟、复位、M_AXI_GP接口)
  • 添加AXI Interconnect并完成连线
  • 分配地址空间并验证设计

嗯,这些操作看起来简单,但每一步都有坑。我当年第一次搭BD时,漏了复位连线,仿真时数据写不进去,查了三天才发现。所以,Validate Design 这步千万别跳过。