1
\$\begingroup\$

I am working on building an interrupt based program flow using the PIC24FV32KA302 module from Microchip. However, I am having some trouble with intermittent resets (it seems).

I am currently using the MPLAB X Simulator, trying to find out where the issue is happening. I was originally using a PICKit3 on real hardware, but I ran into some trouble where, after a few cycles through the main loop, the debugger would stop and throw an error "PC at 0x0", which is an address without a valid instruction. I don't think these issues are related, but I can't really eliminate the possibility.

In essence, my program flow is as follows:

main()
{
   init();
   print_an_init_message();
   while(1)
   {
       check_some_flags();
       if(flags_are_set)
       {
          do_something_about_them();
       }
   }  
}

During simulation, the program repeatedly prints the init message. It does catch on a breakpoint in the while(1) loop - so it must be entering. Also, if I continue from that breakpoint, it will loop around to it (not reset). If I step from that breakpoint, the debugger crashes.

So, I tried printing out values from the RSON register (which is supposed to be set during any reset condition) in the init message. None of the values are set. If I add a simple delay (built-in XC16 __delay_ms()) to the while loop, the _AddressError interrupt is called. The instruction from which the address error originates is always a ULNK instruction from one of the functions used in do_something_about_them() (which represents multiple flags and functions, it seems to be possible that it is any of the functions, but most often it is the first or last).

Also worth noting, if I put a breakpoint on the last line of main (return -1), it is never hit, but the simulator continuously prints the init message.

Is this likely a problem with the simulator, or my program? Could this be related to my PC 0x0 problem when running on real hardware, or are those likely different?

If anyone wants real code to help out, I can provide it - I just didn't provide it right away because I feel it would obfuscate my real problem, which I don't think is being caused by my C code.

Thanks in advance!

After a little bit of debugging, I managed to get the PC 0x0 error to go away. I'm still dealing with landing in the address error ISR. This is now programming real hardware with the PICKit3.

I believe the code that is causing the problem is a queue that I am dealing with.

typedef struct uint16_queue_tag {
    uint16_t *contents;
    int front;
    int back;
    int maxSize;
    int cnt;
} uint16_queue;

bool uint16_InitQueue(uint16_queue *queueP, uint8_t queueSize)
{
    uint16_t newContents[queueSize];

    queueP->contents = &newContents[0];
    queueP->maxSize = queueSize;
    queueP->cnt = 0;
    queueP->front = -1;
    queueP->back = -1;

    return true;
}

bool uint16_IsQueueEmpty(uint16_queue *queueP)
{
    return (bool) (queueP->cnt == 0);
}

bool uint16_IsQueueFull(uint16_queue *queueP)
{
    return (bool) (queueP->cnt == queueP->maxSize);
}

bool uint16_ClearQueue(uint16_queue *queueP)
{
    queueP->front = -1;
    queueP->back = -1;
    queueP->cnt = 0;

    return true;
}

bool uint16_PushQueue(uint16_queue *queueP, uint16_t element)
{
    if (uint16_IsQueueFull(queueP))
    {
        return false; // We can't push to the queue, its full
    }
    else
    {
        queueP->back++;
        queueP->contents[(queueP->back % queueP->maxSize)] = element;
        queueP->cnt++;
        return true;
    }
}

uint16_t uint16_PullQueue(uint16_queue *queueP)
{
    if (uint16_IsQueueEmpty(queueP))
    {
        return NULL;
    }
    else
    {
        queueP->front++;
        queueP->cnt--;
        return queueP->contents[(queueP->front % queueP->maxSize)];
    }
}

This is all working fine for some iterations it seems, but at some point a uint16_PushQueue(&queue_obj, value) breaks the code. After the queue is initialized, all that I do is check if it is full, Push, and Pull. Strangely, it seems like the value of the contents pointer is different - after it breaks, the pointer is pointing to the same value as "front", which is definitely not the same value as it started at. Also, the element at the place that it is pointing is reported, by MPLAB, as 0.0 (which is definitely not a float).

So I guess this is just some kind of pointer assignment stupidity, which kind of explains the whole address problem too - I am somehow moving where the contents pointer is pointing to.

\$\endgroup\$
2
\$\begingroup\$

The way you initialize your queue is not OK:

bool uint16_InitQueue(uint16_queue *queueP, uint8_t queueSize)
{
    uint16_t newContents[queueSize];
    queueP->contents = &newContents[0];
    queueP->maxSize = queueSize;
    queueP->cnt = 0;
    queueP->front = -1;
    queueP->back = -1;
    return true;
}

The newContents variable-length array will be initialized on the uint16_InitQueue's stack. Once the function returns, the queueP->contents will point to the stack and any attempt to modify it's content will corrupt the stack and potentially cause a crash.

This is all working fine for some iterations it seems, but at some point a uint16_PushQueue(&queue_obj, value) breaks the code. After the queue is initialized, all that I do is check if it is full, Push, and Pull.

This makes total sense, as doing a uint16_PushQueue will corrupt the stack.

The array needs to be either dynamically allocated (not good practise for embedded systems without an OS) or globally allocated. When programming embedded system, you usually set a maximum size for the queue (or any other container) at compile time.


Some nitpicking:

bool uint16_IsQueueEmpty(uint16_queue *queueP)
{
    return (bool) (queueP->cnt == 0);
}

The cast is totally useless. You can do: return (queueP->cnt == 0) instead.

The less code, the better.

\$\endgroup\$
  • \$\begingroup\$ This answer seems correct. I changed the struct to uint16_t contents[32];, and stopped initializing the array. I suppose the essence of what I was trying to do was create a new array, somewhere else in memory, then point to it using the contents variable. This is the essence of allocation though, which I can't really do in embedded systems, and didn't work because I did it wrong :). I appreciate the help! \$\endgroup\$ – Ken Oct 19 '15 at 12:28
2
\$\begingroup\$

It sounds like one of two possible problems. Either the code is hitting a null pointer or some interrupt is enabled that does not have a handler. In either case the code tries to jump to effectively nowhere and that error could be set.

To verify that this is the problem create four interrupt for the built in traps. Look at the top four interrupts in the interrupt section of the data sheet.

  1. Oscillator fail trap
  2. Address error trap
  3. Stack error trap
  4. Math error trap

Debug the code like you were before and check if you land in the Address error trap. This would confirm the problem.

\$\endgroup\$
  • \$\begingroup\$ Simulator result (I don't have hardware with me, will check later) - Without any delay in while(1): prints init repeatedly. With delay in while(1): Gets caught in _AddressError ISR, "Trap due to Stack Error, unimplemented FLASH memory access, unimplemented RAM access" instruction 0x000A9C, which corresponds to the ULNK instruction at the end of my DelayMS function (just bakes together ClrWdt and __delay_ms built-in). I also enabled a default interrupt, I do not land in that at all. \$\endgroup\$ – Ken Oct 16 '15 at 16:25
  • \$\begingroup\$ So I do land in the _AddressError trap routine, but the fact that I don't land in _DefaultInterrupt at all means that I do not have an extra interrupt enabled. Null pointer then? It just seems peculiar that it always happens on the ULNK instruction of a call in the while(1) loop. \$\endgroup\$ – Ken Oct 16 '15 at 16:32
  • \$\begingroup\$ @KenK I do not know assembler but the ULNK instruction has to do with pointers "Unlink Frame Pointer". I would suspect a null pointer issue. \$\endgroup\$ – vini_i Oct 16 '15 at 16:56
  • \$\begingroup\$ It looks like it could be a problem with my Delay function, which is weird, because it consists of ClrWdt() and __delay_ms(). I can't see why that would cause a null pointer - I do both of those things otherwise and it works fine. I think I might need to try this on the actual hardware (not simulator) before I'll be able to get anywhere... \$\endgroup\$ – Ken Oct 16 '15 at 17:17
0
\$\begingroup\$

It seems that you might an watchdog timer issue. Make sure the watchdog is in fact disable or the you are properly resetting the watchdog timer. Sometimes the watchdog timer are enabled as default option. So I believe that the program is the issue here. And the problem that you are experiencing in the hardware is the same PC at 0x0 literally means that the program counter when back to zero, in other words the hardware has been reset.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your answer! I am using the watchdog timer, but I call the ClrWdt() command at the beginning of every cycle through while(1), and before every delay. The WDT is also set with the maximum prescaler. I just tried turning the WDT off, same problem. In addition, the RCONbits.WDTO is not set on reset, which implies that the WDT was not the cause. \$\endgroup\$ – Ken Oct 16 '15 at 16:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.