[应用相关] STM32 缓存的对齐问题

[复制链接]
1094|13
 楼主| 药无尘 发表于 2021-11-15 12:40 | 显示全部楼层 |阅读模式
前言
在我们对 STM32 进行编程的时候,都会用到变量,因为我们的 MCU 32 位的,所以在申请变量
的时候
,就会存在变量长度不一致,需要对齐的问题.这个变量长度对齐的问题,小则可以只是影响代码执
行的效率
,大则会出现系统 hard-fault 的问题.下面我们将详细的解说这个问题.  

 楼主| 药无尘 发表于 2021-11-15 12:42 | 显示全部楼层
前言
在我们对 STM32 进行编程的时候,都会用到变量,因为我们的 MCU 是 32 位的,所以在申请变量
的时候,就会存在变量长度不一致,需要对齐的问题.这个变量长度对齐的问题,小则可以只是影响代码执
行的效率,大则会出现系统 hard-fault 的问题.下面我们将详细的解说这个问题.
struct stu my_stu __attribute__((at(0X20001001))) = {0};
编译报错 :
732906191e505bdc48.png
 楼主| 药无尘 发表于 2021-11-15 12:42 | 显示全部楼层
空间
变量地址对齐了,就会带来空间的浪费,但这个一般体现在结构体上.由下面的例子我们可以看出,就
算一样的结构体,不同的顺序,会带来不一样的总长度.在 Struce B 中,char 虽然物理上是 2 个 byte,但是
由于对齐的原因,加上他紧随的变量和他的变量不一致,所以他需要额外的 2 个 byte 来使结构体内部对齐.
所以在定义结构体的变量时,我们一般最好做到同样的顺序,即使类型从小到大排序,或着从大到小来排序.
这样可以带来空间上的节省.最直接的方式就是你可以用 SIZEOF()这个函数来查看结构体的大小
  1. //以下这种结构体的排序长度为 8 个 byte.
  2. typedef struct
  3. {
  4. int a; //4
  5. char b; //2
  6. short c; //2
  7. }Struce_A;
73106191e53fe52b2.png
  1. //以下这种排序是 12 个 BYTE 的
  2. typedef struct
  3. {
  4. char b; //4
  5. int a; //4
  6. short c; //4
  7. }Struce_A;
700046191e54cbd837.png
 楼主| 药无尘 发表于 2021-11-15 12:55 | 显示全部楼层
综合
通过上述,一般以为只要按照最好的排序方式,结构体就会对齐了.其实并不是这样的,他只会对不
同类型的变量之间做对齐,如果如下面的结构体.
  1. typedef struct
  2. {
  3. Uint8_t a1; //1
  4. uint8_t a2[5]; //7
  5. uint32_t a3[2]; //4
  6. }Struce_A;
因为第一个和第一个变量的类型是一致的,所以系统会合并,所以 a1+a2 的大小才是 8byte.如果如下,就
会对齐了,但是长度就会编程 16byte.
  1. 在第一个例子里面会存在效率的问题,如果在一些低功耗上,或者对算法实时性要求较高的地方,就会是个
  2. 不小的问题.为什么?因为 a2 没有对齐.这会带来效率上的问题,下面我们通过实际代码来测试这个效率
  3. 差.


 楼主| 药无尘 发表于 2021-11-15 12:56 | 显示全部楼层
案例与总结
案例 1 :
  1. //首先先定义变量,为了更好的查看里面的数据,我将其放到一个固定的地址.
  2. struct stu{
  3. uint32_t a1;
  4. uint8_t a2[5];
  5. uint32_t a3;
  6. };
  7. struct stu my_stu __attribute__((at(0X20001000))) = {0};
390546191e86f580be.png 通过一个 for 循环来检测他的运行时间
  1. for(i = 0;i < 1000;i++)
  2. {
  3. my_stu.a2[0] += x1;
  4. if( my_stu.a2[0] > 200)
  5. my_stu.a2[0] = 0;
  6. }
这串代码所需的 tick
运行之前:0x3D3F
615436191e887b6ee4.png


运行之后:0x31EC
643166191e892ba055.png
可以看出这段代码所需时间为: 0x3D3F - 0x31EC = 0x0B53


 楼主| 药无尘 发表于 2021-11-15 12:58 | 显示全部楼层
案例 2 :
  1. //这里我们将 a1 改成 uint8_t,这种方式就是很多 C 语言教材里面推荐的方式,结构体的排序由小到大排序,后
  2. 面我们看看结果.
  3. struct stu{
  4. uint8_t a1;
  5. uint8_t a2[5];
  6. uint32_t a3;
  7. };


有下图我们可以看到 a1 和 a2 之间并不像案例 1 之间有填补.
104506191e8b83e675.png
同样通过一个 for 循环来检测他的运行时间.
  1. for(i = 0;i < 1000;i++)
  2. {
  3. my_stu.a2[0] += x1;
  4. if( my_stu.a2[0] > 200)
  5. my_stu.a2[0] = 0;
  6. }
这串代码所需的 tick
运行之前:0x3D3F
307136191e8d9263ca.png
运行之后:
298506191e8e7b44bb.png
可以看出这段代码所需时间为: 0x3D3F - 0x2DF1 = 0x0F4E


 楼主| 药无尘 发表于 2021-11-15 12:59 | 显示全部楼层
总结 :
106966191e8fc117ac.png
可以看出,简单的三行代码,在是否对齐下,增加 35.1%的时间,如果在一些复杂度要求较高的应用,低功耗
的应用中,这将会是一个很大的问题.
 楼主| 药无尘 发表于 2021-11-15 13:01 | 显示全部楼层
建议 :
所以如果在一些比较大或者运用的比较多的结构体,我们可以人为的错开不同的类型变量,因为就
算你外部添加了强制的对齐命令,在现有的 IDE 里面(KEIl 5.20),暂时还没有对同种类型的变量进行对齐.
如果基本都是同种类型的变量,建议可以嵌套结构体来避开.这样错开了,剩下的就有系统帮你完成对齐动
作.
redone 发表于 2021-11-17 11:43 | 显示全部楼层
额,变量的类型和长度,从来没关心过,用到哪种定义哪种
coshi 发表于 2021-12-9 20:46 | 显示全部楼层
应该是可以在寄存器中配置的吧
aoyi 发表于 2021-12-9 20:51 | 显示全部楼层
这个其实挺关键的
drer 发表于 2021-12-9 20:54 | 显示全部楼层
要不然根本读不正确数据
gwsan 发表于 2021-12-9 20:56 | 显示全部楼层
一般就是默认的了
kxsi 发表于 2021-12-9 21:00 | 显示全部楼层
变量的定义和操作很重要
您需要登录后才可以回帖 登录 | 注册

本版积分规则

79

主题

623

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部