ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Zynq接口适配实战:MII转GMII与EMIO UART0配置全攻略

Zynq接口适配实战:MII转GMII与EMIO UART0配置全攻略 1. 为什么Zynq设计里会同时出现MII转GMII和EMIO UART0我到现在还记得第一次在Zynq上调试MII转GMII时的状态板子回来原厂Demo用的PHY芯片是RGMII接口但我手上这颗PHY只支持MII我想当然地觉得“MII不就是把GMII的数据位减半吗直接把引脚接出去不就行了”。结果接是接出去了PHY的Link状态死活起不来用示波器看TX_CLK方向才发现GMII和MII根本不是位宽减半那么简单两套接口的时钟体系、控制信号对齐方式、甚至数据有效窗口都不一样。这一章标题里有两个独立配置一个是MII转GMII的配置一个是EMIO UART0的配置。它们放在同一章不是巧合。在Zynq-7000系列里PS侧外设GEM以太网控制器、UART串口、SPI、I2C、SDIO等默认都是走MIO专用引脚MIO一共54个分给GEM之后往往就不够用了以太网PHY又可能只给MII口这时候就需要用PL侧逻辑做接口转换。而UART0被MIO占用时也要靠EMIO绕到PL端重新引出去。可以说这两个配置本质上解决的是同一个问题PS外设的物理接口不匹配时怎么用PL把它适配成你需要的形态。无论你是在做基于Zynq的工业控制板、边缘网关还是做通信接口测试平台这一套配置方法都很实用。我尽量把操作链路写得完整不光是让你能动鼠标还帮你看懂每一步背后的硬件时序关系。接下来先理清MII和GMII的接口差异再给配置步骤然后单独讲EMIO UART0的迁移和验证。2. 先把MII、GMII这两种接口讲透位宽、时钟、方向都不同很多人一看到“MII转GMII”就以为只是把4根数据线变8根其实错得很远。MII和GMII是两个时代的接口协议它们的位宽、时钟速率、数据对齐方式、引脚定义都有本质区别。MIIMedia Independent Interface是10/100Mbps以太网时代的标准MAC-PHY接口。数据线只有4位TXD[3:0]和RXD[3:0]。发送方向由PHY提供TX_CLK速率在100Mbps时是25MHz10Mbps时降到2.5MHzCPU侧跟着这个时钟把数据打出去。接收方向由PHY提供RX_CLK同样25MHz或2.5MHzMAC侧需要跟随这个时钟采数据。MII还有一对很重要的控制线TX_EN和TX_ER、RX_DV和RX_ER分别表示数据有效和数据错误。GMIIGigabit Media Independent Interface是千兆以太网时代的接口数据线变成8位TXD[7:0]和RXD[7:0]时钟也换成125MHz。但这里有个容易踩坑的点GMII在1000Mbps下稳定跑125MHz在100Mbps和10Mbps下它的数据总线仍然是8位只是有效数据不是每个周期都有需要用TX_EN/RX_DV信号来指示数据实际上是以字节形式批量塞进去的。发送时钟GTX_CLK是从MAC侧输出的125MHz方向跟MII完全相反。我用一张表把两个接口的关键参数列出来方便对照对比项MIIGMII数据位宽4位8位支持速率10/100Mbps10/100/1000Mbps发送时钟方向PHY提供TX_CLKMAC提供GTX_CLK发送时钟频率2.5MHz / 25MHz125MHz接收时钟频率2.5MHz / 25MHz125MHz由PHY恢复控制信号TX_EN、TX_ER、RX_DV、RX_ERTX_EN、TX_ER、RX_DV、RX_ER半双工支持有COL、CRS有COL、CRS典型引脚数约16根约24根做MII转GMII本质上不是“加速”而是把一个8位宽、125MHz时钟域的GMII信号翻译成4位宽、25MHz或2.5MHz时钟域的MII信号。数据宽度变了时钟域也不一样这中间必须做跨时钟域缓冲不能直接把信号拉直连。Xilinx的MII to GMII Converter IP干的就是这件事它把MAC侧GMII接口和PHY侧MII接口之间的数据宽度、时钟速率、有效信号重新对齐让一个只支持MII的PHY能接到GMII的MAC上并且可以正常自协商到10M或100M速率。再补一句关于接口选择的前因后果。为什么Zynq的GEM外设要配成GMII而不是直接配成MII因为Zynq PS侧的GEM虽然理论上支持多种接口模式但MIO引脚资源非常紧张MII模式的引脚未必能映射到可用的MIO上而在PL侧做GMII转MII反而更灵活。另一方面如果外部PHY是RGMII那还能用GMII-to-RGMII IP做转换做法思路差不多只是RGMII涉及DDR双边沿采样时序要求更高。本章选择MII场景主要因为它能帮你把时钟方向和接口转换的底层逻辑理解透之后切到RGMII也能举一反三。3. MII转GMII实操Block Design里的配置顺序与连线在Vivado里配置MII转GMII核心步骤是四步配PS端GEM、添加转换IP、连线、约束引脚。我按工程实操顺序一个个讲这里用Zynq-7000系列做例子具体板子上的PHY引脚号请对照你的原理图。3.1 配置PS端GEM走EMIO并选择GMII打开你的Block Design添加ZYNQ7 Processing System IP后双击进入配置界面。在Peripheral I/O Pins页面里找到Gigabit Ethernet Controller也就是GEM0或GEM1取决于你用哪个把接口类型选为GMII同时勾选EMIO选项。这里为什么要走EMIO因为GMII接口的数据线、控制线加起来二十多根MIO引脚区根本放不下而且Zynq的MIO引脚也不是所有外设都能任意映射。勾选EMIO之后PS侧的GEM GMII信号会全部绕到PL内部以GEM0_GMII_*的形式出现在Block Design上这样我们就可以在PL里接IP做转换了。同时注意Vivado会自动生成MDIO和MDC引脚MDIO用于PHY寄存器管理后面接转换器或PHY时要一起连出去。3.2 添加MII to GMII Converter IP在Block Design画布空白处右键选择Add IP搜索MII to GMII Converter。Vivado自带这个IP不需要额外下载。双击IP进入配置界面重点看几个选项Speed Support一般选10/100Mbps因为MII侧本来就不支持千兆。如果你板子的PHY只工作在10/100M选这个就行。Include MDIO这里有个选择。如果你的PHY需要PS GEM通过MDIO管理那么建议勾选让转换器把MDIO透传到PHY侧如果你直接在PS的MIO上已经有MDIO通道可能不需要重复引出。Slave/Master通常选Master由转换器向PHY发起MDIO时钟。IP添加之后画布上会出现一个带GMII侧和MII侧信号的模块。GMII侧面向PS GEMMII侧面向外部PHY。两侧的端口名称不同很容易混淆我列一个对应表给你。转换器GMII侧端口方向连接目标gmii_txd[7:0]输入PS GEM0_GMII_TXD[7:0]gmii_tx_en输入PS GEM0_GMII_TX_ENgmii_tx_er输入PS GEM0_GMII_TX_ERgmii_rxd[7:0]输出PS GEM0_GMII_RXD[7:0]gmii_rx_dv输出PS GEM0_GMII_RX_DVgmii_rx_er输出PS GEM0_GMII_RX_ERgmii_rx_clk输出PS GEM0_GMII_RX_CLKgmii_tx_clk输入PS GEM0_GMII_GTX_CLK转换器MII侧端口方向连接目标mii_txd[3:0]输出外部PHY的TXD[3:0]mii_tx_en输出外部PHY的TX_ENmii_tx_er输出外部PHY的TX_ERmii_txd_rx输入外部PHY的TXD对MII侧发送时钟是从PHY来的mii_tx_clk输入外部PHY的TX_CLKmii_rxd[3:0]输入外部PHY的RXD[3:0]mii_rx_dv输入外部PHY的RX_DVmii_rx_er输入外部PHY的RX_ERmii_rx_clk输入外部PHY的RX_CLK特别注意MII侧的mii_tx_clk方向是从PHY输入到转换器不是从转换器输出。这是因为MII协议规定发送时钟由PHY负责产生MAC侧这里是转换器必须跟着这个时钟把数据打出去。这个方向和GMII完全相反很多人第一次就是栽在这里。3.3 连线与时钟对齐手动连线时先把PS的GEM0_GMII_TXD[7:0]等信号接到转换器的gmii_txd[7:0]等端口。时钟部分把PS的GEM0_GMII_GTX_CLK连到转换器的gmii_tx_clk这个信号是125MHz的发送时钟。转换器输出的gmii_rx_clk连回PS的GEM0_GMII_RX_CLK。MII侧则全部做成外部端口Make External这些端口最终要约束到FPGA引脚上连接到PHY芯片。PHY还需要一个复位信号通常用PL的普通GPIO控制或者接一个常量。这里建议在PS端留一个GPIO来给PHY复位方便后面软件里做复位时序。MDIO/MDC信号也要引出MDC给PHY提供管理时钟MDIO是双向数据线。如果你勾选了Include MDIO那么MDIO信号从转换器引出如果没勾就从PS的GEM直接引出。具体看PHY芯片的接线方式不同板子做法不一样。3.4 引脚约束把所有端口从Block Design引出后打开XDC文件添加物理约束。MII侧信号定义类似这样set_property PACKAGE_PIN U18 [get_ports {mii_txd[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {mii_txd[0]}] set_property PACKAGE_PIN U19 [get_ports {mii_txd[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {mii_txd[1]}] set_property PACKAGE_PIN V18 [get_ports {mii_txd[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {mii_txd[2]}] set_property PACKAGE_PIN V19 [get_ports {mii_txd[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {mii_txd[3]}] set_property PACKAGE_PIN W18 [get_ports mii_tx_en] set_property IOSTANDARD LVCMOS33 [get_ports mii_tx_en] set_property PACKAGE_PIN W19 [get_ports mii_tx_clk] set_property IOSTANDARD LVCMOS33 [get_ports mii_tx_clk]引脚号和IO电平都必须以你板卡原理图为准我这里的引脚只是举例。上板之前重点确认三点第一PHY的供电引脚电平比如常见的3.3V PHY就要配LVCMOS33第二MII输入时钟引脚有没有加IBUFVivado一般会自动推断但如果你手动加了引脚约束注意不要重复约束第三PHY复位脚是否需要反极性很多PHY复位是低有效。3.5 综合、实现、生成比特流在Vivado里跑综合和实现时序报告里重点看mii_tx_clk和mii_rx_clk等输入时钟在IO约束里是否被正确约束。因为这些时钟来自外部PHYVivado不知道它们的频率需要在XDC里补充create_clock约束否则时序分析会有警告严重的会导致采样不稳定。举例create_clock -name mii_tx_clk -period 40.0 [get_ports mii_tx_clk] create_clock -name mii_rx_clk -period 40.0 [get_ports mii_rx_clk]40.0对应的就是25MHz时钟周期。如果你的PHY支持10M模式可以单独加一个2.5MHz的约束或者用set_clock_groups区分不同模式。这个细节很多教程不提但实际PHY自协商到10M的时候时钟频率变了后面运行时反而容易出问题。4. UART0从MIO改到EMIO这不只是改一个勾选框MII转GMII的配置和EMIO UART0的配置共同点都是“PS外设往外引”的问题但侧重点不同。UART0不是接口不匹配而是引脚被占用或者布局受限需要绕到PL侧重新走线。4.1 MIO和EMIO的本质区别Zynq的PS外设有两种I/O路径MIOMultiplexed I/O和EMIOExtended Multiplexed I/O。MIO是PS专有的物理引脚共54个分布在PS I/O banks里直接连接到PS核心。UART、SPI、I2C、SDIO、USB、GEM等外设都可以通过MIO复用表映射到这些引脚。MIO的好处是零PL资源消耗延迟低时序由PS直接保证。坏处是引脚是固定的比如UART0通常映射到MIO14TX和MIO15RX一旦这两个引脚被其他外设占用比如你想在MIO14上接SDIO的时钟两边就冲突了。EMIO是从PS延伸到PL的接口。它不直接连接到物理封装外部引脚而是先进入PL的布线网络再由FPGA的IO引脚PL侧的引脚引出到外部。EMIO的好处是引脚选择非常灵活你可以通过XDC任意约束到FPGA封装上任何一个可用的IO引脚相当于把PS外设“接长”到PL。坏处是它消耗PL的布线资源也增加了引脚约束和电平标准的配置工作。用表格对比可能更直观对比项MIOEMIO引脚位置PS封装固定引脚PL侧任意IO引脚PL资源消耗无有布线资源和输入输出缓冲消耗引脚约束不需要XDC必须在XDC中约束路由灵活性低高延迟低略高但通常不影响UART功能典型场景默认配置、高速要求引脚冲突、特殊布局、电平转换4.2 什么情况下必须把UART0改成EMIO实际项目里最常见的情况有三种。第一种MIO14/15已经被其他外设征用。Zynq的MIO复用表里SPI、I2C、SDIO几乎都跟UART有引脚交叉很多工程配置完SDIO和I2C之后MIO14/15就没有了这时候UART0必须走EMIO。第二种板级布局需要把串口放到特定位置。有些板卡设计希望UART靠近某颗主控芯片或者板边有特定的接插件MIO引脚物理位置固定无法改动只能通过EMIO把信号引到PL端的指定引脚。第三种你想在PL里对串口信号做处理。比如加电平转换、做多路选择、加隔离芯片控制这些逻辑只能在PL端实现MIO直接连出去就没法插手了。4.3 Vivado里切换UART0到EMIO的具体操作在Zynq PS配置界面里找到Peripheral I/O Pins页面展开UART0选项默认会勾选MIO 14和MIO 15两个引脚。取消这两个勾选然后勾选EMIO那一列。保存配置后回到Block Design画布你会看到ZYNQ IP上多出两个端口UART0_TX和UART0_RX。其中UART0_TX是PS输出到PL的信号方向是输出未来要连到PL引脚再到外部UART0_RX是PL输入到PS的信号方向是输入来自外部引脚。如果芯片支持流控配置页面里会有UART0的调制解调器信号选项勾选后还会多出UART0_CTS和UART0_RTS两个端口。CTS和RTS是异步流控信号CTS是输入RTS是输出。一般调试不需要流控可以先不勾。把UART0_TX和UART0_RX分别做成外部端口。然后在XDC里加物理约束例如set_property PACKAGE_PIN K17 [get_ports uart0_tx] set_property IOSTANDARD LVCMOS33 [get_ports uart0_tx] set_property PACKAGE_PIN K18 [get_ports uart0_rx] set_property IOSTANDARD LVCMOS33 [get_ports uart0_rx]引脚号和IO电平以你的板卡为准。这里有个经常被忽略的问题引脚位于哪个BankBank的VCCO电压是多少必须和IO标准匹配。如果你在3.3V的Bank上用了LVCMOS18标准信号完全不对。5. EMIO UART0上板验证与串口调试细节配置改完、比特流生成之后真正的验证还要在硬件上过一遍。这一节我讲一下从SDK软件驱动到物理连接的完整验证路径。5.1 生成硬件平台并导入SDK在Vivado里导出硬件Export Hardware勾选Include bitstream然后Launch Vitis或SDK。创建应用工程时会看到一个基于当前硬件配置的平台工程里面已经包含了UART0的驱动。打开BSP设置在xparameters.h里能看到UART0的基地址和相关配置。无论UART0走MIO还是EMIO它的地址都是一样的驱动代码也完全复用。这也是EMIO的一个隐形好处软件侧感知不到硬件引脚变化调试起来省事。5.2 SDK里初始化UART0并打印写一个最简单的Hello World验证程序#include xparameters.h #include xuartps.h #include xil_printf.h #include sleep.h #define UART0_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID static XUartPs Uart0; int main(void) { XUartPs_Config *Config; int status; Config XUartPs_LookupConfig(UART0_DEVICE_ID); if (Config NULL) { return -1; } status XUartPs_CfgInitialize(Uart0, Config, Config-BaseAddress); if (status ! XST_SUCCESS) { return -1; } XUartPs_SetBaudRate(Uart0, 115200); xil_printf(EMIO UART0 Test Start\r\n); while (1) { xil_printf(Hello from Zynq EMIO UART0\r\n); usleep(1000000); } return 0; }这段代码做的事很简单查找UART0配置初始化驱动设置波特率115200然后每秒打印一行。但要注意xil_printf默认输出到哪个串口取决于BSP里标准输入输出设备的配置。在Vitis的BSP设置里确认stdout绑定到了UART0而不是UART1或者其他外设。如果BSP默认是uart1打印会没有反应你还需要用XUartPs_Send直接往UART0寄存器里发数据来验证。5.3 硬件连接与常见接线错误EMIO UART0的TX/RX引脚约束到FPGA引脚之后硬件上需要接一个电平转换板常见USB转TTL模块就行。接线的核心原则是交叉连接。Zynq的uart0_tx要接USB转TTL模块的RXDZynq的uart0_rx要接模块的TXD。很多人直接把同名信号连在一起结果收不到任何数据。另外共地一定要接好。USB转TTL模块的GND和Zynq板卡的地必须连通否则信号电平完全是浮动的串口助手可能偶尔显示乱码或者无响应。我用一个表整理常见接线问题现象可能原因排查方法串口助手收不到任何数据TX/RX接反对调TX/RX再试收到乱码波特率不一致或者共地不良确认PS端波特率和PC端一致检查GND连接按复位后能收到一次乱码TX引脚悬空导致上电抖动检查UART0_TX引脚约束是否正确防止浮动只在JTAG连接时正常USB转TTL模块供电不足给模块独立供电或换带隔离的模块数据显示正常但偶发丢字节使用了流控但没有接CTS/RTS关闭流控或者把CTS拉高5.4 PL内部回环验证的小技巧上板之前我特别推荐先在PL内部做一个UART0 TX到RX的回环验证。具体做法在Block Design里不把UART0_TX和UART0_RX引出到物理引脚而是直接把这两个端口短接。这时候跑SDK里的程序你会看到一个现象程序里xil_printf打印的内容并没有真正输出到电脑但是你在代码里用XUartPs_Recv读UART0的接收FIFO可以读到自己发出去的数据。这说明PS侧UART0的外设链路是通的问题只出在外部接线。排除了PS侧故障之后再把TX/RX解开引出就能比较有把握地说剩下的问题一定在PL引脚约束或者外围电路上。这个技巧我每次调试新板卡都用能省下很多排查时间。6. 这个组合配置的避坑清单时钟方向、引脚冲突、软件适配最后这一部分我把MII转GMII和EMIO UART0配置过程中最容易踩的坑统一整理一下。这些坑我不是第一次踩了有些甚至是第二次、第三次踩完才真正记住。6.1 MII的TX时钟方向问题这是MII转GMII里最典型的坑。拿到一个MII PHY第一反应是“MAC提供时钟给PHY”因为GMII就是这么干的。但MII协议比GMII早它在100Mbps以太网时代设计的时候TX时钟就是由PHY产生并回传给MAC的目的是让MAC侧的部分逻辑可以跟PHY完全同步简化设计。这个方向的差异直接决定你的信号连接方案。如果你把MII的mii_tx_clk当作输出去驱动PHYPHY根本不会响应反过来如果你把PHY的mii_tx_clk接错到GMII MAC的GTX_CLK引脚上两个时钟域完全对不上。记住GMII时钟由MAC出MII时钟由PHY出转换器的职责就是把这两个时钟域之间的数据搬运过去。6.2 MII转GMII不改变速率上限不少人在设计评估阶段会有个错觉既然GMII是千兆接口那我把MII PHY挂到这个转换器后面是不是也能自协商到千兆答案是不行。MII物理层最大速率就是100Mbps转换器只是把MAC侧的GMII协议降级适配到MII PHY数据通路仍然受限于PHY的能力。如果你的产品确实需要千兆吞吐老老实实选RGMI、SGMII或者直接GMII的PHY。MII转GMII只适合那些“主控已经是GMII/GEM但PHY只有MII”的存量硬件方案。6.3 EMIO引脚约束遗漏导致信号悬空UART0被设置成EMIO之后如果XDC里漏了某个引脚的约束Vivado也不会报错它会在实现阶段把这个引脚放到一个随机位置或者当成无负载的信号。这种情况最迷惑人复位之后软件跑起来串口有时有输出有时没有似乎看运气。我的处理方式是约束文件里把每个EMIO端口都用get_ports单独约束一遍然后写一个简单的Tcl脚本检查所有端口都有物理位置分配set unmapped_ports [get_ports -quiet -filter {PACKAGE_PIN }] if {$unmapped_ports ne } { puts Unmapped ports: $unmapped_ports }在Vivado的Tcl Console里跑一下能快速发现遗漏的引脚约束。6.4 PHY复位和上电时序MII PHY芯片一般都有复位引脚很多芯片要求上电后保持低电平至少10ms再释放复位。如果你直接把PHY复位引脚接到一个常量或者在上电瞬间复位没拉足够长的时间PHY内部的自协商可能起不来表现为MDIO读不到PHY ID或者MII侧没有任何时钟输出。正确做法是把PHY复位引脚连到PS的GPIO EMIO或者PL端GPIO由软件在初始化早期控制复位时序。比如在GEM驱动初始化之前先把GPIO拉低延时50ms再拉高。6.5 SDK里标准输入输出绑定错误UART0走EMIO之后软件工程里的BSP标准输出默认不一定绑定到UART0。特别是使用Vitis 2023.2之后的版本平台工程里需要显式配置stdin/stdout设备。很多报“串口没反应”的问题恰恰是这里配置错了硬件链路完全正常。在BSP设置里找到stdout配置项选择ps7_uart_0保存后重新生成应用工程。然后重新编译烧写通常一次就能通过。6.6 综合时序约束要区分以太网时钟模式如果你的PHY支持10M和100M两种模式MII的TX_CLK和RX_CLK可能是25MHz或2.5MHz。在XDC里写死25MHz的create_clock约束跑10M模式时会留下很大的时序裕量或者导致Vivado在实现阶段把时序优化到不合适的方向。我的建议是用set_clock_groups把两个模式的时钟定义成asynchronous告诉工具这两个时钟不会同时存在避免跨时钟域路径被过度约束。如果工程简单也可以直接用set_false_path跳过MII时钟路径的时序分析但这种方式对复杂设计不推荐。7. 一个可复用的集成架构思路把MII转GMII和EMIO UART0独立验证通过之后你会发现它们其实可以整合进同一个设计里而且整合方式非常有规律。整体框架大致是Zynq PS作为主控GEM0通过EMIO引出GMII信号PL内接MII to GMII Converter IP转换器MII侧连接到板载PHY芯片UART0也通过EMIO引出PL端口约束到串口接插件的TX/RX引脚。PS侧跑Linux或者 bare-metal用标准的GEM驱动和UART驱动软件几乎不用改动。如果你用的是PetaLinux对应热搜里的PetaLinux 2025.1还需要在设备树里把GEM和UART0的配置对齐到实际PL设计。设备树里ethernet-phy节点的reg要跟MDIO地址一致UART0节点确认status okay。这个阶段最容易出现的怪问题是系统启动后串口控制台没有任何输出但SDK里裸机程序明明能打印。通常原因是U-Boot的默认串口配置指向了uart1而不是uart0需要改U-Boot环境变量bootargs里的consolettyPS0。至于制作SD卡的boot.bin、boot.scr、image.ub这些环节跟本章的EMIO UART0没有直接关系但软件引导时串口在哪一路会影响你观察启动日志的位置。我的经验是在PetaLinux里先把UART0作为stdout-path这样从FSBL到U-Boot到内核的日志都能打印在同一路串口上调试事半功倍。做这个整合设计时我个人的习惯是按照“先物理引脚再IP连接然后软件配置”的顺序推进先把所有PHY和串口的引脚约束检查一遍再在Block Design里连线每连一组信号就核对着方向等实现通过后再去Vitis或者PetaLinux里配驱动。每层都确认无误之后剩下的问题往往只在硬件焊接和信号完整性上那种问题反而靠示波器比靠软件好解决得多——这也是我做了这么多块板子之后最大的体会。
返回列表