存储器系统的非对齐访问

发布者:幸福之路最新更新时间:2023-10-30 来源: elecfans关键字:存储器系统  非对齐访问  STM32 手机看文章 扫描二维码
随时随地手机看文章

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


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

图片

图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;

}

执行结果如下:

图片

图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;

}

执行结果如下:

图片

图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;

}

执行结果如下:

图片

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

  {



  }

}

图片

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

  {



  }

}

图片

图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("

");

图片

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

  {

  }

 }

图片

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

  {



  }

 }

图片

图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("

[1] [2]
关键字:存储器系统  非对齐访问  STM32 引用地址:存储器系统的非对齐访问

上一篇:Linux系统移植开发篇1:系统移植前说明及源码编译
下一篇:C语言面向对象编程的最佳实

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved