1. 项目概述:从寄存器手册到可操作的嵌入式网络驱动
如果你曾经在嵌入式系统上调试过以太网功能,大概率遇到过这样的场景:硬件连接一切正常,PHY芯片的指示灯也在闪烁,但网络就是不通。你翻遍了数据手册,面对上百个寄存器地址和密密麻麻的位域描述,感觉像是在读天书。最终,问题可能就出在某一个控制位的配置上,或者某个中断标志没有正确清除。今天,我们就来彻底拆解TI(德州仪器)嵌入式处理器中一个非常核心但文档又极其枯燥的模块——EMAC/MDIO的寄存器世界。
这份来自TI官方技术手册(SPRUH77C)的寄存器描述,是驱动开发的“宪法”。它定义了硬件所有可编程行为的法律边界。但手册的表述是严谨而冰冷的,它告诉你每个位是干什么的,却很少告诉你为什么要这么干,以及怎么干才是最优的。我的目标,就是结合我过去在工业网关和车载通信控制器开发中踩过的坑,把这份冰冷的寄存器列表,翻译成有温度、可实操的驱动开发指南。
EMAC(Ethernet Media Access Controller)和MDIO(Management Data Input/Output)是现代嵌入式SoC中实现以太网功能的黄金搭档。简单来说,EMAC负责数据包的“高速搬运”,处理MAC层协议,实现数据的成帧、CRC校验和流量控制;而MDIO则像一个“慢速管家”,通过一个两线制的串行接口(MDC时钟线和MDIO数据线)去配置和监控连接在物理网线上的PHY芯片,比如设置速率、双工模式,或者读取链接状态。很多网络不通的“玄学”问题,根源都在MDIO配置不当。
理解这些寄存器,价值巨大。它意味着你能摆脱现成驱动库的“黑盒”,在出现复杂网络故障时,有能力进行寄存器级的诊断和修复;也意味着你能为了极致的性能或特定的功能(如精确的流量整形、自定义的帧过滤),对硬件进行精细化的微调。无论是开发工业实时以太网设备、车载信息娱乐系统,还是高可靠性的网络网关,这份底层的掌控力都至关重要。
2. 核心模块功能与寄存器地图总览
在深入每一个比特位之前,我们得先建立起一个宏观的认知。TI的这份文档将EMAC/MDIO模块的寄存器分成了几个清晰的部分,这本身就是一种设计思想的体现。
2.1 EMAC控制模块与中断管理
EMAC控制模块并不直接处理数据流,而是扮演着“调度中心”和“报警中心”的角色。它管理着多个传输(TX)通道,并负责产生和协调中断。文档中提到的CnTXIMAX寄存器(例如C0TXIMAX, C1TXIMAX, C2TXIMAX)就是一个非常精妙的设计,它属于中断节流机制的一部分。
为什么需要中断节流?想象一下,在一个千兆以太网端口全速转发小包时,每秒可能产生数十万甚至上百万个数据包。如果每个包完成都产生一个中断通知CPU,那么CPU将完全陷入中断处理的泥潭,什么正经活也干不了,这被称为“中断风暴”。CnTXIMAX寄存器就是为了平滑中断流而生的。
它的核心字段TXIMAX(位5-0)定义了一个目标值:每毫秒期望产生的最大传输中断脉冲数。这个值不是硬性上限,而是一个调节目标。模块内部有一个动态的步进算法(Pacing Algorithm),会根据实际的中断频率来调整一个内部的pace_counter,从而动态地阻塞或允许中断。手册里那段伪代码就是其灵魂:
if(interrupt_count > 2*TXIMAX) pace_counter = 255; // 严重过载,几乎完全阻塞中断 else if(interrupt_count > 1.5*TXIMAX) pace_counter = previous_pace_counter*2 + 1; // 过载,大幅降低中断频率 else if(interrupt_count > 1.0*TXIMAX) pace_counter = previous_pace_counter + 1; // 略超,轻微降低 else if(interrupt_count > 0.5*TXIMAX) pace_counter = previous_pace_counter - 1; // 适中,轻微提高 else if(interrupt_count != 0) pace_counter = previous_pace_counter/2; // 很低,大幅提高中断响应度 else pace_counter = 0; // 无中断,完全开放实操要点:这个寄存器的配置需要根据你的系统负载和实时性要求来权衡。对于低流量、高实时性要求的控制网络,你可以设置一个较大的TXIMAX(例如60,即每毫秒60次中断),让CPU能更灵敏地感知发送完成事件。对于高吞吐量的数据采集系统,则可以设置一个较小的值(例如10),牺牲一点延迟来换取更高的整体吞吐量,避免CPU过载。通常,你需要结合性能测试来找到最佳值。
2.2 MDIO模块:PHY芯片的指挥官
MDIO模块的寄存器表看起来整齐得多,它们围绕着几个核心任务展开:
- 身份与状态:
REVID(版本ID)、CONTROL(控制)、ALIVE(PHY应答状态)、LINK(链接状态)。 - 中断管理:
LINKINTRAW/MASKED(链接状态变化中断)、USERINTRAW/MASKED(用户命令完成中断)及其对应的掩码设置/清除寄存器。 - 命令通道:
USERACCESS0/1和USERPHYSEL0/1,这是与PHY通信的“双手”。
这里有一个关键设计:双通道用户访问。TI提供了USERACCESS0和USERACCESS1两套几乎完全相同的寄存器组。这意味着你可以同时发起两个独立的MDIO读写操作(例如,一个用于周期性的链路状态轮询,另一个用于应用程序发起的配置修改),它们会在硬件队列中排队执行。这大大提高了MDIO总线的利用率和软件设计的灵活性。
寄存器地图速查表: 为了让你对MDIO模块的寄存器布局有个直观印象,我整理了以下核心寄存器的偏移地址和功能摘要:
| 偏移地址 | 寄存器缩写 | 全称 | 核心功能简述 |
|---|---|---|---|
| 0x0 | REVID | Revision ID | 只读,获取MDIO模块硬件版本。 |
| 0x4 | CONTROL | Control | 全局控制:使能状态机、设置时钟分频、使能物理层故障检测等。 |
| 0x8 | ALIVE | PHY Alive Status | 位图寄存器,每位代表一个PHY地址(0-31),1表示该地址有PHY应答。 |
| 0xC | LINK | PHY Link Status | 位图寄存器,每位代表一个PHY地址,1表示该PHY链接已建立。 |
| 0x10 | LINKINTRAW | Link Int. Raw | 原始(未掩码)链接状态变化中断标志。 |
| 0x14 | LINKINTMASKED | Link Int. Masked | 被掩码后的链接状态变化中断标志。 |
| 0x20 | USERINTRAW | User Cmd Int. Raw | 原始用户命令完成中断标志。 |
| 0x24 | USERINTMASKED | User Cmd Int. Masked | 被掩码后的用户命令完成中断标志。 |
| 0x28 | USERINTMASKSET | User Int. Mask Set | 写1置1,用于使能(打开)用户命令完成中断掩码。 |
| 0x2C | USERINTMASKCLEAR | User Int. Mask Clear | 写1置1,用于禁用(关闭)用户命令完成中断掩码。 |
| 0x80 | USERACCESS0 | User Access 0 | 用户访问通道0:设置PHY地址、寄存器地址、数据,并触发读写。 |
| 0x84 | USERPHYSEL0 | User PHY Select 0 | 为通道0指定要监控链接状态的PHY地址,并使能链接变化中断。 |
| 0x88 | USERACCESS1 | User Access 1 | 用户访问通道1(功能同通道0)。 |
| 0x8C | USERPHYSEL1 | User PHY Select 1 | 为通道1指定要监控链接状态的PHY地址(功能同通道0)。 |
注意:
USERINTMASKSET和USERINTMASKCLEAR的操作是“写1有效”。这是一个常见的硬件设计模式,可以确保在多线程或中断环境下,设置和清除掩码位的操作是原子的,不会因为“读-改-写”过程被中断打断而导致状态错误。例如,想使能通道0的中断,就向USERINTMASKSET寄存器的位0写入1;想关闭它,就��USERINTMASKCLEAR寄存器的位0写入1。直接去读/写USERINTMASKED寄存器可能无法达到预期效果。
3. 关键寄存器深度解析与配置实战
理解了整体框架,我们现在可以深入几个最核心、也最容易出问题的寄存器,看看在代码中应该如何正确地操作它们。
3.1 MDIO控制寄存器(CONTROL):启动与基准时钟设置
CONTROL寄存器是MDIO模块的“总开关”。它的位域分布如下(基于文档描述):
- 位31 (IDLE):只读。1表示状态机空闲,可以接受新命令。
- 位30 (ENABLE):读写。1使能MDIO状态机,0禁用。关键点:即使你在状态机忙碌时将其禁用,它也会完成当前操作后才停止。
- 位28-24 (HIGHEST_USER_CHANNEL):只读。指示可用的最高用户通道编号。文档说当前是1,证实了我们有
USERACCESS0和USERACCESS1两个通道。 - 位20 (PREAMBLE):读写。0使用标准32位前导码,1则禁用。除非你对接的PHY有特殊要求,否则永远保持为0(使用标准前导码)。前导码是MDIO帧开始前的特定比特模式,用于同步。
- 位19 (FAULT):写1清除(W1C)。物理层故障指示。如果MDIO模块驱动到MDIO引脚上的电平与读回来的电平不一致,此位置1,同时状态机会被复位。这是一个重要的硬件故障诊断位。
- 位18 (FAULTENB):读写。物理层故障检测使能。通常建议使能(设为1),以便及时检测到MDIO总线短路、断路等问题。
- 位15-0 (CLKDIV):读写。这是最需要计算的字段。它决定了MDIO时钟
MDIO_CLK的频率。公式为:MDIO_CLK频率 = 外设总线频率 / (CLKDIV + 1)。
配置实战与计算示例: 假设你的SoC外设总线(例如,连接EMAC/MDIO模块的L3/L4总线)时钟频率为100 MHz,而MDIO协议规范要求MDIO_CLK最高不能超过2.5 MHz。为了留有余量,我们目标设定为2.0 MHz。
计算CLKDIV值:CLKDIV = (外设时钟频率 / 目标MDIO_CLK频率) - 1 = (100 MHz / 2.0 MHz) - 1 = 50 - 1 = 49
用十六进制表示,49就是0x31。因此,在初始化MDIO模块时,你需要将CLKDIV字段设置为49。同时,确保ENABLE位为1,FAULTENB位为1。
C语言代码片段示例:
// 假设 MDIO_BASE 是 MDIO 模块的基地址 #define MDIO_CONTROL_OFFSET 0x04 volatile uint32_t *mdio_control_reg = (uint32_t*)(MDIO_BASE + MDIO_CONTROL_OFFSET); // 步骤1:计算并设置CLKDIV,同时使能故障检测,使用标准前导码 // 位18 FAULTENB = 1, 位20 PREAMBLE = 0, 位15-0 CLKDIV = 49 uint32_t control_value = (1 << 18) | (49 & 0xFFFF); // 步骤2:写入寄存器,此时先不使能状态机(ENABLE=0) *mdio_control_reg = control_value; // 步骤3:最后,置位ENABLE,启动MDIO状态机 *mdio_control_reg |= (1 << 30);3.2 MDIO用户访问寄存器(USERACCESSn):与PHY通信的桥梁
这是你与PHY芯片对话的“话筒”和“耳朵”。以USERACCESS0为例,其位域至关重要:
- 位31 (GO):写1置位(W1S)。这是“执行”按钮。写入1后,MDIO状态机会在空闲时开始本次读写操作。操作完成后,硬件会自动将此位清零。在GO位为1期间,软件对
USERACCESS0寄存器的任何写入都会被硬件阻塞。因此,在发起新命令前,必须检查此位或通过中断确认上一次命令已完成。 - 位30 (WRITE):读写。0表示读操作,1表示写操作。
- 位29 (ACK):读写。这是一个状态位。当执行读操作时,如果目标PHY应答,硬件会将此位置1;如果无应答(PHY不存在或地址错误),则为0。对于写操作,此位通常不考虑。
- 位25-21 (REGADR):读写。指定要访问的PHY芯片内部寄存器地址(0-31)。
- 位20-16 (PHYADR):读写。指定要访问的PHY芯片的MDIO地址(0-31)。通常,一个PHY芯片的地址由硬件电路(如上下拉电阻)决定。
- 位15-0 (DATA):读写。对于写操作,这是要写入PHY寄存器的数据;对于读操作,当GO位清零且操作完成后,这里存放着读回来的数据。
完整的PHY寄存器读取流程(轮询方式):
- 准备命令:将
PHYADR(例如0x01)、REGADR(例如0x01,可能是状态寄存器)、WRITE(设为0)组合成一个值。GO位和ACK位初始为0,DATA位忽略。 - 检查状态机:读取
CONTROL寄存器的IDLE位,确保为1(空闲)。或者,更稳妥的方法是等待上一次命令的GO位自动清零。 - 发起读取:将准备好的命令字(包含PHYADR, REGADR, WRITE=0)写入
USERACCESS0寄存器,同时将GO位置1。这通常通过一次“或”操作完成:command = (phy_addr << 16) | (reg_addr << 21) | (0 << 30) | (1 << 31);。 - 等待完成:循环读取
USERACCESS0寄存器,直到其GO位变为0。注意:在繁忙循环中,最好加入超时机制,避免因PHY无响应导致死循环。 - 检查并获取结果:检查
ACK位。如果为1,表示PHY应答成功,此时DATA字段的值就是读取到的PHY寄存器内容。如果为0,则说明访问失败(PHY不存在或地址错误)。
中断方式优化: 轮询方式低效且占用CPU。更好的方式是使用中断。流程变为:
- 配置
USERINTMASKSET寄存器,使能USERACCESS0的命令完成中断。 - 在中断服务程序(ISR)中,读取
USERINTMASKED寄存器,确认是哪个通道的中断,并进行相应的处理(如读取DATA字段)。 - 必须向
USERINTMASKED或USERINTRAW的相应位写1来清除中断标志。这是W1C(Write-1-to-Clear)类型的寄存器,写1是清除,写0无效。
3.3 链接状态与中断管理:LINK,ALIVE,USERPHYSELn
网络链接的通断是动态变化的,驱动需要及时感知。MDIO模块提供了两种机制:
- 主动轮询:软件可以定期读取
LINK寄存器(这是一个位图,每位对应一个PHY地址),或者通过USERACCESSn读取PHY的状态寄存器来获取链接状态。 - 中断通知:这是更高效的方式。通过
USERPHYSEL0寄存器,你可以指定一个需要监控的PHY地址(PHYADRMON字段),并使能链接变化中断(LINKINTENB位置1)。当该PHY的链接状态发生变化时(从连接到断开,或从断开到连接),LINKINTRAW寄存器的对应位(USERPHY0)会被置1。如果该中断被使能(通过USERPHYSEL0的LINKINTENB),那么LINKINTMASKED的对应位也会置1,并可能向CPU产生中断。
ALIVE寄存器的妙用:这是一个“PHY探测”寄存器。每次通过MDIO访问某个地址的PHY后(无论读写),硬件都会更新ALIVE寄存器中对应的位:成功应答则置1,无应答则清0。因此,在系统初始化时,你可以通过快速扫描ALIVE寄存器,了解总线上实际连接了哪些PHY芯片,而无需对每个地址进行完整的寄存器读取和超时等待。
实操心得:在复杂的多PHY系统中(例如交换机芯片),我通常会这样初始化:
- 初始化MDIO控制器(设置
CONTROL寄存器)。 - 读取
ALIVE寄存器,获取在线PHY地址列表。 - 为每个在线PHY配置
USERPHYSELn以监控其链接状态,并使能链接变化中断。 - 通过
USERACCESSn配置每个PHY的工作模式(��度、双工等)。 - 在链接变化中断服务程序中,读取
LINK寄存器或直接读取PHY的状态寄存器,更新系统的网络接口状态,并通知上层应用。
4. EMAC核心寄存器与数据流控制
MDIO管“管家事务”,EMAC则管“数据搬运”。它的寄存器数量庞大,但可以归类为几个核心功能组。
4.1 发送与接收控制:TXCONTROL,RXCONTROL,TXTEARDOWN
TXCONTROL/RXCONTROL:非常简单,通常只有一个有效位TXEN/RXEN。写1使能整个EMAC模块的发送或接收功能。这是数据流的总开关。注意:在使能前,务必确保DMA描述符列表、缓冲区等已正确设置。TXTEARDOWN:这是一个“安全拆除”寄存器。当某个发送通道出现异常或需要动态关闭时,你不能简单地禁用整个发送器。向TXTDNCH字段写入通道号(0-7),硬件会优雅地停止该通道的DMA活动,完成当前正在处理的数据包,并将资源释放回空闲池。这对于实现热插拔或故障恢复至关重要。
4.2 DMA描述符指针寄存器:数据搬运的引擎
这是EMAC高性能的基石。TXnHDP(发送通道n头描述符指针)和RXnHDP(接收通道n头描述符指针)寄存器,指向的是你在系统内存中创建的描述符链表。
描述符是什么?它是一个数据结构,通常包含:指向数据缓冲区的指针、数据包长度、拥有权标志(Ownership,由软件或硬件设置)、下一个描述符的指针等。EMAC的DMA引擎会沿着这个链表自动搬运数据。
工作流程:
- 发送:软件准备好数据包,将其放入缓冲区,并设置好一个或多个描述符(将“拥有权”交给硬件)。然后将该描述符的地址写入对应的
TXnHDP寄存器。EMAC硬件会读取这个指针,找到描述符,将缓冲区中的数据搬移到MAC层发送出去。完成后,硬件会更新TXnCP(完成指针)寄存器,并通过中断通知软件。 - 接收:软件预先准备一串空闲的描述符和缓冲区,并将第一个描述符的地址写入
RXnHDP。EMAC硬件收到数据包后,会将其DMA到描述符指向的缓冲区,更新描述符状态,并可能产生接收中断。软件在中断中处理数据包,然后将处理完的描述符重新链接到接收队列末尾,确保接收持续进行。
关键点:TXnHDP是只写的(由软件写入新包指针),而TXnCP是只读的(由硬件更新,指示已完成到哪个描述符)。通过比较软件提交的指针和硬件完成的指针,可以判断发送队列的繁忙程度。
4.3 中断状态与掩码寄存器:事件驱动的核心
EMAC的中断系统是分层且精细的。文档中列出了TXINTSTATRAW,TXINTSTATMASKED,TXINTMASKSET,TXINTMASKCLEAR等,接收和MAC层也有类似的一套。
*INTSTATRAW:原始中断状态寄存器。任何中断事件发生,对应的位就会置1,无论该中断是否被使能(掩码)。这个寄存器非常适合用于调试和诊断,你可以看到硬件产生的所有事件。*INTSTATMASKED:被掩码后的中断状态寄存器。只有当中断事件发生并且该中断在掩码寄存器中被使能时,对应的位才会置1。通常,CPU的中断控制器会连接到这个寄存器的某个汇总信号上。在中断服务程序(ISR)中,你应该读取这个寄存器来确定是哪个具体事件触发了中断。*INTMASKSET/*INTMASKCLEAR:中断掩码设置和清除寄存器。它们是W1S/W1C类型。想使能某个中断(例如“发送完成中断”),就向TXINTMASKSET的对应位写1;想禁用它,就向TXINTMASKCLEAR的对应位写1。
标准的中断处理流程:
- 初始化:向
TXINTMASKCLEAR等寄存器写全1,清除所有可能遗留的中断掩码(禁用所有中断)。然后,根据需要向TXINTMASKSET写1,使能特定中断(如发送完成、接收完成)。 - ISR内: a. 读取
TXINTSTATMASKED或RXINTSTATMASKED,获取待处理的中断位图。 b. 根据位图,处理相应事件(如释放已发送的缓冲区,处理接收到的数据包)。 c.必须向TXINTSTATMASKED或TXINTSTATRAW的对应位写1,以清除中断标志。如果不清除,退出ISR后会立即再次进入中断,形成死循环。 - 优化:对于高性能场景,可以采用中断合并(NAPI-like)机制。在ISR中,可以暂时禁用该中断源(向
*INTMASKCLEAR写1),然后调度一个底半部(Bottom Half)任务在进程上下文进行批量包处理,处理完毕后再重新使能中断(向*INTMASKSET写1)。
5. 网络统计寄存器:性能监控与故障诊断的利器
文档后半部分列出了大量的统计寄存器,如RXGOODFRAMES,RXCRCERRORS,TXCOLLISION等。这些寄存器是只读的,由硬件在相应事件发生时自动递增。它们是你的“网络健康仪表盘”。
- 性能监控:
RXGOODFRAMES和TXGOODFRAMES可以让你计算实时的网络吞吐量。RXOCTETS和TXOCTETS则提供了字节级别的流量统计。 - 故障诊断:
RXCRCERRORS持续增长:可能表明物理链路质量差(电缆过长、干扰大、接口接触不良)。RXALIGNCODEERRORS增长:可能表示与对端设备的时钟不同步或协商有问题。TXCOLLISION,TXLATECOLL增长(在半双工模式下):表明网络冲突严重,可能需要检查网络拓扑或流量负载。TXUNDERRUN置位:这是一个严重错误!表示DMA发送数据的速度跟不上MAC发送的速度,导致发送FIFO被读空。这通常是因为系统总线太忙,或者发送描述符队列耗尽,CPU来不及补充新的发送缓冲区。需要优化发送路径或增加发送描述符环的大小。RXOVERSIZED/RXUNDERSIZED:有助于识别非法的或恶意的数据包。
实操建议:在产品的调试版本中,定期(例如每秒)读取并记录这些统计寄存器的值,将其作为系统日志的一部分。当现场出现网络问题时,这些历史统计数据是定位问题根源的宝贵线索。在生产版本中,可以根据需要选择性地监控关键指标,用于触发告警或降级操作。
6. 常见问题排查与调试技巧实录
基于这些寄存器,我总结了一些调试嵌入式网络驱动时最常遇到的“坑”和解决方法。
问题一:MDIO读写PHY一直失败,ACK位始终为0。
- 排查步骤:
- 检查电源和时钟:确保PHY芯片和SoC的MDIO模块供电正常,复位已释放。
- 检查硬件连接:用示波器测量MDC和MDIO线。MDC应有时钟(频率是否符合
CLKDIV计算值?),MDIO线上在读写期间应有数据波形。如果MDIO线一直是高电平或低电平,可能是上拉/下拉电阻问题,或者引脚复用配置错误(被配置为GPIO或其他功能)。 - 检查
CONTROL寄存器:确认ENABLE=1,IDLE=1。检查CLKDIV设置是否正确,频率是否在PHY支持的范围内(通常最高2.5MHz)。频率过高是常见死因。 - 检查
ALIVE寄存器:进行一次简单的PHY扫描。尝试读取不同地址的PHY,观察ALIVE寄存器的位图变化。如果某个地址的位始终不能置1,基本可以确定物理层问题或PHY地址错误。 - 检查
FAULT位:如果FAULTENB已使能,检查FAULT位是否被置1。如果置1,说明存在物理层故障,需要检查MDIO线路是否对地/电源短路,或者PHY的MDIO引脚是否损坏。 - 检查PHY地址:确认硬件原理图上PHY的地址配置(通过引脚上下拉)与软件中配置的
PHYADR是否一致。
问题二:网络能连接,但吞吐量极低,且TXUNDERRUN错误频发。
- 原因分析:这是典型的“发送喂不饱”问题。CPU或DMA准备数据包的速度慢于网络发送的速度。
- 解决方案:
- 增大发送描述符环:增加
TXnHDP指向的描述符链表长度,让硬件有更多的缓冲包可以发送,给软件更长的准备时间。 - 优化数据准备路径:检查数据拷贝是否过多?是否可以使用零拷贝技术?发送缓冲区的内存是否位于非缓存(Cache)区域,或者是否正确进行了缓存维护(Cache Invalidate/Writeback)?DMA操作非缓存一致性内存时,必须手动处理缓存,否则会读到脏数据或写丢数据。
- 调整中断策略:如果每个包都产生中断,开销太大。可以:
- 使用发送完成中断聚合,配合
CnTXIMAX寄存器进行中断节流。 - 采用轮询模式(Polling)进行高速发送,仅在队列快空时才使用中断通知。
- 使用发送完成中断聚合,配合
- 提升总线优先级:检查EMAC模块的DMA总线主设备优先级设置(通常在SoC的系统配置模块中),适当提高其优先级,确保在总线竞争时能优先获取数据。
- 增大发送描述符环:增加
问题三:链接状态不稳定,频繁通断,LINK寄存器位闪烁。
- 排查步骤:
- 硬件检查:更换网线、检查RJ45接口是否松动、测量PHY的模拟电源是否干净。
- 软件配置:通过MDIO读取PHY的特定状态寄存器(如标准MII的
BMCR、BMSR或厂商扩展寄存器),获取更详细的错误信息,如“FIFO溢出”、“符号错误”等。 - 协商模式:尝试强制设置PHY的速率和双工模式,而不是自动协商,以排除协商过程中的不兼容问题。
- 检查统计寄存器:观察
RXCRCERRORS、RXALIGNCODEERRORS是否在链接断开前有增长。如果有,则是链路质量问题的铁证。
问题四:使能EMAC发送(TXEN=1)后系统异常或网络风暴。
- 可能原因:
TXnHDP寄存器被初始化为一个非法地址(如NULL),或者指向的描述符结构格式错误(例如“拥有权”位未正确交给硬件)。这会导致DMA引擎从随机内存地址读取数据并发送,产生错误的数据包,甚至触发内存访问错误(Bus Error)。 - 预防措施:
- 在设置
TXnHDP前,务必确保描述符链表已正确初始化,并且第一个描述符的“拥有权”位是给硬件的。 - 在使能
TXEN前,先将TXnHDP指向一个明确已知的、有效的描述符地址,或者直接设为0(NULL),然后由驱动在第一个数据包准备好后再写入有效指针。 - 仔细检查描述符的数据结构定义,确保与硬件手册要求完全一致,特别是位域的对齐和顺序。
- 在设置