ARM平台的对齐问题.doc_第1页
ARM平台的对齐问题.doc_第2页
ARM平台的对齐问题.doc_第3页
ARM平台的对齐问题.doc_第4页
全文预览已结束

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ARM平台的对齐问题(有关_packed)前言ARM流行已久,做嵌入式开发的不知道ARM不大可能。鉴于其所具备的较低功耗下的较高性能,也就成了大多数嵌入式设备的首选了。不过对于刚上手的人来说,有可能会遇到一些稀奇古怪的问题。毕竟大部分人都习惯了IA-32下的程序设计,虽然两者都是32位的处理器,但是体系架构完全不同,于是也导致了一些隐含的问题。这里想描述一下一个有点蛊惑的问题,即在ARM上访问非对齐地址内容,会出现所谓“不可预料”结果的问题。ARM内存访问的对齐问题按照ARM文档上的描述,其访问规则如下:1. 一次访问4字节内容,该内容的起始地址必须是4字节对齐的位置上;2. 一次访问2字节内容,该内容的起始地址必须是2字节对齐的位置上;(单字节的没有这个问题,就不用考虑啦。 )好,既然规则如此,那应该遵守。不过么,不安分的人往往喜欢破坏规则,喜欢看看不遵守规则会有什么结果;另外么,即便遵规蹈距的人,有时也难免考虑不周,犯个错也是正常现象。好,那么让我们来看看犯错的结果吧。例如下面的代码:char buff8 = 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xbc, 0xcd;int v32, *p32;short v16, *p16;p32 = (int*)&( buff1 ); /unalignmentp16 = (short*)&( buff1 ); /unalignmentv32 = *p32; /whats the result?v16 = *p16; /whats the result?如果上面这段代码在IA-32上运行,那么结果应该如下:v32 = 0x9a785634v16 = 0x5634即便非对齐地址上访问,IA-32也就是牺牲一点性能,但是结果保证是正确的。恩,这也是我们所期望的可是 换到ARM上呢?我们来看看在ADS1.2编译后,执行的结果如下:v32 = 0x12785634v16 = 0x1234这个结果有点奇怪了吧。照理说指向0x34,那么如果是Big-Endian的话,v32应该是0x3456789a,如果是Little-Endian的话,就是前面IA-32的结果。可现在的结果呢?两者都不是,莫名地把更低地址的0x12给凑进来了 而如果看看编译生成的汇编code的话,这两个赋值很简单,分别用了ldr和ldrsh指令,指令没有问题,分别用于读取32位和16位数据,都是最基本的指令。嗯,嗯,这就是我们所要描述的访问非对齐地址的问题了。 问题的缘由(个人猜测,非官方资料)个人感觉呢,这是ARM体系架构实现的问题,或者说这本来就是By Design的。这样做简化了处理器的实现,IA-32实现的时候肯定会对读取地址是否对齐进行判断,然后转换为相应的操作 ,而ARM呢?没有做这个事情,默认认为大家都按照规矩办事,你要是胆敢破坏,俺就给你好看那有没有办法解决呢?这个问题其实ARM自己也知道,所以呢,它在编译器里面,已经添加了部分支持。不过有人会问,那上面那个情况呢?为什么结果还是不对呢?好像没有添加什么支持嘛嗯,其实ARM是做了一定的努力的,只是这个情况它没办法解决 它做的事情就是:在编译器能够的得知的情况下,尽量保证访问内容的正确。这句话有点笼统,那么把具体情况一个个来看看吧。编译器的努力(1) 所有局部/全局/静态等变量都放在4字节对齐的地址上其实这个努力很常见,由于在32位平台上,一次访问4字节是效率最高的,所以大多数32平台的编译器都如此处理,ARM的ADS也不例外。编译器的努力(2) 填充、填充、再填充这个事情么,其实也是常见的。各类编译器上,对于某些结构定义中会产生不对齐的情况,自动填充,以提高访问效率(例如IA-32上访问非对齐的,会加1个周期的)。而ARM的编译器也一样操作,不过感觉这里不单单是为了提高效率,也能够顺带解决这个不对齐的问题。编译器的努力(3) 产生特殊代码嗯,这个就是关键了,也是ARM编译器的与众不同之处。先来看一段代码:_packed typedef struct _test char a; short c; int d; test;char buff8 = 0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xbc, 0xcd;test *p = (test *)buff;v32 = p-d; /这里的v32借用上面的定义; 貌似多了个限定为_packed的struct,以此来造成不对齐的状况,看不出多大区别嘛。可是运行一下的话,就会发现这里的结果是正确的。我们来看看ADS生成的汇编代码吧。 v32 = q-d;0xe2890003 add r0,r9,#30xeb000088 bl _rt_uread40xe1a05000 mov r5,r0看到这里的那条bl _rt_uread4的指令了吧。对ARM指令有一定了解的都知道bl其实就是一个函数调用。所以,这里的代码其实是调用了ADS自己提供的_rt_uread4函数,该函数完成的操作就是读取四个字节。ADS提供了类似的一系列函数,针对signed/unsigned,以及4字节/2字节的读取/写入操作。估计看到这里,大家会问,如果没有_packed限定符呢?猜对了,没有_packed限定符,那么编译器会对上面的情况pending,所以这个struct里面的d所在的位置是4字节对齐的(编译期信息,而非实际运行期信息)。所以就回到类似最初的例子了。那么,还有一种情况,就是在有_packed的情况下,而struct里的字段都是符合对齐要求的,那么生成的代码会是怎么样的呢?从实际生成的代码来看,和上面的这段汇编代码,唯一的区别就是第一条指令把#3改成了#4,而后面仍旧调用_rt_uread4函数。嗯,这样结论就出来了:编译器会在使用_packed的情况下,自动对其中的4字节/2字节访问添加特殊代码,以保证其结果的正确。好了,这个关于这个问题描述得差不多了,可能的话,尽量倚赖编译器的这些功能,而对于编译器无能为力的部分,就要靠万分小心了 p.s. 其实这里有很多事情可以来尽量预防此类问题,比如嵌入式项目往往喜欢自己管理内存分配,那么自己写的内存分配函数就保证返回的地址都是4字节对齐位置上的 32位嵌入式系统的字节对齐!(重要)32位嵌入式系统的软件开发过程中,字节对齐问题是相当重要的。我们现在就拿ARM处理器和ADS1.2开发环境作为例子说明字节对齐的概念。 在此之前,我先声明几个基本的概念: (1)、对象:在C语言中使用结构体类型、共同体类型、或内部基本类型所定义的变量或常量,就称为对象。对象占据了一块实际的存储器空间,这块空间有固定的起始地址和字节数。 (2)、引用:使用对象有两种方法:“对象名”和“引用”。当你在源代码中定义一个对象时,编译器就会为它分配一块存储器,此时你就可以使用“对象名”来操作该对象。但是对于程序运行时动态分配的某一块存储器空间(对象),你就没法使用“对象名”了,而只能使用“引用”,所以,“引用”就是指向特定类型的对象的指针。 好了,我们转入正题。 在32位嵌入式系统中,单字节对象是1字节对齐的;双字节对象是2字节对齐的;四字节对象是4字节对齐的;其它结构体或共同体对象是8字节对齐的。也就是说,当你定义一个单字节对象时,该对象的起始地址可以是任何整数;当你定义一个双字节对象时,该对象的起始地址必定是2的倍数的整数;当你定义一个四字节对象时,该对象的起始地址必定是4的倍数的整数;当你定义一个结构体或共同体对象时,该对象的起始地址必定是8的倍数的整数。以上说的对象包括“结构体或共同体对象的成员对象”。 字节对齐的故障只能出现在“引用”的使用过程中。当你使用“对象名”来操作对象时,根本不用担心字节对齐问题。 在ADS环境下,有“ALIGN” 、“_align(x)” 、“_packed”关键字用于字节对齐处理。ALIGN用于汇编语言,_align(x)用于C语言,_packed用于放弃字节对齐。 单字节对齐类型的引用可以操作任何对象,双字节对齐类型的引用可以操作双字节、四字节、八字节对齐的对象,。只有遵守这个规则,你的程序才可能是健壮的。 如果你确实想使用双字节对齐类型的引用来操作单字节对齐对象,那么你在定义该引用时必须使用_packed关键字! 好了,再多的东西我也说不清楚,给大家这么一个提醒已经足够了,希望大家引起注意。Accessing unaligned data from CDescriptionIt is sometimes necessary to access unaligned data in memory, in particular for embedded systems. The following describes how this can be doen from C code for ARM or Thumb code. Like other RISCs, ARM and Thumb processors are designed to efficiently access aligned data (i.e. words which lie on addresses that are multiples of 4, halfwords which lie on addresses that are multiples of 2). This is because memory controllers typically ignore A1:0 for word accesses and A0 for halfword accesses. Using a conventional C pointer ( *) to read a word, ARM compilers will use anLDRinstruction in the generated code. This works as expected when the address is a multiple of 4 (i.e. on a word boundary). However, if the address is not a multiple of 4, then anLDRwill return a rotated result rather than performing a true unaligned word load. For more details on howLDRworks, seeWhat does the ARM core read/write when using non-aligned addresses? and the ARM7TDMI datasheet. Generally this rotation is not what the programmer is expecting. All data accesses can be classified into the following categories:(1) Natural alignment (eg. words on word boundaries, for example at0x1000) ARMs compilers normally align variables and pad structures so that these items are accessed efficiently using theLDR/STRinstructions. (2) Known, but non-natural, alignment (eg. word at address0x1001) Packed structures used to store records without padding may contain non-natural alignment fields.a) Declare the entire struct as _packed. Each field then inherits the _packed qualifier. _packed struct mystruct char c; short s; As part of its optimization, the ARM compiler will try to deduce the alignment of each field, to improve access to the fields. However, be aware that this optimization is only possible with local structures (and global structures with-zat4), and may not work in every case. A better alternative is to use b) below.b) Declare non-aligned fields as _packed. This is the recommended approach, and the only way of guaranteeing fast access to naturally aligned members within the struct. This also makes it clearer to the programmer which fields are non-aligned, but be careful when adding/deleting fields from the struct.struct mystruct char c; _packed short s; When a non-aligned member of a packed struct is accessed, the ARM compiler will use multiple aligned memory accesses (LDR/STR/LDM/STM) combined with fixed shifting and masking to access the correct bytes in memory. For more details on packed structs please see theSDT 2.50 Reference Guide, section 3.1.4 (3) Unknown alignment (eg. a pointer to a word that can be at any address) This is specified by a pointer to a_packeditem, eg. _packed int *pi; In this case, the ARM compilers generate code which correctly accesses the value regardless of the alignment of the pointer. This code generated will be a sequence of byte accesses, or variable alignment-dependent shifting and masking (depending on the compile options) and will therefore incur a performance penalty. Note that *any*_packedobject accessed through a pointer has unknown alignment, even packed structures. Porting code Legacy C code for other architectures (e.g. x86 CISC) may perform accesses to unaligned data using pointers which will not work on the ARM. This is non-portable code - such accesses must be identified and corrected to work on RISC architectures which expect aligned data. Identifying the unaligned accesses can be difficult, because use of load or store with unaligned addresses will give incorrect behavior. But it will be difficult to trace which part of the C source is causing the problem. ARM processors with full MMUs (e.g. ARM920T) support optional alignment checking where the processor will check every access to ensure it is correctly aligned. The MMU will raise a data abort if an incorrectly aligned access occurs. Some ARM partners using simple cores such as the ARM7TDMI have implemented alignment-checking for their ASIC/ASSP. This can be done with an additional hardware block external to the ARM core, which monitors the access size and the least significant bits of the address bus for every data access. The ASIC/ASSP can be configured to raise the ABORT signal in the case of an unaligned access. ARM recommends that such logic is included on ASIC/ASSP devices where code will be ported from other architectures. If the system is configured to abort on unaligned accesses, a data abort exception handler should be installed. When an unaligned access occurs, the data abort handler will be entered - this can identify the erroneous data access instruction which is located at (r14-8). Once identified, the data access must be fixed by changes to the C source. These changes can be made conditional using the following technique: #ifdef _ARMCC #define PACKED _packed#else #define PACKED#endif: PACKED int *pi;: It is best to minimise accesses to unaligned data because of code size and performance overheads.Be careful when accessing memory-mapped peripherals using_packed, because the ARM compilers can use multiple memory accesses to retrieve the data, and may also access nearby locations which might correspond to other peripheral registers. When bitfields are used, the ARM compiler curren

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论