PIC32MX Interrupt crashes system

I started a new thread because since I am using Harmony 3, I believe this is a new issue, even if others have seen similar behavior before.

With MLAB X 5.30, the XC Complier, and the latest Harmony 3 code configurator, I have written a small test project that sends “Hello” out UART2, and toggles a GPIO output pin, which is jumpered back to a CN (change modification) pin, IE an interrupt is generated on a change detected.

So two sources of interrupt (UART TX and CN pin) are generated at a leisurely pace (about 1/sec). This sample project runs successfully, with no FreeRTOS in the mix.


Then using Harmony 3, I add FreeRTOS to the project, not changing any settings, and the program crashes with an exception type 7 “Data bus error”. This exception occurs as soon as the first interrupt occurs of either type.

I have tried changing various parameters, but nothing I have tried has made any difference. From my reading, I know that FreeRTOS has some fancy tricks for context switching, using a separate ISR stack. Perhaps some recent change in Harmony has created an incompatibility.

Any help would be appreciated, I have spent 2 weeks on this, my outside consultant, and the Microchip engineer working on this have yet to find a fix either.

As you say you used Harmony to add FreeRTOS into the project you would imagine it should work - have you asked this question to Microchip also as it is all code generated by their tools?

Please post the code for the two interrupt handlers so I can see what they are doing - no doubt have questions after that too.

Moving to Microchip category. @Microchip might have a quicker answer.

@rtel Thank you for your reply. I have asked Microchip and at least two of their people are looking at this issue. One of them encouraged me to ask for help at FreeRTOS, since they knew more about the integration than Microchip did!

Interrupt handlers in Harmony 3, at least the part I am expected to code, look very different than those of the past. Most interrupt processing now takes place “under the hood” it seems. In fact the Interrupt handler for the UART is not my code at all, Harmony creates it. Anyway here they are:

In the case of the CN pin, the only part I need to create is a read-back routine, and then register it. Here is the code for the callback:

    uintptr_t cn7_context;
    int  cn_registration_success;
    void callback_fn( CN_PIN pin, uintptr_t cn7_context ) {
        GPIO_PinToggle(GPIO_PIN_RD0); // on-board RED led
        }

Here is the code to register and enable interrupts on the pin:

    cn7_context = 0;
    cn_registration_success = GPIO_PinInterruptCallbackRegister( CN7_PIN, callback_fn,  cn7_context);
    GPIO_PinInterruptEnable(CN7_PIN);

This is the code (created by Harmony 3 I am guessing) that calls my read-back routine:

    /* Function:
        void CHANGE_NOTICE_InterruptHandler()

      Summary:
        Interrupt Handler for change notice interrupt.

      Remarks:
    	It is an internal function called from ISR, user should not call it directly.
    */
    void CHANGE_NOTICE_InterruptHandler()
    {
        uint8_t i, bitPosition;
        uint32_t latestPortValue, mask;
        bool currPinValue;

        /* Check which CN interrupt has occurred and call callback if registered */
        for(i = 0; i < TOTAL_NUM_OF_INT_USED; i++)
        {
            latestPortValue = *(volatile uint32_t *)(&PORTA + ((cnPinObj[i].gpioPin >> 4) * 0x10));
            bitPosition = cnPinObj[i].gpioPin % 16;
            mask = 1 << bitPosition;
            currPinValue = (bool)((latestPortValue & mask) >> bitPosition);
            if((cnPinObj[i].prevPinValue != currPinValue) && (cnPinObj[i].callback != NULL))
            {
                cnPinObj[i].prevPinValue = currPinValue;
                cnPinObj[i].callback (cnPinObj[i].cnPin, cnPinObj[i].context);
            }
        }
        IFS1CLR = _IFS1_CNIF_MASK;
    }

Here is the code (Harmony 3 created probably) for the UART interrupts:

    void UART_2_InterruptHandler (void)
    {
        /* Call RX handler if RX interrupt flag is set */
        if ((IFS1 & _IFS1_U2RXIF_MASK) && (IEC1 & _IEC1_U2RXIE_MASK))
        {
            UART2_RX_InterruptHandler();
        }
        /* Call TX handler if TX interrupt flag is set */
        else if ((IFS1 & _IFS1_U2TXIF_MASK) && (IEC1 & _IEC1_U2TXIE_MASK))
        {
            UART2_TX_InterruptHandler();
        }
        /* Call Error handler if Error interrupt flag is set */
        else if ((IFS1 & _IFS1_U2EIF_MASK) && (IEC1 & _IEC1_U2EIE_MASK))
        {
            UART2_FAULT_InterruptHandler();
        }
    }

In the case of a TX interrupt (the only type I am using right now) is here (again this is not code I wrote myself. Like most above, it was created by Microchip tools.

    static void UART2_TX_InterruptHandler (void)
    {
        if(uart2Obj.txBusyStatus == true)
        {
            while((!(U2STA & _U2STA_UTXBF_MASK)) && (uart2Obj.txSize > uart2Obj.txProcessedSize) )
            {
                U2TXREG = uart2Obj.txBuffer[uart2Obj.txProcessedSize++];
            }

            /* Clear UART2TX Interrupt flag after writing to buffer */
            IFS1CLR = _IFS1_U2TXIF_MASK;

            /* Check if the buffer is done */
            if(uart2Obj.txProcessedSize >= uart2Obj.txSize)
            {
                uart2Obj.txBusyStatus = false;

                /* Disable the transmit interrupt, to avoid calling ISR continuously */
                IEC1CLR = _IEC1_U2TXIE_MASK;

                if(uart2Obj.txCallback != NULL)
                {
                    uart2Obj.txCallback(uart2Obj.txContext);
                }
            }
        }
        else
        {
            // Nothing to process ;
        }
    }

There is a file called interrupts.c, which is of little help either:

    void CHANGE_NOTICE_InterruptHandler( void );
    void UART_2_InterruptHandler( void );



    /* All the handlers are defined here.  Each will call its PLIB-specific function. */


    void CHANGE_NOTICE_Handler (void)
    {
        CHANGE_NOTICE_InterruptHandler();
    }

    void UART_2_Handler (void)
    {
        UART_2_InterruptHandler();
    }

---------------------------------------

and there is another, called interrupts_a.S shown here:

    #include <xc.h>
    #include "ISR_Support.h"

       .extern  CHANGE_NOTICE_Handler

       .section .vector_26,code, keep
       .equ     __vector_dispatch_26, IntVectorCHANGE_NOTICE_Handler
       .global  __vector_dispatch_26
       .set     nomicromips
       .set     noreorder
       .set     nomips16
       .set     noat
       .ent  IntVectorCHANGE_NOTICE_Handler

    IntVectorCHANGE_NOTICE_Handler:
        la    $26,  _CHANGE_NOTICE_Handler
        jr    $26
        nop
        .end    IntVectorCHANGE_NOTICE_Handler

       .section .CHANGE_NOTICE_Handler_vector_text, code, keep
       .set     nomicromips
       .set     noreorder
       .set     nomips16
       .set     noat
       .ent  _CHANGE_NOTICE_Handler

    _CHANGE_NOTICE_Handler:
        portSAVE_CONTEXT
        la    s6,  CHANGE_NOTICE_Handler
        jalr  s6
        nop
        portRESTORE_CONTEXT
        .end    _CHANGE_NOTICE_Handler
       .extern  UART_2_Handler

       .section .vector_32,code, keep
       .equ     __vector_dispatch_32, IntVectorUART_2_Handler
       .global  __vector_dispatch_32
       .set     nomicromips
       .set     noreorder
       .set     nomips16
       .set     noat
       .ent  IntVectorUART_2_Handler

    IntVectorUART_2_Handler:
        la    $26,  _UART_2_Handler
        jr    $26
        nop
        .end    IntVectorUART_2_Handler

       .section .UART_2_Handler_vector_text, code, keep
       .set     nomicromips
       .set     noreorder
       .set     nomips16
       .set     noat
       .ent  _UART_2_Handler

    _UART_2_Handler:
        portSAVE_CONTEXT
        la    s6,  UART_2_Handler
        jalr  s6
        nop
        portRESTORE_CONTEXT
        .end    _UART_2_Handler

Is your callback making any calls to the FreeRTOS API? If so, are you using the interrupt safe API? (the functions will end “FromISR” is so).

It appears this is installed on its on vector, so is branched to by the hardware directly rather than through a single interrupt entry point. Is that the case?

Is your callback making any calls to the FreeRTOS API? If so, are you using the interrupt safe API? (the functions will end “FromISR” is so).

In this example project, I am not using the FreeRTOS API at all. (But I am really looking for to doing so once this Interrupt issue is resolved.)


It appears this is installed on its on vector, so is branched to by the hardware directly rather than through a single interrupt entry point. Is that the case?

I do not know the answer to your question. While I have programmed embedded systems for over 20 years, this is my first try in using Microchip products or FreeRTOS. Up to this point, I was using Zilog processors and writing my own version of an RTOS.

It is not clear to me how all this fits together.

If you are not using the FreeRTOS API in the interrupt handler it can only be the interrupt entry and exit changes that could be the source of the issue. Try increasing the configISR_STACK_SIZE value in FreeRTOSConfig.h.

The ISR stack size was set to 512, I changed it to 4096. No apparent difference, it seems to fail the same way. BTW, The call stack indicates the exception was line 114 in app.c.
Ran it several times, 114 is consistent.

void APP_Tasks ( void )

{

/* Check the application's current state. */
switch ( appData.state )         <------------------this is line 114
{
    /* Application's initial state. */
    case APP_STATE_INIT:
    {
        bool appInitialized = true;


        if (appInitialized)
        {

            appData.state = APP_STATE_SERVICE_TASKS;
        }
        break;
    }

If that line is aborting my guess would be the value of appData is invalid at that point, so attempting to dereference it takes you into the weeds.

If that is the issue, then I assume to context was not restored.

Hi,
To find the function in which issue is generating please go through the following steps:

  1. In MPLAB X IDE , open Windows->Target Memory Views->Execution Memory in which you can refer to “label” where the specific function causing the problem will be detected.
    2.Also, lets check if there would be any function calling in an ISR. In FreeRtos without using proper ThreadSafe API’s like portYieldFromISR, portEndswitchfromISR, API’s should not be called in ISR. If you did that, scheduler might be halted at “exception handler” and causes “Target halted due to bus instruction error”.

Lets check this once:

in callbackfn file.c
extern TaskHandle_t xled;

uintptr_t cn7_context;
int cn_registration_success;
void callback_fn( CN_PIN pin, uintptr_t cn7_context ) {

  BaseType_t xYieldRequired;
 /* Resume the suspended task. */
    xYieldRequired = xTaskResumeFromISR( xled);
    if( xYieldRequired == pdTRUE )
   {      
      portYIELD_FROM_ISR();  // [or portEND_SWITCHING_ISR()]
   }

    }

now in : system_tasks.c file
#include “system_config.h”
#include “system_definitions.h”
#include “app.h”

TaskHandle_t xled;

void _ToggleLed(){

while(
              vTaskSuspend( NULL );
          GPIO_PinToggle(GPIO_PIN_RD0); // on-board RED led
           SYS_CONSOLE_MESSAGE("\n Hello");  // To Uart2
            vTaskDelay(1000 / portTICK_PERIOD_MS);
   )

}

Create a Task:
xTaskCreate((TaskFunction_t) _ToggleLed,
“led_toggling”,200, NULL, 1, xled);

@saikumar.k Thank you for your suggestions. I tried inserting the additions to callback_fn() but I must need some includes, as it no longer compiles, see below. Also I do not have a file called “system_tasks.c” there is a file called “tasks.c” is that the one you are suggesting changing? This sample project came from a Microchip engineer, and the led toggle takes place inside main.c

make[2]: Entering directory 'C:/MTT/H3_UART2_RTOS_RSL/firmware/UART_RTOS.X'

…/src/main.c:31:1: error: unknown type name ‘TaskHandle_t’
extern TaskHandle_t xled;
^
…/src/main.c: In function ‘callback_fn’:
…/src/main.c:37:3: error: unknown type name ‘BaseType_t’
BaseType_t xYieldRequired;
^
…/src/main.c:39:5: error: implicit declaration of function ‘xTaskResumeFromISR’ [-Werror=implicit-function-declaration]
xYieldRequired = xTaskResumeFromISR( xled);
^
…/src/main.c:40:27: error: ‘pdTRUE’ undeclared (first use in this function)
if( xYieldRequired == pdTRUE )
^
“C:\Program Files (x86)\Microchip\xc32\v2.30\bin\xc32-gcc.exe” -g -x c -c -mprocessor=32MX795F512L -ffunction-sections -O1 -I…/src -I…/src/config/default -I…/src/packs/PIC32MX795F512L_DFP -I…/src/mips -Werror -Wall -MMD -MF build/default/production/_ext/1360937237/main.o.d -o build/default/production/_ext/1360937237/main.o …/src/main.c -DXPRJ_default=default -no-legacy-libc “-mdfp=C:/Program Files (x86)/Microchip/MPLABX/v5.30/packs/Microchip/PIC32MX_DFP/1.1.215”
nbproject/Makefile-default.mk:208: recipe for target ‘build/default/production/_ext/1360937237/main.o’ failed
make[2]: Leaving directory ‘C:/MTT/H3_UART2_RTOS_RSL/firmware/UART_RTOS.X’
nbproject/Makefile-default.mk:91: recipe for target ‘.build-conf’ failed
…/src/main.c:40:27: note: each undeclared identifier is reported only once for each function it appears in
…/src/main.c:42:7: error: implicit declaration of function ‘portYIELD_FROM_ISR’ [-Werror=implicit-function-declaration]
portYIELD_FROM_ISR(); // [or portEND_SWITCHING_ISR()]
^
cc1.exe: all warnings being treated as errors
make[2]: *** [build/default/production/_ext/1360937237/main.o] Error 1
make[1]: Leaving directory ‘C:/MTT/H3_UART2_RTOS_RSL/firmware/UART_RTOS.X’
make[1]: *** [.build-conf] Error 2
nbproject/Makefile-impl.mk:39: recipe for target ‘.build-impl’ failed
make: *** [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 359ms)

Okay. I was given you just the pseudo code. Sorry for not providing full information.
In microchip, MPLAB X IDE if you go to harmony configurator and generate the code with selecting Freertos in Thirdparty in Framework, You will get generated project file with system_task.c.
Whatever the file either it might be main.c or sytem_task.c no problem. but if you want an application to run, a task must be created like the pseudo given above.

also, please refer to Mplab harmony help and Freertos reference manuals demo projects in the folder : C:\microchip\harmony\v2_06\apps\rtos\freertos\basic\firmware\basic_freertos.X.
Hope it helps you!

@saikumar

C:\microchip\harmony\v2_06\apps\rtos\freertos\basic\firmware\basic_freertos.X.

Based on the above link, I think your suggestions may apply to Harmony 2 rather than harmony 3?

One of your suggestions involves adding code to, I assume, each interrupt handler. But in the case of a UART, the framework creates all the interrupt handlers without code like you suggest. So either the Framework makes a mistake in its creation of those interrupt handlers, or the interrupt process itself has a bug.


One of the issues with Harmony 3 is that it is rather new and very few demo projects are available. I have created so many different projects trying out my own or others ideas that I have trouble keeping them straight. The one thing, at least so far, is that no mater what I try, the system crashes in the same way.

I think the problem exists in one of these files:

ISR_Support.h
interrupt_a.S

The first was written by the FreeRTOS folks, and the second by Microchip. The first defines macros for saving and restoring the context of interrupted code, while the second looks to be the initial entry/exit point for all interrupts.

Once FreeRTOS is added to a Harmony 3 project, even before adding any calls to the FreeRTOS API, processing of interrupts is altered by adding the above 2 files (and likely other changes). There does not seem to be any options to avoid this new interrupt processing. From what I have read, the new FreeRTOS interrupt processing does two things:

  1. It uses a separate ISR stack, so that each task does not have to have room in it’s own stack, and, 2) code is added to allow higher priority interrupts to interrupt lower priority ones.

Maybe this is true with Harmony 2 as well, not sure. Do these files exist in a Harmony 2 project?

Hi Robert,

I found that the whole Harmony thing was a bit of a pain.

  1. Make sure that in the configuration bits you have:
    #pragma config FSRSSEL = PRIORITY_7

By default Harmony sets that as 0 which crashes the whole thing at the first interrupt.

  1. For the external pin interrupt, to take an easy example, I have the following code Obviously you need to initialise the interrupt controller and the interrupt priority settings for it to fire.

In the a name.c file

void vInitExternalInterrupt( void)
{
    IPC1SET = ((2<<_IPC1_INT1IP_POSITION)&_IPC1_INT1IP_MASK)| 0x0;
    INTCONCLR = _INTCON_INT1EP_MASK; //External interrupt on falling edge
    IFS0CLR = _IFS0_INT1IF_MASK; // Clear the external interrupt flag
    IEC0SET = _IEC0_INT1IE_MASK; // Enable the external interrupt
}

void __attribute__( (interrupt(IPL0AUTO), vector(_EXTERNAL_1_VECTOR))) vExternalInterruptWrapper( void );

void vExternalInterruptHandler( void )
{
    BaseType_t bTaskWasWoken = pdFALSE;
    IFS0CLR = _IFS0_INT1IF_MASK; // Clear the interrupt
    // Post a message into a queue using the FromISR variant
    portYIELD_FROM_ISR(bTaskWasWoken);
}

In a name_isr.s file (assembly, yes :frowning: )

#include <xc.h>
#include <sys/asm.h>
#include "ISR_Support.h"

	.set	nomips16
	.set 	noreorder
 	
	.extern vExternalInterruptHandler
	.extern xISRStackTop
	.global	vExternalInterruptWrapper

	.set	noreorder
	.set 	noat
	.ent	vExternalInterruptWrapper

vExternalInterruptWrapper:

	portSAVE_CONTEXT
	jal vExternalInterruptHandler
	nop
	portRESTORE_CONTEXT

	.end	vExternalInterruptWrapper

Hope this helps solve your problem.

1 Like

Hi SergentSloGin,

Thank you for posting your code. It took a bit of time, but I was able to get a CN Interrupt to work by modifying your example.

I wonder why Harmony 3 does not generate the needed code automatically (they already generate so much I would think these few lines would not be a big deal), or if not, at least make it easier to find details as to what is needed. As I see it, there is a complete disconnect between the Interrupt Hardware and the Interrupt routines they do provide, with no hints that something is missing. It would seem that the EVIC section would be the place for these “wrapper” routines. If not the code, at least some notes. If there are already docs about this, I did not find them even after I looked for several weeks, including other people pitching in, trying to solve this issue.

BTW, I was not able to find the header containing the prototype for:
portYIELD_FROM_ISR(bTaskWasWoken);
So I commented out that line. I think to use that function, there is a call to make to set pdFalse first. I am not interested in making a task switch when processing an interrupt in any case. I remember reading about that in my research. It seems to run without it. I was also not able to find a Symbolic name for the vector 26, so I just put in the number instead.

Come to think of it, I think I will create a file called evic_a.S and put all the Internet (asm) wrappers in there.

Hi Robert,

portYIELD_FROM_ISR is defined in portmacro.h which resides under FreeRTOS\Source\include\portable\MPLAB\PIC32MX

The same folder contains ISR_Support.h which is included in the assembler files.

I tend to keep the ISR wrappers in separate assembler files along with the “peripheral” C code.
Putting all the interrupt wrappers in a single file also works, just makes it slightly less transferable between projects.

My version of a change notification ISR:

void __attribute__( (interrupt(IPL0AUTO), vector(_CHANGE_NOTICE_VECTOR))) vChangeNotificationInterruptWrapper( void );
void vChangeNotificationInterruptHandler( void )
{
    BaseType_t bTaskWasWoken = pdFALSE;
    IFS1CLR = _IFS1_CNIF_MASK; // Clear the interrupt
    // post a message to a queue with bTaskWasWoken changed by the call
    portYIELD_FROM_ISR(bTaskWasWoken);
}

Note that the interrupt priority (IPL0AUTO) is irrelevant here, the priorities are set in the IPCx registers.

Jean

Hi Jean,

You are right the AmazonFreeRTOS does define portYIELD_FROM_ISR( x ) in portmacro.h but strangely the CMSIS version does not. Looking at the definition, it just becomes a conditional call to portYIELD.

I am thinking that permitting a portYIELD while in an ISR is not necessary, perhaps not even desirable, for my application. I plan to put all my interrupts at the same priority, and to clear the interrupt flags at the end of processing rather than the beginning. That being the case, this is what I am doing, please comment.

In interrupts.c, I added this line:

**void __attribute__( (interrupt(IPL0AUTO), vector(_CHANGE_NOTICE_VECTOR))) vChangeNotifierInterruptWrapper( void );**

This sets up the interrupt to call the assembler wrapper, which I have set to call CHANGE_NOTICE_Handler a function in plib_gpio.c created by harmony 3.

So I register my call back like this:

cn_registration_success = GPIO_PinInterruptCallbackRegister( CN6_PIN, callback_fn,  cn6_context);

To enable the interrupts, I have these two lines. Not sure if both are needed.

EVIC_SourceEnable( INT_SOURCE_CHANGE_NOTICE );
GPIO_PinInterruptEnable(CN6_PIN);

Thanks,
Robert

Hi Robert,

I initially tried to use the Harmony generated code as well, but because of the issues that you are running into, I decided to scrap that and just use Harmony as a bare bones code generator which I then incorporated into my own source tree.
It’s taken quite a bit of effort to figure out what was generated and what was in the sample programs and how they differed from each other.
Now that I have a working “template” I will stick to this hybrid solution.

I must admit that Harmony generated code is still some ways off to being usable in the more complex setups. It’s fine for simple programs but the integration with an RTOS is far from smooth sailing, unlike on other platforms (ST comes to mind).

As for whether you need to call portYieldFromISR or not, that’s entirely application specific. If you place something into a queue or message buffer from the ISR it will eventually get picked up by the rest of your application code. The reason to try and yield from the ISR is so that any task that is waiting on the object that you just posted can be activated on return from the ISR rather than waiting for a forced context switch from the scheduler.

It’s a trade-off between predictable task sequencing and system responsiveness. As the saying goes: horses for courses.

Jean