存储器系统的非对齐访问

存储技术

606人已加入

描述

我是之前在试验STM32G031G8U6单片机内部FLASH读操作时候发现的这个问题:STM32F103的flash数据可以从任意地址读,而STM32G031G8U6就不行,读数据地址不对齐的话会死机,

关于什么是非对齐访问,可以参考下图1,来源于CM3权威指南CH5相关内容:

STM32F103

图1

先说结论:

1.Cortex-M0内核支持****非对齐访问

2.Cortex-M3内核支持非对齐访问

3.intel i5支持非对齐访问

4 .是否支持对存储器的非对齐访问取决于具体使用的内核

本次试验的完成耽误了很久,因为最近一周我得新冠了,体质差得新冠了啥也做不了。以下记录了我的 实验验证过程,过程很长, 没时间看的小伙伴了解上面的结论就够了 。我将在三种不同的内核平台上进行测试,为简化试验,我只在全局区验证,原因在于:相同数据类型变量在全局区和栈区的内存地址排列方式是不同的

一、在搭载intel i5 64位内核、安装了64位windows的电脑上,创建一个win32控制台应用程序,编译方式选择release模式**(关于CPU位数、操作系统位数、应用程序位数三者的内部关系这里不展开,我暂时也不咋清楚,本篇只关注非对齐访问这个主题)**

1 单字节数据类型: 全局变量紧挨着,按地址递减排列, 可以在任意地址访问,当然了单字节压根不存在非对齐访问的问题。 以下为试验代码,图2为执行结果

#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;
}

执行结果如下:

STM32F103

图2

2 两字节数据类型:全局变量的首地址自动按4字节对齐,因此没有挨着排列(2个padding字节) ,并且地址是从高到低的 ,但是可以进行非对齐访问。 以下试验代码的执行结果如图3,对变量c地址的两字节赋值操作,赋值后,它自个儿在内存中跨了两个4字节区域,也就是数据本身没有对齐,这个和它向下生长有关系,和我这里讨论的非对齐访问不是一回事,然后读的时候能顺利读出来,程序没有发生死机,也就是可以非对齐访问。

#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;
}

执行结果如下:

STM32F103

图3

3.四字节数据类型: 全局变量的首地址自动按4字节对齐,紧挨着、地址递减排列,可以进行非对齐访问。 执行结果如图4,在这个平台环境下,四字节数据的放置规则:首地址按四字节对齐,但是数据本身不对齐,和两字节数据一样,拆成了上下两部分,另外三个字节放到高地址的四个字节区域。

#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;
}

执行结果如下:

STM32F103

图4

二、32位的cm0内核,stm32g031单片机,裸机编程

1 单字节数据类型:全局变量紧挨着,和windows平台不一样, cm0内核平台在这里按地址递增排列 ,可以在任意地址访问,当然了单字节压根不存在非对齐访问的问题。以下为试验代码,图5为执行结果

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)
  {


  }
}

STM32F103

图5

2.两字节数据类型:全局变量的首地址自动按2字节对齐,因此没有挨着排列(2个padding字节) ,并且地址是从低到高的 ,不可以进行非对齐访问(无论进行非对齐的读还是写都会导致死机)。 以下试验代码的执行结果如图6,可通过解开屏蔽的代码来验证首地址对齐规则和非对齐访问规则

//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)
  {


  }
}

STM32F103

图6

3.四字节数据类型: 全局变量的首地址自动按4字节对齐,紧挨着、地址递增排列,不可以进行非对齐访问 (无论进行非对齐的读还是写都会导致死机) 验证代码的执行结果如图7,可以通过解开屏蔽的代码来验证首地址对齐规则和非对齐访问规则

//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("
");

STM32F103

图7

三、32位的cm3内核,stm32f103单片机,裸机编程

1.单字节数据类型:全局变量紧挨着,和windows平台不一样, cm3内核平台在这里按地址递增排列 ,可以在任意地址访问,当然了单字节压根不存在非对齐访问的问题。另外 和cm0内核不一样的是,cm3的编译严格要求变量声明在局部作用域的最前面 ,比如以下验证代码我将指针p的声明放到了最前面,否则编译将无法通过,图8为执行结果

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)
  {
  }
 }

STM32F103

图8

2.两字节数据类型:全局变量的首地址自动按2字节对齐,因此没有挨着排列(2个padding字节) ,并且地址是从低到高的 ,可以进行非对齐访问。 以下试验代码的执行结果如图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)
  {


  }
 }

STM32F103

图9

3.四字节数据类型: 全局变量的首地址自动按4字节对齐,紧挨着、地址递增排列,可以进行非对齐访问。 如下验证代码的执行结果如图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)
  {


  }
 }

STM32F103

图10

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分