项目上用 STM32H743 跑 LwIP 做工业网关,主频 480MHz,为了让 SDRAM 刷屏和网络都跑得动,把 D-Cache 打开了。结果以太网收发出现偶发丢包和校验错误,前后排查了两天才定位到是 D-Cache 与以太网 DMA 的一致性没有处理好。把配置要点和踩坑过程整理一下。
一、现象
使能 I-Cache 与 D-Cache 后,ping 丢包率约 3%~10%,大包更容易出错;关闭 D-Cache 后一切正常。说明问题出在 Cache 与 DMA 的配合,而不是 PHY 或走线。
二、根因分析
Cortex-M7 的 D-Cache 是写回(Write-Back)策略,CPU 写入的数据可能停留在 Cache 里没有落到 SRAM,而以太网 DMA 直接读 SRAM,读到的就是旧数据;反过来 DMA 写入新数据后,CPU 若命中 Cache 中的旧行,也会读到旧值。所以描述符与数据缓冲区所在内存必须处理一致性。
三、推荐配置(MPU + 内存布局)
1. 把以太网描述符区、收发缓冲区所在的 SRAM 区域(例如 0x30000000 起始的 SRAM1/SRAM2)通过 MPU 配置为 Non-cacheable(或 Write-Through),属性 TEX=1、C=0、B=0,这样 CPU 与 DMA 看到的数据始终一致,软件最省心。
2. 若必须使用 Cache 提升性能,则描述符区单独设为 Non-cacheable,数据缓冲区采用 clean/invalidate 手动维护。
3. 缓冲区与描述符都要 32 字节对齐(Cache Line 长度),否则单次 clean 会波及相邻数据。
4. 在链接脚本里单独划分 eth 段,避免和普通变量混在一起。
四、手动维护 Cache 的顺序要点
1. 发送:CPU 填好数据后,先 SCB_CleanDCache_by_Addr 把数据刷到 SRAM,再启动 DMA 发送。
2. 接收:DMA 完成后,先 SCB_InvalidateDCache_by_Addr 让 Cache 失效,再让 CPU 读数据。
3. 顺序不能颠倒,否则会把尚未写入 SRAM 的数据丢掉(invalidate 后再 clean 等于什么都没做)。
4. 对同一块缓冲区不要既 clean 又 invalidate 混用,建议收发缓冲区分离。
五、实际踩的坑
1. 最开始只把描述符设为 Non-cacheable,数据缓冲区忘了处理,导致小包正常、大包偶发校验错。原因是小包数据量小于 Cache Line,恰好被整体写完。
2. 描述符数组没有 32 字节对齐,clean 操作把相邻的描述符也刷了,引起描述符状态错乱。
3. 中途改过 SRAM 分配(把 LwIP 的 mempool 从 DTCM 移到 SRAM1),没有同步调整 MPU 区域,问题复现了一次。
4. 中断里调用 invalidate 时,如果此时 CPU 正在读同一个缓冲区,会出现数据竞争,最终把收发处理放到任务里,用信号量同步后稳定。
六、实测结果
按上述 MPU + 对齐方案配置后,长时间 ping(-l 1472)零丢包,TCP 吞吐稳定在 90Mbps 以上(百兆 PHY 满速),连续运行 72 小时无异常。
七、小结
H7 的性能很强,但 Cache 与 DMA 共存时必须显式处理一致性。最省事的做法是把以太网相关的描述符和缓冲区整体放到 Non-cacheable 的 MPU 区域;若追求性能再单独为数据区做 clean/invalidate,并严格遵守先 clean 后启动 DMA、先 invalidate 后读数据的顺序。
|
|