首页 / 资讯中心 / 文章详情

嵌入式以太网驱动开发实战:MAC与PHY架构、DMA描述符环及性能优化

嵌入式以太网驱动开发实战:MAC与PHY架构、DMA描述符环及性能优化 ★ FEATURED ARTICLE
1. 嵌入式以太网驱动开发到底在搞什么做嵌入式这行十来年从裸机跑到RTOS再到Linux以太网驱动始终是绕不开的一块硬骨头。很多人第一次接触网络驱动脑子里全是问号MAC和PHY到底怎么分工RMII和RGMII走线有什么区别为什么ping不通为什么丢包为什么千兆跑不满这些问题我在不同项目里几乎都踩过一遍。今天这篇就把嵌入式以太网驱动开发从框架到实操完整拆一遍不管你是刚入行的新手还是做了几年想补网络这块短板的老人都能拿到可以直接复用的思路和代码骨架。先说清楚这个领域的基本盘。嵌入式以太网驱动本质上就是让一颗MCU或者SoC能通过物理线缆收发以太网帧。它涉及三个层面的东西MAC控制器通常在SoC内部、PHY收发器外部芯片或集成、以及驱动软件初始化、收发描述符、中断处理、协议栈对接。这三个东西缺一不可任何一个环节出问题网络就是不通。而驱动开发者的核心工作就是让MAC和PHY协同工作把上层协议栈丢下来的数据帧准确地送出去把收到的帧准确地递上去。这篇文章适合谁看如果你正在做STM32、i.MX、瑞芯微、全志这类平台的网络驱动移植或调试或者你在写LwIP、协议栈的底层网卡接口又或者你只是想知道以太网驱动到底是怎么回事那这篇内容就是给你准备的。我会从整体架构讲到寄存器级别的操作从PHY协商讲到DMA描述符环从常见故障讲到实测排查手法尽量把每个“为什么”都讲透。2. 整体架构与方案选型MAC、PHY和驱动的三角关系2.1 MAC和PHY的分工逻辑很多新手搞不清楚MAC和PHY的区别我用一个生活化的类比来解释。MAC就像是你公司的收发室负责按照公司规定打包和拆包快递管理寄件登记和收件分发。PHY就像是楼下的快递柜和快递员负责实际的物理运输和信号转换。收发室不关心快递是怎么从A城市运到B城市的它只管把包裹按格式整理好交给快递员快递员也不关心包裹里装的是什么它只管把电信号变成光信号或者差分信号送出去。具体到硬件层面MAC控制器负责的事情包括帧的封装与解封装、CRC校验的生成与验证、地址过滤、流量控制、DMA描述符管理。PHY负责的事情包括物理编码子层PCS、物理介质接入子层PMA、自动协商、链路状态检测、时钟恢复。两者之间通过标准接口连接常见的有MII、RMII、GMII、RGMII、SGMII这几种。选哪种接口取决于你的速率需求和引脚预算。MII需要16根线RMII精简到7根但需要50MHz参考时钟GMII是千兆版本需要24根线RGMII用DDR方式把千兆的引脚数压到12根SGMII则是串行方案只需要几对差分线。我在实际项目里百兆场景基本选RMII千兆场景首选RGMII引脚特别紧张或者需要背板连接的时候才考虑SGMII。2.2 驱动框架的选型考量驱动框架这块裸机、RTOS和Linux三条路差别很大。裸机环境下你得自己写完整的初始化流程和收发逻辑好处是可控性极强坏处是什么都要自己来。RTOS环境下通常会配合LwIP这样的轻量协议栈驱动只需要实现netif的底层收发接口。Linux环境下则是标准的net_device框架驱动要注册probe、ndo_start_xmit、中断处理等回调。我个人的经验是如果你的产品对网络吞吐要求不高、成本敏感裸机加LwIP是最优解代码量小、调试直观。如果系统本身就跑Linux那就老老实实写platform driver别想着绕过框架。RTOS场景介于两者之间用LwIP的netif接口是最省事的做法。注意不管选哪种框架MAC和PHY的初始化顺序不能乱。必须先配置MAC的时钟和引脚复用再复位PHY然后等待PHY完成自动协商最后才能使能MAC的收发通道。顺序搞反了大概率出现链路不通或者能通但丢包的情况。2.3 描述符环的设计思路DMA描述符环是以太网驱动性能的核心。所谓描述符就是一块内存区域里面记录了数据缓冲区的地址、长度、状态标志。MAC控制器通过DMA自动读取描述符来发送和接收数据CPU只需要在合适的时候填充或者回收描述符。描述符环的设计有几个关键决策点。第一是描述符的数量太少会导致频繁中断和丢包太多会浪费内存和增加延迟。我一般百兆场景用32到64个千兆场景用128到256个。第二是缓冲区的大小标准以太网帧最大1518字节但考虑到VLAN标签和巨帧缓冲区通常分配1536到2048字节。第三是描述符的所有权管理必须严格区分CPU所有和DMA所有两种状态搞混了就会出现数据错乱。3. 核心细节解析从寄存器到数据流的完整链路3.1 PHY寄存器读写与自动协商PHY芯片内部有一组标准寄存器地址0到31其中前16个是IEEE 802.3定义的标准寄存器后面的是厂商自定义寄存器。最常用的几个寄存器0是控制寄存器可以软复位和设置速率双工寄存器1是状态寄存器能读取链路状态和协商结果寄存器2和3是PHY标识符寄存器4和5是自动协商通告和结果。读写PHY寄存器通过MDIO接口这是一个两线串行总线MDC是时钟MDIO是数据。时序上要注意MDC频率一般不超过2.5MHz太快了PHY可能不响应。读操作的流程是发送前导码32个1发送起始码01发送操作码10表示读发送PHY地址5位发送寄存器地址5位然后切换MDIO为输入读取16位数据。自动协商的过程是这样的PHY上电后如果使能了自动协商它会通过快速链路脉冲FLP把自己的能力告诉对端同时接收对端的能力信息然后双方取交集选择最高的共同能力。协商完成后寄存器1的bit5会置位表示协商完成寄存器1的bit2会置位表示链路建立。我见过很多新手在这里踩坑读寄存器1的时候没有等待协商完成就急着配置MAC结果链路一直不通。// PHY寄存器读写的典型实现 static int phy_read(struct mii_bus *bus, int phy_addr, int reg) { int data; // 发送前导码和读命令 mdiobus_write(bus, phy_addr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); // 等待协商完成 while (!(mdiobus_read(bus, phy_addr, MII_BMSR) BMSR_ANEGCOMPLETE)) msleep(10); // 读取协商结果 data mdiobus_read(bus, phy_addr, MII_LPA); return data; }3.2 MAC初始化的关键步骤MAC初始化是整个驱动的地基顺序和参数都不能马虎。第一步是时钟使能包括MAC的AHB时钟和PHY的参考时钟。第二步是引脚复用配置把GPIO切换到以太网功能。第三步是软复位MAC写复位寄存器然后等待复位完成。第四步是配置MAC的工作模式包括速率、双工、接口类型。第五步是配置DMA包括描述符环的基地址、突发长度、仲裁策略。第六步是使能MAC的收发通道和中断。这里重点说一下DMA的配置。以常见的DesignWare MAC为例DMA控制寄存器里有几个关键字段PBL可编程突发长度决定了一次DMA传输多少个数据我一般设成16或者32DSL描述符跳跃长度在描述符环不是连续内存时需要设置FIFO大小要根据实际吞吐需求调整太小了容易溢出丢包。实操心得MAC初始化完成后不要急着打开收发使能。先读一遍所有关键寄存器的值确认写入生效了。我遇到过因为时钟没使能导致寄存器写入被忽略的情况排查了半天才发现是时钟树配置漏了一项。3.3 数据发送的完整流程发送一个以太网帧从协议栈到线上信号中间要经过这些步骤。协议栈调用驱动的发送接口传入一个sk_buff或者pbuf。驱动检查发送描述符环里有没有空闲的描述符如果没有就返回忙或者启动队列。如果有空闲描述符就把数据拷贝到描述符指向的缓冲区或者直接把缓冲区地址填到描述符里。然后设置描述符的状态位标记为DMA所有。最后写MAC的发送轮询寄存器通知DMA开始传输。DMA传输完成后MAC会产生发送完成中断。中断处理函数里要回收已经发送完成的描述符把它们的所有权改回CPU然后唤醒上层队列。这里有个细节发送完成中断可能一次对应多个描述符因为DMA会连续处理多个描述符后才产生中断。所以中断处理里要循环检查所有描述符的状态位不能只处理一个。发送过程中最常见的坑是缓冲区对齐问题。很多MAC控制器要求数据缓冲区按4字节或者8字节对齐不对齐会导致DMA传输异常或者性能下降。我在一个项目里因为缓冲区没有对齐导致千兆速率只能跑到300Mbps对齐之后直接跑满。3.4 数据接收的完整流程接收流程和发送相反。MAC的DMA自动把收到的帧写入描述符指向的缓冲区然后更新描述符的状态位产生接收中断。中断处理函数里遍历接收描述符环找到状态位标记为有效的描述符把数据递交给上层协议栈然后重新初始化描述符交还给DMA。接收这边有几个容易出问题的地方。第一是帧长度检查要过滤掉太短小于64字节和太长大于1518字节或者巨帧配置值的帧。第二是CRC校验如果MAC硬件没有自动校验驱动里要自己算。第三是多播和广播过滤如果配置不当会收到大量无关的帧浪费CPU。第四是接收溢出如果中断处理太慢DMA会把新收到的帧丢弃这时候要检查FIFO溢出计数器。// 接收中断处理的典型骨架 static irqreturn_t mac_rx_interrupt(int irq, void *dev_id) { struct net_device *ndev dev_id; struct mac_priv *priv netdev_priv(ndev); while (1) { struct rx_desc *desc priv-rx_ring[priv-rx_idx]; if (!(desc-status RX_DESC_OWN)) break; if (desc-status RX_DESC_ERR) { ndev-stats.rx_errors; goto next; } // 递交数据到协议栈 skb build_skb(desc-buffer, desc-length); netif_rx(skb); ndev-stats.rx_packets; next: // 重新初始化描述符 desc-status RX_DESC_OWN; priv-rx_idx (priv-rx_idx 1) % RX_RING_SIZE; } return IRQ_HANDLED; }4. 实操过程从零搭建一个可用的以太网驱动4.1 硬件确认与引脚规划动手写代码之前先把硬件确认清楚。你需要知道MAC控制器的型号和基地址、PHY芯片的型号和地址、两者之间的接口类型、参考时钟的来源和频率、复位引脚的连接方式、中断引脚的连接方式。以RMII接口为例引脚包括REF_CLK50MHz参考时钟、TXD0、TXD1、TX_EN、RXD0、RXD1、CRS_DV、MDC、MDIO。一共9根线。RGMII的话还要加上TXC、RXC、TXD2、TXD3、RXD2、RXD3、TX_CTL、RX_CTL一共12根线。PHY地址的确定方式有两种一种是硬件引脚拉高拉低决定一种是软件配置。我建议在原理图阶段就把PHY地址固定下来避免软件里还要扫描地址。PHY地址范围是0到31实际用的时候避开0和31因为有些PHY芯片对这两个地址有特殊行为。注意RMII的REF_CLK必须由外部提供不能由MAC或者PHY自己产生。我见过有人把REF_CLK接到MAC的时钟输出上结果PHY一直不工作。RMII的参考时钟频率必须是50MHz精度要求±50ppm以内否则会出现丢包。4.2 寄存器级初始化代码下面是一个典型的MAC初始化序列以DesignWare MAC为例。不同厂商的MAC寄存器定义不同但流程是类似的。// MAC初始化核心步骤 static int mac_hw_init(struct mac_priv *priv) { // 1. 软复位MAC writel(MAC_CONFIG_SOFT_RESET, priv-base MAC_CONFIG); while (readl(priv-base MAC_CONFIG) MAC_CONFIG_SOFT_RESET) udelay(10); // 2. 配置MAC工作模式 u32 config MAC_CONFIG_PS | MAC_CONFIG_FES | MAC_CONFIG_DM; writel(config, priv-base MAC_CONFIG); // 3. 配置MAC地址 writel(priv-mac_addr[0] | (priv-mac_addr[1] 8) | (priv-mac_addr[2] 16) | (priv-mac_addr[3] 24), priv-base MAC_ADDR0_HIGH); writel(priv-mac_addr[4] | (priv-mac_addr[5] 8), priv-base MAC_ADDR0_LOW); // 4. 配置DMA writel(DMA_BURST_32 | DMA_PBL_32, priv-base DMA_BUS_MODE); writel((u32)priv-rx_ring_phys, priv-base DMA_RX_BASE_ADDR); writel((u32)priv-tx_ring_phys, priv-base DMA_TX_BASE_ADDR); // 5. 使能收发 writel(MAC_CONFIG_TE | MAC_CONFIG_RE, priv-base MAC_CONFIG); writel(DMA_START_RX | DMA_START_TX, priv-base DMA_OP_MODE); return 0; }这段代码里MAC_CONFIG_PS表示端口选择MAC_CONFIG_FES表示全双工MAC_CONFIG_DM表示速率。DMA_BURST_32表示突发长度32DMA_PBL_32表示可编程突发长度32。这些参数要根据实际硬件调整不是固定值。4.3 中断处理与NAPI机制中断处理是以太网驱动性能的关键。传统的中断方式是每收到一个帧就产生一次中断在高负载下会导致CPU被中断淹没。NAPI机制的做法是第一个帧到达时产生中断中断处理函数关闭接收中断然后通过轮询的方式批量处理接收描述符处理完一批后再重新打开中断。Linux下的NAPI实现需要注册napi_struct实现poll函数。poll函数里循环处理接收描述符直到处理完所有待处理的帧或者达到预算值通常是64。如果还有剩余的帧返回预算值表示需要继续轮询如果处理完了返回0表示可以重新打开中断。static int mac_poll(struct napi_struct *napi, int budget) { struct mac_priv *priv container_of(napi, struct mac_priv, napi); int work_done 0; while (work_done budget) { struct rx_desc *desc priv-rx_ring[priv-rx_idx]; if (!(desc-status RX_DESC_OWN)) break; // 处理接收帧 mac_rx_frame(priv, desc); work_done; } if (work_done budget) { napi_complete(napi); // 重新使能接收中断 writel(DMA_INT_RX, priv-base DMA_INT_ENABLE); } return work_done; }实操心得NAPI的预算值不要设太大64到128比较合适。设太大了会导致单次轮询时间过长影响其他中断的响应设太小了会导致频繁切换中断和轮询模式反而降低效率。4.4 链路状态监测与自适应链路状态监测是保证网络可靠性的重要环节。PHY的链路状态可能因为网线插拔、对端设备重启、协商失败等原因发生变化。驱动需要定期读取PHY的状态寄存器检测链路状态变化然后相应地调整MAC的速率和双工配置。实现方式有两种一种是用定时器定期轮询一种是利用PHY的中断引脚。轮询方式简单可靠我一般用100ms到1s的间隔。中断方式响应更快但需要PHY支持中断输出而且中断处理里还是要读寄存器确认状态。链路状态变化时的处理流程读取PHY的状态寄存器判断是链路建立还是链路断开。如果是链路建立读取协商结果根据协商的速率和双工配置MAC。如果是链路断开关闭MAC的收发通道等待下一次链路建立。static void mac_link_adjust(struct mac_priv *priv) { u16 bmsr phy_read(priv-phydev, MII_BMSR); u16 lpa phy_read(priv-phydev, MII_LPA); if (!(bmsr BMSR_LSTATUS)) { // 链路断开 priv-link 0; netif_carrier_off(priv-ndev); return; } // 解析协商结果 int speed (lpa LPA_100) ? 100 : 10; int duplex (lpa LPA_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; // 配置MAC mac_set_speed(priv, speed); mac_set_duplex(priv, duplex); priv-link 1; netif_carrier_on(priv-ndev); }5. 常见问题与排查技巧实录5.1 链路不通的排查思路链路不通是最常见的问题排查要按顺序来。第一步检查PHY的电源和复位。用万用表量PHY的供电电压是否正常复位引脚在上电后是否释放。第二步检查参考时钟。用示波器量REF_CLK是否有50MHz的波形幅度是否正常。第三步读PHY的标识符寄存器。如果读出来是0xFFFF或者0x0000说明MDIO通信有问题检查MDC和MDIO的连线、上拉电阻、时序。第四步检查自动协商状态。读寄存器1的bit5如果一直不置位说明协商失败可能是网线问题或者对端设备问题。第五步检查MAC的配置。确认速率、双工、接口类型配置正确。我遇到过一个案例PHY的标识符能读出来自动协商也完成了但就是ping不通。后来用示波器量TX_EN信号发现根本没有波形。查了半天发现是MAC的发送通道没有使能原因是在配置MAC的时候先写了发送使能位然后又写了一次配置寄存器把发送使能位覆盖掉了。这种问题就是典型的寄存器操作顺序错误。5.2 丢包问题的定位方法丢包问题比链路不通更难排查因为涉及的因素更多。首先要区分是发送丢包还是接收丢包。发送丢包的表现是上层协议栈报告发送失败或者发送队列满接收丢包的表现是ping有丢包但发送正常。发送丢包的常见原因描述符环太小高负载下没有空闲描述符DMA突发长度设置不当导致总线仲裁失败缓冲区没有对齐DMA传输效率低MAC的FIFO太小来不及发送。接收丢包的常见原因中断处理太慢DMA溢出接收描述符环太小过滤配置不当收到了大量无关帧CRC校验失败。排查工具方面我一般用这几个ifconfig或者ip命令看统计信息ethtool看链路状态和统计计数器示波器看信号质量ping和iperf测吞吐和丢包率。Linux下还可以用/proc/net/dev看详细的收发统计。问题现象可能原因排查方法解决方案链路不通PHY未复位量复位引脚电平检查复位电路和时序链路不通参考时钟缺失示波器量REF_CLK检查时钟源和引脚配置链路不通MDIO通信失败读PHY标识符检查MDC/MDIO连线协商失败网线质量差换网线测试使用合格网线发送丢包描述符不足看发送队列统计增加描述符数量接收丢包中断处理慢看CPU占用率启用NAPI或增大预算吞吐低缓冲区未对齐检查缓冲区地址按4/8字节对齐吞吐低DMA突发太小看DMA配置增大突发长度5.3 性能优化的几个关键点以太网驱动的性能优化核心是减少CPU占用和提高DMA效率。第一个优化点是启用NAPI把中断模式改成轮询模式减少中断次数。第二个优化点是使用零拷贝发送直接把上层缓冲区的地址填到描述符里避免数据拷贝。第三个优化点是调整DMA突发长度和FIFO阈值找到吞吐和延迟的平衡点。第四个优化点是使用多队列把不同的流分配到不同的描述符环利用多核并行处理。零拷贝发送的实现要点上层协议栈传入的缓冲区必须是DMA可访问的物理连续内存而且要在发送完成之前保持有效。Linux下可以用skb_shinfo(skb)-nr_frags和dma_map_single来实现。裸机环境下需要自己管理缓冲区的分配和释放。实操心得性能优化不要一步到位要逐项测试。我一般先测基线性能然后每次只改一个参数测完再改下一个。这样能清楚地知道每个优化点的贡献也方便回退。5.4 常见问题速查表故障现象优先检查项工具预期结果PHY标识符读不到MDC/MDIO连线、上拉电阻万用表、示波器读到正确的PHY ID自动协商不完成网线、对端设备、PHY配置ethtool协商完成标志置位链路时断时续网线接触、电源纹波示波器链路状态稳定ping不通但链路正常MAC地址、IP配置、防火墙ping、arp能ping通网关大包丢小包不丢MTU配置、缓冲区大小ping -s大包也能通千兆跑不满时钟精度、DMA配置、CPU性能iperf达到900Mbps以上长时间运行后丢包描述符泄漏、内存泄漏统计计数器计数器不持续增长6. 调试工具与实战手法6.1 硬件调试工具的使用调试以太网驱动光靠软件打印是不够的必须配合硬件工具。示波器用来看时钟质量和信号完整性特别是REF_CLK、TXC、RXC这些时钟信号以及TXD、RXD这些数据信号。逻辑分析仪用来看MDIO的读写时序确认命令和数据是否正确。协议分析仪用来看线上的以太网帧确认发送和接收的帧格式是否正确。我个人的习惯是拿到一块新板子先不写驱动先用示波器和逻辑分析仪把硬件信号确认一遍。时钟频率对不对幅度够不够时序满不满足要求这些确认了再写代码能省掉很多调试时间。6.2 软件调试手段软件层面最常用的手段是打印寄存器的值。在初始化的每个步骤之后把关键寄存器的值打印出来和手册上的预期值对比。Linux下可以用dev_dbg或者pr_debug裸机下可以用串口打印。另一个手段是统计计数器。在驱动里维护一套统计计数器记录发送帧数、接收帧数、发送错误数、接收错误数、DMA溢出次数等。这些计数器能快速定位问题出在哪个环节。Linux下可以用net_device_stats结构体裸机下自己定义。还有一个手段是回环测试。把MAC配置成回环模式发送的帧直接回到接收端不经过PHY和网线。这样能隔离问题判断是MAC的问题还是PHY的问题。如果回环测试通过但正常模式不通那问题就在PHY或者网线上。6.3 实战案例一个千兆跑不满的排查过程说一个我实际遇到的案例。一个千兆以太网项目ping正常但iperf测吞吐只有400Mbps左右远低于预期的900Mbps以上。排查过程如下。第一步确认链路协商结果。ethtool显示链路是1000Mbps全双工协商没问题。第二步看CPU占用率。发现CPU占用率很高接近100%说明瓶颈在CPU而不是网络。第三步看中断统计。发现每秒中断次数非常多说明中断处理占用了大量CPU。第四步检查NAPI配置。发现NAPI没有启用驱动还是纯中断模式。第五步启用NAPI并调整预算值。吞吐提升到700Mbps。第六步检查DMA配置。发现DMA突发长度是8改成32之后吞吐提升到900Mbps以上。这个案例说明性能问题往往是多个因素叠加的结果要逐项排查和优化。中断模式、NAPI预算、DMA突发长度、缓冲区对齐每一个都会影响最终性能。6.4 不同平台的适配要点不同厂商的MAC控制器寄存器定义和初始化流程差别很大但核心逻辑是相通的。ST的ETH控制器DMA描述符是链式结构每个描述符有4个32位字。TI的CPSW支持多队列和硬件时间戳。NXP的FEC描述符结构比较简单但中断处理有特殊要求。瑞芯微的GMAC支持RGMII和RMII两种接口配置方式不同。适配新平台的时候我的做法是先找到厂商提供的参考驱动把初始化流程和寄存器操作对照手册过一遍。然后搭建最小系统只做回环测试确认MAC基本功能正常。再接入PHY做链路协商和ping测试。最后做性能测试和优化。这个流程能保证每一步都有明确的验证目标不会一上来就陷入复杂的调试。注意不同平台的缓存一致性处理方式不同。有些平台DMA和CPU共享缓存需要手动做cache flush和invalidate有些平台有硬件缓存一致性不需要软件干预。搞错了会导致数据错乱而且这种问题很难排查因为现象是随机的。7. 驱动开发的工程化建议7.1 代码结构的分层设计以太网驱动代码建议分成三层硬件抽象层、驱动核心层、接口适配层。硬件抽象层封装寄存器的读写操作不同平台只需要改这一层。驱动核心层实现初始化、收发、中断处理等核心逻辑这部分代码可以跨平台复用。接口适配层对接上层协议栈Linux下是net_deviceLwIP下是netif裸机下是自己定义的接口。这种分层设计的好处是换平台的时候只需要改硬件抽象层核心逻辑不用动。我在多个项目之间复用同一套驱动核心代码移植到新平台通常只需要一两天。7.2 调试接口的设计驱动里要预留足够的调试接口。我一般会实现这几个寄存器读写接口可以通过命令行或者调试器读写任意寄存器统计信息接口可以查看收发计数和错误计数回环测试接口可以切换回环模式PHY寄存器读写接口可以读写任意PHY寄存器。这些调试接口在开发阶段非常有用能快速定位问题。在产品发布时可以裁剪掉或者加权限控制避免被误用。7.3 测试用例的设计以太网驱动的测试要覆盖这几个场景基本连通性测试用ping验证链路和协议栈正常吞吐测试用iperf验证性能达标稳定性测试长时间运行验证不丢包不崩溃异常测试拔插网线、对端重启、发送异常帧验证驱动的容错能力压力测试满负载运行验证描述符和缓冲区管理正确。我一般会写一个自动化测试脚本把这些测试串起来每次代码改动后跑一遍确保没有引入回归问题。8. 写在最后以太网驱动开发这件事说难也难说简单也简单。难在细节多寄存器、时序、DMA、中断、协议栈每个环节都有坑。简单在逻辑清晰只要理解了MAC和PHY的分工理解了描述符环的运作机制剩下的就是按部就班的配置和调试。我这些年做下来最大的体会是硬件确认比软件调试重要基础配置比高级优化重要稳定可靠比性能极致重要。很多问题其实在硬件阶段就埋下了软件再怎么调也是事倍功半。基础配置做扎实了性能优化才有意义。而产品最终要的是稳定可靠不是跑分好看。如果你正在做以太网驱动遇到问题不要慌按链路层、MAC层、DMA层、协议栈层逐层排查总能找到原因。实在找不到就用回环测试隔离问题用示波器看信号用逻辑分析仪看时序。工具用对了问题就解决了一半。
阅读完成 · 觉得有帮助?
咨询建站