我是之前在試驗(yàn)STM32G031G8U6單片機(jī)內(nèi)部FLASH讀操作時(shí)候發(fā)現(xiàn)的這個(gè)問題:STM32F103的flash數(shù)據(jù)可以從任意地址讀,而STM32G031G8U6就不行,讀數(shù)據(jù)地址不對(duì)齊的話會(huì)死機(jī),
關(guān)于什么是非對(duì)齊訪問,可以參考下圖1,來源于CM3權(quán)威指南CH5相關(guān)內(nèi)容:
圖1
先說結(jié)論:
1.Cortex-M0內(nèi)核不支持****非對(duì)齊訪問
2.Cortex-M3內(nèi)核支持非對(duì)齊訪問
3.intel i5支持非對(duì)齊訪問
4 .是否支持對(duì)存儲(chǔ)器的非對(duì)齊訪問取決于具體使用的內(nèi)核
本次試驗(yàn)的完成耽誤了很久,因?yàn)樽罱恢芪业眯鹿诹?,體質(zhì)差得新冠了啥也做不了。以下記錄了我的 實(shí)驗(yàn)驗(yàn)證過程,過程很長(zhǎng), 沒時(shí)間看的小伙伴了解上面的結(jié)論就夠了 。我將在三種不同的內(nèi)核平臺(tái)上進(jìn)行測(cè)試,為簡(jiǎn)化試驗(yàn),我只在全局區(qū)驗(yàn)證,原因在于:相同數(shù)據(jù)類型變量在全局區(qū)和棧區(qū)的內(nèi)存地址排列方式是不同的
一、在搭載intel i5 64位內(nèi)核、安裝了64位windows的電腦上,創(chuàng)建一個(gè)win32控制臺(tái)應(yīng)用程序,編譯方式選擇release模式**(關(guān)于CPU位數(shù)、操作系統(tǒng)位數(shù)、應(yīng)用程序位數(shù)三者的內(nèi)部關(guān)系這里不展開,我暫時(shí)也不咋清楚,本篇只關(guān)注非對(duì)齊訪問這個(gè)主題)**
1 單字節(jié)數(shù)據(jù)類型: 全局變量緊挨著,按地址遞減排列, 可以在任意地址訪問,當(dāng)然了單字節(jié)壓根不存在非對(duì)齊訪問的問題。 以下為試驗(yàn)代碼,圖2為執(zhí)行結(jié)果
#include "stdafx.h"
#include "stdint.h"
uint8_t a, b, c, d, e, f;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 0;
*(uint8_t*)&a = 0x01;
*(uint8_t*)&b = 0x02;
*(uint8_t*)&c = 0x03;
*(uint8_t*)&d = 0x04;
*(uint8_t*)&e = 0x05;
*(uint8_t*)&f = 0x06;
printf("global var addr: %p %p %p %p %p %p
", &a, &b, &c, &d, &e, &f);
printf("global var value: %x %x %x %x %x %x
", a, b, c, d, e, f);
printf("the memory region:
");
uint8_t* p = (uint8_t*)(((uint32_t)&a) % 4 + (uint32_t)&a);
printf("global var addr: %p
", p);
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)(p - i));
}
printf("
");
getchar();
return 0;
}
執(zhí)行結(jié)果如下:
圖2
2 兩字節(jié)數(shù)據(jù)類型:全局變量的首地址自動(dòng)按4字節(jié)對(duì)齊,因此沒有挨著排列(2個(gè)padding字節(jié)) ,并且地址是從高到低的 ,但是可以進(jìn)行非對(duì)齊訪問。 以下試驗(yàn)代碼的執(zhí)行結(jié)果如圖3,對(duì)變量c地址的兩字節(jié)賦值操作,賦值后,它自個(gè)兒在內(nèi)存中跨了兩個(gè)4字節(jié)區(qū)域,也就是數(shù)據(jù)本身沒有對(duì)齊,這個(gè)和它向下生長(zhǎng)有關(guān)系,和我這里討論的非對(duì)齊訪問不是一回事,然后讀的時(shí)候能順利讀出來,程序沒有發(fā)生死機(jī),也就是可以非對(duì)齊訪問。
#include "stdafx.h"
#include "stdint.h"
uint16_t a, b, c, d;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 0;
*(uint16_t*)((uint8_t*)&a + 2) = 0x01ff;
*(uint16_t*)((uint8_t*)&b + 1) = 0x02ff;
*(uint16_t*)&c = 0x03ff;
*(uint16_t*)&d = 0x04ff;
printf("global var addr: %p %p %p %p
", &a, &b, &c, &d);
printf("global var value: %x %x %x %x %x %x
", *(uint16_t*)((uint8_t*)&a + 2), *(uint16_t*)((uint8_t*)&b + 1), a, b, c, d);
printf("the memory region:
");
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)((uint8_t*)&a - i + 4));
}
printf("
");
getchar();
return 0;
}
執(zhí)行結(jié)果如下:
圖3
3.四字節(jié)數(shù)據(jù)類型: 全局變量的首地址自動(dòng)按4字節(jié)對(duì)齊,緊挨著、地址遞減排列,可以進(jìn)行非對(duì)齊訪問。 執(zhí)行結(jié)果如圖4,在這個(gè)平臺(tái)環(huán)境下,四字節(jié)數(shù)據(jù)的放置規(guī)則:首地址按四字節(jié)對(duì)齊,但是數(shù)據(jù)本身不對(duì)齊,和兩字節(jié)數(shù)據(jù)一樣,拆成了上下兩部分,另外三個(gè)字節(jié)放到高地址的四個(gè)字節(jié)區(qū)域。
#include "stdafx.h"
#include "stdint.h"
uint32_t a, b;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 0;
*(uint32_t*)((uint8_t*)&a+1) = 0x01020304;
*(uint32_t*)&b = 0x05060708;
printf("global var addr: %p %p
", &a, &b);
printf("global var value: %08x %08x
", a, b);
printf("the memory region:
");
uint8_t* p = (uint8_t*)(((uint32_t)&a) % 4 + (uint32_t)&a);
printf("global var addr: %p
", p);
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)(p - i + 4));
}
printf("
");
getchar();
return 0;
}
執(zhí)行結(jié)果如下:
圖4
二、32位的cm0內(nèi)核,stm32g031單片機(jī),裸機(jī)編程
1 單字節(jié)數(shù)據(jù)類型:全局變量緊挨著,和windows平臺(tái)不一樣, cm0內(nèi)核平臺(tái)在這里按地址遞增排列 ,可以在任意地址訪問,當(dāng)然了單字節(jié)壓根不存在非對(duì)齊訪問的問題。以下為試驗(yàn)代碼,圖5為執(zhí)行結(jié)果
uint8_t a, b, c, d, e, f;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_DMA_Init();
MX_USART2_UART_Init(230400);
int i = 0;
*(uint8_t*)&a = 0x01;
*(uint8_t*)&b = 0x02;
*(uint8_t*)&c = 0x03;
*(uint8_t*)&d = 0x04;
*(uint8_t*)&e = 0x05;
*(uint8_t*)&f = 0x06;
printf("global var addr: %p %p %p %p %p %p
", &a, &b, &c, &d, &e, &f);
printf("global var value: %x %x %x %x %x %x
", a, b, c, d, e, f);
printf("the memory region:
");
uint8_t* p = (uint8_t*)((uint32_t)&a - ((uint32_t)&a) % 4);
printf("global var addr: %p
", p);
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)(p + i));
}
printf("
");
while (1)
{
}
}
圖5
2.兩字節(jié)數(shù)據(jù)類型:全局變量的首地址自動(dòng)按2字節(jié)對(duì)齊,因此沒有挨著排列(2個(gè)padding字節(jié)) ,并且地址是從低到高的 ,不可以進(jìn)行非對(duì)齊訪問(無論進(jìn)行非對(duì)齊的讀還是寫都會(huì)導(dǎo)致死機(jī))。 以下試驗(yàn)代碼的執(zhí)行結(jié)果如圖6,可通過解開屏蔽的代碼來驗(yàn)證首地址對(duì)齊規(guī)則和非對(duì)齊訪問規(guī)則
//uint8_t padding;
uint16_t a, b, c, d;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_DMA_Init();
MX_USART2_UART_Init(230400);
int i = 0;
*(uint16_t*)((uint8_t*)&a + 2) = 0x01ff;
//*(uint16_t*)((uint8_t*)&b + 1) = 0x02ff;
*(uint16_t*)&c = 0x03ff;
*(uint16_t*)&d = 0x04ff;
printf("global var addr: %p %p %p %p
", &a, &b, &c, &d);
//printf("global var value: %x %x %x %x %x %x
", *(uint16_t*)((uint8_t*)&a + 2), *(uint16_t*)((uint8_t*)&b + 1), a, b, c, d);
printf("global var value: %x %x %x %x %x %x
", *(uint16_t*)((uint8_t*)&a + 2), *(uint16_t*)((uint8_t*)&b + 0), a, b, c, d);
printf("the memory region:
");
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)((uint8_t*)&a + i));
}
printf("
");
while (1)
{
}
}
圖6
3.四字節(jié)數(shù)據(jù)類型: 全局變量的首地址自動(dòng)按4字節(jié)對(duì)齊,緊挨著、地址遞增排列,不可以進(jìn)行非對(duì)齊訪問 (無論進(jìn)行非對(duì)齊的讀還是寫都會(huì)導(dǎo)致死機(jī)) 。 驗(yàn)證代碼的執(zhí)行結(jié)果如圖7,可以通過解開屏蔽的代碼來驗(yàn)證首地址對(duì)齊規(guī)則和非對(duì)齊訪問規(guī)則
//uint8_t padding;
uint32_t a, b;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_DMA_Init();
MX_USART2_UART_Init(230400);
int i = 0;
//*(uint32_t*)((uint8_t*)&a+1) = 0x01020304;
*(uint32_t*)((uint8_t*)&a) = 0x01020304;
*(uint32_t*)&b = 0x05060708;
printf("global var addr: %p %p
", &a, &b);
printf("global var value: %08x %08x
", a, b);
printf("the memory region:
");
uint8_t* p = (uint8_t*)((uint32_t)&a - ((uint32_t)&a) % 4);
printf("global var addr: %p
", p);
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)(p + i));
}
printf("
");
圖7
三、32位的cm3內(nèi)核,stm32f103單片機(jī),裸機(jī)編程
1.單字節(jié)數(shù)據(jù)類型:全局變量緊挨著,和windows平臺(tái)不一樣, cm3內(nèi)核平臺(tái)在這里按地址遞增排列 ,可以在任意地址訪問,當(dāng)然了單字節(jié)壓根不存在非對(duì)齊訪問的問題。另外 和cm0內(nèi)核不一樣的是,cm3的編譯嚴(yán)格要求變量聲明在局部作用域的最前面 ,比如以下驗(yàn)證代碼我將指針p的聲明放到了最前面,否則編譯將無法通過,圖8為執(zhí)行結(jié)果
uint8_t a, b, c, d, e, f;
int main(void)
{
int i = 0;
uint8_t* p = NULL;
uart_init(115200);
*(uint8_t*)&a = 0x01;
*(uint8_t*)&b = 0x02;
*(uint8_t*)&c = 0x03;
*(uint8_t*)&d = 0x04;
*(uint8_t*)&e = 0x05;
*(uint8_t*)&f = 0x06;
printf("global var addr: %p %p %p %p %p %p
", &a, &b, &c, &d, &e, &f);
printf("global var value: %x %x %x %x %x %x
", a, b, c, d, e, f);
printf("the memory region:
");
p = (uint8_t*)((uint32_t)&a - ((uint32_t)&a) % 4);
printf("global var addr: %p
", p);
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)(p + i));
}
printf("
");
while (1)
{
}
}
圖8
2.兩字節(jié)數(shù)據(jù)類型:全局變量的首地址自動(dòng)按2字節(jié)對(duì)齊,因此沒有挨著排列(2個(gè)padding字節(jié)) ,并且地址是從低到高的 ,可以進(jìn)行非對(duì)齊訪問。 以下試驗(yàn)代碼的執(zhí)行結(jié)果如圖9
uint8_t padding;
uint16_t a, b, c, d;
int main(void)
{
int i = 0;
uart_init(115200);
*(uint16_t*)((uint8_t*)&a + 2) = 0x01ff;
*(uint16_t*)((uint8_t*)&b + 1) = 0x02ff;
//*(uint16_t*)&c = 0x03ff;
*(uint16_t*)&d = 0x04ff;
printf("global var addr: %p %p %p %p
", &a, &b, &c, &d);
printf("global var value: %x %x %x %x %x %x
", *(uint16_t*)((uint8_t*)&a + 2), *(uint16_t*)((uint8_t*)&b + 1), a, b, c, d);
//printf("global var value: %x %x %x %x %x %x
", *(uint16_t*)((uint8_t*)&a + 2), *(uint16_t*)((uint8_t*)&b + 0), a, b, c, d);
printf("the memory region:
");
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)((uint8_t*)&a + i));
}
printf("
");
while (1)
{
}
}
圖9
3.四字節(jié)數(shù)據(jù)類型: 全局變量的首地址自動(dòng)按4字節(jié)對(duì)齊,緊挨著、地址遞增排列,可以進(jìn)行非對(duì)齊訪問。 如下驗(yàn)證代碼的執(zhí)行結(jié)果如圖10
uint8_t padding;
uint32_t a, b;
int main(void)
{
int i = 0;
uint8_t* p = NULL;
uart_init(115200);
*(uint32_t*)((uint8_t*)&a+1) = 0x01020304;
//*(uint32_t*)((uint8_t*)&a) = 0x01020304;
*(uint32_t*)&b = 0x05060708;
printf("global var addr: %p %p
", &a, &b);
printf("global var value: %08x %08x
", a, b);
printf("the memory region:
");
p = (uint8_t*)((uint32_t)&a - ((uint32_t)&a) % 4);
printf("global var addr: %p
", p);
for (i = 0; i < 20; i++)
{
if (i % 4 == 0)
{
printf("
");
}
printf("0x%02x ", *(uint8_t*)(p + i));
}
printf("
");
while (1)
{
}
}
圖10
評(píng)論