How to solve Address Errors (on PIC24F)

Your compiler can help you out with some very common cause: the -Wcast-align flag. Every time you cast a value to something that was bigger than the previous one, you can get into serious problems. Prevent using any of these constructions…

char *a_char_pointer;
uint32_t *my_big_pointer = (uint32_t*) char_pointer;

The a_char_pointer might be badly alligned to do such operation, simply killing your proces! Read the rest of the story to fully understand it.

This specific problem has occupied a timespan of about 3 months (spare time). Surely it didn’t take this long to fix, but figuring out what was going wrong, what an address error was, what could cause it and where it was actually caused is really time consuming. Therefore, I’m going to write these things down for others!

What is an Address Error & what causes it?

In the PIC24F datasheets you’ll find some explanation what this error is, and what might cause it. But it all comes down to your coding messing with memory locations in a way it shouldn’t. IMHO, there are two major causes: accessing a piece of memory that you shouldn’t access. But as long as you are using a C compiler to generate your machine code, this shouldn’t be too probable.

The other cause is the most probable. The 24F series have 16 bit processors, meaning that the processor will always access memory in an alligned way. Have a look at this memory map:

   MS Byte       LS Byte
---------------------------
|   0x0001   |   0x0000   |
|   0x0003   |   0x0002   |
|   0x....   |   0x....   |
---------------------------

The processor always has to use both the MSB and the LSB, but each of them do have a separate address. Microchip has chosen in their CPU architecture to entirely crash whenever you would be trying to retrieve memory in a badly aligned way.

You might be wondering what happens when you would like to get a char (uint8_t, a single byte)? If you want to get the byte at memory location 0X0000, the processor will have to retrieve 0X0001 too, but it will not generate an address error (otherwise programming would become impossible!). Check the datasheets for more information!

How do I know when an Address Error occured?

Have you ever noticed that your PIC 24F just resets? Most probably this is because you didn’t implement the necesary code to catch a certain interrupt. By default, the handler for an interrupt is to simply reset! An Address Error causes the address error trap (interrupt) to be triggered. I advise you to always implement the handlers and if possible have a dedicated LED controlled by such events.


//_DefaultInterrupt() is the Default interrupt service routine (ISR).
void __attribute__((__interrupt__, auto_psv)) _DefaultInterrupt(void)
{
LED1_R_LAT = 1; // Turn on LED
uart(0xAA)
while(1);
}

void __attribute__((__interrupt__, auto_psv)) _OscillatorFail(void)
{
LED1_R_LAT = 1; // Turn on LED
uart(0x51)
while(1);
}

void __attribute__((__interrupt__, auto_psv)) _AddressError(void)
{
volatile unsigned int temp = 0;
LED1_R_LAT = 1; // Turn on LED
uart(0x52)
//while(1);
}

void __attribute__((__interrupt__, auto_psv)) _StackError(void)
{
LED1_R_LAT = 1; // Turn on LED
uart(0x53)
while(1);
}

I’ve also added a uart call, so I get a little more information, and I stay in this routine forever (till it gets fixed). The default handler is also nice. If you ever forget to implement a handler, this one will catch it!

What causes an Address Error?

The big question! At this moment, I’ll give you my n° 1!


*( (uint32_t*) (a_char_pointer + a_variable) )

At first sight, this might seem ok. You get a pointer, add something to it (so you’ll point somewhere else), you cast this to a 32 bit pointer and you dereference it to get the value. But, let me give you a fun case!


char buffer[20] = {0, 1, 2, ... , 19}
char *a_char_pointer = buffer; // Let's say the value is 0X0002
char a_variable = 1;

*( (uint32_t*) (a_char_pointer + a_variable) )

First thing to know: the buffer pointer will ALWAYS be nicely aligned (= an even address), your compiler will make sure of that. So, now add 1 to 0X0002, and you get 0X0003 (FYI, that’s an odd address; do you see it coming?). You make a 32 bit pointer out of this, and then BOOOOOM.

   MS Byte       LS Byte
---------------------------
|   0x0001   |   0x0000   |
|!!!0x0003!!!|   0x0002   |
|!!!0x0005!!!|!!!0x0004!!!|
|   0x0007   |!!!0x0006!!!|
---------------------------

Your 32 bit value has to be retrieved over 3 words, but the worst is… it’s badly alligned. This makes your processor break super hard!
If you do pointer arythmetics, be damn sure you know what you are doing. In general, casting an existing pointer to a bigger pointer than it used to be, will always break things. So don’t do it! (yeah yeah, there are ways to fix it, but just don’t do it!)

FYI, this same problem might not occur on your fancy ARM/x86/whatever CPU. Their architecture copes with the problem, but at a cost of reduced performance.

Nice, but how do I fix it?

The nice thing about an address error, is that it is recoverable. Put your system in debug mode, put a breakpoint in your address error handling routine (remove that while loop). Once you get into the code, you can set INTCON1 to 0x00 (clear the flag that caused this interrupt). By advancing step by step, you’ll get back to the place a little past where the error occured. Check the code before this area (might be in another function), and you will most probably find it.

Now, get rid of your crappy code, and put something decent in there 😀

Microchip has put some code on their website to properly retrieve the error location: http://www.embeddedcodesource.com/codesnippet/address-error-trap

Put the assembly code in your interrupt routine (you can use the asm() function to make it in-line). I’m not super sure how different this is from just clearing the interrupt in your routine, but it seems to work.

Share this post
Share on Facebook0Share on Google+0Tweet about this on TwitterShare on LinkedIn0
Leave a comment

3 Comments

  1. Lin

     /  March 17, 2013

    Great!

    Reply
  2. chippy

     /  June 30, 2015

    hey, i think that link to embeddesourcecode has changed:

    http://www.embeddedcodesource.com/codesnippet/address-error-trap

    Reply
  3. admin

     /  June 30, 2015

    Thanks a lot Chippy. I’ve updated the post!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe now
Name
Email *