STM32H7性能问题优化实践
WangGaojie Lv4

最近开发一个项目,采用STM32H7单片机,遇到了一个我以前没有遇到过的一个性能问题,通过分析解决该问题的过程让我对STM32高性能数据传输有更加深刻的理解。

关键字:总线、DMA、FIFO


项目背景

某项目开发中需要将Linux的数据通过USB传输到单片机,数据在单片机中经过适当的处理后再将数据传输到硬件外设上。

第一次性能瓶颈 - USB

USB的理论带宽有480MHz,使用批量传输模式可实现较高的传输速度。最开始选用的是Tinyusb协议栈,在使用了一段时间后才发现该协议栈不支持DMA,这导致在进行高带宽数据传输时导致CPU占用非常高,接近40%的CPU资源都用于协议栈传输数据。过高的CPU占用将会为后续的数据处理阶段带来较大的隐患,所以必须要解决DMA支持的问题。

将协议栈更换为CherryUSB后,DMA的问题解决了。在CPU仅占用1%的情况下,可达到30MB/s的传输速度。

内存带宽限制

最开始我认为将USB的数据缓存放在靠近USB外设的区域,也就是AHB总线矩阵附近会有更多优势,但是我却忽略了一个带宽问题。这里的内存带宽是32bit的,在后续数据处理阶段,CPU访问这里的数据会有一定的性能瓶颈。将USB的缓存切换到AXI矩阵下,CPU的处理性能提高了一倍,且对USB的传输性能基本上没有太大的影响。

除了带宽问题外,AHB矩阵下的Cache策略也有一定的影响,这里是关闭了DCache的。将数据缓存放到AXI-SARM后也需要面临DCache的问题。

这里有两个选择,1:开启DCache,但是需要在适当的时候进行内存同步。2:关闭该区域的DCache,这样不需要额外的内存同步操作。
分别测试了两种情况,最终开启DCache的方案性能要好得多(优50%以上)。这看起来是不太合理的,因为这里涉及到了USB的DMA,该区域的Cache特性反而是一种负担。但是关闭Cache后性能却大受影响。

外设传输性能的优化

将数据传输到外设中基本上没什么特别的选择,只有DMA能够胜任。但是在落实到具体的配置上有很多优化的地方需要注意,这都是影响性能的关键点。

外设上的FIFO,我最开始并没有意识到外设FIFO的作用,因为这里已经使用了DMA,外设上的FIFO作用并不明显。但该FIFO能够提高总线的利用率,通过FIFO缓存数据后,可用将数据位宽进行压缩传输,目标外设为16bit时,通过将两次传输压缩为32bit进行一次传输到FIFO内,从而提高传输性能。

此外,利用DMA上的FIFO配合突发模式能够进一步提高传输性能,这对总线性能提高非常明显,因为前面的USB数据和该外设数据会经过同一条数据总线,使用突发模式传输数据可用在适当情况下避免总线发生抢占而导致CPU或USB发生不必要的等待。总线上的访问模式优化将系统的整体性能提高了10%,效果非常的明显。

数据处理上需要注意的地方

本次开发过程中涉及到了许多的非完整宽度的数据类型,例如12bit、14bit的有符号数,在C语言中一般使用位域能够非常轻松的处理这种数据类型。但是在实践的过程中发现,使用位域赋值时会存在一定的性能问题,导致数据处理的效率降低。因为处理器每次对一个非完整宽度的数据赋值时需要先从内存中加载一部分数据,这可能导致不必要的内存IO开销,因为LDR、STR指令是拖慢系统运行的关键因素。在算法的实践中需要减少IO的访问,将一部分数据算法使用寄存器进行处理,之后再将寄存器的数据写入到内存中。

在开发C代码过程中,可能很多人无法靠直觉想到一段C代码有多少LDR、STR指令,但在经过一定程度的练习后还是能够这种天赋的。推荐一个网站Compiler Explorer ,该网站能够将C代码转为汇编,并提供可选的编译选项,可以作为学习的平台和工具。想要编写出真正的高性能代码,掌握基本的汇编语言也是比不可少的,理解各种汇编指令的时钟周期对编写高性能代码更加重要,这里就不展开了。

总结

单片机内部数据总线在平时开发过程中并没有特别注意,因为没有直接对内核或外设产生影响,但是在面临大数据带宽、多个外设并行时,合理的配置以优化总线的利用将有助于系统整体的优化。在本次开发过程中,还研究了总线矩阵上的一下其它配置,如Qos、总线优先级等等,但由于本次开发时间有限且项目已满足设计要求,没有进一步的深入探究,在这些地方应该还有优化的空间。