做FPGA以太网通信的兄弟应该都有这种经历:板子画好了,PHY芯片焊上去了,RJ45座子也亮了灯,结果跑UDP的时候要么PC端收不到包,要么收到的全是CRC错误,要么Debug一抓发现数据压根没从IP核里出来。我最开始调ALTERA Triple-Speed Ethernet IP核的时候,光一个SGMII接口和外部PHY芯片的搭配问题就折腾了快两周。后来把TSE的配置逻辑和PHY芯片的分工彻底理清楚之后,才发现大部分问题都出在接口模式选错、寄存器没配齐、复位时序不对这几类原因上。这篇文章不聊那些官方手册里已经写得很清楚的基础概念,直接把我实际项目中配置TSE IP核跑UDP通信的经验拆开讲,包括IP核参数怎么选、MAC之上的UDP协议栈怎么搭、SGMII和PHY芯片配合时为什么必须配成MAC模式,以及上板验证时SignalTap、Wireshark、iperf3这一套组合拳怎么打。适合准备在自家板子上用Altera/Intel FPGA做以太网通信的硬件工程师,也适合刚接手TSE IP核相关项目的软件工程师参考。
1. 为什么UDP通信选TSE IP核,而不是自研MAC或买软核
1.1 先算笔时间账:自研MAC到底值不值
很多人一上来就说"以太网MAC不复杂,不就是把MII接口的数据包一下发出去吗",这话只对了一半。如果你只是想让两个FPGA之间点对点传数据,不经过交换机、不接电脑、不用考虑兼容性,那自己用状态机写个简化MAC确实可行,大概几百行Verilog就能搞定。但只要你的板子要接到标准以太网环境里,要跟PC、路由器、交换机互通,事情就完全不一样了。
一个能用的千兆以太网MAC要处理的东西包括:前导码和SFD的生成与检测、帧间隙IFG管理、CRC32计算与校验、冲突退避(半双工时需要)、流控帧的发送与响应、巨型帧支持、VLAN标签处理、MDIO管理接口、各种错误帧的识别和统计。把这些全部靠自己写一遍、调通、再通过严苛的兼容性测试,以我个人的经验,一个熟练的RTL工程师至少需要三到四个月,而且这还没算上后续维护的成本。相比之下,TSE IP核把这些功能全部封装好了,你要做的是理解它的接口和时序,而不是从零开始造轮子。
1.2 TSE IP核能干什么、不能干什么
ALTERA Triple-Speed Ethernet IP核,全称很直白:三速以太网MAC IP核,支持10Mbps、100Mbps、1000Mbps三种速率。它本质上是一个MAC层控制器,主要干这几件事:
- 以太网帧的封装和解封装,包括前导码、帧起始符、CRC校验等
- 提供标准的Avalon-ST接口给用户逻辑,收发数据都走这个接口
- 支持GMII、RGMII、SGMII、MII等多种PHY侧接口
- 内部可选集成收发FIFO,缓冲数据突发
- 集成MDIO控制器,可以通过MDIO接口读写外部PHY芯片的内部寄存器
- 支持统计计数器,可以统计收发包数量、CRC错误数量、冲突次数等
但它不负责的事也很清楚:它不帮你实现IP层和UDP层的协议解析,不维护ARP缓存表,不替你处理IP分片重组,更不会自动帮你把数据塞进UDP负载里。这些都是用户逻辑层要做的事。很多第一次用TSE的朋友以为配完IP核就可以直接UDP通信了,这是个常见的误解,协议栈的活儿得自己干。
1.3 三种典型使用形态,提前想好你要哪种
TSE IP核在实际项目中有三种常见用法,配置方式完全不同:
第一种,TSE当作纯MAC使用,通过GMII/RGMII接口连接外部PHY芯片,PHY芯片负责PCS/PMA层和物理层信号发送。这种用法最经典,IP核和PHY的分工边界很清晰,调试时出了什么问题也容易定位。很多开发板上的FPGA千兆以太网就是这种架构。
第二种,TSE开启内部PCS,通过SGMII接口连接外部PHY芯片。这种用法下,TSE不仅包含MAC层,还集成了SGMII的PCS层,负责8B/10B编解码和SGMII自动协商,外部PHY芯片则负责物理层信号调制。这里就出现了一个关键选择:TSE内部PCS做SGMII编解码,还是让外部PHY芯片完成SGMII编解码、TSE只当纯MAC用。
第三种,TSE配合Nios II或软核处理器使用,把IP核挂在Avalon-MM总线上,由处理器运行TCP/IP协议栈,FPGA逻辑负责加速数据通路。这种方式适合复杂业务场景,比如需要同时处理多路UDP连接,或者还要跑TCP协议。
我在下面的章节里主要讲前两种形态,因为这是UDP通信实战中最常见的搭配,也是配置上最容易踩坑的地方。
2. 配置TSE IP核的关键选项:按生产项目标准来
2.1 在Quartus里新建TSE IP核时需要确定的核心参数
无论是Quartus Prime还是老版本的Quartus II,创建TSE IP核的界面都差不多。在IP Catalog里搜索Triple-Speed Ethernet,双击进入参数配置界面。这里有几个关键选项,每一个都直接影响后面的硬件设计和使用方式,建议在动手之前就确定好。
| 配置项 | 可选值 | 我的推荐和理由 |
|---|---|---|
| Speed | 10/100/1000 Mbps | 一般选三速Triple Speed,因为IP核会自动根据PHY协商结果切换速率,适用性更广 |
| MAC Mode | MAC / 软MAC | 配外部PHY时选MAC Mode,软MAC还需要额外购买许可,一般不选 |
| PHY Interface | GMII / RGMII / SGMII / MII | 根据板级设计决定。引脚紧张的板子选SGMII,要求时序简单选GMII |
| Data Path Width | 8-bit / 32-bit | GMII用8-bit(125MHz时钟),SGMII内部PCS模式用32-bit(时钟较低) |
| Internal FIFO | 使能或关闭,可设深度 | 强烈建议使能,收发包深度至少4KB,否则大包容易被丢弃 |
| MDIO | 使能 | 几乎必选,没有MDIO你没法读PHY芯片的状态寄存器,没法排查链路问题 |
| Statistics Counters | 使能 | 调试阶段很有用,可以统计CRC错误、帧数量,量产时再关掉省资源 |
| CRC Checking | 使能 | MAC层硬件做CRC校验,省去用户逻辑的麻烦 |
有一个细节容易被忽略:数据通路宽度。如果你选GMII接口,TSE的数据位宽在千兆模式下是8位,用户侧Avalon-ST接口的数据宽度通常也可以单独配置,有8位、16位、32位可选。我一般选32位,因为用户侧逻辑处理32位数据时时钟频率可以降到31.25MHz(千兆时),对后端时序压力小很多。如果你选了8位,千兆模式下用户侧也是125MHz,时序约束更紧。
2.2 接口时序:GMII、RGMII、SGMII各有各的脾气
TSE IP核对外接口的选择,直接决定了你FPGA和PHY芯片之间的连线难度和调试成本。
GMII接口是标准并行千兆接口,有8根发送数据线、8根接收数据线,加上TX_CLK、RX_CLK、TX_EN、TX_ER、RX_DV、RX_ER等控制信号,总共要占用二十多根FPGA引脚。优点是每个信号单独一根线,每个周期的含义非常直接,逻辑分析仪抓波形的时候一目了然。缺点是引脚占用多,PCB布线麻烦,千兆模式下时钟是125MHz,信号完整性要求高。
RGMII接口是Reduced GMII,把数据线从8根减到4根,用DDR双沿采样来补齐带宽。千兆模式下时钟还是125MHz,但数据在时钟上升沿和下降沿各采4位,组合起来就是8位数据。RGMII的时序在PCB设计和FPGA约束里比较敏感,IDELAY、ODELAY这些延迟调整手段经常要用到。如果你用的是带RGMII接口的PHY芯片,比如瑞昱RTL8211系列,那TSE这边选RGMII接口就行。
SGMII接口是串行千兆接口,发送和接收各一对差分信号,总共才4根线。它需要在物理层做8B/10B编解码,所以接口两侧必须有一方承担PCS功能。如果TSE内部不带PCS,那就要选一个自带SGMII PCS功能的PHY芯片来做这个事。TSE通过SGMII接口跟PHY芯片对接,这是很多中高端板卡采用的方案。
2.3 Avalon-ST收发接口的握手逻辑,不搞懂根本写不了逻辑
TSE IP核给用户侧的接口叫Avalon-ST,这是一种简单的流式接口,核心就是Valid/Ready握手。发送方向上,用户逻辑作为master,TSE作为slave,主要信号有:
tx_ready:TSE告诉用户逻辑"我准备好接收数据了",高电平有效tx_valid:用户逻辑告诉TSE"当前时钟周期上的数据是有效的"tx_data[]:并行数据总线tx_sop:Start of Packet,一个帧的第一个周期拉高tx_eop:End of Packet,一个帧的最后一个周期拉高tx_error:用户逻辑通知TSE本帧出错,TSE会主动发出错误帧
发送数据的前提是tx_ready和tx_valid同时为高,这个握手是双向的,两者缺一不可。我在第一次写发送逻辑时想省事,不管tx_ready的状态直接往FIFO里灌数据,结果就是数据没发出去,查了半天才发现tx_ready因为TSE内部FIFO快满了已经被拉低。
接收方向类似,TSE作为master把收到的帧推给用户逻辑,rx_valid、rx_data[]、rx_sop、rx_eop、rx_error这些信号由TSE驱动。用户逻辑在rx_valid为高时采样数据就行。
这里还有一个小细节:如果使能了TSE的内部FIFO,那么tx_ready和rx_valid的时序会受到FIFO水位的影响,IP核不会一接收到数据就立刻往上抛,而是会缓存一定数量。所以调试时看到数据有延迟别慌,多半是FIFO在起作用。
2.4 MDIO管理接口:链路状态的体检窗口
MDIO接口是MAC和PHY芯片之间的管理通道,由两根线组成:MDC(管理时钟)和MDIO(管理数据)。TSE内部的MDIO控制器通过MDIO接口读写外部PHY芯片的寄存器,常见的寄存器地址空间是0到31,每个寄存器16位。
在实际调试中,我在初始化代码里通常按这样的顺序读PHY寄存器:
- 读寄存器0(控制寄存器),确认PHY的复位状态和操作模式
- 读寄存器1(状态寄存器),确认链接是否建立、速率和双工模式是否协商成功
- 读寄存器5(协商能力寄存器),确认PHY支持哪些速率模式
- 读寄存器17或18(厂商自定义寄存器),读取具体PHY芯片的链路状态和信号质量
这些读取操作在TSE的Avalon-MM管理接口或者用户逻辑里都可以完成。如果你用的是Nios II软核,通过Avalon-MM总线读这些寄存器很方便;如果纯RTL工程,自己写一个简单的MDIO读状态机也不复杂,就是按照时序要求拉MDC和MDIO信号就行。
3. 在MAC之上跑UDP:最小可行协议栈的搭建思路
3.1 一帧UDP数据在网线上到底长什么样
TSE帮你处理了MAC层,但它的用户接口里出来的是不含前导码、SFD和FCS的完整以太网帧。你要自己构造和解析的,是这四层东西:
- 目的MAC地址(6字节)
- 源MAC地址(6字节)
- EtherType字段(2字节),IPv4对应0x0800,ARP对应0x0806
- 净荷部分(IP包,包括IP头、UDP头、UDP负载)
这一整块数据就组成了一个以太网帧的数据区,长度从46字节到1500字节不等。够不够46字节很关键,因为以太网规定帧长最小64字节(不含前导码),减去14字节以太网头,再减去4字节FCS(TSE硬件会加),你的数据区必须撑到至少46字节。如果你发一个只有1字节UDP负载的包,IP头20字节加UDP头8字节加1字节负载,一共才29字节,还差17字节,那就要用填充字段补齐到46字节。
对应到UDP包内部,IP头中有一个总长度字段,UDP头中也有一个长度字段,这两处长度的含义不一样。IP总长度包含IP头本身,UDP长度包含UDP头本身。填错一个字段,Wireshark里抓包就会提示长度不符。
3.2 发送通路:一个简单的状态机就能搞定
在TSE的用户侧发UDP包,我的实现思路是这样:
第一步,确定目的MAC地址。如果给PC发,你要填PC网卡的MAC地址,这个可以在PC上用命令行工具查出来。如果目标MAC在设备侧是动态变化的,那就要靠ARP协议去获取,下面会讲到。
第二步,按字节序组装以太网头。这里有个大头问题:以太网协议里多字节字段都是大端序(Big-Endian),也就是高字节在前。在Verilog里你用{8'h08, 8'h00}来表示EtherType值0x0800,这个顺序一定不能反,否则Wireshark会显示成未知协议。
第三步,组装IP头。版本号4位,头部长度4位,一个字节搞定,值为0x45。服务类型字段一般填0,总长度字段要算准,IP头20字节加UDP头8字节加UDP负载长度。标识字段随便填,一般递增就行。标志位和片偏移字段填0,除非你处理分片。TTL字段填64,协议字段填17(UDP),头部校验和要计算整个IP头的16位累加和取反。
第四步,组装UDP头。源端口、目的端口各2字节,UDP长度字段是UDP头加负载的总长,UDP校验和字段在IPv4下可以填0,有些场景要求必须算,看你的应用场景。PC上一般能容忍校验和为0的UDP包,但如果对接的是某些严格协议的设备,最好还是算一下。
第五步,发数据。把整个帧通过Avalon-ST接口灌给TSE,注意tx_sop在帧的第一个周期拉高,tx_eop在最后一个周期拉高。如果帧长不足46字节,记得在负载后面补0填充,让它满足最小帧长要求。
3.3 接收通路:地址过滤和端口识别是核心
接收方向相对简单,TSE已经帮你做了CRC校验和前导码剥离,从用户接口上来的数据就是完整的以太网帧。你需要在用户逻辑里做三件事:
第一,过滤MAC地址。判断目的MAC是不是本机的MAC地址,不是就整个丢掉。或者看是不是广播地址(FF:FF:FF:FF:FF:FF),广播包需要特殊处理。
第二,判断EtherType。如果是0x0800,继续解析IP头;如果是0x0806,交给ARP处理逻辑;其他类型一概不关心。
第三,IP层和UDP层解析。判断IP头中的协议字段是不是17(UDP),然后看UDP目的端口是不是你需要监听的端口,匹配成功之后把UDP负载里的数据提取出来。
这三步在逻辑上就是一个简单的状态机,根据偏移量在不同阶段提取相应的字段。要注意的是,TSE给的数据是连续的,从rx_sop开始到rx_eop结束,你可以在一个帧内维持一个字节计数器,根据计数器的值判断当前处于帧头的哪一段。
3.4 ARP应答:让PC端先找到你
如果你只是单方面往一个静态MAC地址发UDP数据,不关心别人能不能主动找到你,那ARP可以不做。但绝大多数场景下,你需要让PC端ping通或发UDP包到FPGA来测试,那就必须支持ARP应答。
PC端在发送UDP数据之前,会先查自己的ARP缓存表,看目标IP地址对应的MAC地址是什么。如果没有,就发一个ARP广播包,问"谁是这个IP地址?请告诉你的MAC地址"。如果FPGA不回答,PC根本不会把数据发出来,抓包只能看到PC一直在重复发ARP请求。
实现ARP应答其实很简单:收到一个目的MAC是全F的广播包,EtherType是0x0806,把包里的ARP字段解析出来。如果操作码是1(请求),目标IP等于本机IP,就把源MAC和目的MAC对调,操作码改成2(响应),填入自己的MAC地址,再加上发送方的IP信息,把这一帧发回去就行。
很多刚开始做的人容易忽略一点:发送ARP应答时,目的MAC要填ARP请求里的发送方MAC,而不是广播地址。填成广播地址虽然PC也可能认,但不规范,在某些严格的网络环境会出问题。
4. SGMII连接PHY芯片:为什么TSE必须配置成MAC模式
4.1 用SGMII的动机很明确:省引脚
如果你的板子上FPGA引脚资源不宽裕,或者PCB层数不够、布线空间紧张,那SGMII这种串行接口就非常有吸引力。一对发送差分线加一对接收差分线,一共才4根信号线,比GMII的二十多根线和RGMII的十几根线省太多了。
但省引脚是有代价的,SGMII本身只是一条串行通道,它不决定以太网的速率和物理层编码方式。SGMII链路上跑的是8B/10B编码后的数据流,这个编码过程必须由某一端的PCS(Physical Coding Sublayer)层来完成:要么是FPGA侧的TSE IP核内部PCS做,要么是外部PHY芯片内部的PCS做,二选一。
4.2 TSE当纯MAC、PHY负责PCS:责任边界要理清
网上一搜就能看到这样的经验之谈:SGMII IP核与PHY芯片一起使用时,应配置成MAC模式。这句话的实质是,SGMII接口两侧必须有一个PCS实体来负责8B/10B编码,而大多数千兆PHY芯片,比如88E1111、RTL8211E、VSC8244这些,内部都已经集成了PCS模块,并且具备SGMII接口。也就是说,用外部PHY芯片时,PCS这层活儿PHY自己就能干,FPGA侧的TSE只需要作为纯MAC角色把用户数据转成符合MAC层格式的比特流交给PHY就行。
在Quartus的TSE配置界面里,当你选择SGMII接口时,有一个选项叫"内部PCS/外部PCS"。如果你的板子上有一颗独立的PHY芯片,而PHY芯片本身自带SGMII PCS功能,那么你就应该选择外部PCS,让TSE以MAC模式运行。如果你非要反过来,让TSE的内部PCS和外部PHY的PCS都参与,两个PCS同时在链路上做8B/10B编码,那数据根本对不上,链路也起不来。
所以,判断标准很简单:只要外部PHY芯片是完成品,自带PCS和SGMII接口,TSE就老老实实配成MAC模式,把8B/10B编码和物理层发送全部交给PHY,TSE只管以太网MAC层协议。
4.3 在Quartus中选"外部PCS"时要注意的注意事项
选择外部PCS之后,实际工程里还有几个容易踩的细节,我在项目里都遇到过。
第一个是TSE的TBI接口问题。当TSE处于MAC模式且使用外部PCS时,TSE内部对外暴露出的接口有时叫TBI(Ten-Bit Interface)或类似的并行10比特接口。你可以理解成TSE这个MAC一侧是10位并行数据,然后由外部PHY把它转成SGMII串行比特流。这和SGMII IP核自带PCS的用法是有差异的。好在Quartus在配置界面里会自动根据你的选择生成合适的对外引脚,你只需要确保在顶层文件里把TSE的SGMII输出引脚接到PHY芯片对应引脚上就行。
第二个是TSE的时钟输入问题。SGMII模式下,TSE需要参考时钟,通常是125MHz。这个时钟源可以来自板载晶振,也可以由PHY芯片提供。调试时需要确认它的频率和相位稳定性,尤其是用内部PLL产生的时钟时,最好用示波器量一下实际频率,别只看配置界面里的数值。
第三个是PHY芯片的SGMII模式和普通千兆模式的配置差别。有些PHY芯片通过引脚strap电阻或寄存器来选择工作模式,如果你没把PHY配置成SGMII模式,它可能默认工作在RGMII或GMII模式,这样FPGA侧发过来的SGMII信号它根本不认。所以硬件设计时就要把PHY的模式引脚用正确的电平拉好,或者上电后通过MDIO写寄存器切到SGMII模式。
4.4 链路建立时序与复位设计
SGMII模式下,链路建立的过程是这样的:
上电 -> PHY芯片上电复位 -> PHY内部初始化 -> FPGA侧TSE复位释放 -> SGMII链路自动协商(或强制速率) -> 链路建立 -> MAC和PHY数据通路就绪。
这个顺序里,最容易出问题的是两侧复位的配合。如果FPGA已经释放复位并且在发数据了,PHY芯片还没初始化完,那这段时间的数据全都白发了。
我在项目中总结的复位顺序是:
- 先给PHY芯片上电,保持FPGA侧复位至少10ms以上
- PHY芯片初始化完成后,释放FPGA侧的硬件复位
- 然后通过MDIO读写PHY寄存器,确认PHY处于正常模式
- 检查PHY寄存器0x11或厂商自定义寄存器中的Link Status位,确认链路已经建立
- 确认无误后再启动用户逻辑的数据收发
有个更省心的方法是:在FPGA初始化代码里把TSE的管理接口寄存器中的复位位做一次软复位,然后再通过MDIO轮询PHY的链路状态,直到Link Up之后再开始数据收发。这样就算上电顺序不太理想,也能在逻辑层兜底。
5. 上板验证:从SignalTap到Wireshark再到iperf3的组合拳
5.1 先用SignalTap II抓一遍内部握手信号
上板之后第一件事,先别急着用PC端工具发包,先把FPGA内部的关键信号在SignalTap II逻辑分析仪里抓出来看一遍。Altera/Intel FPGA对应的调试工具叫SignalTap II,功能跟Xilinx的ILA IP核类似。
我在调试TSE时通常观察这几组信号:
- TSE用户侧的
tx_ready和tx_valid,确认发送通路的握手是否正常 - 用户逻辑里的状态机当前状态,确认它有没有卡在某个状态
tx_data的前几个字节,确认目的MAC、EtherType这些字段填得对不对- 复位信号和时钟使能信号
一个很常见的调试发现是,发送逻辑因为状态机条件写错,压根没进到"发送帧"的状态,只看PC端当然什么都收不到。用SignalTap一抓,状态机的状态一目了然,比自己瞎猜高效得多。
5.2 Wireshark抓包验证协议字段
PC端用Wireshark抓包是验证以太网协议最直观的手段。把PC网卡接到跟FPGA同一个交换机的端口,或者直连FPGA板子的RJ45口,然后在Wireshark里监听对应网卡,就能看到FPGA发过来的所有包。
抓包之后重点看这几个字段:
- 帧头EtherType是不是0x0800,如果不是,说明MAC层EtherType填错了
- IP头源地址是不是FPGA侧配置的IP地址
- IP头的协议字段是不是17(UDP)
- UDP目的端口和源端口对不对
- 总长度字段是否和实际数据长度一致
Wireshark里还能筛选特定协议的包,比如过滤UDP包就输入udp,过滤特定源地址用ip.src == 192.168.1.10。有网友问过如何筛选UDP前后两包的时间间隔,在Wireshark里可以添加时间列并设置筛选条件,或者在分析时使用tcp.time_delta之类的特性,UDP场景下可以用frame.time_delta_displayed来查看相邻帧的时间间隔。这个在测试发包频率和链路稳定性时很实用。
5.3 iperf3 UDP打流考验极限吞吐
当功能跑通之后,你需要知道这条UDP链路到底能承受多大的数据量,这时候就该iperf3上场了。iperf3是一个经典的网络性能测试工具,支持TCP和UDP打流测试。
在PC端作为服务端运行时,命令是这样的:
iperf3 -s -p 5001在PC端作为客户端向FPGA发起UDP打流时,假设FPGA的IP是192.168.1.10,监听端口是5001,那么:
iperf3 -c 192.168.1.10 -u -b 100M -l 1400 -t 10-u表示UDP模式,-b 100M表示目标带宽100Mbps,-l 1400表示UDP负载大小1400字节,-t 10表示持续测试10秒。测试完成后iperf3会报告实际发送的数据量、接收端反馈的丢包率和抖动值。如果丢包率很高,就要回头查FPGA侧的发送FIFO深度、链路质量、时钟稳定性这些问题。
要注意一点:iperf3是标准的端到端测试工具,如果你FPGA侧只实现了UDP接收但没实现回应逻辑,那就只能测单向打流,服务端放在PC上,客户端放在另一个设备上,或者反过来用iperf3的-R反向模式。总之把iperf3当作"压力测试机"来用,它不会替你做FPGA内部的协议栈调试。
5.4 用网络调试助手快速做应用层联调
除了Wireshark和iperf3,我平时还会用UDP网络调试助手这类工具,它可以在PC上直接绑定一个端口发UDP数据,也能显示收到的UDP数据,以十六进制和ASCII两种方式查看。
调试接收通路时,我用调试助手往FPGA的IP和端口发一串固定的十六进制数据,比如AA 55 01 02 03 04,然后看FPGA内部SignalTap抓到的数据是不是这串字节。这样就把"网络链路通不通""FPGA接收通路对不对"这两个问题彻底隔离了:网络层的事看Wireshark,FPGA内部的事看SignalTap,应用层的事看调试助手。
调试发送通路时,让FPGA定时往PC的调试助手端口发数据,如果调试助手能稳定收到并且数据内容正确,那基本可以确认FPGA侧发送通路全链路是通的。
6. 踩过的坑串烧:USB-Blaster驱动到链路异常,一个都别放过
6.1 USB-Blaster被Windows报代码39的处理
很多人在上板调试之前就卡在了下载器这一关。USB-Blaster插到Windows电脑上,设备管理器里显示一个带黄色感叹号的设备,属性里写"由于其配置信息(注册表中的)不完整或已损坏,Windows 无法启动这个硬件设备(代码39)"。
这个问题的原因多半是驱动没装对,或者系统里有旧版本驱动和Quartus自带的新版本驱动冲突。我在Windows 10上处理过一次,解决步骤是这样的:
- 拔掉USB-Blaster,同时打开设备管理器
- 在设备管理器菜单栏选择查看 -> 显示隐藏的设备
- 找到之前残留的USB-Blaster条目,右键卸载,勾选"删除此设备的驱动程序软件"
- 重新插上USB-Blaster
- 右键点击带感叹号的设备,选择更新驱动程序 -> 手动查找 -> 浏览到Quartus安装目录下的drivers文件夹,比如
D:\intelFPGA\18.1\quartus\drivers\usb-blaster - 选择对应系统的驱动,安装好之后重启电脑再试一次
如果手动装完还是代码39,可以尝试使用一小段系统工具修复注册表残留,或者检查一下是不是用了USB 3.0口,换到USB 2.0口往往就好了。另外,有些USB-Blaster是克隆版,跟Altera原装驱动不完全兼容,这种情况就得找卖家要专门的驱动程序。
6.2 时钟配置错了会出现哪些症状
时钟问题是TSE调试中非常隐蔽又非常致命的问题。我遇到过两种情况,都让人抓狂。
第一种是TSE参考时钟的频率给错了。SGMII模式要求参考时钟是125MHz,但有人图方便直接用了开发板上的100MHz系统时钟,结果链路上数据全是乱的。这种问题不会直接报错,表现就是PHY链路状态是Up的,但收发的数据完全对不上,Wireshark里充斥着一堆"Malformed Packet"。
第二种是用户侧Avalon-ST接口的时钟频率和TSE内部时钟没对上。如果你把TSE配置成32位数据通路,千兆速率下它内部工作时钟是31.25MHz,但你的用户逻辑跑在50MHz或者100MHz,那数据进到TSE的时候需要做跨时钟域处理。TSE内部FIFO理论上可以处理跨时钟域,但前提是两侧的时钟频率关系要满足IP核的要求,而且必须正确约束。
最稳妥的做法是:在Quartus工程里给TSE相关时钟加上create_clock和set_clock_groups约束,明确告诉时序分析工具多快时钟之间的关系。不要指望不写约束也能靠IP核的内部逻辑硬扛过去,时序收敛不了,上板就是随机性故障,一会儿通一会儿不通。
6.3 链路信号时好时坏,多半是复位和同步出问题
还有一种很让人头疼的故障是"复位之后要等好几秒才能收到数据",或者"运行几分钟之后突然断流,重启又好了"。这类问题基本都是链路状态和复位时序惹的祸。
链路状态问题有两个常见来源:
第一个是PHY芯片的Link Status位不稳定。有些PHY芯片在自动协商期间Link Status是0,协商完成以后变成1。如果你在Link Status还是0的时候就让TSE开始发包,那些数据会直接丢在PHY里。解决方法是配置一个轮询逻辑,定期读PHY寄存器,确认Link Status为1再启动用户逻辑。
第二个是TSE的复位信号释放时间和PHY的时钟稳定时间不匹配。如果你在PHY的参考时钟还没稳定时就释放了TSE复位,TSE可能会采到不确定的电平,导致内部状态机初始化失败。这种情况的典型特征是:复位后第一次发包就丢,后面正常。解决方案是确保复位释放至少等到参考时钟稳定之后,多等个几十毫秒不丢人。
6.4 能跑通UDP之后,再进阶思考的几件事
UDP能正常收发不代表这个设计就完美了,我建议你在基本链路跑通之后,再花点时间把下面几件事做了:
按实际需要算一下UDP校验和。虽然IPv4下UDP校验和可以填0,但某些网络设备或抓包工具会显示警告,如果你对接的软件对校验和敏感,建议在FPGA逻辑里用增量校验的方式计算,避免每包都做全量求和带来的时序压力。
把ARP超时机制加上。PC端的ARP缓存有超时时间,一般是几十秒到几分钟,超时之后PC会重新发ARP请求。FPGA侧的ARP应答逻辑不能只工作几秒就不干活了,要保证每次收到ARP请求都能正常响应。
考虑一下DMA的对接。如果你的FPGA工程里有DDR和DMA控制器,把TSE收到的数据直接灌进DMA,就可以避免CPU挨个寄存器读数据。这是跑高性能UDP通信必须走的路,否则CPU中断处理会成为瓶颈。
我自己的体会是,TSE IP核是一个看似简单但细节很多的IP,很多配置项的取舍都要结合实际板卡和项目需求来决定。UDP通信作为应用层协议,它考验的不仅是协议本身的理解,更考验你对整个以太网链路每一层功能边界的把握。把TSE的MAC职责、PHY芯片的PCS职责、用户逻辑的协议栈职责这三者的分工搞清楚,这个项目的坑就已经少了一大半。