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.
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).
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With