Skip to content

How to solve Address Errors (on PIC24F)

The short answer (30 second read)

Your compiler can help you out with the most common cause of the Address Error: the -Wcast-align flag. Every time you cast a value to something that is bigger than the initial one, you can get into serious problems. This flag will give a warning when you use constructs like this one:

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

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

→ We want to know what you’re working on! Share your project in the comments

The long answer (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 code messing with memory locations in a way it shouldn’t. 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.
  • Accessing a misaligned piece of memory. The 24F series have 16 bit processors, meaning that the processor always accesses memory in an aligned 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 occurred?

Have you ever noticed that your PIC 24F just resets? Most probably this is because you didn’t implement the necessary 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 for a different interrupt , this one will catch it!

What exactly causes an Address Error?

The big question! I’ll give you the easiest example of how you can trigger this yourself.

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

At first sight, this might seem pointer magic, but acceptable. You get a pointer, add something to it (so you’ll point somewhere else), you cast this to a 32 bit pointer and you de-reference it to get the value. But, let’s look at this 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. You make a 32 bit pointer out of this, and then BOOOOOM: the address error is triggered.

   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 aligned. This makes your processor break super hard!
If you do pointer arithmetic, be damn sure you know what you are doing. In general, casting an existing pointer (pointer to 8 bit) to a bigger pointer (pointer to 32 bit) 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 handles the problem, but at a cost of reduced performance.

Nice, but how do I fix my error?

The nice thing about an address error, is that it is recoverable. It doesn’t mean that you should just ignore the error, but it does help you to debug more easily! Put your system in debug mode, put a break point 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 occurred. Check the code before this area (might be in another function), and you will most probably find it.

Now, you know exactly the location of your problematic function and you can fix it!

Addendum

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.

→ We want to know what you’re working on! Share your project in the comments
Share this article
Published inSoftware

4 Comments

  1. Lin Lin

    Great!

  2. admin admin

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

  3. Mojtaba Mojtaba

    Thanks for sharing information. It was so helpful.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.