portENTER_critical

Hi! I am using the AT90CAN128, IAR compiler and Freertos 4.01. I reduce my app to only one task that calls the following function. At AvrStudio I take look at some varibales and for example tempRtc starts at address 0x2AA. After the taskENTER_CRITICAL() call the address of tempRtc changed to 0x2A9 (???), and of course the function result is erroneous. After taskEXIT_CRITICAL() the address of tempRtc goes to the initial value (0x2AA). Any ideas of what goes wrong with taskENTER_CRITICAL()?   void updateTime (void)   {     char tempRtc[10];     char *s;     if (readRTC(tempRtc))            {       taskENTER_CRITICAL();       realTime.year = tempRtc[7];       realTime.month = tempRtc[6];       realTime.day = tempRtc[5];       realTime.weekday = tempRtc[4];       realTime.hours = tempRtc[3];       realTime.minutes = tempRtc[2];       realTime.seconds = tempRtc[1];       taskEXIT_CRITICAL();     }   }

portENTER_critical

Different ports use two methods for entering and exiting critical sections. The first pushes the interrupt status onto the stack, then disables interrupts to enter, and simply pops the status off the stack to exit.  It sounds like this is what your port is doing.  This will only work if you have the compiler options set in a particular way.  Otherwise, if you are using stack frame based addressing, changing the stack will cause the sort of problem you are seeing. The way around this is to use the other method, which defines functions to enter and exit critical sections rather than modifying the stack.  This is less efficient but guaranteed to work with all compiler options. Take a look at some of the ARM ports to see what to do.  Basically: Define a variable in port.c, say ucCriticalNesting, and initialise it to a high value, say 0xf0. Redefine portENTER_CRITICAL and portEXIT_CRITICAL (in portmacro.h) to call functions vPortEnterCritical() and vPortExitCritical().  These functions take the following form: void vPortEnterCritical( void ) {     portDISABLE_INTERRUPTS();     ucCriticalNesting++; } void vPortExitCritical( void ) {     ucCriticalNesting–;     if( ucCriticalNesting == 0 )     {         portENABLE_INTERRUPTS();     } } This is the easy part.  Then you have to save and restore the variable ucCriticalNesting on the stack of each task: In pxPortInitialiseStack() add another byte to the initial stack setup for the task.  Set this byte to 0 so the task does not start within a critical section. In portSAVE_CONTEXT save the value of ucCriticalNesting in the same location on the stack. In portRESTORE_CONTEXT restore the value from the stack into ucCriticalNesting. This is an overview.  For details I suggest you look at one of the ARM IAR ports and just copy that.  I think the variable is called uxCriticalNesting or ulCriticalNesting in that port.

portENTER_critical

Which version of the compiler are you using?  I am trying to update to the latest version but the links on the IAR site don’t seem to be working. Regards.

portENTER_critical

I have updated this in SVN to use the alternative critical section method.  This will be incorporated into V4.1.0 which will be released today or tomorrow. Regards.

portENTER_critical

I know this is stale, and the issue seems to be widely known, but I’d like to highlight that the counter method proposed unconditionally enables interrupts when the outermost critical section is left. This makes it unuseable in interrupts or any function which might be used in an interrupt. I spent a weekend working this out when we tried to port to using FreeRTOS critical sections. It is pretty easy to fix though. I’ve attached my alternative which can be used with interrupt code and ordinary code. This is coded for an MSP430F5438A on an IAR SBW development system. It’s tested and works. It also has the advantage of working independantly of FreeRTOS, so can be used before starting the task scheduler. The disadvantage is that for portable code, a generic means to test the current interrupt state would be needed; I think it would be worth doing though.
#define portENTER_CRITICAL()                                                            
{                                                                                       
extern volatile unsigned short usCriticalNesting;                                       
                                                                                        
    if (__get_SR_register() & __SR_GIE)                                                 
    {                                                                                   
        __disable_interrupt();                                                          
        /* If interrupts are set, start at zero. We then know to reenable interrupts */ 
        /* when the count returns to zero. If interrupts are disabled, we have no    */ 
        /* way of knowing if this is a first call, or a nested call. just increment. */ 
        usCriticalNesting = 0;                                                          
    }                                                                                   
    usCriticalNesting++;                                                                
}                                                                                       


#define portEXIT_CRITICAL()                                                             
{                                                                                       
extern volatile unsigned short usCriticalNesting;                                       
                                                                                        
    usCriticalNesting--;                                                                
    if (usCriticalNesting == 0)                                                         
    {                                                                                   
        /* About to leave the outermost critical section; the next critical section  */ 
        /* started might be with interrupts enabled or might not, so set to 1.       */ 
        usCriticalNesting = 1;                                                          
        __enable_interrupt();                                                           
    }                                                                                   
}