
做了这么多年FPGA和USB打交道是躲不开的坎。以前用USB 2.0 Device接口要么上STM32USB PHY要么用CY7C68013A这种自带8051的片子真正想在FPGA里实现一个灵活的高速USB设备选择并不多。Xilinx官方提供的AXI USB2.0 Device IP核算是在Zynq和FPGA平台里一个比较标准的解法但它配置起来有点绕官方文档也有不少语焉不详的地方。这篇文章就把我踩过的坑、读文档时卡住的地方、以及在实际工程里验证过的配置方法整理出来希望能帮到正在折腾这个IP的人。这个IP核本质上是一个USB 2.0 OTG控制器支持Device、Host以及OTG三种模式挂在AXI4-Lite总线上用来做寄存器配置数据通路则通过用户自定义逻辑直接访问UTMI接口或ULPI接口。在多数FPGA项目里大家看中的是它的Device模式——也就是让FPGA板卡作为USB外设和PC或嵌入式主机通信。它解决了FPGA和外部系统高速、低成本、低延迟数据交互的问题适合USB采集卡、高速数据记录仪、FPGA加速卡管理通道以及医疗仪器里的数据上行链路等场景。对于想把FPGA当成一个大号USB外设来用、又不想额外加一颗USB控制芯片的开发者这个IP就是为你准备的。下面从整体设计思路开始把接口、时序、寄存器、软件配置等内容拆开讲。1. 整体设计与数据通路拆解1.1 IP核的定位与三种工作模式AXI USB2.0 Device IP在Xilinx IP Catalog里搜索“USB2.0”就能找到全名是“AXI USB2.0 Device”支持的器件范围很宽从Artix-7、Kintex-7到Zynq-7000都能用7系列之后的部分器件也有对应版本。它内部集成了USB 2.0 Transceiver MacrocellUTMI和ULPI接口逻辑也就是说PHY层可以有两套方案一是直接用FPGA内部资源模拟的ULPI接口需要外加ULPI PHY芯片二是通过SelectIO实现UTMI接口通常配合USB3320这类ULPI PHY或者直接接USB3300做UTMI。从我实际使用经验来看最省事的做法是外接ULPI PHY比如常用的USB3320或USB3300。ULPI接口把80MHz的8位并行总线变成DDR模式的上下沿采样有效降低引脚数量布局布线难度也小很多。UTMI接口则需要60MHz或48MHz的接口时钟信号宽度更大适合想要完全控制底层时序的场景但调试成本会高不少。三种模式里Device模式最常用配置成Device后IP核会自动处理USB枚举、复位、暂停等总线事件用户只需要关心端点FIFO里的数据。Host模式也可以做但Xilinx这个IP的Host能力相对弱一些更复杂的Host功能建议去看USB Controller IP或者干脆用Zynq PS自带的USB控制器。OTG模式需要额外做ID引脚检测和会话请求协议SRP/HNP在纯FPGA工程里复杂度会上升一个量级。选择Device模式时IP核作为USB设备连接到主机主机通过USB总线枚举并配置设备之后通过端点Endpoint进行批量传输、中断传输、等时传输等。FPGA内部逻辑通过AXI4-Lite接口读写寄存器来配置设备地址、端点、DMA等而用户数据则通过端点的FIFO接口送入或取出。1.2 AXI4-Lite配置通道与用户数据通道分离这个IP核一个比较好的设计是配置和数据的通道分离。AXI4-Lite接口只负责寄存器读写数据不经过CPU或ARM核而是直接由用户逻辑和内部端点FIFO对接。这样做的好处是带宽可控、延迟可预测不会出现USB数据被CPU总线阻塞的情况非常适合流式数据场景。配置通道包括IP核的设备模式配置寄存器、端点配置寄存器、中断状态寄存器、DMA控制寄存器等。数据通道则是每个端点对应一个独立的FIFO接口支持写入通知、读取通知、FIFO满/空状态等信号用户逻辑只需要轮询或中断触发来搬运数据即可。对于Zynq平台来说AXI4-Lite通常会接到PS端的GP或HP端口上PS通过SDK里写好的驱动来控制IP核。对于纯FPGA平台则可以由MicroBlaze软核、外部ARM、或者状态机逻辑来读写寄存器。无论哪种方式寄存器地址映射和驱动逻辑都是相通的。1.3 AXI DMA还是直接访问端点FIFOXilinx官方示例里通常建议搭配AXI DMA IP使用特别是在Zynq上数据从PS内存搬运到USB端点FIFO中间经过AXI DMA和AXI SmartConnect。这个方案的优点是CPU不需要参与每一次的数据搬运DMA完成后触发中断通知CPU对大数据块传输非常友好。但我实际在多个项目里发现很多场景下直接通过用户逻辑读写端点FIFO控制器也完全够用反而省掉了DMA的配置和调试。例如在FPGA图像处理链路上图像数据从MIPI或LVDS进来经过ISP处理后直接写入USB端点FIFO无需DMA这样整条链路只受USB实际带宽限制。只有当PC上位机要求CPU在数据帧之间做复杂协议处理时才会考虑DMA方案。使用DMAD模式时需要注意IP核的DMA控制器寄存器并不多但不小心配置错地址就会导致总线错误。我建议先跑通无DMA的裸寄存器版本再考虑上DMA这样排查问题会快很多。2. 核心接口信号与关键寄存器解析2.1 ULPI/UTMI接口连接要点无论选ULPI还是UTMI和PHY芯片的时序、复位、时钟都要处理好。ULPI PHY需要外部24MHz或19.2MHz参考时钟PHY内部锁相后输出60MHz的ULPI接口时钟同时产生8位双向数据总线。ULPI接口上DIR、NXT、STP三个信号的控制时序非常关键很多初学者在仿真里死活不通基本都是这三根线的握手逻辑没搞对。实际连接时可以用IP核输出的ulpi_clk作为PHY时钟的反馈也可以直接把板上的晶振接到PHY的XI引脚。常见做法是板上放一颗24MHz无源晶振经PHY内部PLL倍频到480MHz用于USB收发再降频到60MHz作为ULPI接口时钟。FPGA端还需注意ULPI数据总线是双向的综合时要正确设置IO标准和方向控制不然上板跑起来数据对不上。如果选UTMI接口则PHY芯片需要换成USB3300这类UTMI PHY输入时钟可以是60MHz或48MHz数据宽度是8位或16位。UTMI接口里有单独的rx_valid、tx_valid、rx_active等信号读写时序相对直观但引脚多、布线难度大在小型PCB上不一定比ULPI方案好。2.2 设备模式的核心寄存器组IP核内部寄存器分布在设备模式基地址上常用的几个寄存器需要背下来USBCMD运行/停止、复位、DMA使能等控制位。上电后必须先把USBCMD里的Run/Stop位置1设备才会开始响应总线复位。USBSTS中断状态、复位状态、端口改变等。轮询或中断时优先读这个寄存器读后要进行对应位清0。DEVICEADDR设备地址寄存器主机在枚举过程中会分配地址需要把主机发来的SET_ADDRESS值写进去。ENDPTCTRL0~ENDPTCTRLx每个端点使能、类型Bulk/Interrupt/Isochronous、方向、数据翻转等配置。ENDPTNAK、ENDPTNAKEN端点NAK控制配置端点前要确认NAK状态没有误使能。RXFIFO/TXFIFO以及端点FIFO地址映射决定端点数据缓冲区的位置和大小。在纯FPGA资源有限的工程里端点的FIFO深度直接占Block RAM所以不能随便乱配。一个批量端点双向各1KB深度加上端点0的64字节控制FIFO大概就会消耗4块18K BRAM。设计时要在吞吐量和BRAM占用之间做权衡。2.3 中断设计与缓冲区管理IP核的中断信号可以配置成脉冲或电平模式连接到Zynq的中断控制器或MicroBlaze的中断输入。常见的中断源包括总线复位、挂起/恢复、端点完成、DMA完成、USB错误等。最常用的端点是端点完成中断——每次USB主机发起的传输IN或OUT完成时IP核会置位对应的完成位。实际项目中我在每个端点完成中断里只做一件事置一个标志位真正的数据搬移放到主循环或空闲状态机里去处理。不要在中断里边做大量数据拷贝否则在高带宽下会丢失中断或者阻塞总线这个经验在PCIE和USB调试中都是通用的。缓冲区管理上推荐采用多缓冲交替机制。比如用两块1KB的BRAM作为端点FIFO的ping-pong bufferUSB写入一块的同时用户逻辑搬运另一块这样能有效提高吞吐率。虽然IP核内部端点FIFO本身有一定深度但配合外部ping-pong逻辑效果会更好。2.4 复位与时钟域处理的坑IP核自身有一个软复位通过AXI4-Lite写入USBCMD的Reset位后需要等待若干微秒再置位Run/Stop。PHY芯片还有自己的复位信号最好拉长到5ms以上等PHY内部PLL锁定后再操作IP核。上电时序不好常见的现象是PC端识别不到USB设备或者设备管理器中显示未知设备插拔一次才能识别。时钟域方面AXI4-Lite接口一般在S_AXI_ACLK时钟域而ULPI接口工作在PHY提供的60MHz时钟域。IP核内部已经处理了跨时钟域但用户逻辑直接操作端点FIFO时序时要去参考IP核用户手册中的FIFO接口时序图特别注意在写入使能之前保证fifo_not_empty或fifo_not_full的状态正确。实际调试时用ILA抓FIFO接口信号和ULPI接口信号往往能快速定位到底是哪一侧出了问题。3. 实战配置流程从Vivado例化到上位机通信3.1 Vivado中创建AXI USB2.0 Device IP在Vivado的IP Catalog中搜索USB2.0找到“AXI USB2.0 Device v8.x”双击配置。配置界面主要选项有Interface Type选ULPI还是UTMI。工作模式选Device Only或OTG如果只做Device建议直接Device Only省掉OTG逻辑。端点数量与端点类型IP核最多支持到16个端点不含端点0。添加端点时需要指定端点号、方向、类型bulk/interrupt/isochronous和FIFO深度。DMA接口需要DMA时开启并配置DMA通道数。配置好端点数量和类型后IP核会自动生成对应的端口包括每个端点的发送/接收FIFO接口、中断输出、ULPI/UTMI引脚等。这里有个容易忽略的点IP核名字里如果带“ULPI”版本一定要把ulpi_clk和ulpi_dir/nxt/stp接到正确引脚电平标准要选LVCMOS33或者和PHY一致的接口电平否则波形乱掉。3.2 引脚分配与PHY芯片选型ULPI PHY最常见的搭档是USB3320这是Microchip原SMSC的经典芯片低功耗、封装小、外围电路简单只需24MHz晶振、两只去耦电容和一对DP/DM差分走线。USB3300则提供UTMI接口适合需要扩展16位数据总线或用到内部ULPI转UTMI桥的场景。做PCB Layout时ULPI_DATA[7:0]、ULPI_DIR、ULPI_NXT、ULPI_STP、ULPI_CLK这12根线要一组DDR信号处理建议走线尽量等长差分对DP/DM要有90欧阻抗控制。实际经验是FIFO接口或GPIO口不做阻抗匹配问题不大但USB的DP/DM必须严格控制阻抗不然信号质量差会出现大量CRC错误和重传PC端表现出来就是复制文件速度极慢。3.3 驱动逻辑编写端点收发示例驱动逻辑分两部分读写寄存器控制和端点数据搬运。寄存器读写可以通过AXI4-Lite总线由ARM或MicroBlaze完成端点数据搬运则可以是纯硬件逻辑。先看寄存器侧伪代码以MicroBlaze为例// 1. 复位设备控制器 usb_write(USBCMD, 0x4); // RST delay_ms(10); // 2. 配置设备地址为0枚举阶段 usb_write(DEVICEADDR, 0); // 3. 配置端点1为Bulk IN使能 usb_write(ENDPTCTRL1, 0x44); // 使能 Bulk IN // 4. 启动控制器 usb_write(USBCMD, 0x1); // Run/Stop端点数据搬运逻辑则在FPGA里做。对于IN端点用户逻辑把FIFO写入IP核的TXFIFO端口然后IP核等待主机发IN Token把数据发出去。对于OUT端点主机发OUT数据后IP核把数据写入RXFIFO用户逻辑读走。实际写Verilog/VHDL时需要关注IP核输出的一组端点状态信号包括端点的TXFIFO空/满状态RXFIFO空/满状态以及FIFO写使能时的握手确认。建议使用Xilinx的FIFO Generator IP加上简单状态机来对接这些信号状态的粒度不需要太小能把数据搬运稳定跑起来就行。3.4 上位机通信与驱动选择USB设备在上位机端的驱动选择很影响调试效率。最直接的是使用WinUSB驱动Windows 7及以上系统无需额外签名使用libusb或Zadig工具把设备绑定到WinUSB然后通过libusb的bulk传输接口就能收发数据。也可以把设备实现为CDC类设备这样PC端自动识别为虚拟串口上位机可以使用串口API直接通信。CDC设备的描述符配置较为简单但对带宽有一定损失不适合大数据量传输。批量传输在高带宽数据采集场合更实用。上位机从USB批量端点读写时一次传输块大小和超时时间都要可配置。我习惯用libusb的异步传输接口一次提交多个URB数据吞吐量会明显提升。同步方式在Linux下也能用但高带宽时CPU占用偏高容易导致瓶颈。3.5 一个实际数据的带宽测试结果在某次项目中FPGA与PC通过USB 2.0 Device IP通信传输16位并行采样数据。没有使用DMA直接由用户逻辑写入端点FIFO测试得到稳定传输速率约37MB/s。使用DMA并且批量传输包大小调整到512字节后速率提升到约39MB/s。USB 2.0全速和高速的Raw带宽是480Mbps实际批量传输扣除协议开销、握手和重传后能达到40MB/s左右已经是比较理想的情况。如果速率达不到需要优先排查PHY和FPGA之间的ULPI接口时序是否干净USB线缆和接插件质量是否过硬上位机驱动轮询间隔是否太慢以及上位机是否有单独线程处理USB读取。很多时候速率瓶颈不在FPGA端而是上位机软件处理不过来。4. 常见问题排查与调试实录4.1 枚举失败或设备无法识别USB设备最常见的故障就是插入电脑后“叮咚”一声但设备管理器中是未知设备或者干脆无反应。排查顺序可以从物理层到协议层一层层来。先确认PHY芯片的复位和时钟是否正常用示波器或逻辑分析仪看24MHz晶振是否起振PHY的CLKOUTULPI时钟是否有60MHz输出DP/DM是否在插入时产生上拉静默状态。如果ULPI时钟没有PHY可能没有正常复位或供电不对。接着用ILA在FPGA内部抓ulpi_dir和ulpi_nxt信号看主机发出总线复位时IP核是否有反应。如果只看到总线复位但IP核没有进入复位配置阶段可能是IP核Run/Stop没置位或是复位信号毛刺导致IP核状态机卡住。还有一个容易忽略的问题USB 2.0设备在FS模式也可以枚举但如果PC端希望设备以HS模式运行而FS握手失败系统会重复枚举多次。可以强制把DP/DM上拉到FS模式排除HS握手问题后再逐步打开HS模式。4.2 批量传输丢包与带宽上不去丢包一般有两个层面USB链路层和FPGA逻辑层。USB链路层出现CRC错误或PID错误会导致主机重传重传频繁时吞吐量下降。可以先抓USB总线数据看是否有大量重传或错误包。有的话重点查物理层设计和线缆质量。没有的话问题大概率在FPGA端比如端点FIFO频繁满/空导致总线空闲或者用户逻辑来不及读走RXFIFO导致主机侧发来更多数据时端点NAK引发重试和带宽降低。另一个隐蔽问题是描述符配置不符合主机预期尤其是端点最大包大小配置不对。批量端点HS模式下最大包为512字节如果描述符里写成64字节实际传输性能会受影响。在调试时我习惯先抓端点0的控制传输确认枚举过程中的所有描述符请求都正确返回。4.3 DMA总线超时与死锁DMA模式下的死锁往往表现为USB传输停止、CPU无法通过AXI4-Lite访问IP核寄存器。产生原因很多本质上是DMA和目标地址之间的AXI总线没有释放。我遇到过两次死锁一次是DMA源地址配置到了不存在的内存区域一次是没开AXI数据宽度转换。解决这类问题的思路是先用JTAG或SDK的寄存器读写工具确认IP核是否还响应AXI4-Lite操作。如果不响应优先怀疑DMA通道没有正常关闭直接把IP核整体复位再把DMA寄存器全部清零重新初始化。如果死活查不出来最笨但有效的办法是设置一个DMA超时看门狗用户逻辑里监控DMA完成中断若超过一定时间未响应自动复位DMA通道并重新发起传输。工程上不丢数据比理论极限带宽更重要。4.4 适用于全速设备和高速设备的共存设计同一块板卡如果既要兼容USB 1.1主机全速模式又要在USB 2.0高速主机上全速运行需要注意端点的最大包大小不能超过64字节。一个项目中我把Bulk端点最大包设置成512字节结果在老式工控机上枚举失败改成64字节后又整机正常。这个兼容性细节在嵌入式设备对老主机有兼容要求时很关键。5. Xilinx AXI USB2.0 Device IP的选型与替代方案对比写到最后讲一讲什么时候该用这个IP核什么时候该绕开它。如果只是单纯做USB转串口、U盘读取等简单功能建议不要折腾这个IP。IP核加上外围电路、驱动开发整个工程量和你直接用一颗USB桥接芯片相比完全不占优势。如果项目本身就在Zynq平台上又需要USB Device做控制和数据通道而且arm端有现成的Linux环境那用Zynq PS自带的USB控制器会更省心驱动成熟、带宽稳定Xilinx官方支持也最到位。AXI USB2.0 Device IP更适合那些没有ARM核、纯FPGA逻辑为主的设计或者在PS端USB资源已经被占用、仍要通过PL扩展一个USB口的场景。和类似方案对比CY7C68013A的优点是硬件成熟、范例多、驱动生态好缺点是需要和8051内核打交道做纯FPGA直通时数据搬运效率并不高。FTDI的FT600/FT601则主打FIFO转USB速度上限更高USB 3.0但协议灵活性不如可自己定义控制逻辑的AXI USB2.0 Device方案。Xilinx这个IP的价值在于它深度绑定PL侧的可编程逻辑替代方案往往需要增加额外的桥接芯片或MCU系统复杂度和成本都会上升。5.1 Xilinx IP核与替代方案的对比总结方案优势劣势适用场景AXI USB2.0 Device IP自定义逻辑深度集成、无额外芯片开发周期长、调试门槛高纯FPGA/Zynq PL需USB扩展、流式大数据Zynq PS内置USB控制器驱动成熟、Linux支持完善占用PS资源引脚分配受限Zynq上需要USB功能且PS资源不紧张CY7C68013A开发资料多、上手快8051和FPGA之间数据交互效率一般中小数据量、快速原型验证FT600/FT601速度快、Host端驱动清晰协议固定无法深度定制高速数据传输但协议简单5.2 后续扩展从USB 2.0升级到USB 3.0的路径USB 2.0在480Mbps理论带宽下实际应用通常只有40MB/s左右。如果项目后续要做超高清图像采集或高速数据记录建议提前考虑USB 3.0方案。Xilinx平台上常用的方案是使用第三方USB 3.0 Device Controller IP例如FTDI的FT601/FT602方案或各大IP厂商的USB3.0 Device Controller IP。相比之下FT601无需过多FPGA逻辑作为外置桥接方案见效最快。如果还想继续沿用纯FPGA逻辑路线赛灵思收购后部分UltraScale器件的GTH收发器本身也具备高速串行能力配合USB 3.0物理层和协议栈IP就能在逻辑层实现USB 3.0 Device。这条路复杂度高需要投入大量人力验证协议一致性一般项目不建议碰除非你觉得现有设计无法满足带宽需求且有足够的研发周期去填坑。根据我个人的经验做FPGA的USB通信最关键的是先把枚举跑通再谈带宽和稳定性。不要一上来就追求高吞吐先把小数据量的控制传输跑顺然后把批量传输一点点加大每一步都验证通过再继续这样最终成功率会高很多。如果一开始就想着跑满40MB/s大概率会被各种底层问题折磨到怀疑人生。