FPGA里做网络通信,最常见也最稳的方案之一,就是Altera(现在叫Intel FPGA)的Triple-Speed Ethernet IP核,搞过以太网的工程师习惯直接叫它TSE。这个IP核支持10/100/1000M三速以太网MAC功能,内部集成了MAC、FIFO,还能根据你选的接口类型把MII/GMII/RGMII/SGMII这些物理层时序一并解决。在很多图像采集、高速数据记录、工业控制项目里,用FPGA通过TSE把数据打包成UDP报文发到PC,PC端再用网络调试助手收包,是特别典型的应用套路。
这篇文章我打算完全按实战来写,从TSE IP核该怎么理解、Quartus里参数怎么配、Avalon-ST接口怎么对接,到UDP收发通路怎么用状态机实现、ARP怎么应答、网络调试助手怎么联调,最后把USB-Blaster的代码39、PHY链路起不来、校验和不对这些坑都过一遍。内容主要针对Altera Cyclone系列和Arria系列,用的软件是Quartus Prime/Quartus II 13.0以上版本,适合刚接触TSE IP核但已经有点FPGA基础的人,也适合做网络通信项目的老手拿来当配置清单查。
1. TSE IP核的整体认知与选型思路
1.1 三个速率模式带来的设计红利
TSE这个名字里的“Triple-Speed”,指的是它支持10Mbps、100Mbps、1000Mbps三挡以太网速率。对于很多工业现场和数据采集设备来说,这个范围几乎覆盖了所有常规以太网应用场景:低速传感器数据采集可以跑10M或100M,图像视频这类高带宽数据直接上千兆。
从内部结构看,TSE IP核把以太网MAC层功能全做了,包括帧的封装与解析、CRC32生成与校验、帧间间隔(IFG)、冲突检测(用于半双工)、全双工/半双工自适应等。用户侧不需要关心MAC层怎么把UDP包塞进以太网帧里,只需要面向Avalon-ST接口读写数据即可。这和“自己用Verilog写一个GMII接口的MAC”完全是两个工作量级,后者光是要处理时序收敛、CRC计算、错误帧丢弃这几个点,就够折腾一两个星期,而且很容易在边界条件上翻车。
TSE的另一个隐含红利是:用户逻辑看到的接口速率不会因为MAC速率的变化而改变。也就是说,不管底层是10M、100M还是千兆,Avalon-ST用户接口都是同一个时钟域、同一套握手时序。这意味着应用逻辑可以完全不用关心物理速率切换,只要配置好对应寄存器,或者让TSE自动协商,用户侧设计基本不用改动。这个抽象对项目迭代来说非常友好。
1.2 你该选哪种物理接口
TSE IP核本身只是MAC层(可选配PCS),要和网口通信,必须外接PHY芯片。PHY与TSE之间的接口有几种选择,我直接用一张表说明:
| 接口类型 | 引脚数 | 工作模式 | 适用场景 |
|---|---|---|---|
| MII | 约16根 | 10/100M | 老方案、低成本低速板卡 |
| GMII | 约24根 | 10/100/1000M | 传统千兆方案,引脚占用大 |
| RGMII | 约12根 | 10/100/1000M | 当前最常见的FPGA-PHY连接方式 |
| SGMII | 差分2对 | 10/100/1000M | 高速串行、引脚少、板级走线简单 |
简单说一下选型逻辑。如果PCB面积紧张、器件密度高,RGMII是主流选择,4位数据线双沿采样,125MHz的DDR速率就能跑千兆,对走线要求比GMII低很多。如果对EMI更敏感,或者要跨背板连接,SGMII更合适,本质是串行差分信号,只需要一对发送、一对接收,板级实现非常干净。
不过要注意,RGMII和GMII只是并行的数据接口,而SGMII内部需要PCS/PMA层做8B/10B编解码,TSE IP核在配置界面里会有一个选项决定是否内置SGMII PCS。如果你的设计选择了SGMII接口,TSE会自动生成PCS逻辑,不需要再单独例化一个GXB Transceiver IP。如果你选的是RGMII或者GMII,TSE就只提供MAC侧的信号,外部PHY芯片直接对接即可。
1.3 SGMII对接PHY时的模式配置
这里必须专门讲讲热搜词里反复出现的那个问题:“sgmii ip核与phy芯片一起使用时,应配置成mac模式”。这个描述不准确,但确实踩过的人太多了。
SGMII链路的两端,一端是MAC(也就是TSE的SGMII接口),另一端是PHY芯片的SGMII接口。正常情况下TSE的SGMII这一端就是MAC角色,PHY芯片那一端是PHY角色,两者通过SGMII自协商机制完成速率匹配。这里的关键在于:有些PHY芯片的SGMII口支持配置成MAC模式,这个模式是为了“PHY级联”或者“CPU直连交换芯片”这种特殊场景准备的。如果外部PHY被配置成了MAC模式,再接TSE的SGMII口,两侧角色就会冲突,表现为链路协商失败、link up不了或者数据错误。
所以正确的理解是:TSE的SGMII口始终按MAC端使用,外部PHY芯片应保持默认的PHY模式。自动协商时,TSE会把自己的速度能力广播给PHY,PHY再根据对端网口速度来确定最终速率。很多PHY芯片有专门的寄存器控制SGMII侧的autoneg,需要确保这个功能处于开启状态,否则协商结果可能锁定在1000M,导致10/100M设备接入后异常。
从配置层面看,TSE IP核里如果选择“SGMII with PCS”,内部会自动生成一个SGMII PCS实例,并在顶层对应引脚引出串行差分对(txp/txn/rxp/rxn)和125MHz参考时钟。外部PHY看到的就是一个标准的SGMII MAC接口。如果选了RGMII,就完全不用关心PCS这些内部细节,直接把RGMII引脚引到PHY即可。
2. Quartus工程与TSE IP核配置实操
2.1 建工程前先理清时钟树和复位顺序
TSE配置看起来简单,但时钟和复位处理不当,后面调试非常痛苦。先说时钟。
一个典型的千兆以太网设计,时钟至少涉及这几路:
- 用户侧Avalon-ST接口时钟:通常用125MHz,对应千兆速率下TSE用户时钟;
- MAC/PHY接口时钟:RGMII模式下需要一个125MHz的参考时钟给PHY芯片(或由FPGA输出,或由PHY提供反馈);
- SGMII模式需要125MHz参考时钟给TSE内部的PCS;
- 如果外接PHY芯片,还需要PHY的工作时钟,这个一般由晶振或FPGA提供。
在Quartus工程里,这些时钟需要建立正确的时钟约束,否则时序分析报告会大量报错。实际操作中,我习惯把所有时钟都约束成同一个source,比如用一个125MHz差分晶振到FPGA,由PLL分出用户时钟和接口时钟,尽量减少异步域。TSE内部的Avalon-ST收发FIFO本身就是异步FIFO,可以处理跨时钟域的缓冲问题,但前提是IP配置里把“Use internal FIFO”打开,并且把两边的时钟都正确连线。
复位方面,TSE IP核有独立的复位输入。需要注意:复位释放后,用户逻辑不能立刻开始收发数据,建议等待一段时间,尤其是外部PHY芯片,MDIO配置完成后、Link Up信号拉高后,再开始业务数据发送。否则数据会丢在MAC的FIFO里,收端什么都等不到。最简单的做法是:FPGA上电复位 → 等待PLL锁定 → 释放TSE复位 → 配置MDIO寄存器 → 等待PHY link up → 开始业务。
2.2 TSE IP核配置清单详解
在Quartus IP Catalog里搜索“Triple-Speed Ethernet”,双击打开配置界面。不同版本参数名略有差异,但关键项基本一致。下表是我在Cyclone V和Cyclone 10 GX上验证过的配置建议:
| 配置项 | 推荐值 | 说明 |
|---|---|---|
| Device family | 按实际器件选择 | 同一IP在不同系列上有细微差异 |
| Speed grade | 按实际器件选择 | 影响时序收敛难度 |
| Interface | 根据PHY选MII/GMII/RGMII/SGMII | RGMII最常用,SGMII适合高速和少引脚 |
| MAC mode | 10/100/1000 Mbps | 可选单速、双速、三速,建议三速 |
| Half/Full duplex | Full duplex only,或支持自协商 | 绝大多数场景用全双工 |
| PCS/PMA | 仅SGMII/千兆光口需要 | RGMII接口不要选 |
| Avalon-ST packet transfer | 打勾 | 用户侧按包传输,不用管FIFO细节 |
| Use internal FIFO | 打勾 | 极大简化设计,推荐开启 |
| MDIO interface | 打勾 | 用来配置和管理外部PHY |
| Share MDIO | 可选项 | 多PHY时共用MDIO,需要地址分配 |
| Suppress wakeup packet | 默认 | 省电场景才需要 |
| Enable ECC | 视可靠性要求 | 航空/高可靠场景建议开启 |
重点关注“Use internal FIFO”这个选项。开了之后,TSE会在MAC核与用户逻辑之间自动插入收发FIFO,用户侧的Avalon-ST接口就不需要精细到每个cycle的时序,只需要在FIFO ready时写入、在FIFO valid时读出。对于第一版调试来说,这个选项能帮你屏蔽掉一半以上的接口时序问题。等整个UDP通路调通了,再考虑去掉FIFO做极致流水线的方案。
另外要强调一个容易忽略的地方:IP核配置界面里有一个“Enable legacy SGDMA interface”之类的选项,如果你不是要用DMA描述符方式,不要勾选。很多新手在配置时看到DMA字眼觉得可以“加速”,结果生成的接口变成Avalon-MM memory mapped,跟网上的例程对不上,整个设计推倒重来。第一版老老实实用Avalon-ST FIFO模式,数据通路最简单。
2.3 Avalon-ST接口的信号与时序理解
用户侧与TSE交互的信号主要有这几个:
- clk:用户时钟
- reset_n:复位
- tx_ready:TSE侧发送FIFO可以接收数据
- tx_valid:用户侧发数据有效
- tx_data:数据总线
- tx_sop:包的起始beat
- tx_eop:包的结束beat
- tx_empty:最后一个beat中的无效字节数(当数据总线比实际数据宽时需要)
- tx_error:用户侧主动上报错误包
接收方向对应的就是rx_valid、rx_data、rx_sop、rx_eop、rx_empty、rx_error。整个握手逻辑和Avalon总线一致:发送侧拉高valid,接收侧拉高ready,当valid和ready同时为高时,当前一拍的数据被成功交接。
在UDP传输这种包式通信中,sop和eop是最关键的控制信号。帧起始时sop拉高一个周期,帧结束对应对t'eop拉高,并且eop所在beat的数据在总线上的有效字节数通过empty信号表示。以32位数据总线为例,如果一包UDP数据长度不是4的倍数,最后一拍数据只有1~3个字节有效,empty就会标识剩余无效字节数。写这个逻辑的时候,很容易把empty的值算错,导致接收端拼接出的数据错位,这是我在联调里见过最多的问题之一。
有一个新手很容易踩的坑:发送时valid不能有毛刺,否则TSE会解析出一个错误的包边界。实战中最好把要发送的一整包数据先在用户侧RAM或FIFO里缓存好,再统一按顺序推给TSE,确保sop/eop之间的数据是连续无气泡的,避免在中间出现valid拉低又拉高的过程,造成接收端丢包。
3. 自研一个最小UDP协议栈
3.1 MAC帧、IP头、UDP头的字段对照
UDP over Ethernet的本质,就是在以太网帧里依次封装IP报文和UDP报文。FPGA这一端要做的就是在发送时把各层头部按顺序填好,接收时按顺序解析校验。我把每一层的字段和偏移整理成下面这组表,开发时直接对照抄就行。
以太网帧头(14字节):
| 偏移 | 长度 | 字段 | 内容 |
|---|---|---|---|
| 0 | 6 | 目的MAC | 对端物理地址 |
| 6 | 6 | 源MAC | 本端物理地址 |
| 12 | 2 | 以太网类型 | 0x0800表示IP,0x0806表示ARP |
IP头(20字节):
| 偏移 | 长度 | 字段 | 示例值 |
|---|---|---|---|
| 0 | 1 | 版本+首部长度 | 0x45(IPv4,20字节头) |
| 1 | 1 | 服务类型 | 0x00 |
| 2 | 2 | IP总长度 | IP头+UDP头+数据 |
| 4 | 2 | 标识 | 每次发送自增 |
| 6 | 2 | 标志+片偏移 | 0x0000(不分片) |
| 8 | 1 | TTL | 64 |
| 9 | 1 | 协议 | 17(UDP) |
| 10 | 2 | 首部校验和 | 对IP头计算 |
| 12 | 4 | 源IP | 如192.168.1.10 |
| 16 | 4 | 目的IP | 如192.168.1.100 |
UDP头(8字节):
| 偏移 | 长度 | 字段 | 内容 |
|---|---|---|---|
| 0 | 2 | 源端口 | 自定义,如5000 |
| 2 | 2 | 目的端口 | 自定义,如6000 |
| 4 | 2 | UDP长度 | UDP头+数据 |
| 6 | 2 | 校验和 | 可选,但建议计算 |
以太网帧结构的最后一个部分是FCS(CRC32),这个完全由TSE的MAC硬件负责生成和校验,用户逻辑不用写。但正因为FCS由硬件算,如果你在用户侧把帧长弄错了(比如短于64字节),MAC可能会自动填充,导致PC端收到的报文长度和你期望不一致。做协议解析时要把这些考虑在内。
3.2 发送通路状态机
FPGA端发送UDP包,整体流程可以分成四段:先把应用数据写入发送缓存;然后计算IP头和UDP头;接着按“以太网头 + IP头 + UDP头 + 数据”的顺序拼包;最后通过Avalon-ST发送给TSE。
状态机可以参考下面的跳转:
- IDLE:等待用户启动发送命令,命令中携带目标IP、目标端口、数据长度、数据在RAM中的起始地址。
- MAC_HDR:发送14字节以太网头。目的MAC可以是固定值(先通过ARP学到),也可以是ARP表里查到的值。
- IP_HDR:发送20字节IP头。总长度字段 = 20 + 8 + data_len,校验和需要预先算好。
- UDP_HDR:发送8字节UDP头。长度字段 = 8 + data_len。
- DATA:从RAM按4字节(或数据总线位宽)切片,逐拍发送。
- WAIT_END:拉高eop,结束本包。
开发时建议先把“伪校验和”的概念搞清楚。UDP校验和的计算范围不仅包含UDP头和数据,还包含一个12字节的伪首部(源IP、目的IP、保留0、协议17、UDP长度)。伪首部本身不发送,只参与校验和计算。算法是把所有16bit字累加,进位回卷,最后取反。如果数据长度不是2的倍数,末尾补一个0字节参与计算。这个计算可以在发送数据的同时做,可以先算出结果再发起发送。
一个实用的技巧:如果调试初期不想算UDP校验和,可以把UDP校验和字段填成0x0000。RFC规定发送方为0表示不校验,接收方可以忽略。对PC上的网络调试助手和Wireshark来说,UDP校验和为0会有一个提示,但基本不影响收包。这样可以把状态机调通后再补上校验和逻辑,分步推进,少踩很多坑。
3.3 接收通路状态机
接收方向,TSE检测到有效以太网帧后,通过Avalon-ST把整个帧(从目的MAC开始一直到数据末尾)送给用户逻辑。用户逻辑需要做的第一件事就是缓存整包或者至少缓存头部,以便解析用途。
接收状态机的典型流程:
- 等SOP:接收帧头前14字节,判断以太网类型是否0x0800,不是IP帧直接丢弃(或者送到其他处理分支)。
- 解析IP头:继续接收20字节,检查版本号,校验首部校验和,确认协议是17(UDP),记录源IP、目的IP。
- 解析UDP头:继续接收8字节,检查目的端口是不是FPGA业务端口,不是则丢弃。
- 接收数据:按数据帧长度把有效载荷写入接收RAM,生成接收完成中断或标志。
这里有一个容易忽略的点:TSE接收FIFO输出的rx_error信号,会在MAC层检测到CRC错误、短帧、长度错误时拉高,并且会对应当前包的某个位置。用户逻辑必须在eop之后检查rx_error,如果这个包出错,直接丢弃,不要写入RAM。即使UDP头都解析正确了,只要CRC不过,整个包都不可信。
我个人建议在接收通路里做一个简单的FIFO缓存,至少保留一个完整包的数据,等解析通过后再把整包交给上层逻辑处理。这样能避免边收边处理导致的半包状态,也方便添加过滤规则。
3.4 ARP应答和Ping
如果没有ARP,上面这套UDP收发只能工作在“目标MAC已经预先填好”的情况下。一旦PC端IP和FPGA IP在不同网段,或者PC的ARP表里没有FPGA的MAC,PC发UDP就会报“无法访问目标主机”。所以一个实用的UDP通信方案,必须实现ARP应答。
FPGA端收到ARP请求后,解析出源MAC和源IP,如果目的IP是本机IP,就把源MAC换成自己的MAC,填入应答报文,发送给请求方。ARP请求是以太网广播帧,目的MAC是FF:FF:FF:FF:FF:FF,类型0x0806,操作码1。应答帧是单播帧,目的MAC就是请求方的MAC,操作码2。
Ping功能的ICMP回显请求,处理逻辑类似:收到ICMP类型8的请求,把类型改成0(回显应答),重新计算ICMP校验和,然后原路发回。虽然不是UDP通信的必需功能,但强烈建议实现。联调的时候,如果FPGA能ping通PC,说明链路、MAC、PHY、ARP这些底层都没问题,再把问题聚焦到UDP协议上层;如果ping都不通,就不用浪费时间去抓应用层问题了。
4. 网络调试助手联调与验证
4.1 先营造一个干净的网络环境
在把FPGA接入复杂网络前,建议先用一根网线直连FPGA板和PC,不要经过交换机、路由器。直连可以排除交换机端口协商、VLAN、防火墙等干扰因素。
PC端需要做几个固定步骤:
- 给有线网卡设置一个静态IP,比如192.168.1.100,子网掩码255.255.255.0;
- 关闭防火墙,或者至少允许UDP端口通过;
- 确认网卡没有启用“随机硬件地址”这类影响MAC地址的选项;
- 在命令行里用ipconfig确认网卡的IP确实生效。
FPGA端建议使用与PC同网段的IP,比如192.168.1.10。这样ARP和UDP都在二层和三层直接可达,不需要网关参与。
如果条件允许,可以先不用FPGA,直接用两台PC通过网线直连,在网络调试助手里做一次UDP收发测试。这个步骤看起来多余,但实测下来非常有价值:它能确认网线OK、PC网卡OK、调试助手配置OK,把环境里的变量全部洗干净,剩下的问题就只可能出在FPGA这一侧。
4.2 网络调试助手的基本操作
网络调试助手的版本很多,基本是同一个操作套路。以常见的NetAssist为例:
- 协议类型选择UDP;
- 本地IP选择PC网卡的IP(192.168.1.100);
- 本地端口填入一个空闲端口,比如9000;
- 远程IP填入FPGA的IP(192.168.1.10);
- 远程端口填入FPGA业务监听的端口,比如6000。
配置完成后打开连接,在发送区输入数据,点发送。如果FPGA端接收正确并返回了数据,接收区就会显示回来的报文。要注意,网络调试助手的“接收区”通常只显示文本或HEX,如果你发的是一串字符串,它不会自动识别UDP包的边界,不会显示以太网头这些信息。要精细地看包结构,还是需要Wireshark。
联调时建议先在发送区发一条固定内容,比如“55 AA 00 FF”这种特征明显的HEX,然后在FPGA的Signal Tap或逻辑分析仪里观察对应的数据是否到达。逐级确认,一步一步缩小问题范围。
4.3 抓包辅助定位问题
Wireshark在FPGA联调中扮演的角色,相当于“以太网协议分析仪”。把PC网卡设置为混杂模式,过滤条件打上udp or arp,就能看到PC和FPGA之间的所有报文。
抓包能快速回答几个问题:
- ARP请求发出去没有?FPGA有没有回ARP应答?
- FPGA发的UDP包格式对不对?IP头、UDP头偏移是否正确?
- 校验和字段是否正确?如果Wireshark标记“checksum incorrect”,说明FPGA算的校验和有问题,或者PC网卡硬件卸载计算导致的假阳性。
- 数据载荷是否符合预期?可以在载荷区看到FPGA发送的原始数据。
我有个习惯:联调阶段PC端始终开着Wireshark抓包。不是说每帧都要看,而是遇到问题时有第一手现场数据,不用猜。“猜”是调试以太网最大敌人,抓包能让你从“猜”切换到“查”模式。
5. 常见问题与排障速查
5.1 USB-Blaster驱动代码39
这个热搜关键词出现得非常多,说明很多人卡在了下载器这一关。现象是:USB-Blaster插入电脑后,设备管理器显示黄色感叹号,属性里报“Windows无法加载这个设备的驱动程序,错误代码39”。
代码39的常见原因包括:驱动签名问题、USB控制器供电不足、第三方USB-Blaster兼容性问题。处理顺序建议是:
- 换一个USB端口,优先插主机背板直出的USB口,不要插前置面板或HUB;
- 在设备管理器里卸载这个设备,勾选“删除驱动程序软件”,然后重新扫描硬件;
- 以管理员身份运行Quartus安装目录下的驱动安装工具,重新安装驱动;
- 如果系统是Win10/Win11,尝试禁用驱动程序强制签名后重装驱动;
- 换一根USB线。是的,USB线也可能出问题,有些劣质线只支持充电不支持数据;
- 最后要考虑是不是兼容版USB-Blaster的问题。虽然兼容版性价比高,但驱动和抗干扰能力确实不如原厂,长时间调试时可以先检查这个环节。
这里提醒一下,USB-Blaster驱动问题往往是环境问题,不是FPGA工程问题。不要在一个环境问题上耗太久,如果以上方法都不行,换一台电脑装Quartus再试,很多场景下立刻就好转了。
5.2 PHY链路起不来怎么办
PC网卡显示“网络电缆被拔出”,说明物理层没有link up。检查思路从底层开始:
- 测量PHY芯片电源、时钟、复位,确认硬件正常;
- 用MDIO读取PHY的寄存器,比如88E1512的0x01寄存器状态、0x11寄存器速度指示,看PHY本身是否检测到对端;
- 确认MDIO通信正常。如果读出来全是0xFFFF,说明MDIO时序有问题或PHY地址不对;
- SGMII模式下,确认TSE的PCS是否完成自协商,可通过IP核提供的status信号观察;
- RGMII模式下,重点检查TSE和PHY之间的时钟极性。RGMII标准规定数据在时钟的上升沿和下降沿都采样,部分PHY支持通过寄存器调整时钟相位偏移。
联调前我通常会在FPGA里写一个简单的MDIO读写模块,把PHY的ID寄存器读出来。能读到正确的PHY ID,说明管理通道通,物理链路才有继续调的基础;读不到,后面所有调试点都无从谈起。
5.3 能收到包但校验和错误
这个现象在PC收FPGA发来的UDP包时比较常见。Wireshark提示IP校验和或UDP校验和错误,但网络调试助手依然能收到数据,很容易让人误判为“没问题”。实际上,很多网卡驱动对UDP校验和并不做严格校验,错误包也会往上送,但某些应用层或者交换机可能会丢弃这种包。
校验和错误一般来自两个地方:
- IP首部校验和计算错误。注意IP头的计算范围只覆盖IP头本身,不包含UDP头和数据;
- UDP校验和计算错误。要检查伪首部和补零逻辑,数据长度是奇数时末尾补0参与计算。
我踩过最多次的坑是IP总长度字段和UDP长度字段没同步更新。比如数据长度改了,但IP头里的总长度还是旧值,IP校验和也按旧值算,Wireshark就会同时报IP长度异常和校验和错误。写代码时建议把长度计算集中到一个功能函数里,所有头部字段都从这个函数的结果来生成,避免多处修改导致不一致。
5.4 数据错位、乱序、丢数据
数据链路已经通了,但PC端收到的数据内容对不上。这类问题大概率出在Avalon-ST的sop/eop和empty处理上。
先说错位。如果接收端按字节拼接数据时,没有正确使用empty信号,最后一拍会把无效字节也拼进来,导致后续数据整体偏移。这种情况通常表现为:第一包数据末尾多出几个字节,第二包开始所有字段都错位。
再说乱序。TSE内部有收发FIFO,如果发送端在FIFO还没ready时强行写入,或者接收端没有及时读取导致FIFO溢出,都可能丢包或乱序。解决办法是先确认每拍数据都是在valid和ready同时有效时写入的,不要漏掉握手的“同时”条件。
丢数据还有一个常见原因是发送端没有等上一个包的eop发完就启动下一包。Avalon-ST要求一个完整包必须连续发送,包与包之间可以间隔,但同一个包内不能有非法的“断流”。如果业务逻辑是不断流水的,建议在发送状态机里增加“发送完成后返回IDLE,再检查是否有新包”的严格流程。
5.5 在线调试的Signal Tap触发设置
Signal Tap(在较新版本中叫In-System Debugger)是定位FPGA内部问题的利器。但很多初学者抓不到有效波形,原因是触发条件设置不对。
对于TSE调试,建议这样设置:
- 把tx_ready、tx_valid、tx_sop、tx_eop、tx_data加入采集列表;
- 触发条件设为tx_valid == 1 && tx_sop == 1,这样可以从一个包的起始位置开始抓;
- 采样深度根据包长的需求设置,通常4K~16K样本足够;
- 如果逻辑里没有输出sop这些信号到观察点,可以在TSE输出端把它们引到顶层测试引脚,或者直接在内部逻辑里加ILA(Intel在较新工具里也支持类似调试IP)。
Signal Tap里我最常查的信号是ready和valid是不是都拉高,以及sop和eop之间的数据节拍数是不是和预期帧长一致。很多时候问题一眼就能看出来:sop之后数据断了一个cycle,或者包长多了一拍,马上就能定位到状态机问题。
5.6 善用IP核自带的Example Design
TSE IP核在生成时,Quartus会附带一个example design工程,里面包含了完整的PHY配置、时钟管理、复位逻辑和测试发送模块。很多问题其实不用自己从头折腾,参考example design能省下大量时间。
具体操作路径:在TSE IP核生成界面点击“Generate Example Design”,Quartus会生成一个独立的工程。可以先用这个工程在目标板上跑起来,配合Signal Tap看TSE的输出是否符合预期。如果example design能跑通,再逐步把example里的PHY配置和复位逻辑迁移到自己的设计里。我见过不少工程师遇到问题不查example,自己硬写MDIO配置,结果PHY模式配置错了半天都定位不出来。
最后从我个人经验出发再补一条:TSE调通UDP通路,最怕“一次集成到位”。正确节奏是先把example design跑起来,再实现ARP应答,让PC能ping通FPGA,最后才加入UDP收发和业务数据。每走一步,都确认一步的返回结果。调UDP通信这种事,底层链路不干净,上层协议怎么调都白搭。按照这个顺序来,绝大多数板卡都能在一天内把UDP通路跑起来,剩下的时间都花在优化业务逻辑上,而不是跟IP核死磕。