打印
[应用相关]

STM32 缓存的对齐问题

[复制链接]
797|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};
编译报错 :

使用特权

评论回复
板凳
药无尘|  楼主 | 2021-11-15 12:42 | 只看该作者
空间
变量地址对齐了,就会带来空间的浪费,但这个一般体现在结构体上.由下面的例子我们可以看出,就
算一样的结构体,不同的顺序,会带来不一样的总长度.在 Struce B 中,char 虽然物理上是 2 个 byte,但是
由于对齐的原因,加上他紧随的变量和他的变量不一致,所以他需要额外的 2 个 byte 来使结构体内部对齐.
所以在定义结构体的变量时,我们一般最好做到同样的顺序,即使类型从小到大排序,或着从大到小来排序.
这样可以带来空间上的节省.最直接的方式就是你可以用 SIZEOF()这个函数来查看结构体的大小
//以下这种结构体的排序长度为 8 个 byte.
typedef struct
{
int a; //4
char b; //2
short c; //2
}Struce_A;

//以下这种排序是 12 个 BYTE 的
typedef struct
{
char b; //4
int a; //4
short c; //4
}Struce_A;

使用特权

评论回复
地板
药无尘|  楼主 | 2021-11-15 12:55 | 只看该作者
综合
通过上述,一般以为只要按照最好的排序方式,结构体就会对齐了.其实并不是这样的,他只会对不
同类型的变量之间做对齐,如果如下面的结构体.
typedef struct
{
Uint8_t a1; //1
uint8_t a2[5]; //7
uint32_t a3[2]; //4
}Struce_A;
因为第一个和第一个变量的类型是一致的,所以系统会合并,所以 a1+a2 的大小才是 8byte.如果如下,就
会对齐了,但是长度就会编程 16byte.
在第一个例子里面会存在效率的问题,如果在一些低功耗上,或者对算法实时性要求较高的地方,就会是个
不小的问题.为什么?因为 a2 没有对齐.这会带来效率上的问题,下面我们通过实际代码来测试这个效率
差.


使用特权

评论回复
5
药无尘|  楼主 | 2021-11-15 12:56 | 只看该作者
案例与总结
案例 1 :
//首先先定义变量,为了更好的查看里面的数据,我将其放到一个固定的地址.
struct stu{
uint32_t a1;
uint8_t a2[5];
uint32_t a3;
};
struct stu my_stu __attribute__((at(0X20001000))) = {0};
通过一个 for 循环来检测他的运行时间
for(i = 0;i < 1000;i++)
{
my_stu.a2[0] += x1;
if( my_stu.a2[0] > 200)
my_stu.a2[0] = 0;
}
这串代码所需的 tick
运行之前:0x3D3F



运行之后:0x31EC

可以看出这段代码所需时间为: 0x3D3F - 0x31EC = 0x0B53


使用特权

评论回复
6
药无尘|  楼主 | 2021-11-15 12:58 | 只看该作者
案例 2 :
//这里我们将 a1 改成 uint8_t,这种方式就是很多 C 语言教材里面推荐的方式,结构体的排序由小到大排序,后
面我们看看结果.
struct stu{
uint8_t a1;
uint8_t a2[5];
uint32_t a3;
};


有下图我们可以看到 a1 和 a2 之间并不像案例 1 之间有填补.

同样通过一个 for 循环来检测他的运行时间.
for(i = 0;i < 1000;i++)
{
my_stu.a2[0] += x1;
if( my_stu.a2[0] > 200)
my_stu.a2[0] = 0;
}
这串代码所需的 tick
运行之前:0x3D3F

运行之后:

可以看出这段代码所需时间为: 0x3D3F - 0x2DF1 = 0x0F4E


使用特权

评论回复
7
药无尘|  楼主 | 2021-11-15 12:59 | 只看该作者
总结 :

可以看出,简单的三行代码,在是否对齐下,增加 35.1%的时间,如果在一些复杂度要求较高的应用,低功耗
的应用中,这将会是一个很大的问题.

使用特权

评论回复
8
药无尘|  楼主 | 2021-11-15 13:01 | 只看该作者
建议 :
所以如果在一些比较大或者运用的比较多的结构体,我们可以人为的错开不同的类型变量,因为就
算你外部添加了强制的对齐命令,在现有的 IDE 里面(KEIl 5.20),暂时还没有对同种类型的变量进行对齐.
如果基本都是同种类型的变量,建议可以嵌套结构体来避开.这样错开了,剩下的就有系统帮你完成对齐动
作.

使用特权

评论回复
9
redone| | 2021-11-17 11:43 | 只看该作者
额,变量的类型和长度,从来没关心过,用到哪种定义哪种

使用特权

评论回复
10
coshi| | 2021-12-9 20:46 | 只看该作者
应该是可以在寄存器中配置的吧

使用特权

评论回复
11
aoyi| | 2021-12-9 20:51 | 只看该作者
这个其实挺关键的

使用特权

评论回复
12
drer| | 2021-12-9 20:54 | 只看该作者
要不然根本读不正确数据

使用特权

评论回复
13
gwsan| | 2021-12-9 20:56 | 只看该作者
一般就是默认的了

使用特权

评论回复
14
kxsi| | 2021-12-9 21:00 | 只看该作者
变量的定义和操作很重要

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

79

主题

619

帖子

2

粉丝