Zynq矿板点灯实战:从PS/PL架构到FPGA开发全流程解析
1. 项目概述与平台选择几年前当一批以Xilinx Zynq 7010为核心的“矿板”流入二手市场时很多硬件爱好者都和我一样心里痒痒的。这玩意儿本质上是一个集成了ARM Cortex-A9双核处理器和传统FPGA逻辑资源的SoC全新的开发板价格不菲而一块功能基本完整的“矿板”EBAZ4025算上运费可能也就几十块钱堪称性价比最高的Zynq入门实验平台。我手上这块板子已经吃灰三年了最近终于下定决心把它从“收藏品”变成“实验品”目标很明确走通一个完整的、基于Zynq的FPGA开发流程让板载的LED闪起来。为什么选择Zynq和这块矿板原因很直接。对于想学习异构计算或者软硬件协同设计的工程师来说Zynq提供了一个绝佳的微观世界。它的PSProcessing System即ARM处理器及其外设和PLProgrammable Logic即可编程逻辑门阵列紧密耦合在同一颗芯片里两者之间有高性能的AXI总线互联。这意味着你可以用C语言在ARM上跑Linux或裸机程序同时用Verilog/VHDL在FPGA里实现高速数据采集、图像处理或自定义协议两者之间可以高效地交换数据。而EBAZ4025矿板虽然出身“草根”但该有的核心资源一样不少256MB DDR3内存、128MB NAND Flash、一个RJ45网口以及引出了大量PL端IO的双列排针。自己动手焊上TF卡座、UART串口和JTAG接口它就是一个完整的、可玩性极高的嵌入式开发平台。这篇文章我就以一个FPGA开发者的视角记录下从零开始在这块矿板上点亮第一个LED的完整过程其中会穿插很多初次接触Zynq时容易踩的坑和必须理清的概念。2. 硬件准备与核心概念解析2.1 矿板硬件盘点与改造拿到EBAZ4025矿板第一步不是急着上电而是先搞清楚手头有什么缺什么。板子核心是一颗XC7Z010-1CLG400C的Zynq芯片。它需要一个12V的直流电源供电这个电源接口是常见的5.5*2.1mm插座。通电后板载的ARM Linux系统就会从NAND Flash启动你可以在串口终端里看到uboot和内核的启动信息。这是PS部分在独立工作。为了进行FPGA开发我们必须连接JTAG。板子上有JTAG的焊盘通常标为TMS、TCK、TDI、TDO你需要自己焊接一个2.54mm间距的4针或6针排针上去。调试器方面一块最基础的Xilinx Platform Cable USB II或者兼容的FTDI芯片的调试器如Digilent的JTAG-HS2就足够了。连接电脑后在Vivado的Hardware Manager里你应该能在扫描链上看到两个设备一个代表PL端的FPGA另一个代表PS端的ARM CoreSight调试模块。这是Zynq区别于普通FPGA的第一个关键点它的JTAG链同时管理着软硬两部分。另一个必须焊接的是UART串口。矿板的UART引脚通常是PS端的MIO24和MIO25也以焊盘形式存在。焊上一个3针排针TX, RX, GND用USB转TTL串口线连接电脑波特率设置为115200你就能获得一个与板载Linux系统交互的终端。这是观察PS系统状态、进行软件调试的窗口。注意焊接时务必确认引脚对应关系。最可靠的方法是找到矿板的原理图通常在开源社区可以搜到对照着焊。接错线可能导致短路或无法通信。2.2 理解Zynq的PS与PL架构这是玩转Zynq最核心的一课如果这里概念模糊后续操作会处处碰壁。你可以把Zynq芯片想象成两个独立的“王国”被封装在了一起PS (Processing System)这是一个完整的、硬化的ARM处理器子系统。包含双核Cortex-A9 CPU、片上存储器OCM、DDR内存控制器、各种外设控制器如UART, SPI, I2C, USB, Ethernet等通过MIO/EMIO连接、中断控制器和用于与PL通信的多个AXI总线接口。PS就像一个标准的ARM SoC可以独立运行上电就从BootROM开始启动流程。PL (Programmable Logic)这就是我们熟悉的FPGA部分由可编程逻辑单元CLB、块RAMBRAM、DSP切片、时钟管理单元等组成。它的功能完全由我们编写的硬件描述语言HDL代码定义。这两个“王国”之间通过多种“通道”连接最主要的就是AXIAdvanced eXtensible Interface总线。AXI是一种高性能、高频率的片上互连协议PS和PL之间通常有多个AXI通道比如用于PS主控访问PL内存的M_AXI_GP端口和用于PL主控访问PS DDR内存的S_AXI_HP端口。此外还有中断信号、时钟信号和复位信号等。对于本次“点灯”实验我们暂时不涉及复杂的AXI通信。但需要理解一个关键点PS可以给PL提供时钟和复位信号。矿板的PL部分没有焊接晶振它的时钟来源之一就是PS内部PLL产生的时钟通过FCLK信号输出到PL。我们的第一个实验就要利用这个时钟。2.3 引脚分配MIO vs. EMIO vs. PL IO引脚分配是Zynq开发中另一个容易混淆的地方直接关系到硬件连接是否正确。MIO (Multiplexed I/O)这是PS专用的IO引脚数量有限Zynq-7010有54个。它们被复用于各种PS外设比如UART、SPI、GPIO等。在Vivado中配置PS时你需要指定哪个外设用到哪几个MIO引脚。MIO的引脚功能是固定的不需要在约束文件.xdc中为它们指定位置只需要在PS配置中启用并分配即可。我们的矿板UART就是连接在PS的MIO24和MIO25上。PL IO这是FPGA传统的可自由分配IO引脚。所有连接到PL部分的用户逻辑的输入输出信号都必须通过约束文件来指定其对应的物理引脚编号和电气标准。矿板上的两个LED通常连接到PL的W13和W14引脚就属于PL IO。EMIO这是一种扩展方式。当PS的MIO引脚不够用时可以将某些PS外设如GPIO、SPI通过EMIO功能“路由”到PL的IO引脚上。此时该外设在PS端看来仍然是一个PS外设但其物理引脚在PL端需要像PL IO一样进行约束。简单总结控制LED连接在PL引脚属于纯粹的PL逻辑需要写约束文件。与Linux通信的UART连接在MIO引脚属于PS外设在Vivado的PS配置界面中设置不需要PL约束文件。3. 开发环境搭建与工程创建3.1 Vivado与Vitis安装要点Xilinx的开发工具链以“庞大”著称。对于Zynq-7000系列我们必须使用Vivado Design Suite。Vitis统一软件平台则用于ARM端的应用程序开发包括裸机和Linux应用。在安装时有几点需要注意版本选择不是越新越好。应选择长期支持LTS版本或与你的教程、参考设计匹配的版本。对于Zynq-7000系列Vivado 2018.3, 2020.1, 2022.1等都是常见的选择。新版本可能对旧器件支持不佳或界面变化较大。我选择的是Vivado 2020.1。安装组件在安装Vivado时务必勾选“Vitis”和对应器件型号7 Series的支持。安装路径不要有中文和空格否则后期可能出各种诡异问题。许可证Vivado WebPACK版本是免费的支持包括Zynq-7010在内的主流中低端器件完全满足个人学习需求。安装后需要申请并加载免费许可证。3.2 创建Vivado工程与Block Design打开Vivado点击“Create Project”。工程类型选择“RTL Project”并勾选“Do not specify sources at this time”我们稍后通过图形化方式添加。在器件选择页面手动输入或通过筛选找到我们的芯片xc7z010clg400-1。这一步至关重要选错器件会导致后续综合实现失败。工程创建好后我们开始使用Zynq开发中最具特色的Block Design设计方式。这是一种基于IP知识产权核集成和图形化连线的高层次设计方法特别适合处理PS-PL这种复杂系统。在Vivado左侧的“Flow Navigator”中点击“Create Block Design”。在Diagram视图的空白处右键选择“Add IP”搜索并添加“ZYNQ7 Processing System”。这个IP核就代表了Zynq芯片的整个PS部分。双击添加进来的ZYNQ7 Processing System模块图中显示为processing_system7_0会打开一个庞大的配置界面Re-customize IP。这里就是我们对PS进行“塑形”的地方。3.3 PS系统配置详解首次打开PS配置界面可能会让人眼花缭乱我们聚焦于本次实验必须设置的几项。时钟配置Clock Configuration在“PS-PL Configuration” - “General” - “Enable Clock Resets”中确保FCLK_CLK0被启用。这就是PS输出给PL的时钟。查看“Input Clock Frequency”确认是否为33.333MHz这与矿板上的PS端晶振频率一致无需修改。FCLK_CLK0的频率默认为50MHz我们可以保持默认也可以修改。这个时钟将作为我们PL逻辑的驱动时钟。DDR配置DDR Configuration在“PS-PL Configuration” - “HP Slave AXI Interface”中可以暂时不关心。关键在“MIO Configuration” - “DDR”部分。矿板通常使用一颗MT41J128M16HA-125或类似的128Mx16bit的DDR3芯片。我们需要在“Memory Part”下拉菜单中选择匹配的型号。如果列表中没有完全一致的可以选择时序参数最接近的。这一步配置错误会导致PS端的Linux无法正确初始化DDR系统无法启动或运行不稳定。MIO配置MIO Configuration为了能让PS的UART工作我们需要分配MIO引脚。根据矿板原理图UART RX/TX连接在PS的MIO24和MIO25上。在“MIO Configuration”视图中找到“I/O Peripherals” - “UART 1”。将其勾选并在其下方的“MIO”下拉框中为UART 1 RX选择MIO 24为UART 1 TX选择MIO 25。注意观察“Bank 0 Voltage”是否与板子IO电压匹配通常是1.8V或3.3V矿板多为1.8V这会影响MIO的电平标准。配置完成后点击“OK”关闭窗口。回到Block Diagram界面你会看到processing_system7_0模块上多出了一些接口比如FCLK_CLK0和UART_1。运行自动化Run Block Automation 点击Diagram上方黄条提示中的“Run Block Automation”。Vivado会自动为我们完成一些必要的连接例如将FCLK_CLK0连接到AXI互联模块的时钟端口。在弹出的对话框中通常直接点击“OK”即可。此时按照很多教程的指引需要手动将FCLK_CLK0端口连接到M_AXI_GP0_ACLK。M_AXI_GP0_ACLK是主设备AXI GP接口的时钟。如果你的设计根本不使用AXI总线就像我们这个简单的点灯实验这个连接不是必须的。连接上也无妨但理解其非必要性有助于厘清概念。4. 添加自定义逻辑与引脚约束4.1 创建顶层封装与引出时钟Block Design设计好后它本身还是一个“黑盒”。我们需要为其创建一个顶层的HDL封装Wrapper才能将PS产生的信号如时钟引出到顶层并与我们自己的PL逻辑连接。在“Sources”窗口的“Design Sources”下右键点击你的Block Design例如design_1.bd选择“Create HDL Wrapper”。在弹出的对话框中务必选择“Let Vivado manage wrapper and auto-update”。这样当Block Design有改动时Vivado会自动更新顶层封装避免手动修改被覆盖。点击“OK”后会生成一个design_1_wrapper.v或.vhd文件这就是我们系统的顶层模块。现在我们需要把PS输出的FCLK_CLK0时钟信号引到顶层模块的端口上这样外部的PL逻辑才能使用它。在Block Diagram中找到processing_system7_0模块上的FCLK_CLK0信号线。右键点击该信号线选择“Make External”。Vivado会自动创建一个名为FCLK_CLK0_0的端口并连接到该信号。你可以将这个端口重命名为更友好的名字比如ps_clk_50m。右键点击该端口选择“Edit Port”。在弹出的窗口中修改“Name”即可。4.2 编写PL端点灯逻辑我们的目标是在PL部分实现一个简单的计数器用PS提供的50MHz时钟驱动让连接到PL IO的两个LED交替闪烁。在“Sources”窗口的“Hierarchy”标签下展开design_1_wrapper实例。我们需要编辑其源文件来添加逻辑。右键点击design_1_wrapper.v选择“Open File”。在打开的Verilog文件中找到模块声明部分。我们需要添加两个输出端口用于连接LED。module design_1_wrapper ( // 其他自动生成的端口如DDR、FIXED_IO、ps_clk_50m等 output wire led_red, output wire led_green );在模块内部添加我们的点灯逻辑。一个经典的呼吸灯或闪烁灯可以用一个计数器实现。// 寄存器声明 reg [31:0] counter; // 计数器逻辑每个时钟沿加1 always (posedge ps_clk_50m) begin counter counter 1; end // 将计数器的高位赋值给LED实现闪烁 // 例如用第25位控制红灯第24位控制绿灯闪烁频率约为50MHz / 2^25 ≈ 1.5Hz assign led_red counter[25]; assign led_green counter[24];实操心得计数器位宽的选择决定了LED闪烁的频率。频率 时钟频率 / (2^N)其中N是你使用的计数器位序号。50MHz时钟下counter[25]的频率约1.5Hz肉眼可见闪烁。如果想做呼吸灯效果可以使用PWM用计数器的高位作为PWM的周期中间位作为比较值通过不断调整比较值来实现亮度渐变。4.3 编写引脚约束文件.xdc这是将逻辑信号映射到物理引脚的关键一步。我们需要知道矿板上两个LED具体连接到了Zynq芯片的哪个PL引脚上。这必须查阅矿板的原理图。假设原理图显示红色LED (LED6) 阳极通过电阻连接到W13引脚对应FPGA的IO_L13P_T2_MRCC_14低电平点亮。绿色LED (LED5) 阳极通过电阻连接到W14引脚对应FPGA的IO_L13N_T2_MRCC_14低电平点亮。在Vivado中点击“Sources”窗口的“”号选择“Add or create constraints”。选择“Create File”命名为ebaz4025_led.xdc点击“OK”。在约束文件中我们需要做两件事指定引脚位置和指定IO电气标准。# 时钟引脚约束从PS引出到顶层端口的时钟 # 注意ps_clk_50m是PS内部产生的时钟在PL端作为输入使用但它的源头在PS我们不需要在PL端为其指定物理引脚位置。 # 我们只需要为PL端的输出信号LED指定引脚。 # 红色LED 低电平有效 连接到W13引脚 BANK电压为1.8V set_property PACKAGE_PIN W13 [get_ports {led_red}] set_property IOSTANDARD LVCMOS18 [get_ports {led_red}] # 绿色LED 低电平有效 连接到W14引脚 BANK电压为1.8V set_property PACKAGE_PIN W14 [get_ports {led_green}] set_property IOSTANDARD LVCMOS18 [get_ports {led_green}]重要提示IOSTANDARD必须与目标硬件Bank的供电电压匹配。Bank电压错误是导致下载后IO无输出或芯片发烫的常见原因。EBAZ4025矿板的PL Bank电压通常是1.8V但务必通过原理图确认。5. 综合、实现与下载测试5.1 生成比特流文件约束文件添加后就可以开始标准的FPGA编译流程了。综合Synthesis点击左侧“Flow Navigator”中的“Run Synthesis”。这一步将我们的Verilog代码和Block Design转换成门级网表。综合过程中会检查语法错误和基本的逻辑错误。如果有错误需要在“Messages”窗口查看并修正。实现Implementation综合成功后点击“Run Implementation”。这一步包含翻译Translate、映射Map和布局布线Place Route。它将门级网表映射到具体的FPGA资源查找表、触发器、布线资源等并生成最终的物理布局。生成比特流Generate Bitstream实现成功后点击“Generate Bitstream”。这一步会生成一个.bit文件这个文件包含了配置FPGA内部所有可编程单元和互连的信息。在“Implementation”完成后强烈建议打开“Implemented Design”视图查看一下“I/O Ports”窗口。这里会以表格形式列出所有顶层端口、其分配的物理引脚、电气标准等。务必核对led_red和led_green的Package Pin是否与你的约束文件一致。这是防止约束文件未生效或生效错误的最后一道关卡。5.2 通过JTAG下载与验证硬件连接确保矿板已上电12VJTAG调试器已连接电脑和板子的JTAG口UART串口线也已连接好。打开串口终端软件如Putty、MobaXterm或Vivado自带的串口终端设置正确的COM口和波特率115200你应该能看到Linux内核启动完毕后的登录提示符或shell。这证明PS部分运行正常。在Vivado中点击“Open Hardware Manager”然后“Open Target” - “Auto Connect”。硬件管理器会扫描JTAG链找到FPGA和ARM CoreSight。在“Hardware”窗口下右键点击FPGA器件通常是xc7z010_1选择“Program Device”。在弹出的对话框中点击“...”选择刚才生成的.bit文件通常位于工程目录下的*.runs/impl_1子文件夹中。确保“Program”框被勾选然后点击“Program”。下载过程很快。如果一切顺利你应该立刻看到矿板上的两个LED开始以不同的频率闪烁同时观察串口终端Linux的打印信息应该没有中断系统仍在正常运行。这个现象直观地展示了Zynq PS和PL的独立性我们重新配置了PL的逻辑下载了新的比特流但PS上运行的Linux系统完全不受影响就像给一台运行着操作系统的电脑更换了一块PCIe加速卡一样。5.3 固化比特流到QSPI Flash可选通过JTAG下载的比特流在断电后会丢失。如果想让FPGA逻辑在上电后自动加载需要将比特流固化到板载的QSPI Flash中。这个过程涉及创建包含FSBLFirst Stage Bootloader和比特流的BOOT.bin文件并通过Vitis或Linux下的flash工具进行烧写。由于步骤相对复杂且矿板的QSPI Flash型号需要确认这里先不展开。首次实验以JTAG下载验证成功为目标即可。6. 常见问题与深度排查指南初次尝试Zynq开发难免会遇到各种问题。下面是我在调试过程中遇到的一些典型情况及其解决方法。6.1 硬件连接与识别问题问题现象可能原因排查步骤Vivado Hardware Manager无法找到设备1. JTAG线未接好或损坏。2. 板子未上电或电源异常。3. Vivado驱动问题。1. 检查JTAG各引脚连接尝试更换JTAG线或调试器。2. 测量板子12V输入及板上各路核心电压如1.0V, 1.8V, 3.3V是否正常。3. 以管理员身份运行Vivado或重新安装JTAG电缆驱动。扫描链上只看到一个设备通常是ARMFPGA的JTAG链可能未正确初始化或者比特流为空。1. 确保已给板子重新上电。有时需要冷启动。2. 尝试先通过Vitis或SDK给PS部分下载一个简单的裸机程序激活整个芯片。串口终端无输出1. UART线接反TX/RX。2. 波特率设置错误。3. PS配置中UART的MIO引脚分配错误。4. Linux内核未使能该UART控制台。1. 交换TX和RX线序再试。2. 尝试常见波特率115200, 9600等。3. 在Vivado中双击ZYNQ IP核确认UART1的MIO引脚是否为24和25。4. 对于预装的Linux系统通常已配置好。如果是自己编译内核需确认设备树和内核配置。6.2 设计编译与实现问题问题现象可能原因排查步骤综合失败报语法错误Verilog/VDHL代码有语法错误。仔细阅读“Messages”窗口的错误信息定位到具体文件和行号进行修改。Block Design中的IP配置冲突也可能导致综合错误。实现失败报布局布线错误1. 时钟约束缺失或错误。2. 引脚约束冲突或电气标准错误。3. 设计规模超出器件资源。1. 检查是否对ps_clk_50m这个输入时钟创建了时钟约束create_clock。虽然它来自PS但在PL域使用时仍需约束。2. 核对.xdc文件确保引脚号正确且同一Bank的IO标准一致均为LVCMOS18。3. 点灯设计资源占用极少此问题可能性小。生成比特流时警告“No clocks found in design”没有为设计中的时钟信号添加约束。即使时钟来自PS也需要在约束文件中为其添加定义。在.xdc文件中添加create_clock -name ps_clk_50m -period 20.000 [get_ports ps_clk_50m]假设时钟频率50MHz周期20ns。6.3 下载后功能异常问题问题现象可能原因排查步骤LED不亮1. LED引脚约束错误。2. LED驱动极性错误应为低电平点亮。3. Bank电压设置错误。4. 比特流未成功下载。1. 在“Implemented Design”的I/O Ports中确认引脚分配。2. 确认原理图LED是阳极接电源、阴极接FPGA引脚低电平点亮还是阴极接地、阳极接FPGA引脚高电平点亮。修改代码中LED的驱动逻辑赋值1‘b0点亮或1’b1点亮。3. 确认Bank电压修改约束文件中的IOSTANDARD。4. 在Hardware Manager中确认下载成功并可尝试“Refresh Device”重新读取器件状态。LED常亮不闪烁1. 时钟信号未正确引入PL。2. 计数器逻辑未生效可能被优化掉。1. 在Block Design中确认FCLK_CLK0已通过“Make External”引出并在顶层模块中正确例化连接。2. 使用Vivado的“Debug”功能将ps_clk_50m和counter的某些位标记为调试信号生成新的比特流下载后用ILA集成逻辑分析仪抓取波形看时钟和计数器是否工作。下载比特流后串口终端卡死或乱码PL的配置可能意外影响了与PS共享的某些引脚如MIO或产生了严重干扰。1. 检查PL部分的约束文件绝对不要对任何PS的MIO引脚进行约束或驱动。2. 确保PL逻辑没有产生过强的噪声或地弹影响PS的稳定运行。对于简单点灯实验此问题罕见。6.4 进阶排查工具ILA集成逻辑分析仪的使用当逻辑行为不符合预期又无法用肉眼观察时ILA是你的最佳伙伴。它相当于一个示波器可以抓取FPGA内部信号的实时波形。标记调试网络在综合后的“Synthesized Design”或“Implemented Design”中在“Netlist”窗口找到你想观察的信号如ps_clk_50m,counter[25],led_red右键点击选择“Mark Debug”。设置ILA IP核Vivado会提示你设置调试核心。通常保持默认它会自动插入ILA IP核并连接被标记的信号。重新综合、实现、生成比特流这个过程会包含ILA逻辑。下载并触发抓波将新生成的比特流下载到FPGA。在Hardware Manager中找到并打开ILA核心。设置触发条件例如当counter等于某个特定值时触发然后运行。你就能在波形窗口中看到这些信号的实际活动情况这对于排查计数器是否工作、时钟是否有毛刺等问题至关重要。第一次成功点亮矿板的LED只是打开了Zynq世界的一扇小窗。这片融合了处理器与可编程逻辑的芯片其真正的威力在于PS与PL的协同。你可以让PS上的Linux应用程序通过/dev/mem或UIO驱动直接访问PL端映射到内存空间的寄存器实现软硬件控制也可以利用AXI DMA让PL高速处理的数据直接搬移到PS的DDR内存中供CPU使用。从简单的GPIO控制到复杂的视频流处理Zynq提供了无尽的想象空间。而这一切的起点就是理解PS与PL如何共存、如何对话并亲手完成一次从软件配置到硬件实现的完整流程。这块吃灰已久的矿板其价值远不止于几十块钱它是一张通往异构计算领域的、性价比超高的门票。