ARTICLE DETAIL

资讯详情

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

FPGA中AXI USB2.0 Device IP核配置与调试实战指南

FPGA中AXI USB2.0 Device IP核配置与调试实战指南 做FPGA这几年我接触最多的外设接口里USB这块是最容易让开发者以为懂了、一调就崩的。尤其是Xilinx的AXI USB2.0 Device IP核配置选项多、总线接口杂、时序约束隐蔽光是把官方手册啃下来就得花不少时间更别提实际跑起来之后遇到的各种枚举失败、数据错位、带宽上不去的问题。这篇文章我打算把这颗IP核从头到尾拆开讲一遍配合我自己项目里实际用过的寄存器配置、AXI Stream数据通路设计、以及和MicroBlaze软核联调时的踩坑记录给正在做或者准备做USB Device功能的开发者一份能直接上手的参考。1. 为什么USB Device功能非用IP核不可纯逻辑实现的门槛很多刚入门的FPGA开发者会有个疑问USB 2.0协议不就是包传输吗我自己用状态机写一个不行吗理论上是可行的但工程上几乎没有人这么干原因在于USB协议栈的完整度远超预期。1.1 USB协议栈的隐藏复杂度你看得见的USB通信是设备枚举、批量传输、中断传输但背后还有一连串底层机制SOF帧同步、微帧调度、CRC5/CRC16校验、位填充与去填充、NRZI编解码、PID确认与重传、端点停摆/恢复、远程唤醒、挂起与恢复。任何一个环节出错主机端就会报无法识别的USB设备或者枚举超时。自己用Verilog从头写光是搞定高速模式下的480Mbps数据率时序收敛就是一个大工程更别说物理层还需要和外部PHY芯片对接。Xilinx这颗AXI USB2.0 Device IP直接把这些底层全包了对外暴露的是UTMI接口和AXI总线接口。换句话说你需要关心的不是USB包怎么编而是数据怎么通过AXI总线送到IP核内部缓冲区以及IP核的中断/状态寄存器怎么告诉软件什么时候收数据、什么时候发数据。这也是这篇文章想讲清楚的核心。1.2 IP核解决的核心问题与适用场景这颗IP本质上是把USB Device控制器Device Controller做成了可配置的硬核/软核挂在AXI总线矩阵上让FPGA内部逻辑或者软核处理器MicroBlaze、Zynq ARM把它当普通外设来操作。它解决了三件事物理层适配自动处理UTMI PHY接口的收发时序对开发者屏蔽掉高速/全速模式切换的麻烦。协议层处理自动应答控制传输、处理标准请求Get Descriptor、Set Address等你只需要在传输完成回调里读取数据。系统集成便利AXI4-Lite接口用来配置寄存器AXI Stream接口用来搬数据这种拆分在Zynq平台特别合适DMA可以直接挂Stream接口不需要CPU参与搬运。适用场景包括数据采集卡、工业控制器的固件升级通道、FPGA加速卡的调试口、自定义HID设备等。如果你只需要把FPGA里的数据发给PC或者接收PC下发的配置指令用这颗IP核是最快路径。提示这颗IP核支持的是USB 2.0 Device模式不支持Host模式也不支持OTG。想做Host功能的开发者需要换用另一颗USB2.0 ControllerIP或者外接USB Host PHY方案。2. AXI USB2.0 Device IP内部架构与数据通路拆解要配置好这个IP先要把它的内部结构摸清楚否则你根本不知道寄存器里那几百个bit都是干什么用的。2.1 从UTMI接口到AXI总线的整体架构官方文档里的框图看起来挺复杂但拆开看其实就三大块USB Device Controller核心负责协议状态机、端点管理、包校验、应答生成。它和外部PHY芯片之间走UTMI接口8位数据线时钟60MHz高速模式或48MHz全速模式。上板调试时你基本不需要关心这部分除非PHY芯片选型有问题。端点缓冲区Endpoint RAM这是最关键的中间存储区。IP核内部为每个端点分配了独立的RAM空间主机发来的数据先落到这里然后通过AXI接口通知你取走。你发数据也是先写到这个RAM然后软件触发IN事务。AXI接口逻辑分成AXI4-Lite从接口和AXI Stream接口。前者负责配置寄存器读写后者负责批量/中断/同步传输的数据搬移。这两个接口是并存的互相独立。我画过一张简化的数据流图主机发出OUT包 - PHY接收 - 控制器校验 - 数据写入Endpoint RAM - 状态寄存器置位/中断拉高 - 你的逻辑通过AXI Stream读取RAM数据。反过来你的逻辑通过AXI Stream写入Endpoint RAM - 软件使能IN端点 - 控制器把数据打包发出 - 主机收到。2.2 AXI4-Lite从机接口与寄存器映射的对应关系AXI4-Lite从接口负责访问控制寄存器地址总线宽度是32位的但实际能用到的偏移地址取决于你的配置。需要重点关注的寄存器组有设备配置寄存器设置设备地址、配置设备描述符、使能/禁止某个端点。端点状态和控制寄存器查询端点的buffer是否为空、有没有数据待处理、是否收到NAK等。中断寄存器包括中断状态寄存器和中断使能寄存器这两个必须配对使用处理完中断后在状态寄存器写1清除对应位。USB命令寄存器触发Bus Reset响应、Remote Wakeup等。要注意的是AXI4-Lite接口的地址是相对于IP核基地址的偏移。在Block Design里MicroBlaze访问时基地址由地址编辑器自动分配比如0x44A00000那么寄存器偏移量要在这个基地址基础上叠加。最经典的坑有人直接把寄存器表里的偏移地址当成绝对地址写驱动结果读写全落到了别的外设上查了一下午。2.3 AXI Stream接口读写方向上最容易理解错的一点AXI Stream接口是数据搬运的主力。IP核支持多个Stream通道分别对应对齐到批量端点、中断端点、同步端点。举个例子如果配置使能了一个批量IN端点和两个批量OUT端点那么Stream接口会有3组通道一组TREADY/TVALID/TDATA/TKEEP/TLAST等信号分别对应一个端点。很多第一次用的人会把Stream接口和普通FIFO接口搞混。Stream接口没有地址只有握手信号但握手不是自动完成的你必须在每次传输前确认对应端点的数据长度寄存器才能知道这一帧数据有多少字节。IP核提供了一个辅助信号来指示当前Stream通道绑定到哪个端点配合端点状态寄存器一起用才能正确处理多端点分时复用的情况。我的建议在开始写RTL控制逻辑前先准备好一张端点号 - Stream通道号 - 中断号的映射表贴在屏幕上。后面调试时你会发现很多莫名其妙的数据串了根本原因就是多端点模式下Stream通道和端点对应错了。3. Vivado工程中的配置流程与关键参数选择这一节我从实际操作角度讲不带大家看手册直接说我项目里是怎么选的以及每个选项背后影响的到底是什么。3.1 创建IP核时的关键配置项与我的默认选择在Vivado IP Catalog里搜索AXI USB2.0 Device就可以看到这个IP。双击打开配置界面主要选项有接口类型选择USB 2.0 Device Only这个没什么好纠结的。速度模式我建议勾选Support High-Speed同时保持Full-Speed兼容。虽然全速模式也能用但480Mbps高速模式才是这个IP的价值所在全速只有12Mbps实用性大打折扣。端点数量与方向根据需求配。通常控制端点0是固定有的批量端点要单独勾选使能可以配置成IN、OUT或双向Bidirectional。注意每增加一个端点AXI Stream通道数和中断信号数都会增加功耗和逻辑资源也会上升不是配置得越多越好。端点FIFO深度这个非常重要。默认值通常比较保守如果你要跑大数据量传输建议把批量端点的FIFO深度调大。但FIFO深度增加意味着RAM资源占用线性上涨需要平衡。我在一个高速数据采集项目里用的是1个控制端点 2个批量端点一个IN一个OUT 1个中断端点IN端点FIFO深度设置成4096字节其余保持默认。这样配置下来实测高速模式批量传输能稳定跑到约42MB/s协议开销吃掉一部分480Mbps理论带宽。3.2 时钟与复位设计UTMI时钟、AXI时钟、参考时钟的关系这颗IP需要至少两个时钟域一是AXI接口时钟通常100MHz或150MHz和你的总线矩阵一致二是UTMI接口时钟60MHz高速模式或48MHz全速模式。如果PHY芯片提供60MHz时钟输出可以直接连到IP的utmi_clk端口如果用fpga内部PLL生成注意保证时钟质量因为UTMI时钟抖动会直接影响USB眼图质量。复位方面IP核需要异步复位信号由外部复位控制器统一控制。我踩过的坑复位释放时机不能早于时钟稳定否则IP核内部状态机可能锁在非法状态表现为中断寄存器读出来全FF。建议用Xilinx的Reset IP核生成复位信号并把dcm_locked信号作为复位释放的条件。3.3 中断连接方案与优先级规划AXI USB2.0 Device IP把中断合成一路irq输出Zynq/MicroBlaze接通用中断控制器即可。但这里面有个细节IP核内部有多个中断源端点0事件、批量端点收发完成、总线复位、挂起唤醒虽然物理上只有一根irq线软件在中断服务程序里还是要读中断状态寄存器来区分触发源。如果多个端点同时需要高优先级实时响应比如一个端点做实时控制指令收发另一个做大数据量传输建议不要只依赖一根irq可以把不同端点中断拆分开来接到优先级不同的IRQ线上。不过这样需要多配置一套中断逻辑。我在实际项目里控制指令走中断端点、IRQ接到CPU的FIQ大数据走批量端点、IRQ接到普通IRQ效果很好控制延迟稳定在几十微秒级别。4. 基于AXI4-Lite的寄存器操作设备枚举背后的软件配合很多教程把重点放在硬件配置上但实际调试时你会发现USB枚举能不能成功软件寄存器操作占了七八成的因素。这里把寄存器操作的关键流程列出来。4.1 从设备地址分配到端点使能的初始化序列IP核上电后不会自动进入设备模式必须由软件完成初始化。标准的初始化序列大致是复位整个IP核等待复位完成标志位置位。设置设备描述符相关的配置寄存器厂商ID、产品ID、设备类等。这些寄存器在IP核内部会自动响应主机的Get Descriptor请求不需要你写代码去处理每个描述符请求。写入设备地址0并启用设备。在未收到Set Address请求前设备必须保持地址0接收控制请求。使能需要的端点配置端点类型、方向、FIFO深度等。使能中断开启全局中断和特定端点中断。这段序列看起来短但每步之间的时序要求很严格。比如步骤2和步骤3的顺序不能颠倒必须先配置描述符再启用设备。另外初始化必须在USB线拔插中断发生后的设备复位流程中重新执行否则设备插到主机上无法识别。4.2 控制传输与标准请求的处理方式控制传输是所有USB通信的基础主机通过控制传输获取描述符、设置地址、配置设备。AXI USB2.0 Device IP对标准请求做了硬件自动处理也就是说Get Descriptor、Set Address、Set Configuration这些请求由IP核硬件回复你的软件不需要干预。但非标准请求class/vendor自定义请求会触发中断并等待软件处理。这意味着你写驱动时不需要自己解析标准描述符请求但必须在端点0的Setup包中断发生时正确读取请求数据、执行相应操作、然后返回数据或状态。这个流程很多人的驱动里写得不对导致设备能被识别、但无法完成特定功能。经验分享用ILA抓取端点0的AXI Stream数据看Setup包的8字节请求内容。你会发现标准请求和厂商请求的data stage方向完全不同别在代码里写死了方向的判断逻辑。4.3 寄存器读回验证与在线调试技巧调试寄存器操作时用Vivado的Hardware Manager配合ILA是最直接的方式。我会在AXI4-Lite接口上挂ILA抓取读写地址、数据、响应信号。重点关注地址位是否对齐DWORD对齐偏移量必须是4的倍数。写寄存器时被写入的值是否被IP核正确接收读回验证是否是同样的值。状态寄存器的某些位是只读的比如端点Buffer状态写操作会被忽略别被误导。有一次我遇到一个怪现象往设备地址寄存器写0x08后读回来是0x00但枚举依然成功。后来查手册发现这个寄存器在设备处于Address状态时会被IP核硬件锁定软件不能随便改。这种字段读回异常的情况多半不是总线问题而是IP核的状态机保护机制。5. AXI Stream数据路径实战缓冲策略与时序握手数据面才是这个IP真正发挥价值的地方。USB的带宽是固定的但你的FPGA逻辑处理数据的能力不一定能跟上这就需要合适的缓冲和时序设计。5.1 为什么需要双缓冲带宽匹配与协议开销USB高速模式理论带宽480Mbps但这是包括协议开销的实际有效吞吐受限于包大小和传输间隔。批量传输一个最大包是512字节加上协议头尾和SOF开销理想情况有效吞吐约40MB/s。如果你希望持续跑满就必须保证任何一个时刻有可用的FIFO空间让IP核写入下一包数据。双缓冲的意思是IP核的Endpoint RAM分成两个区一个区在往AXI Stream输出当前数据时另一个区已经在接收主机发来的下一包。如果没有双缓冲主机发完一个包要等你读完缓冲区才发下一个带宽会掉到只有十几MB/s。CXilinx这颗IP核在批量端点内部已经实现了硬件双缓冲但前提是你在配置时把FIFO深度设得足够大至少能容纳两包否则硬件会直接NAK主机导致主机端看到吞吐下降。5.2 写数据路径OUT端点的TREADY/TVALID/TLAST处理细节OUT端点的数据流是主机发数据 - IP核写入Endpoint RAM - IP核把数据从Stream接口推给你的逻辑。你的逻辑作为AXI Stream的从端需要控制TREADY信号。最容易出错的是TLAST的生成。对于批量传输一包就是一次传输事务TLAST表示最后一个有效数据。由于批量包大小固定512字节最后一个包可能小于512你的逻辑需要在每接收512字节时拉高一次TLAST表示一帧结束。如果TLAST延时一拍下游的DMA或者FIFO会认为这是一个超长数据包出现粘包现象。我在设计里是这么做的用计数器对TVALIDTREADY有效周期计数当计数值等于当前端点FIFO深度对应的包大小比如1024或512时拉高TLAST一个周期。注意不要直接用数据长度寄存器的值来判断TLAST时机因为该寄存器在IP核内部更新有时序延迟直接用可能导致判断漏拍。5.3 读数据路径IN端点的背压处理与吞吐优化IN端点方向相反你的逻辑通过AXI Stream把数据推给IP核IP核打包发给主机。这里的核心问题是你的逻辑写入速度可能比USB实际发送速度快。IP核的做法是通过TREADY信号反馈背压——当端点RAM没空间时TREADY拉低你的写端必须暂停。如果使用DMA自动搬运DMA控制器会遵循TREADY自动暂停问题不大。但如果是写RTL逻辑千万不要让写端在TREADY拉低时丢失数据或覆盖未发送的数据。一个稳妥的方案是在Stream接口前加一个异步FIFO做隔离FIFO写端面向你的业务逻辑FIFO读端面向USB IP核这样即使USB暂停几千个时钟周期你的业务逻辑也不会被阻塞。在我项目里的实测数据不加大容量FIFO直接用寄存器打拍转发高速批量IN端点的吞吐只有不到20MB/s加了32KB异步FIFO后稳定跑上了40MB/s差距非常明显。6. 与MicroBlaze/软核集成时的常见坑与排查链路把IP核接入Block Design只是第一步真正让整个系统跑起来往往要经历一轮哪哪都对、就是不通的折磨。这里分享三个我实际遇到并解决的典型案例每个都附上排查链路。6.1 地址分配与地址计算错误一个偏移地址引发的数据错乱症状是MicroBlaze读USB控制寄存器的值和ILA抓到的不一致有些位对不上。排查了很久发现问题出在地址映射上。Block Design里给IP核分配的基地址假设是0x44A00000当我用SDK写驱动时程序里用的是绝对地址。由于寄存器偏移量定义在头文件里正数是增加偏移但我误在基地址的基础上加了两次偏移导致访问到了别的寄存器。更隐蔽的是有些寄存器位段对地址错位不敏感读出来看起来正常导致误导。排查方法写一个简单的读回ID寄存器测试函数如果读出的设备ID固定值和手册一致说明地址映射正确如果不一致再用ILA抓AXI总线的AWADDR/ARADDR对照基地址偏移量逐项检查。6.2 数据粘包/断包问题的定位TLAST与PC驱动收发缓冲的博弈现象是PC端上位机接收数据时经常出现一帧数据被拆成两段或者两帧数据合在一起。这个问题在批量传输里很常见通常是因为FPGA侧对TLAST的判断条件不对导致包长度不定。我从应用层往底层逐层排查首先确认PC驱动有没有开启USB批量传输的短包作为帧结束语义。USB协议里批量传输以短包小于最大包长度作为传输结束如果每帧刚好是512字节整数倍需要主动发一个0字节包来标记结束。然后检查FPGA侧TLAST是否严格按包边界拉高。用ILA抓TREADY/TVALID/TLAST数一下每两个TLAST之间的有效数据量是否符合预期。最后发现问题是我在计数器中使用了包长度寄存器作为判断依据但寄存器更新有延迟导致TLAST晚了几个周期。改回固定字节计数后问题彻底消失。这里想提醒的是TLAST的判定标准应该是IP核Stream协议定义的传输事务边界而不是USB传输逻辑包边界两者在Out端点有细微差别。多看几遍IP核产品指南里的时序图比自己瞎猜高效得多。6.3 枚举失败从硬件配置到驱动兼容性的全景排查枚举失败是最令人崩溃的问题因为症状一样PC提示无法识别USB设备但原因可能五花八门。我总结的排查顺序先用示波器量UTMI接口的60MHz时钟是否存在且频率正确。很多板子的PHY芯片供电不稳导致时钟丢失IP核直接躺平。用ILA抓USB IP的中断寄存器看看有没有总线复位中断。如果复位中断都没触发说明PHY/UTMI链路有问题换PHY芯片或检查焊接。检查设备地址寄存器是否被正确初始化。有时SDK里启动顺序不对IP核还没初始化就被上位机发送请求导致一堆奇怪错误。最后查PC驱动。Windows下如果之前装过其他设备的驱动可能出现驱动冲突。Linux下则要确认内核是否加载了相应class驱动。这里分享一个我自己的暴力但有效的排查法把IP核配置成只枚举不做数据传输的裸模式也就是只使能控制端点不使能任何数据端点。如果这样设备能被PC识别为USB Input Device或Unknown Device说明控制通路没问题问题一定出在数据端点配置、驱动和软件上。反过来如果枚举都过不去重点查硬件。7. 调试工具与实测数据从仿真到上板验证的完整闭环最后这部分聊测试验证。很多人习惯把代码写完直接上板出问题后再对着板子发呆。正确的做法是先仿真、再上板上板后用ILA和上位机联合验证全程用数据说话。7.1 AXI Verification VIP与仿真环境的搭建思路仿真阶段你需要在Testbench里例化IP核的AXI4-Lite和AXI Stream接口然后用Xilinx提供的AXI Verification VIP来发起读写事务。这里要给一个建议不要从零手写AXI BFM直接用VIP它的配置界面可以自定义地址空间、读写数据模式、错误注入等能省大量时间。VIP使用的核心是pkg定义的事务类型。比如你可以创建一组写事务往控制寄存器写入预设值然后创建一组读事务验证IP核状态寄存器的值是否与预期一致。整个过程无需真实USB主机参与在仿真器里就能把初始化和枚举流程基本跑通。7.2 上板后ILA抓取的典型波形特征判断上板调试时ILA是核心工具。我通常会同时抓这几组信号AXI4-Lite写通道AWVALID/AWREADY/WVALID/WREADY/BVALID/BREADYAXI Stream读数据通道从OUT端点输出的TVALID/TREADY/TDATA/TLAST中断信号irq以及清中断的写操作典型正常波形特征是CPU写入寄存器时AWVALID和WVALID同时有效AWREADY和WREADY在几个周期内拉高BVALID随后拉高一次性完成写响应。Stream输出则是TVALID保持高TREADY周期性拉低FIFO满了或者DMA暂停。如果TVALID和TREADY长时间只有一个为高说明数据流卡住了找到对应的端点FIFO水位寄存器看看是不是满了或者空了。7.3 实测吞吐与延迟数据参考最后给组实测数据作为参考。我在Xilinx Artix-7XC7A35T平台上用这款IP核做了测试PHY芯片是USB3300处理器是软核MicroBlazeAXI时钟100MHz。结论如下批量IN端点FPGA到PC持续吞吐约40MB/sCPU占用率较低主要由DMA搬运。批量OUT端点PC到FPGA持续吞吐约42MB/s略高于IN方向因为IN方向需要额外的握手开销和NAK逻辑OUT方向由主机主导节奏效率更高一些。中断端点传输延迟从FPGA拉高中断到PC收到数据约50微秒这里面既包含了IP核内部处理时间也包含了PC端驱动调度延迟。全速模式下批量传输吞吐约1.1MB/s和理论值接近适用于FPGA里不需要高速传数据的场景比如仅用于调试寄存器。如果你最后跑出来的吞吐和上面差距过大优先检查三件事FIFO深度是否足够、Stream接口背压是否正常、上位机接收缓冲区是否够大。绝大多数性能问题都出在这三个环节而不是IP核本身。7.4 调试过程中容易被忽视的时序收敛问题这是一个鲜有人提但非常重要的点。AXI USB2.0 Device IP内部有跨时钟域逻辑UTMI时钟和AXI时钟不同的情况很常见在实现阶段工具会做CDC约束检查但很多默认约束比较宽松。如果你发现IP核在板级偶尔工作、偶尔不工作认真看一下时序报告里有没有未满足的跨时钟域路径必要时可以给IP核设置false path约束或async_reg属性但改之前先确认是否真的安全。另外这个IP核对复位释放有严格要求如果复位释放时UTMI时钟尚未稳定状态机会进入未定义状态。建议在复位网络里加一个时钟稳定后再释放的延迟至少等上几十微秒再解复位。这种问题在仿真里很难复现但在真实硬件上几乎必现。我用这个IP核做过好几个项目了一路踩坑下来最大的体会是这颗IP核本身的质量很高问题几乎都出在集成者对AXI协议的理解和对USB协议栈的轻视上。如果你打算在自己的设计里引入它先把AXI4-Lite和AXI Stream的握手时序吃透再把端点和中断映射表画清楚然后才是动手写代码。外设芯片手册里的时序图不是给IC工程师看的就是给集成工程师看的别跳过去。
返回列表