What is non-aligned access? (ARM/Keil)
Asked Answered
D

5

7

I'm using Keil to write Assembly for ARM 7.

I have the following runtime error:

Non-aligned Access: ARM Instruction at 000000F8H, Memory Access at 7F7F7F7FH
Data Abort: ARM Instruction at 000000F8H, Memory Access at 7F7F7F7FH

This doesn't really help me, because I don't know what 'non-aligned access' is, (other than the obvious, but I don't really understand what it means) and I am trying to access (store) to 0x7F7F7F7F, what's the issue?

Searching I only found a couple of similar issues, both using C, and resolved by some means very specific to their code and which didn't relate to this issue.

I'm doing:

LDR R0, =0x7F7F7F7F
LDR R1, LABEL
STR R1, [R0]

I then do a similar thing with a different label, and offsets of R0, but it fails here first.

Doukhobor answered 4/1, 2014 at 20:32 Comment(0)
U
8

The issue is that the address you use for a 32-bit (4-byte) memory operation must be aligned to a 4-byte boundary. This means the address must be a multiple of 4, or if you prefer, the bottom two bits of the address must be zero.

In this case, the closest 4-byte aligned addresses would be 0x7F7F7F7C or 0x7F7F7F80.

Similarly, LDRH/STRH require 2-byte alignment, whereas LDRB/STRB can operate anywhere (1-byte alignment == unaligned).

In general the compiler/assembler takes care of making sure your variables are aligned correctly for the size they are - you should only run into this if you're generating addresses yourself (as per the question).

Upholster answered 4/1, 2014 at 20:38 Comment(5)
D'oh. Of course. What happens if you do LDR Rd [Rx] where Rx is not aligned? Does it round up, down, or throw an error?Doukhobor
@OllieFord depending on the setting of the alignment flag in the SCTLR, you'll either get an exception, or the data from the rounded-down address rotated (basically the result of an LDRB without the final mask down to 8 bits) - I've been mostly working with v7 and v8 stuff that don't have this restriction so believe the ARM ARM over me if that's not quite right ;)Upholster
Thank you for your help. Managed to work around it, use different addresses. I still get the data abort error though? (Same code as above, but R0 := 0x0F000000)Doukhobor
@OllieFord is there actually anything mapped at that address? Your SoC manual should tell you the memory map i.e. what addresses actually have RAM or I/O ports behind them.Upholster
Bugger. Got it, thanks. This will be harder than I thought. Was meant to be a 'clever' trick to speed code up, but it's actually looking like by the time I've made it work, it may well slow it down!Doukhobor
H
5

When we talk about decades we talk about the 70's or 80s, etc. We dont talk about the 62s or the 94s. 70, 80, 90 are numbers that are aligned on tens. 10 to the power 1. centuries are things aligned on 10 to the power 2, 100s. the 1900s the 1400s, etc. Or think of it as that many number of zeros at the end of the number.

Same for addressing bytes. With a single year being the smallest unit in the example above a byte is the smallest unit when we talk about memory addresses, a bit is smaller yes but we dont address bits individually. We address bytes. Like years above, any individual year can be talked about 1971, 1436, etc. Same with an address 0x1234, 0x21. but when we want to start doing 16 bit accesses using an 8 bit addressing scheme that is like talking about decades, 2 to the power 1 so units of 2 0,2,4,6,8 are ALIGNED address for accessing 2 to the power of 1 number of bytes (one 16 bit number, 2 bytes). if we want to do a 32 bit access that is a 4 byte access or 2 to the power 2, like centuries above we need two zeros at the end of the address 0x0, 0x4, 0x8, and so on (a 4 is 100 binary, an 8 is 1000 binary 0xC is 1100 binary, two zeros at the end). And so on 64 bit accesses are 8 bytes or 2 to the power of 3 number of bytes so an aligned address has 3 zeros at the end anything without 3 zeros at the end is unaligned.

Your 32 bit access above uses an address that ends in 0x7F which in binary is 01111111 the last two bits are 11 which is not zeros, so that is not an aligned access.

What does arm or mips or any other computer do when you do an unaligned access, some trap an exception and not let you do it some swizzle the data around in a way you wouldnt expect, and some just let you do it. And some like the newer arms can be configured at runtime for different responses, newer arms can let you have that x86 like experience.

Unfortunately there are too many x86's and to many bad programming habits that have come out of the x86 not expressing the penalty more, there is definitely a penalty on an x86 for using unaligned accesses, the penalty is performance. arm and mips and others prefer to just kill your program with an exception as a very harsh penalty but a good one because it teaches you not to do that.

If you have something at that address then you should probably access it using smaller transfer sizes (four individual byte transfers or two byte and one halfword) and the combine the bytes together into a 32 bit number if you really need it as a 32 bit number.

Houck answered 5/1, 2014 at 0:49 Comment(7)
Actually, on modern Intel architectures it's not such a big deal performance wise. On the i7 it is essentially the same.Cleotildeclepe
ALL architectures do, the memory bus is not infinitely wide, there would be billions of pins in order to remove the penalty. the penalty also hits the cache, etc, in fact caches CAN (not on every access) amplify the penalty of not doing aligned accesses. No architecture can avoid this.Houck
What I'm trying to do is branch to a different location, based on register content. Load the value Rx points to, and branch to the address specified in that location. Your comments made me re-think however, and I realised this method would have been sub-optimal anyway, thanks to the necessary LDR. I am instead now setting flags with previous instruction, then a BEQ and BNE - obviously whichever is second has a 1 instr penalty, so I'm playing with the order to decide which works better.Doukhobor
you definitely cannot branch to an unaligned address, all instructions are aligned be it 16 bit thumb or 32 bit arm instructions.Houck
to branch to a register address you use bx, bx r2 or whatever the register is.Houck
'which in binary is 91111111' - A mistake I think.Pliocene
thanks, yep, pretty close though, one key on the keyboard away from the target.Houck
C
1

Some platforms will not allow unaligned access to memory, as you have noticed. Reads and writes are expected to be aligned on N byte boundaries. I don't know your platform, but let's assume we require 4-byte alignment.

You have an address 0x7F7F7F7F. 0x7F7F7F7F % 4 == 3, i.e., you have a remainder and this address is not aligned on a four byte boundary. Unaligned access on many platforms will be slower than aligned access (you may want to look at how C pads structures), but some architectures simply do not allow it at all.

So, if you're going to be plopping data down at fixed addresses, make sure said address begins at an N byte boundary (where N is 4 for LDR on an ARM 7). Your compiler will synthesize aligned access for you, but not if you're hardcoding your addresses (obviously).

From some quick reading, it seems that unaligned accesses are in fact allowed on ARM7, but there is a caveat. From the link below:

Further, unaligned accesses are only allowed to regions marked as Normal memory type, and unaligned access support must be enabled by setting the SCTLR.A bit in the system control coprocessor. Attempts to perform unaligned accesses when not allowed will cause an alignment fault (data abort).

Further reading: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka15414.html

Cleotildeclepe answered 4/1, 2014 at 20:38 Comment(1)
I won't duplicate my comment: #20926886Doukhobor
A
0

Everything above is correct but i am not sure that the code you show will do what you really want: I guess you define correctly LABEL which is word aligned so why to load an hex address as the place you want to store. Maybe you just want to store the value x'7F7F7F7F' at the memory place LABEL. In this case you will have to write STR R0, [R1]

Anatolio answered 3/3, 2014 at 17:38 Comment(0)
F
-1

check the data type.

eg Suppose you have declared an array as unsigned int V1[25][25]; and you have extern it as extern int (*V1)[22];

suppose you are using function that returns this as,

unsigned long func()
   {`unsigned long k;
      return (V1[0][0]+k);   //you will get an error.'

   }

to avoid this use same data type in extern as extern unsigned int V1[25][25].

Ferullo answered 19/3, 2014 at 6:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.