Exceptional Code
A strange thing happened on the way to my last blog post - I was interrupted. Interrupts are pretty common when working with embedded systems, but this was something that I’d never seen before. This was an interrupt for my own good, supposedly.
A Programmer Walks Into A Bar - OUCH!
While I was doing research for my DMA post, I tried to move all of the Flash memory in my processor into a peripheral to calculate the Flash checksum. I’m using an ARM processor which locates a bunch of information so that it starts at address 0: the start address, initial stack pointer, and interrupt vector tables.
When I ran it, my program didn’t iterate through Flash: it generated an exception (a high priority interrupt generated by the processor to indicate that something really bad happened) very soon after it started.
The code looked like this:
#define START_OF_FLASH 0x00000000U /* Start address of the code FLASH */
#define FLASH_SIZE 262144
uint32_t* flashPointer;
uint32_t i;
flashPointer = (uint32_t*) START_OF_FLASH;
for (i = 0U; i < FLASH_SIZE; i++) {
CRC->DR = *flashPointer++;
}
Pretty straightforward stuff, just iterate from location 0, through to the top of Flash, moving 32-bit chunks into the CRC peripheral’s data register. But, instead, the program immediately threw an IBUSERR exception.
I’m using a recent version of the GCC C compiler and I use it with all warnings turned on. There were no errors or warnings being reported.
I figured that there must be some sort of memory protection happening around location 0. I wasn’t surprised since this is code space, and the memory protection unit might mark this area as execute only. I checked the documentation but, no, that isn’t correct.
This processor has a built in bootloader and, depending on the voltage on a pin at boot time, will either put your code or the bootloader at location 0. Perhaps reading location 0 is illegal because location 0 doesn’t actually exist, it’s just a figment of the architecture. Checking around I discovered that - no - that theory wasn’t correct either.
I must go deeper so look what we get when C generates the assembly code:
08001a9c: movs r3, #0 08001a9e: ldr r3, [r3, #0] 08001aa0: udf #255 ; 0xff
The first instruction shows that we will be using general purpose register 3 as a pointer to our source data. Register 3 is initialized to 0. This would be the code generated from the command:
flashPointer = (uint32_t*) START_OF_FLASH;
The second instruction loads into register 3, the data pointed at by register 3 plus a displacement of 0. I stepped through that instruction and register 3 did get loaded with the data stored at location 0.
I would expect that the next instruction would store the content of register 3 into the CRC peripheral, but instead we have:
udf #255 ; 0xff
I didn’t recognize this instruction, and when I stepped through it, my program immediately jumped to the interrupt handler. What is UDF? I had to search the internet for this one: UDF is the permanently undefined instruction instruction. It intentionally generates an undefined instruction exception.
But Why?
One of the things that C does for you, when your program begins, is it zeros out all of your RAM memory. If you have a pointer that doesn’t get set to a reasonable value, it will probably have the value 0. What C is doing is protecting us from using an uninitialized pointer.
When the compiler analyzed the code, it saw that the pointer had been set to 0. Even though 0 is a reasonable value for our code, it is unlikely to be correct because reading location 0 is not that common.
So putting in the code to halt my program isn’t a bad choice, but the compiler should have generated a message, warning that I was dereferencing a null pointer.
On The Other Hand
Using my black belt google-fu, I found that there is a compiler command line option that turns off the generation of the UDF command:
-fno-delete-null-pointer-checks
So we can get around the issue, but a better idea would be to have a warning generated when the UDF instruction is inserted.
This would be a really annoying, hard to find bug for most people. Elecia remembered an article that describes the exception handler system on ARM Cortex-M processors and has code to walk through the registers to tell you what happened. I’ll have to use this in my next project.
This post is part of a series. Please see the other posts here.
Music to program by - King Sunny Ade, Juju Music