深入解析以太网DMA控制器:描述符机制与性能优化实战
1. 项目概述与核心价值
搞嵌入式网络开发,尤其是涉及到像TI的Tiva C系列这类微控制器,以太网性能优化是个绕不开的坎。CPU资源宝贵,如果每个网络数据包的搬移都要它亲力亲为,那系统就别干别的了,光伺候网卡都够呛。这时候,DMA(直接内存访问)控制器就成了救星。它就像一个专业的数据搬运工,能独立完成内存和外设(这里就是以太网MAC)之间的大块数据搬运,把CPU彻底解放出来。但光有DMA还不够,如何高效地告诉DMA“搬哪里”、“搬多少”、“搬完了怎么办”,这就是描述符(Descriptor)机制大显身手的地方。
你手头这份TI的芯片手册片段,虽然看起来是冷冰冰的寄存器位描述和状态机流程图,但它实际上揭示了一套非常经典且高效的以太网DMA控制器工作模型。这套模型的核心思想,就是通过一组在内存中预先定义好的“任务清单”(描述符环),让DMA能够自主、流水线式地处理网络数据包的发送和接收。OWN位的巧妙设计,实现了CPU和DMA之间对缓冲区所有权的安全交接,避免了数据竞争。而OSF(Operate on Second Frame)模式、中断聚合、时间戳等高级特性,则是为了进一步压榨硬件潜力,降低延迟,提升吞吐量。
理解这套机制,价值巨大。首先,它能让你写出更高效、更稳定的底层网络驱动,不再是简单地调用库函数,而是真正掌控数据流向。其次,在调试网络丢包、延迟高、CPU占用率异常等问题时,你能深入到描述符状态、DMA中断这些层面进行精准定位,而不是停留在应用层瞎猜。最后,对于设计需要高实时性、高带宽的网络应用(如工业控制、视频流、高频交易等),深刻理解DMA和描述符是进行深度优化的前提。这篇文章,我就结合手册内容和自己踩过的坑,带你把这套机制的里里外外、五脏六腑都捋清楚。
2. 核心概念与架构总览
在深入TX/RX的具体流程之前,我们必须先建立几个核心概念的认知模型。这些概念是理解后续所有复杂操作的基础。
2.1 描述符:DMA的“任务工单”
你可以把描述符想象成快递员(DMA)手里的送货/取货单。每一张“单子”都明确写清楚了几个关键信息:
- 缓冲区地址(Buffer Address):数据放在内存的哪个地方(TDES2/RDES2)。
- 缓冲区长度(Buffer Size):这个缓冲区有多大,能放多少数据(TDES3/RDES3)。
- 控制与状态字(Control/Status):这是最核心的部分,通常放在第一个双字(如TDES0/RDES0)。它包含了:
- OWN位(Bit 31):所有权标志。
1表示这个描述符及其关联的缓冲区归DMA所有,CPU不能动;0表示归CPU所有,DMA已经处理完毕。这是实现“乒乓”操作和安全同步的生命线。 - 首描述符(First Descriptor, FD):标记一个数据帧的开始。
- 末描述符(Last Descriptor, LD):标记一个数据帧的结束。一个帧可能跨越多个缓冲区,因此会有多个描述符,只有最后一个的LD位会被置位。
- 中断完成(Interrupt on Completion, IC):当该描述符对应的操作(发送或接收)完成时,是否触发中断。
- 各种错误状态位(如Underflow, Overflow, CRC Error等)。
- OWN位(Bit 31):所有权标志。
描述符在内存中通常以环形队列(Ring Buffer)的方式组织,形成一个“描述符环”。DMA会沿着这个环依次处理描述符,处理完最后一个后会自动回到第一个,形成一个高效的流水线。
2.2 DMA引擎与状态机
DMA控制器内部是一个精密的状态机。手册中反复提到的RUN、SUSPEND、STOP状态,就是这个状态机的核心。
- STOP:初始或停止状态。DMA不进行任何操作,等待软件(CPU)通过配置寄存器(如设置
EMACDMAOPMODE.ST/SR位)将其唤醒。 - RUN:运行状态。DMA活跃地轮询(Poll)描述符列表,获取任务并执行数据传输。
- SUSPEND:挂起状态。这是一个关键状态,通常由异常触发,比如DMA试图获取一个OWN位为0(属于CPU)的描述符,或者发生了FIFO下溢(Underflow)错误。进入SUSPEND后,DMA会暂停当前操作,等待软件干预(例如,补充新的描述符或处理错误)并通过Poll Demand命令将其唤醒。
理解这个状态机,对于调试“DMA卡住”、“数据不发了”、“网卡不收了”这类问题至关重要。你需要学会查看相关状态寄存器(如EMACDMARIS)来判断DMA当前处于何种状态,以及因何进入该状态。
2.3 TX/RX控制器与FIFO:数据的中转站
DMA负责在系统内存和TX/RX控制器之间搬运数据。而TX/RX控制器则负责在DMA和以太网MAC之间进行数据缓冲和时钟域转换。它内部有两个独立的FIFO(先入先出队列):
- TX FIFO:发送FIFO。DMA把要发送的帧数据放到这里,MAC再从这里面取数据发到物理线路上。这解决了DMA传输速率和MAC发送速率可能不匹配的问题。
- RX FIFO:接收FIFO。MAC把从线路上收到的数据放到这里,DMA再从这里把数据搬走。这解决了数据到达的突发性和DMA搬运的节奏问题。
手册中提到的Threshold模式和Store-and-Forward模式,就是针对FIFO数据转发策略的配置。
- Threshold模式(直通模式):当FIFO中的数据量达到一个预设的阈值(如64字节)时,就开始向下一级(MAC或DMA)转发数据,无需等待整个帧完全进入FIFO。这降低了传输延迟(Latency)。
- Store-and-Forward模式(存储转发模式):必须等待一个完整的帧全部进入FIFO后,才开始转发。这种方式可以提前过滤掉一些错误的帧(因为错误状态通常在帧尾),提高可靠性,但会增加延迟。
3. 发送(TX)DMA操作深度解析
发送流程是DMA工作的主要场景之一,其核心目标是高效、无误地将CPU准备好的网络帧通过MAC发送出去。TI的控制器提供了两种模式:默认模式和OSF模式。
3.1 默认发送模式:稳扎稳打
默认模式逻辑清晰,适合理解基本流程。我们结合手册中的步骤和状态图,拆解其工作流程:
CPU准备阶段:驱动软件在内存中准备好要发送的以太网帧数据,并设置好对应的发送描述符(TDES0-TDES3)。关键操作包括:填写缓冲区地址和长度,设置
FD和LD位来标记帧的起止,并根据需要设置IC位。最后,将描述符的OWN位(TDES0[31])置为1,表示“任务单”已准备好,交给DMA处理。DMA启动与轮询:软件通过设置
EMACDMAOPMODE.ST位来启动TX DMA引擎,使其进入RUN状态。DMA开始轮询发送描述符环。描述符获取与检查:DMA读取当前指向的描述符。首先检查
OWN位:- 如果
OWN=0(属于CPU),说明这个描述符对应的任务还没准备好,或者已经被CPU回收但尚未布置新任务。DMA会立即挂起(SUSPEND),并设置EMACDMARIS.TU(发送缓冲区不可用)和NIS(正常中断汇总)位,通知CPU“我没活干了”。流程跳至步骤9。 - 如果
OWN=1(属于DMA),且无错误,则继续。
- 如果
数据搬运:DMA从描述符中解码出缓冲区地址(TDES2),开始从系统内存中读取数据,并通过内部总线将数据搬运到TX/RX控制器的TX FIFO中。
多缓冲区处理:如果一个帧很大,被分割存储在多个缓冲区(即多个描述符链),DMA在搬完当前缓��区后,会“关闭”这个中间描述符(主要是更新一些状态),然后获取链中的下一个描述符,重复步骤3和4,直到遇到
LD位被设置的描述符,表示帧数据已全部搬运完毕。帧发送完成与状态回写:当MAC最终将整个帧从TX FIFO发送到物理链路完成后,会返回一个发送状态给TX/RX控制器,再传递给DMA。
- DMA将这个状态信息(成功、各种错误等)写回当前描述符的TDES0。关键一步:在写回状态的同时,DMA会将
OWN位清零。这意味着该描述符及其缓冲区的主权归还给CPU,CPU可以安全地复用这个缓冲区或释放内存。 - 如果使能了IEEE 1588时间戳功能,且该帧需要打时间戳,DMA还会将捕获到的时间戳写入TDES6(低32位)和TDES7(高32位)。
- DMA将这个状态信息(成功、各种错误等)写回当前描述符的TDES0。关键一步:在写回状态的同时,DMA会将
中断触发:如果这个帧的最后一个描述符设置了
IC位(TDES0[30]),DMA会在完成状态回写后,设置EMACDMARIS.TI(发送中断)位。这通常会触发一个中断,通知CPU“某个帧发送完成了”。循环与挂起恢复:完成上述操作后,DMA返回步骤3,继续获取下一个描述符。如果之前因为
OWN=0进入了SUSPEND状态,DMA会停在这里等待。恢复的方法有两种:- Poll Demand(轮询请求):CPU在补充了新的描述符(并设置
OWN=1)后,向EMACTXPOLLD寄存器执行写操作(写任何值均可),这会触发一个“轮询需求”,让DMA尝试重新获取描述符,返回RUN状态。 - 停止:如果CPU清除了
EMACDMAOPMODE.ST位,DMA则进入STOP状态。
- Poll Demand(轮询请求):CPU在补充了新的描述符(并设置
实操心得一:OWN位的原子性操作在驱动代码中,设置描述符
OWN位必须是整个准备过程的最后一步,并且要确保内存写入已经同步(通常需要内存屏障指令,如DSB或__sync_synchronize())。绝对不能先设OWN=1,再去填充缓冲区数据或设置其他字段,否则DMA可能读到半成品数据,导致发送错误。同样,CPU在检查OWN=0后读取状态和数据时,也要注意缓存一致性。
3.2 OSF发送模式:性能加速器
默认模式有个问题:DMA必须等待一个帧完全发送完成、状态回写后,才能去处理下一个帧的描述符。这中间存在“空窗期”。OSF模式就是为了填补这个空窗期,实现流水线优化。
在OSF模式下(通过设置EMACDMAOPMODE.OSF位开启),DMA的行为有一个关键变化:它会在关闭(写状态回)当前帧的最后一个描述符之前,就提前去获取下一个帧的描述符。
其核心流程如下:
- DMA开始发送帧1,并将数据搬运到TX FIFO。
- 当帧1的最后一个数据块进入TX FIFO后,DMA不等帧1的状态返回,立刻去轮询描述符环,获取帧2的描述符。
- 如果帧2的描述符
OWN=1且有效,DMA会立即开始将帧2的数据搬运到TX FIFO。此时,TX FIFO里可能同时有帧1的尾部数据和帧2的头部数据,而MAC正在发送帧1。 - DMA等待帧1的发送状态和时间戳(如果使能)返回。
- 收到帧1的状态后,DMA执行“关闭”操作:写时间戳(如果需要)到帧1的TDES6/TDES7,写状态并清
OWN位到帧1的TDES0。注意:这个“关闭”操作,发生在DMA已经开始搬运帧2数据之后。 - 如果帧1的
IC位使能,则触发TI中断。 - 如此循环。
这种“预取”机制,使得帧2的数据搬运和帧1的线上发送及状态回收在时间上重叠了,显著提升了总线利用率和整体发送吞吐量,尤其对于背靠背的小包发送场景,性能提升明显。
注意事项一:OSF模式的描述符环大小手册中特别强调:“Therefore, the descriptor chain should have more than two different descriptors for correct and proper operation.” 这是因为OSF模式至少需要两个以上的有效描述符才能形成流水。如果环里只有两个描述符,可能会出现DMA刚把帧1状态写回,把描述符A还给CPU,然后立刻又去取描述符A准备给帧3用,但此时CPU可能还没来得及把帧3的数据填入描述符A,导致
OWN位还是0,从而引起不必要的挂起。因此,在实际使用中,描述符环的大小最好不少于4个,以确保流水线顺畅。
3.3 发送过程的其他关键点
- 帧格式要求:DMA期望缓冲区里存放的是完整的以太网帧数据,但不包含前导码(Preamble)、帧起始定界符(SFD)、以及可选的填充字节(Pad)和帧校验序列(FCS)。目的地址(DA)、源地址(SA)、类型/长度字段必须是有效的。如果通过描述符指示MAC禁用CRC生成或Pad插入,那么缓冲区里就必须包含完整的、带CRC的帧。
- 发送挂起条件:发送轮询会在两种情况下挂起:(1) 遇到CPU拥有的描述符(
OWN=0);(2) 发送过程中检测到下溢(Underflow)错误。后者是更严重的问题,通常是因为DMA往TX FIFO灌数据的速度跟不上MAC发送数据的速度,导致FIFO被掏空。此时会触发AIS(异常中断汇总)和UNF中断。 - TX FIFO刷新操作:通过设置
EMACDMAOPMODE.FTF位可以立即刷新TX FIFO。这是一个非常规操作,会中断正在进行的发送,导致产生一个残缺帧(Runt Frame)并可能引发下溢错误。该位会自动清零。这个功能通常用于错误恢复或协议栈重置。
4. 接收(RX)DMA操作深度解析
接收流程相对发送更为被动,由MAC接收到的数据包驱动,但其描述符管理和状态机同样复杂,目标是在不同负载下都能可靠地接收数据,并及时通知CPU。
4.1 默认接收流程:被动中的主动
- 初始化:CPU初始化接收描述符环,并设置所有描述符的
OWN位(RDES0[31])为1,表示缓冲区空闲,交由DMA管理。 - 启动与预取:设置
EMACDMAOPMODE.SR位启动RX DMA。DMA进入RUN状态后,会立即尝试预取一个描述符。这是一个重要的优化,让DMA在数据包到来之前就准备好存放数据的缓冲区地址,减少初始延迟。 - 数据包到达与存放:当MAC接收到一个通过地址过滤的帧,并且RX FIFO中的数据达到阈值(或整帧到达,取决于模式)时,DMA开始将数据从RX FIFO搬运到当前描述符指向的缓冲区。
- 缓冲区管理与描述符链:
- 如果当前缓冲区被填满,但帧还没接收完(未收到EOF),DMA会“关闭”当前描述符(清
OWN位,标记为中间描述符,即LS=0),然后立即获取下一个描述符,继续存放剩余数据。 - 如果帧接收完成,DMA会进行最终处理。
- 如果当前缓冲区被填满,但帧还没接收完(未收到EOF),DMA会“关闭”当前描述符(清
- 帧接收完成处理:
- 如果使能了时间戳,DMA将时间戳写入当前描述符的RDES6和RDES7。
- DMA将接收状态(包括CRC校验结果、长度、各种错误标志等)写入RDES0,并清除
OWN位(交还CPU),同时设置LS位(Last Segment)标记此为帧的最后一个描述符。 - 如果该描述符使能了接收中断(RDES1[31]),则设置
EMACDMARIS.RI位。
- 下一个描述符检查与挂起:DMA检查下一个描述符的
OWN位。如果为1(属于DMA),则返回步骤4,等待下一个帧。如果为0(属于CPU),说明描述符环已耗尽,DMA设置EMACDMARIS.RU(接收缓冲区不可用)位,并进入SUSPEND状态。 - 挂起恢复:在SUSPEND状态下,如果有新的帧到达或CPU发出了Receive Poll Demand,DMA会尝试重新获取当前描述符。如果其
OWN位已变为1(CPU已补充),则恢复运行;否则,根据EMACDMAOPMODE.DFF(禁用刷新)位的配置,决定是丢弃该帧还是保持挂起。
4.2 接收描述符的获取时机
DMA不会��等一个帧处理完才去要下一个缓冲区。为了保持流水线,它会在多个时机尝试“预取”下一个描述符:
- 刚进入RUN状态时。
- 当前缓冲区被填满,但帧还没结束。
- 一个帧接收完成,但当前描述符尚未关闭时。
- 接收过程因
OWN=0挂起后,有新帧到达时。 - 任何时候收到接收轮询请求(Poll Demand)。
这种积极的预取策略,是保证高吞吐量、低丢包率的关键。
4.3 接收过程挂起与帧丢弃
接收挂起最常见的原因就是描述符环耗尽(所有描述符OWN=0)。此时DMA处于SUSPEND状态,但MAC可能还在源源不断地收包,数据会进入RX FIFO。这里的行为由DFF位控制:
DFF=0(默认,使能刷新):如果新帧到达时DMA仍处于SUSPEND状态(即下一个描述符仍属于CPU),DMA会丢弃RX FIFO顶部的这个帧,并增加“丢失帧”计数器。这可以防止旧数据堵塞FIFO。DFF=1(禁用刷新):DMA不会丢弃帧,而是保持挂起,并设置RU状态。这给了驱动软件更多时间来处理描述符,但风险是如果软件响应太慢,RX FIFO可能被填满,导致后续帧被MAC层丢弃。
实操心得二:接收描述符环的大小与中断策略接收描述符环的大小需要仔细权衡。环太小,容易耗尽导致丢包;环太大,则会占用过多内存,且可能增加中断延迟。一个实用的策略是结合使用正常接收中断(RI)和早期接收中断(ERI)。
- RI:在每个帧接收完成时触发(如果描述符中使能)。适合处理大包或对实时性要求极高的场景,但可能造成中断风暴。
- ERI:当DMA填充了数据缓冲区的前半部分时触发。这允许驱动软件在帧还没收完的时候就开始处理描述符,提前回收和补充缓冲区,能有效降低在突发流量下描述符环被耗尽的风险。通常,将接收缓冲区大小设置为典型MTU(如1514字节)的两倍,并启用ERI,是一个不错的折中方案。
- 接收中断看门狗(EMACRXINTWDT):这是一个重要的防呆设计。如果使能了看门狗超时值,即使没有描述符设置RI位,当DMA完成一个帧的传输后,看门狗计数器启动。超时后,DMA会强制产生一个RI中断。这确保了即使驱动软件错误配置了中断,也不会因为永远等不到中断而丢失对描述符的控制。在驱动初始化时,建议设置一个合理的非零看门狗超时值(例如,对应几毫秒)。
5. 中断与错误处理机制
DMA控制器通过中断与CPU协同工作。中断分为两大类:正常中断(Normal)和异常中断(Abnormal),它们在EMACDMARIS寄存器中汇总为NIS和AIS位。
5.1 中断类型详解
正常中断(NIS):
- TI(发送中断):一个帧发送完成。这是最常用的中断,用于释放已发送帧的缓冲区。
- TU(发送缓冲区不可用):TX DMA因遇到CPU拥有的描述符而挂起。提示CPU需要补充发送描述符了。
- RI(接收中断):一个帧接收完成。用于通知CPU处理新收到的数据包。
- ERI(早期接收中断):接收缓冲区被填充了一半。用于提前回收缓冲区,优化吞吐。
异常中断(AIS):
- TPS(发送过程停止):发送过程被停止。
- TJT(发送Jabber超时):发送帧过长,超过Jabber定时器限制。
- OVF(接收FIFO溢出):RX FIFO满了,但MAC还有数据要写入。这是严重的丢包信号,通常意味着DMA搬运速度太慢或CPU处理太慢。
- UNF(发送下溢):TX FIFO空了,但MAC还要数据发送。意味着DMA供给数据的速度跟不上MAC发送速度。
- RU(接收缓冲区不可用):RX DMA因描述符耗尽而挂起。需要CPU紧急补充接收描述符。
- RPS(接收过程停止):接收过程被停止。
- RWT(接收看门狗超时):接收到的帧超长(>2KB或>10240字节,如果使能了巨帧)。
- ETI(早期发送中断):一个要发送的帧已完全传输到TX FIFO。可用于实现更精确的发送调度。
- FBI(致命总线错误):DMA在访问系统内存时遇到总线错误(如访问了非法地址)。这是最严重的错误,通常需要复位整个MAC和DMA模块。
5.2 中断处理流程与注意事项
- 中断使能:通过
EMACDMAIM寄存器有选择地使能关心的中断位。通常TI、RI、TU、RU、OVF、UNF是必须处理的。FBI更是需要全局监控。 - 中断服务程序(ISR)处理:
- 读取
EMACDMARIS寄存器,获取中断源。 - 先处理异常中断(AIS):检查OVF、UNF、RU、FBI等。这些通常意味着系统有严重问题,需要立即处理(如复位、调整描述符数量、检查内存等)。
- 再处理正常中断(NIS):批量处理TI和RI。通常的做法是,在TI中断中,遍历发送描述符环,找到所有
OWN=0的描述符,释放其对应的缓冲区或标记为可用。在RI中断中,遍历接收描述符环,找到所有OWN=0的描述符,将数据包上传给协议栈,并立即回收该描述符(重置缓冲区,设置OWN=1),放回环中。 - 清除中断:向
EMACDMARIS寄存器的相应位写1来清除中断标志。重要:必须读取中断状态后再清除,并且通常采用“读-处理-写回”的方式,避免丢失在ISR执行期间新产生的中断标志。
- 读取
- 中断不排队:手册明确指出,中断是不排队的。如果在驱动响应并清除一个中断标志之前,同一事件再次发生,不会产生新的中断。因此,ISR必须有能力处理“一个中断代表多个事件”的情况。例如,一个TI中断可能意味着连续多个帧发送完成。ISR需要循环检查描述符环,直到找不到
OWN=0的发送描述符为止。
避坑指南:中断风暴与性能在高流量场景下,如果每个帧都产生中断(TI/RI),会导致CPU被频繁打断,产生“中断风暴”,严重影响整体性能。解决方法有:
- 中断聚合(Coalescing):虽然这份手册片段未提及具体寄存器,但许多现代以太网控制器支持中断聚合,即让DMA在收到一定数量的帧或经过一段时间后再产生一次中断。这能显著降低中断频率。
- 轮询模式:在极端性能需求下,可以完全禁用TI/RI中断,由驱动软件主动定时轮询描述符环的状态。这消除了中断上下文切换的开销,但增加了CPU的空转消耗。
- NAPI(New API):在Linux等高级操作系统中,采用NAPI机制。中断被用于在低负载时唤醒处理程序,在高负载时,处理程序会关闭中断,进入轮询模式处理一批数据包,处理完毕后再打开中断。这是一种混合策略。
6. 时间戳与高级特性
6.1 IEEE 1588时间戳
对于需要网络精确时间同步的应用(如PTP协议),时间戳功能至关重要。控制器硬件支持在发送和接收时捕获精确的时刻。
- 发送时间戳:当帧的最后一位离开MAC时,硬件捕获当前时间。如果该帧使能了时间戳,这个64位值会被DMA写回该帧最后一个描述符的TDES6(低32位)和TDES7(高32位)。
- 接收时间戳:当帧的SF(Start of Frame)定界符到达MAC时,硬件捕获当前时间。同样,如果使能,会被DMA写回对应接收描述符的RDES6和RDES7。
- 关键点:时间戳的写回只发生在帧的最后一个描述符上(由
LD位标识)。对于发送,时间戳在状态回写(清OWN位)之前写入;对于接收,则在状态回写之前写入。如果时间戳不可用(例如FIFO满),DMA会向描述符写入全1(0xFFFFFFFF`)作为错误指��。
6.2 描述符错误处理
接收过程中可能遇到描述符错误(Descriptor Error, DE),由RDES0中的DE位指示。这通常发生在多缓冲区接收一个帧时,当前缓冲区用完,但DMA获取下一个描述符时发现其OWN=0(属于CPU)。此时,DMA无法继续存放帧的剩余部分。
- 如果
DFF=0(使能刷新),DMA会将当前描述符标记为最后一个(LS=1),但可能包含不完整的帧数据,并设置错误状态。后续的帧数据会被丢弃。 - 如果
DFF=1,行为可能有所不同。 驱动软件在RI中断中处理数据包时,必须检查DE位以及其他错误位(如CECRC错误,OE溢出错误等),来决定是否将数据包上传给协议栈。
7. 实战配置与调试技巧
理解了原理,最终要落到代码和调试上。以下是一些基于此硬件模型的实战要点。
7.1 描述符结构体定义
在C语言中,你需要定义一个与硬件描述符布局完全一致的结构体,并使用volatile关键字防止编译器优化,同时确保对齐(通常需要32位对齐)。
typedef struct { volatile uint32_t status; // RDES0/TDES0: 状态与控制字 volatile uint32_t size; // RDES1/TDES1: 缓冲区大小与控制位 volatile uint32_t buf_addr; // RDES2/TDES2: 缓冲区物理地址 volatile uint32_t next_desc_addr;// RDES3/TDES3: 下一个描述符地址 // 增强描述符可能还有RDES4-RDES7/TDES4-TDES7 volatile uint32_t ext_status; // RDES4/TDES4 volatile uint32_t reserved5; volatile uint32_t ts_lo; // RDES6/TDES6: 时间戳低32位 volatile uint32_t ts_hi; // RDES7/TDES7: 时间戳高32位 } dma_desc_t; // 确保结构体大小和布局符合手册要求 _Static_assert(sizeof(dma_desc_t) == 32, "DMA descriptor size mismatch"); _Static_assert(offsetof(dma_desc_t, buf_addr) == 8, "Buffer address offset mismatch");7.2 初始化流程
- 内存分配:在非缓存(Non-cacheable)或写回(Write-back)但需要正确维护缓存一致性的内存区域,为描述符环和数据缓冲区分配连续物理内存。描述符环通常需要是缓存行大小的整数倍。
- 描述符环初始化:
- 将每个描述符的
next_desc_addr指向环中下一个描述符的物理地址,形成闭环。 - 将每个描述符的
buf_addr指向对应的数据缓冲区物理地址。 - 设置
size字段,配置缓冲区长度和中断使能(如IC位)。 - 关键:将所有描述符的
status字段的OWN位置1,其他状态位清零。表示所有缓冲区初始状态都归DMA所有。
- 将每个描述符的
- DMA寄存器配置:
- 将描述符环的基地址(第一个描述符的物理地址)写入
EMACDMARDLAR(接收描述符列表地址寄存器)和EMACDMATDLAR(发送描述符列表地址寄存器)。 - 配置
EMACDMAOPMODE:设置ST/SR启动DMA,根据需求配置OSF、TSF(发送存储转发)、RSF(接收存储转发)、DFF等模式位,配置TTC/RTC阈值。 - 配置
EMACDMAIM:使能所需的中断(如TI, RI, RU, TU, OVF, UNF, FBI)。 - 如果使用时间戳,配置
EMACTIMSTCTRL寄存器。
- 将描述符环的基地址(第一个描述符的物理地址)写入
7.3 常见问题排查表
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 发送卡住,无数据发出 | 1. TX DMA未启动 (ST=0)。2. 发送描述符环耗尽,且未处理TU中断。 3. 所有描述符 OWN=0,DMA在SUSPEND状态。4. 总线错误(FBI)。 | 1. 检查EMACDMAOPMODE.ST位。2. 检查 EMACDMARIS寄存器,查看是否有TU中断挂起。检查发送描述符环,看是否所有OWN位都为0。3. 确认驱动在发送函数中是否正确设置了描述符并置 OWN=1。4. 检查 EMACDMARIS.FBI和EMACDMARIS.AE位,确认描述符和数据缓冲区地址是否有效。 |
| 接收不到数据包 | 1. RX DMA未启动 (SR=0)。2. 接收描述符环耗尽,且未处理RU中断。 3. 所有描述符 OWN=0,DMA在SUSPEND状态。4. MAC地址过滤或帧过滤规则丢弃了包。 5. 物理链路问题。 | 1. 检查EMACDMAOPMODE.SR位。2. 检查 EMACDMARIS.RU位。检查接收描述符环。3. 确认驱动在RI中断中是否回收了描述符(置 OWN=1)。4. 检查 EMACFRAMEFLTR寄存器配置,确认是否误开启了严格过滤。可暂时设置RA(接收所有)位进行测试。5. 检查链路状态寄存器。 |
| 系统频繁进入中断,性能差 | 1. 中断风暴。每个帧都产生中断。 2. 描述符环太小,导致频繁的TU/RU中断。 | 1. 考虑使用中断聚合(如果支持)、增大描述符环、或采用轮询/NAPI策略。 2. 增大发送和接收描述符环的数量。 |
| 收到大量CRC错误或畸形包 | 1. 数据缓冲区内存越界,破坏了描述符。 2. 缓存一致性问题。CPU写入的数据还在缓存,DMA就读走了旧数据。 3. 发送描述符中 LD位描述符的缓冲区长度(size)为0。 | 1. 检查缓冲区大小与描述符中size字段是否匹配。2. 确保在DMA操作前,对描述符和数据缓冲区执行了正确的缓存维护操作(Clean & Invalidate)。使用非缓存内存可简化问题。 3.手册特别强调:最后一个描述符的缓冲区大小必须非零。 |
| 时间戳值全为0xFFFFFFFF | 1. 时间戳功能未使能。 2. 接收FIFO在时间戳捕获前已满。 3. 该帧不是时间戳使能的帧。 | 1. 检查EMACTIMSTCTRL.TSEN位。2. 检查是否发生OVF错误,考虑优化DMA性能或增大RX FIFO阈值。 3. 检查发送/接收控制位中时间戳使能是否设置。 |
调试时,最有力的工具就是读取描述符环的内容和DMA状态寄存器。通过打印或在线调试查看关键描述符的OWN、LS、FD位以及各种错误位,可以清晰地看到DMA的执行停滞在哪个环节。同时,监控EMACDMARIS寄存器能快速定位是正常流程中断还是异常错误中断。
