汇顶嵌入式二面:DMA 出错与总线仲裁
- 轮次
- 二面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- DMA 在数据传输出错的时候会出现什么情况?该怎么处理?
原帖补充:面试官最后给的思路是——DMA 是挂载在总线上的,可能因为访问同一条总线导致总线资源不够,此时 CPU 就得做仲裁。作者自评这部分理解比较薄弱。
《参考解析》
先把「出错」分类,再谈处理
DMA 的「出错」不是一个含义,面试官想听你把类别拆开:
- 传输错误(Transfer Error):DMA 访问的源/目的地址非法、越界,或从设备返回总线错误响应。多数控制器在状态寄存器里给出 TE / TEIF 之类的标志,并触发错误中断。
- FIFO / 溢出错误:外设侧生产或消费数据的速度跟 DMA 搬运速度不匹配,出现 overrun / underrun,标志位常见为 FE / DME。
- 传输未完成或半完成:DMA 通道被抢占、仲裁失败、或者软件过早复位通道,导致 NDTR(剩余传输数)不为 0,数据只搬了一半。
- 一致性问题:CPU 侧 cache 没做 clean/invalidate,看起来「数据错了」,其实是读到旧值或写了没回写。这是实际项目里最高频的假错误。
发现与处理的标准动作
- 中断里读状态寄存器判断是哪一类错误,清标志位(很多控制器要求先清标志再重启通道,否则立刻再次进中断)。
- 通道级处理:关闭通道 → 复位/重新配置(地址、长度、方向)→ 重传;连续多次失败则上报上层,不要死循环重试。
- 软件兜底:驱动层加超时(比正常传输时间留 2~3 倍余量),超时就认为本次 DMA 失败;上层做数据校验(CRC / 长度校验)和降级(改用 CPU 搬运或分小块)。
- 内存一致性:搬运前后按架构要求做
dma_sync_single_for_device/cpu(Linux dmaengine 体系)或手动 clean/invalidate cache;buffer 要用一致性内存或正确对齐。 - 可观测性:把错误计数、最后一次错误时的地址与长度打进日志,否则线上只能看到「数据偶尔不对」。
Linux 下这套机制落在 dmaengine 框架里:dma_async_tx_descriptor 的完成回调 + residue(未完成字节数)就是判断「是否全搬完」的依据;描述符状态里的 DMA_ERROR 对应传输失败,驱动需要把错误透传给使用者(比如 SPI/UART 驱动的超时逻辑)。
总线仲裁:这才是面试官想听的点
DMA 控制器在系统里是一个 bus master,和 CPU、GPU、其他 DMA 一样挂在同一条总线(AHB/AXI 或总线矩阵)上。同一条总线同一时刻只能有一个 master 拿到使用权,于是需要一个 arbiter(仲裁器) 来授予:
- 仲裁策略:固定优先级、轮转(round-robin)、或者带权重的分级仲裁。CPU 通常优先级高,但 DMA 若要搬运大块数据,长时间低优先级会导致吞吐崩掉。
- 占用粒度:AXI/AHB 的传输以 burst 为单位。burst 越长,单次占用总线越久,其他 master 被阻塞的时间越长;反过来 burst 太短则效率低、开销大。
- 后果:CPU 与 DMA 抢总线时,CPU 取指和访存都会被拖慢,表现为「一开 DMA,主循环就卡」;DMA 之间互抢则表现为某个通道吞吐不达标。
- 工程解法:用总线矩阵分层(把 DDR 访问打散到多条通道,减少冲突面);把大传输拆成多个较小 burst 交替让出总线;给实时性要求高的通道更高优先级但限制单次占用长度;用双缓冲(ping-pong)把连续流拆成块,避免一个通道长期霸占;必要时把 DMA 缓冲放到多端口内存或 TCM/SRAM 上,绕开主存总线。
为什么会被评价「没说到点子上」
原帖作者的复盘很实在:只答了「出错会怎样」的表层,没有答出错误与总线行为的因果。嵌入式面试里,DMA 这类题目通常按「寄存器级现象 → 中断与状态位 → 驱动处理 → 总线与体系结构影响」四层给分,只答一层就会被打断。准备这类题时,把「这个硬件模块在系统里连到谁、抢什么资源、抢不到会怎样」想清楚,比背寄存器名字更管用。