ARM中的对齐问题

合集下载

非对齐数据访问

非对齐数据访问

ARM体系中存储系统非对齐的存储访问操作A RM系列处理器是RISC (Reducded Instruction Set Computing)处理器。

很多基于ARM的高效代码的程序设计策略都源于RISC处理器。

和很多RISC处理器一样,ARM系列处理器的内存访问,也要求数据对齐,即存取“字(Word)”数据时要求四字节对齐,地址的bits[1:0]==0b00;存取“半字(Halfwords)”时要求两字节对齐,地址的bit[0]==0b0;存取“字节(Byte)”数据时要求该数据按其自然尺寸边界(Natural Size Boundary)定位。

ARM编译程序通常将全局变量对齐到自然尺寸边界上,以便通过使用LDR和STR指令有效地存取这些变量。

这种内存访问方式与多数CISC (Complex Instruction Set Computing)体系结构不同,在CISC体系结构下,指令直接存取未对齐的数据。

因而,当需要将代码从CISC体系结构向ARM处理器移植时,内存访问的地址对齐问题必须予以注意。

在RISC体系结构下,存取未对齐数据无论在代码尺寸或是程序执行效率上,都将付出非常大的代价。

本文将从以下几个方面讨论在ARM体系结构下的程序设计问题。

未对齐的数据指针C和C++编程标准规定,指向某一数据类型的指针,必须和该类型的数据地址对齐方式一致,所以ARM编译器期望程序中的C指针指向存储器中字对齐地址,因为这可使编译器生成更高效的代码。

比如,如果定义一个指向int数据类型的指针,用该指针读取一个字,ARM 编译器将使用LDR指令来完成此操作。

如果读取的地址为四的倍数(即在一个字的边界)即能正确读取。

但是,如果该地址不是四的倍数,那么,一条LDR 指令返回一个循环移位结果,而不是执行真正的未对齐字载入。

循环移位结果取决于该地址向对于字的边界的偏移量和系统所使用的端序(Endianness)。

例如,如果代码要求从指针指向的地址0x8006载入数据,即要载入0x8006、0x8007、0x8008和0x8009四字节的内容。

ARM的七种异常类型

ARM的七种异常类型

ARM7支持六种操作模式:(1)用户模式(usr):正常的程序执行状态(2)FIQ模式(fiq):支持数据传送或通道处理(3)IRQ模式(irq):用于通用的中断处理(4)管理模式(svc):用于操作系统的保护模式(5)异常模式(abt):数据或者指令预取异常时进入(6)无定义模式(und):当无定义指令被执行时进入(7)软件控制,外部中断,异常处理都可以改变操作模式。

大部分的应用程序在用户模式下执行。

其他模式,比如管理模式,在中断、异常服务、或者访问被保护资源时进入。

ARM 的中央寄存器集是16 个用户寄存器R0 – R15。

这些寄存器均是32 位宽度,R0 – R12 没有其他特殊功能,寄存器R13 – R15在CPU中有特殊功能。

R13被用作栈指针(stack pointer,SP)。

R14被称为链接寄存器(link register, LR),当调用一个函数时返回地址被自动保存到链接寄存器,在函数返回时有效。

这使得快速进入和返回“叶”函数(不调用其他函数的函数)成为可能。

如果函数是分支的一部分(即该函数将调用另一个函数),链接寄存器必须入栈(R13)。

R15 是程序计数器(program counter, PC)。

有趣的是,许多指令也可以在R13 – R15中执行,就像它们是标准的用户寄存器。

ARM中断的问题ARM的七种异常类型---------1> 复位异常2> 数据访问中止异常3> 快速中断请求异常4> 一般中断请求5> 预取指令异常6> 软件中断异常7> 未定义异常-------------------------问题:1> 为什么除了进入复位异常模式外,在别的异常处理模式中都允许FIQ中断?2> 数据访问中止异常的优先级大于 FIQ异常,为什么在数据访问异常处理模式中,还允许 FIQ中断?这样不就成了:在高优先级异常处理中允许低优先级的中断发生?即使这样,因为FIQ中断的优先级 < 数据异常中断优先级,也不会进入 FIQ中断处理程序啊,这样不就更没有用处了??ARM体系的各种异常的分析(学习日记)- [ARM7TDMI]版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明/logs/10669519.html1.复位异常(1)当内核的nRESET信号被拉低时,ARM处理器放弃正在执行的指令,当nRESET信号再次变高时,ARM处理器进行复位操作;(2)系统复位后,进入管理模式对系统进行初始化,复位后,只有PC(0x00000000)和CPSR (nzcvqIFt_SVC)的值是固定的,另外寄存器的值是随机的。

ARM处理器下的内存对齐问题

ARM处理器下的内存对齐问题

ARM处理器下的内存对齐问题介绍内存访问可以分为aligned和未对齐unaligned.对齐内存访问发生在数据分配在natural size boundary,如果这个数据的大小是4 bytes,而且它分配的地址可以被4整除,它就是分配在natural size boundary的,它就是内存对齐的.未对齐内存访问就是其他的所有情况(内存地址不能被4整除);ARM处理器被设计成可以高效的访问对齐数据,在ARM处理器上尝试访问未对齐内存数据将得到两种结果:错误数据或显著的执行差异(很快会讨论这些不同的表现).这不同于其他的CISC类型的处理器,它们可以正常的访问未对齐数据.这篇文档将会描述一些对应用程序来说通用的方式处理未对齐内存访问和提供一些推荐的解决方案以解决这些问题.症状上述问题针对所有ARM架构的.然而,根据MMU是否使能和操作系统的支持,应用程序在不同的平台上有不同的表现.在默认情况下,未对齐内存访问不会被捕捉,而是返回一个错误数据.在使能了MMU的平台上,OS将可以捕捉未对齐内存访问而且在运行时调整正确.返回的结果将是正确的数据,但是将花费 10-20个cpu周期.通常原因类型分配Code:void my_func(char *a){int *b = (int *)a;DBGPRINTF("%d", *b);}这个简单的例子可以生成未对齐内存访问,因为我们不能保证参数char* a是在4字节边界上的.这样的类型定义在任何时候都应该避免.使用数据buffer大多数常见的未对齐内存访问发生在错误的处理数据buffer,这些数据buffer可能包含任何从usb端口,网路,或文件中读取的数据.通常会设置这些数据为packed,意味着没有padding嵌入以确保buffer中的数据是natural size boundary的.在这个例子中,我们将讨论装载从一个文件一个windows BMP格式数据,然后解析其文件头的情况.一个windows BMP文件包含一个以下数据项的文件头,文件头由两个结构体组成:Code:typedef PACKED struct{unsigned short int type;unsigned int size;unsigned short int reserved1, reserved2;unsigned int offset;} HEADER;typedef PACKED struct{unsigned int size;int width,height;unsigned short int planes;unsigned short int bits;unsigned int compression;unsigned int imagesize;int xresolution,yresolution;unsigned int ncolours;unsigned int importantcolours;} INFOHEADER;注意HEADER和INFOHEADER结构体的大小分别是14和40字节.假设我们想在程序运行是检测图片的宽带和高度,得到这些数据的代码如下:Code:#define INFOHEADER_OFFSET (sizeof(HEADER))#define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width))#define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height))int imageWidth, imageHeight;void * fileBuf;pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ);if (pMe->mFile){IFILE_GetInfo(pMe->mFile, &fileInfo);fileBuf = MALLOC(fileInfo.dwSize);if (fileBuf){result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize);if (result == fileInfo.dwSize){imageWidth = *((uint32*)(((byte*)fileBuf) + WIDTH_OFFSET));imageHeight = *((uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET));}}}注意宽度和高度的偏移.因为它们位于一个half-word boundary,以上面的代码访问它们的值将是未对齐内存访问.一些推荐的方式解决这个问题如下:推荐方案使用memcpy我们第一个选择是简单的使用memcpy将数据从buffer处理到我们的局部变量中:Code:if (result == fileInfo.dwSize){MEMCPY(&imageWidth,(((byte*)fileBuf)+WIDTH_OFFSET),sizeof(uint32));MEMCPY(&imageHeight,(((byte*)fileBuf)+HEIGHT_OFFSET),sizeof(uint32));}结果是内存被紧密的拷贝,避免了对齐问题.使用PACKED编译指令或者,我们可以使用PACKED编译指令以允许使用指针直接的访问我们想要的数据.也就是强制编译器处理对齐问题.在BREW环境下,PACKED定义如下:Code:#ifdef __ARMCC_VERSION#define PACKED __packed#else#define PACKED#endif通过标明一个指针是PACKED的,ARM编译器将始终生成合适的指令可以正确的访问内存.不管对齐,上边的例子的一个修改的版本,使用PACKED的指针,如下:Code:#define INFOHEADER_OFFSET (sizeof(HEADER))#define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width))#define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height))PACKED uint32 * pImageWidth;PACKED uint32 * pImageHeight;uint32 imageWidth, imageHeight;void * fileBuf;pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ);if (pMe->mFile){IFILE_GetInfo(pMe->mFile, &fileInfo);fileBuf = MALLOC(fileInfo.dwSize);if (fileBuf){result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize);if (result == fileInfo.dwSize){pImageWidth = (uint32*)(((byte*)fileBuf) + WIDTH_OFFSET);pImageHeight = (uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET);imageWidth = *pImageWidth;imageHeight = *pImageHeight;}}}定义Well-Aligned数据结构虽然我们一般不能控制定制的数据格式,比如上面例子中的BMP文件头,但是,当我们定义自己的数据结构我们可以将数据设计成Well-Aligned方式.以下例子演示这种方式:Code:#ifdef __ARMCC_VERSIONtypedef PACKED struct{short a; // offsetof(a) = 0int b; // offsetof(b) = 2 ?misalignment problem!short c; // offsetof(c) = 6} BAD_STRUCT;typedef struct{int b; // offsetof(b) = 0 ?no problem!short a; // offsetof(a) = 4short c; // offsetof(c) = 6} GOOD_STRUCT;简单的重新定义结构提成员的顺序,我们可以解决一些对齐问题.同时注意如果BAD_STRUCT没有定义为PACKED,编译器一般将会插入padding以使每个成员是Well-Aligned的.然而,这通常是不可取的,因为它浪费内存,而且几乎总是可以通过按顺序声明减少大小而避免。

gcc arm cache aligned 参数

gcc arm cache aligned 参数

gcc arm cache aligned 参数
在GCC中,可以使用特定的参数来控制生成的代码对ARM架构的缓存对齐要求。

这主要涉及数据结构的对齐,以便更有效地利用硬件特性,例如减少内存访问延迟和提高数据存取速度。

在GCC中,你可以使用aligned属性来指定一个变量或结构体应该对齐到特定的地址边界。

例如,要指定一个变量对齐到16字节边界,你可以这样做:
c
int __attribute__((aligned(16))) my_variable;
对于结构体,你可以这样指定:
c
struct __attribute__((aligned(16))) MyStruct {
int field1;
double field2;
// 其他字段...
};
这样,GCC将会生成代码以确保my_variable和MyStruct的实例对齐到指定的地址边界。

对于缓存对齐,你需要确保你的数据结构对齐到缓存的边界,通常是缓存行的大小。

这有助于提高数据访问的速度并减少缓存未命中(cache misses)的可能性。

请注意,具体的缓存行大小取决于具体的ARM架构和处理器实现。

你需要查阅相关的ARM架构文档或处理器手册以获取准确的缓存行大小信息。

此外,你还可以使用GCC的-malign-double和-malign-double-armv8选项来强制对齐双精度浮点数和ARMv8双精度浮点数。

这些选项可以影响编译器生成的代码,以确保双精度浮点数对齐到适当的地址边界。

cortex-m3栈的8字节对齐

cortex-m3栈的8字节对齐

cortex-m3栈的8字节对齐⼀、什么是栈对齐?栈的字节对齐,实际是指栈顶指针须是某字节的整数倍。

因此下边对系统栈与MSP,任务栈与PSP,栈对齐与SP对齐这三对概念不做区分。

另外下⽂提到编译器的时候,实际上是对编译器汇编器连接器的统称。

之前对栈的8字节对齐理解的不透,就在⽹上查了好多有关栈字节对齐、还有⼀些ARM对齐伪指令的资料信息,⼜做了⼀些实验,把这些零碎的信息拼接在⼀起,总觉得理解透这个问题的话得长篇⼤论了。

结果昨天看了AAPCS⼿册、然后查到了没有使⽤PRESERVE8伪指令出现错误的实例,突然觉得长篇⼤论不存在了,半篇⼩论这问题就能理顺了。

⼆、AAPCS栈使⽤规约在ARM上编程,但凡涉及到调⽤,就需要遵循⼀套规约AAPCS:《Procedure Call Standard for the ARM Architecture》。

这套规约⾥⾯对栈使⽤的约定如下:5.2.1.1Universal stack constraintsAt all times the following basic constraints must hold:Stack-limit < SP <= stack-base. The stack pointer must lie within the extent of the stack.SP mod 4 = 0. The stack must at all times be aligned to a word boundary.A process may only access (for reading or writing) the closed interval of the entire stack delimited by [SP, stack-base – 1](where SP is the value of register r13).NoteThis implies that instructions of the following form can fail to satisfy the stack discipline constraints, even when reg points within the extent of the stack.ldmxx reg, {..., sp, ...} // reg != spIf execution of the instruction is interrupted after sp has been loaded, the stack extent will not be restored, so restarting theinstruction might violate the third constraint.5.2.1.2Stack constraints at a public interfaceThe stack must also conform to the following constraint at a public interface:SP mod 8 = 0. The stack must be double-word aligned.可以看到,规约规定,栈任何时候都得4字节对齐,在调⽤⼊⼝得8字节对齐。

ARM平台的地址对齐问题

ARM平台的地址对齐问题

ARM平台的地址对齐问题前言ARM流行已久,做嵌入式开发的不知道ARM不大可能。

鉴于其所具备的较低功耗下的较高性能,也就成了大多数嵌入式设备的首选了。

不过对于刚上手的人来说,有可能会遇到一些稀奇古怪的问题。

毕竟大部分人都习惯了IA-32下的程序设计,虽然两者都是32位的处理器,但是体系架构完全不同,于是也导致了一些隐含的问题。

这里想描述一下一个有点蛊惑的问题,即在ARM上访问非对齐地址内容,会出现所谓“不可预料”结果的问题。

ARM内存访问的对齐问题按照ARM 文档上的描述,其访问规则如下: 1. 一次访问4字节内容,该内容的起始地址必须是4字节对齐的位置上; 2. 一次访问2字节内容,该内容的起始地址必须是2字节对齐的位置上;(单字节的没有这个问题,就不用考虑啦。

)好,既然规则如此,那应该遵守。

不过么,不安分的人往往喜欢破坏规则,喜欢看看不遵守规则会有什么结果;另外么,即便遵规蹈距的人,有时也难免考虑不周,犯个错也是正常现象。

好,那么让我们来看看犯错的结果吧。

例如下面的代码:char buff[8] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xbc, 0xcd};int v32, *p32;short v16, *p16;p32 = (int*)&( buff[1] ); //unalignment p16 = (short*)&( buff[1] ); //unalignment v32 = *p32; //what’s the result? v16 = *p16; //what’s the result? 如果上面这段代码在IA-32上运行,那么结果应该如下: v32 = 0x9a785634 v16 = 0x5634 即便非对齐地址上访问,IA-32也就是牺牲一点性能,但是结果保证是正确的。

恩,这也是我们所期望的…… 可是…… 换到ARM上呢?我们来看看在ADS1.2编译后,执行的结果如下: v32 = 0x12785634 v16 = 0x1234 这个结果有点奇怪了吧。

arm字节对齐

arm字节对齐

arm中的字节对齐问题(2011-04-02 11:03:56)转载标签:杂谈昨天调程序,发现一个不得解的问题,传过来的地址明明是正确的,可是一读却是一条非法指令。

不写操作。

很长时间不能解决,师兄过来看了一下,才现是字节对齐的问题。

唉,终于碰到对齐问题了,那就好好解决一下吧。

先说下我遇到的问题typedef sturct{char a;char b;char c[255];} FS;FS fs;....disk_read(...,fs->c,...)....void disk_read(...,int* p,...){....*p++=0x01010101; //error:....}到了这一步,也就可以看一下问题发生的原因了,在结构体中,对将c做到字节对齐,这样和disk_read中的P的四字节对齐是不同的,这样写的时候由于不是四字节对齐,就会出错下面转两篇字节对齐的文章。

戒之戒之=====================================================有了上面的基础后,在一些数据结构中就要消除这些字节带来的影响,特别是在文件访问的过程中,在各个平台上都会遇到。

文件为了保持最小,利用空间的原则,会按照字节来存储的,但是我们在内存中定义的结构会按最优原则使效率最大,这样会保持边界对齐。

那么如何消除影响呢,先看在 vc中如保操作#pragma pack(push, 1) == #paragma pack(push) #paragma pack(1) struct T{int a;char b;}#paragma pack(pop)再来看看ads 在arm平台是如何操作的__packedstruct T{int a;char b;}最后来看看gcc下面的操作__attribute__((__packed__))struct T{int a;char b;}最后,让我们来看看怎么定义一个结构才是移植性够好的结构PACK_STRUCT_BEGINstruct ip_hdr {PACK_STRUCT_FIELD(u16_t _id);PACK_STRUCT_FIELD(struct ip_addr src);} PACK_STRUCT_STRUCT;PACK_STRUCT_END通过引个宏来改变这些相应的结构#ifdef __GNU_C__#define PACK_STRUCT_FIELD(x) x#define PACK_STRUCT_STRUCT __attribute__((__packed__))#define PACK_STRUCT_BEGIN#define PACK_STRUCT_END#elif__ADS__#define PACK_STRUCT_FIELD(x) __packed x#define PACK_STRUCT_STRUCT#define PACK_STRUCT_BEGIN __packed#define PACK_STRUCT_END#elif __VC__#define PACK_STRUCT_FIELD(x) x#define PACK_STRUCT_STRUCT#define PACK_STRUCT_BEGIN #pargma pack (push, 1) (问题代码) #define PACK_STRUCT_END #pargma pack (pop) (问题代码)#else#define PACK_STRUCT_FIELD(x)#define PACK_STRUCT_STRUCT#define PACK_STRUCT_BEGIN#define PACK_STRUCT_END#endif好啦,到此为止一切都结束了,这就是相关的字节对齐的一些操作,原来我只以为只有最后这一种情况才是呢,最上面的那种情况是后来调试才遇到的,至于以后,可能还会有,再做补充了ps:上面有两行问题代码,自己用的时候才发现,宏定义中出现"#",几乎不可能,所以放弃这样的想法宏定义中的#可以把一个数字变成字串 ##则代表字符串连接。

arm32位的cpu对齐方式

arm32位的cpu对齐方式

arm32位的cpu对齐方式
ARM 32位CPU的对齐方式是指数据在内存中的存储位置。

对齐
是指数据在内存中的起始地址是否符合一定的规则。

ARM架构中,
数据对齐通常指的是按照数据的大小将数据存储在内存中的起始地址,以提高数据访问的效率和性能。

在ARM 32位架构中,数据对齐通常遵循以下规则:
1. 字节对齐,8位数据的起始地址可以是任意地址;16位数据
的起始地址必须是2的倍数;32位数据的起始地址必须是4的倍数。

2. 半字对齐,16位数据的起始地址必须是2的倍数;32位数
据的起始地址必须是4的倍数。

3. 字对齐,32位数据的起始地址必须是4的倍数。

对齐的好处在于可以提高内存访问的速度,因为处理器可以更
快地访问对齐数据。

如果数据没有按照规定的对齐方式存储,处理
器可能需要多次访问内存,降低了访问效率,甚至可能导致错误或
异常。

总的来说,ARM 32位CPU的对齐方式是按照数据的大小将数据存储在内存中的起始地址,以提高数据访问的效率和性能。

这种对齐方式是为了充分利用处理器的特性,提高数据访问的效率。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

ARM中的对齐问题
在ARM 中,当吧一个内存区域初始化为某个结构体时,必须注意字节对
齐情况。

1. 简介
在ARM 中,有ARM 和Thumb 两种指令。

ARM 指令:每执行一条指令,PC 的值加4 个字节(32bits).一次访问4 字节内容,该字节的起始地址必须是4 字节对齐的位置上,
即地址的低两位为bits[0b00],也就是说地址必须是4 的倍数。

Thumb 指令:每执行一条指令,PC 的值加2 个字节(16bits).).一次访问2 字节内容,该字节的起始地址必须是2 字节对齐的位置上,
即地址的低两位为bits=0,也就是说地址必须是2 的倍数。

遵循以上方式叫对齐(aligned)方式,不遵守这样方式称为非对齐(unaligned)的存储访问操作。

ARM CPU 不支持未对齐双字(8 bytes)访问。

双字访问必须是8 字节/4 字节对齐.
2. ARM 平台中的字节对齐关键字
(1) __align(num)
(2) __packed
进行一字节对齐。

(3) __unaligned
用于修饰某个变量,可按照非对齐方式访问。

3. __packed 与#pragma pack(1)的区别
4. __attribute__((aligned))
用于指定类型的最低对齐要求.。

相关文档
最新文档