0

Properly handle interrupts on RZ/A1 with GCC (KPIT)

Hi,

Here are some changes needed to properly handle interrupts in ASM code as indicated in RZ's manual.
Original FreeRTOS code port for Cortex-A9 is incomplete for Renesas RZ/A1.
Without these changes, a software using an heavy load of interrupts leads to reboots.

Modified files below (can't attach zip!)

Directory: portable\gcc\cortex_a9

port.c:

/*
    FreeRTOS V8.0.0 - Copyright (C) 2014 Real Time Engineers Ltd.
    All rights reserved

    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS provides completely free yet professionally developed,    *
     *    robust, strictly quality controlled, supported, and cross          *
     *    platform software that has become a de facto standard.             *
     *                                                                       *
     *    Help yourself get started quickly and support the FreeRTOS         *
     *    project by purchasing a FreeRTOS tutorial book, reference          *
     *    manual, or both from: http://www.FreeRTOS.org/Documentation        *
     *                                                                       *
     *    Thank you!                                                         *
     *                                                                       *
    ***************************************************************************

    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.

    >>! NOTE: The modification to the GPL is included to allow you to distribute
    >>! a combined work that includes FreeRTOS without being obliged to provide
    >>! the source code for proprietary components outside of the FreeRTOS
    >>! kernel.

    FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  Full license text is available from the following
    link: http://www.freertos.org/a00114.html

    1 tab == 4 spaces!

    ***************************************************************************
     *                                                                       *
     *    Having a problem?  Start by reading the FAQ "My application does   *
     *    not run, what could be wrong?"                                     *
     *                                                                       *
     *    http://www.FreeRTOS.org/FAQHelp.html                               *
     *                                                                       *
    ***************************************************************************

    http://www.FreeRTOS.org - Documentation, books, training, latest versions,
    license and Real Time Engineers Ltd. contact details.

    http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
    including FreeRTOS+Trace - an indispensable productivity tool, a DOS
    compatible FAT file system, and our tiny thread aware UDP/IP stack.

    http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High
    Integrity Systems to sell under the OpenRTOS brand.  Low cost OpenRTOS
    licenses offer ticketed support, indemnification and middleware.

    http://www.SafeRTOS.com - High Integrity Systems also provide a safety
    engineered and independently SIL3 certified version for use in safety and
    mission critical applications that require provable dependability.

    1 tab == 4 spaces!
*/

/* Standard includes. */
#include <stdlib.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"

//TODO remove
#include "sample_led.h"

#ifndef configINTERRUPT_CONTROLLER_BASE_ADDRESS
    #error configINTERRUPT_CONTROLLER_BASE_ADDRESS must be defined.  See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
#endif

#ifndef configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET
    #error configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET must be defined.  See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
#endif

#ifndef configUNIQUE_INTERRUPT_PRIORITIES
    #error configUNIQUE_INTERRUPT_PRIORITIES must be defined.  See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
#endif

#ifndef configSETUP_TICK_INTERRUPT
    #error configSETUP_TICK_INTERRUPT() must be defined.  See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
#endif /* configSETUP_TICK_INTERRUPT */

#ifndef configMAX_API_CALL_INTERRUPT_PRIORITY
    #error configMAX_API_CALL_INTERRUPT_PRIORITY must be defined.  See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html
#endif

#if configMAX_API_CALL_INTERRUPT_PRIORITY == 0
    #error configMAX_API_CALL_INTERRUPT_PRIORITY must not be set to 0
#endif

#if configMAX_API_CALL_INTERRUPT_PRIORITY > configUNIQUE_INTERRUPT_PRIORITIES
    #error configMAX_API_CALL_INTERRUPT_PRIORITY must be less than or equal to configUNIQUE_INTERRUPT_PRIORITIES as the lower the numeric priority value the higher the logical interrupt priority
#endif

#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1
    /* Check the configuration. */
    #if( configMAX_PRIORITIES > 32 )
        #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32.  It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
    #endif
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

/* In case security extensions are implemented. */
#if configMAX_API_CALL_INTERRUPT_PRIORITY <= ( configUNIQUE_INTERRUPT_PRIORITIES / 2 )
    #error configMAX_API_CALL_INTERRUPT_PRIORITY must be greater than ( configUNIQUE_INTERRUPT_PRIORITIES / 2 )
#endif

#ifndef configCLEAR_TICK_INTERRUPT
    #define configCLEAR_TICK_INTERRUPT()
#endif

/* A critical section is exited when the critical section nesting count reaches
this value. */
#define portNO_CRITICAL_NESTING            ( ( uint32_t ) 0 )

/* In all GICs 255 can be written to the priority mask register to unmask all
(but the lowest) interrupt priority. */
#define portUNMASK_VALUE                ( 0xFF )

/* Tasks are not created with a floating point context, but can be given a
floating point context after they have been created.  A variable is stored as
part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
does not have an FPU context, or any other value if the task does have an FPU
context. */
#define portNO_FLOATING_POINT_CONTEXT    ( ( StackType_t ) 0 )

/* Constants required to setup the initial task context. */
#define portINITIAL_SPSR                ( ( StackType_t ) 0x1f ) /* System mode, ARM mode, IRQ enabled FIQ enabled. */
#define portTHUMB_MODE_BIT                ( ( StackType_t ) 0x20 )
#define portINTERRUPT_ENABLE_BIT        ( 0x80UL )
#define portTHUMB_MODE_ADDRESS            ( 0x01UL )

/* Used by portASSERT_IF_INTERRUPT_PRIORITY_INVALID() when ensuring the binary
point is zero. */
#define portBINARY_POINT_BITS            ( ( uint8_t ) 0x03 )

/* Masks all bits in the APSR other than the mode bits. */
#define portAPSR_MODE_BITS_MASK            ( 0x1F )

/* The value of the mode bits in the APSR when the CPU is executing in user
mode. */
#define portAPSR_USER_MODE                ( 0x10 )

/* The critical section macros only mask interrupts up to an application
determined priority level.  Sometimes it is necessary to turn interrupt off in
the CPU itself before modifying certain hardware registers. */
#define portCPU_IRQ_DISABLE()                                        \
    __asm volatile ( "CPSID i" );                                    \
    __asm volatile ( "DSB" );                                        \
    __asm volatile ( "ISB" );

#define portCPU_IRQ_ENABLE()                                        \
    __asm volatile ( "CPSIE i" );                                    \
    __asm volatile ( "DSB" );                                        \
    __asm volatile ( "ISB" );


/* Macro to unmask all interrupt priorities. */
#define portCLEAR_INTERRUPT_MASK()                                    \
{                                                                    \
    portCPU_IRQ_DISABLE();                                            \
    portICCPMR_PRIORITY_MASK_REGISTER = portUNMASK_VALUE;            \
    __asm(    "DSB        \n"                                            \
            "ISB        \n" );                                        \
    portCPU_IRQ_ENABLE();                                            \
}

#define portINTERRUPT_PRIORITY_REGISTER_OFFSET        0x400UL
#define portMAX_8_BIT_VALUE                            ( ( uint8_t ) 0xff )
#define portBIT_0_SET                                ( ( uint8_t ) 0x01 )

/*-----------------------------------------------------------*/

/* Needed to overcome GCC inline ASM variable limitation */
extern void set_fpscr(unsigned long);
extern unsigned long get_apsr(void);

/*
 * Starts the first task executing.  This function is necessarily written in
 * assembly code so is implemented in portASM.s.
 */
extern void vPortRestoreTaskContext( void );

/*-----------------------------------------------------------*/

/* A variable is used to keep track of the critical section nesting.  This
variable has to be stored as part of the task context and must be initialised to
a non zero value to ensure interrupts don't inadvertently become unmasked before
the scheduler starts.  As it is stored as part of the task context it will
automatically be set to 0 when the first task is started. */
volatile uint32_t ulCriticalNesting = 9999UL;

/* Saved as part of the task context.  If ulPortTaskHasFPUContext is non-zero then
a floating point context must be saved and restored for the task. */
uint32_t ulPortTaskHasFPUContext = pdFALSE;

/* Set to 1 to pend a context switch from an ISR. */
uint32_t ulPortYieldRequired = pdFALSE;

/* Counts the interrupt nesting depth.  A context switch is only performed if
if the nesting depth is 0. */
uint32_t ulPortInterruptNesting = 0UL;

__attribute__(( used )) const uint32_t ulICCIAR = portICCIAR_INTERRUPT_ACKNOWLEDGE_REGISTER_ADDRESS;
__attribute__(( used )) const uint32_t ulICCEOIR = portICCEOIR_END_OF_INTERRUPT_REGISTER_ADDRESS;
__attribute__(( used )) const uint32_t ulICDABR0    = portICDABR0_ACTIVE_BIT_REGISTER_0_ADDRESS;
__attribute__(( used )) const uint32_t ulICDIPR0    = portICDIPR0_INTERRUPT_PRORITY_REGISTER_0_ADDRESS;
__attribute__(( used )) const uint32_t ulICCHPIR    = portICCHPIR_HIGHEST_PENDING_INTERRUPT_ADDRESS;
__attribute__(( used )) const uint32_t ulICCPMR    = portICCPMR_PRIORITY_MASK_REGISTER_ADDRESS;
__attribute__(( used )) const uint32_t ulMaxAPIPriorityMask = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );

/*-----------------------------------------------------------*/

/*
 * See header file for description.
 */
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
    /* Setup the initial stack of the task.  The stack is set exactly as
    expected by the portRESTORE_CONTEXT() macro.

    The fist real value on the stack is the status register, which is set for
    system mode, with interrupts enabled.  A few NULLs are added first to ensure
    GDB does not try decoding a non-existent return address. */
    *pxTopOfStack = ( StackType_t ) NULL;
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) NULL;
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) NULL;
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;

    if( ( ( uint32_t ) pxCode & portTHUMB_MODE_ADDRESS ) != 0x00UL )
    {
        /* The task will start in THUMB mode. */
        *pxTopOfStack |= portTHUMB_MODE_BIT;
    }

    pxTopOfStack--;

    /* Next the return address, which in this case is the start of the task. */
    *pxTopOfStack = ( StackType_t ) pxCode;
    pxTopOfStack--;

    /* Next all the registers other than the stack pointer. */
    *pxTopOfStack = ( StackType_t ) 0x00000000;    /* R14 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x12121212;    /* R12 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x11111111;    /* R11 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x10101010;    /* R10 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x09090909;    /* R9 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x08080808;    /* R8 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x07070707;    /* R7 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x06060606;    /* R6 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x05050505;    /* R5 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x04040404;    /* R4 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x03030303;    /* R3 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x02020202;    /* R2 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) 0x01010101;    /* R1 */
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
    pxTopOfStack--;

    /* The task will start with a critical nesting count of 0 as interrupts are
    enabled. */
    *pxTopOfStack = portNO_CRITICAL_NESTING;
    pxTopOfStack--;

    /* The task will start without a floating point context.  A task that uses
    the floating point hardware must call vPortTaskUsesFPU() before executing
    any floating point instructions. */
    *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;

    return pxTopOfStack;
}
/*-----------------------------------------------------------*/

BaseType_t xPortStartScheduler( void )
{
    uint32_t ulAPSR;

    #if( configASSERT_DEFINED == 1 )
    {
        volatile uint32_t ulOriginalPriority;
        volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( configINTERRUPT_CONTROLLER_BASE_ADDRESS + portINTERRUPT_PRIORITY_REGISTER_OFFSET );
        volatile uint8_t ucMaxPriorityValue;

        /* Determine how many priority bits are implemented in the GIC.

        Save the interrupt priority value that is about to be clobbered. */
        ulOriginalPriority = *pucFirstUserPriorityRegister;

        /* Determine the number of priority bits available.  First write to
        all possible bits. */
        *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;

        /* Read the value back to see how many bits stuck. */
        ucMaxPriorityValue = *pucFirstUserPriorityRegister;

        /* Shift to the least significant bits. */
        while( ( ucMaxPriorityValue & portBIT_0_SET ) != portBIT_0_SET )
        {
            ucMaxPriorityValue >>= ( uint8_t ) 0x01;
        }

        /* Sanity check configUNIQUE_INTERRUPT_PRIORITIES matches the read
        value. */
        configASSERT( ucMaxPriorityValue == portLOWEST_INTERRUPT_PRIORITY );

        /* Restore the clobbered interrupt priority register to its original
        value. */
        *pucFirstUserPriorityRegister = ulOriginalPriority;
    }
    #endif /* conifgASSERT_DEFINED */

    /* Only continue if the CPU is not in User mode.  The CPU must be in a
    Privileged mode for the scheduler to start. */
#if 0
    __asm volatile ( "MRS %0, APSR" : "=r" ( ulAPSR ) );
#else
    ulAPSR = get_apsr();
#endif

    ulAPSR &= portAPSR_MODE_BITS_MASK;
    configASSERT( ulAPSR != portAPSR_USER_MODE );

    if( ulAPSR != portAPSR_USER_MODE )
    {
        /* Only continue if the binary point value is set to its lowest possible
        setting.  See the comments in vPortValidateInterruptPriority() below for
        more information. */
        configASSERT( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE );

        if( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE )
        {
            /* Interrupts are turned off in the CPU itself to ensure tick does
            not execute    while the scheduler is being started.  Interrupts are
            automatically turned back on in the CPU when the first task starts
            executing. */
            portCPU_IRQ_DISABLE();

            /* Start the timer that generates the tick ISR. */
            configSETUP_TICK_INTERRUPT();

            /* Start the first task executing. */
            vPortRestoreTaskContext();
        }
    }

    /* Will only get here if xTaskStartScheduler() was called with the CPU in
    a non-privileged mode or the binary point register was not set to its lowest
    possible value. */
    return 0;
}
/*-----------------------------------------------------------*/

void vPortEndScheduler( void )
{
    /* Not implemented in ports where there is nothing to return to.
    Artificially force an assert. */
    configASSERT( ulCriticalNesting == 1000UL );
}
/*-----------------------------------------------------------*/

void vPortEnterCritical( void )
{
    /* Mask interrupts up to the max syscall interrupt priority. */
    ulPortSetInterruptMask();

    /* Now interrupts are disabled ulCriticalNesting can be accessed
    directly.  Increment ulCriticalNesting to keep a count of how many times
    portENTER_CRITICAL() has been called. */
    ulCriticalNesting++;
}
/*-----------------------------------------------------------*/

void vPortExitCritical( void )
{
    if( ulCriticalNesting > portNO_CRITICAL_NESTING )
    {
        /* Decrement the nesting count as the critical section is being
        exited. */
        ulCriticalNesting--;

        /* If the nesting level has reached zero then all interrupt
        priorities must be re-enabled. */
        if( ulCriticalNesting == portNO_CRITICAL_NESTING )
        {
            /* Critical nesting has reached zero so all interrupt priorities
            should be unmasked. */
            portCLEAR_INTERRUPT_MASK();
        }
    }
}
/*-----------------------------------------------------------*/
#ifdef COUNT_INT
uint32_t gcount_int_tick = 0;
#endif
void FreeRTOS_Tick_Handler( void )
{

//    R_LED_Off(LEDRUN);

#ifdef COUNT_INT
    gcount_int_tick++;
#endif
    /* Set interrupt mask before altering scheduler structures.   The tick
    handler runs at the lowest priority, so interrupts cannot already be masked,
    so there is no need to save and restore the current mask value.  It is
    necessary to turn off interrupts in the CPU itself while the ICCPMR is being
    updated. */
    portCPU_IRQ_DISABLE();
    portICCPMR_PRIORITY_MASK_REGISTER = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );
    __asm(    "dsb        \n"
            "isb        \n" );
    portCPU_IRQ_ENABLE();

    /* Increment the RTOS tick. */
    if( xTaskIncrementTick() != pdFALSE )
    {
        ulPortYieldRequired = pdTRUE;
    }

    /* Ensure all interrupt priorities are active again. */
    portCLEAR_INTERRUPT_MASK();
    configCLEAR_TICK_INTERRUPT();
//    R_LED_On(LEDRUN);
}
/*-----------------------------------------------------------*/

void vPortTaskUsesFPU( void )
{
    uint32_t ulInitialFPSCR = 0;

    /* A task is registering the fact that it needs an FPU context.  Set the
    FPU flag (which is saved as part of the task context). */
    ulPortTaskHasFPUContext = pdTRUE;

    /* Initialise the floating point status register. */
#if 0
    __asm( "FMXR     FPSCR, %0" :: "r" (ulInitialFPSCR) );
#else
    set_fpscr(ulInitialFPSCR);
#endif
}
/*-----------------------------------------------------------*/

void vPortClearInterruptMask( uint32_t ulNewMaskValue )
{
    if( ulNewMaskValue == pdFALSE )
    {
        portCLEAR_INTERRUPT_MASK();
    }
}
/*-----------------------------------------------------------*/

uint32_t ulPortSetInterruptMask( void )
{
uint32_t ulReturn;

    /* Interrupt in the CPU must be turned off while the ICCPMR is being
    updated. */
    portCPU_IRQ_DISABLE();
    if( portICCPMR_PRIORITY_MASK_REGISTER == ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) )
    {
        /* Interrupts were already masked. */
        ulReturn = pdTRUE;
    }
    else
    {
        ulReturn = pdFALSE;
        portICCPMR_PRIORITY_MASK_REGISTER = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT );
        __asm(    "dsb        \n"
                "isb        \n" );
    }
    portCPU_IRQ_ENABLE();

    return ulReturn;
}
/*-----------------------------------------------------------*/

#if( configASSERT_DEFINED == 1 )

    void vPortValidateInterruptPriority( void )
    {
        /* The following assertion will fail if a service routine (ISR) for
        an interrupt that has been assigned a priority above
        configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API
        function.  ISR safe FreeRTOS API functions must *only* be called
        from interrupts that have been assigned a priority at or below
        configMAX_SYSCALL_INTERRUPT_PRIORITY.

        Numerically low interrupt priority numbers represent logically high
        interrupt priorities, therefore the priority of the interrupt must
        be set to a value equal to or numerically *higher* than
        configMAX_SYSCALL_INTERRUPT_PRIORITY.

        FreeRTOS maintains separate thread and ISR API functions to ensure
        interrupt entry is as fast and simple as possible.

        The following links provide detailed information:
        http://www.freertos.org/RTOS-Cortex-M3-M4.html
        http://www.freertos.org/FAQHelp.html */
        configASSERT( portICCRPR_RUNNING_PRIORITY_REGISTER >= ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) );

        /* Priority grouping:  The interrupt controller (GIC) allows the bits
        that define each interrupt's priority to be split between bits that
        define the interrupt's pre-emption priority bits and bits that define
        the interrupt's sub-priority.  For simplicity all bits must be defined
        to be pre-emption priority bits.  The following assertion will fail if
        this is not the case (if some bits represent a sub-priority).

        The priority grouping is configured by the GIC's binary point register
        (ICCBPR).  Writting 0 to ICCBPR will ensure it is set to its lowest
        possible value (which may be above 0). */
        configASSERT( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE );
    }

#endif /* configASSERT_DEFINED */
/*-----------------------------------------------------------*/

portasm.S:

/*
    FreeRTOS V8.0.0 - Copyright (C) 2014 Real Time Engineers Ltd.
    All rights reserved


    ***************************************************************************
     *                                                                       *
     *    FreeRTOS tutorial books are available in pdf and paperback.        *
     *    Complete, revised, and edited pdf reference manuals are also       *
     *    available.                                                         *
     *                                                                       *
     *    Purchasing FreeRTOS documentation will not only help you, by       *
     *    ensuring you get running as quickly as possible and with an        *
     *    in-depth knowledge of how to use FreeRTOS, it will also help       *
     *    the FreeRTOS project to continue with its mission of providing     *
     *    professional grade, cross platform, de facto standard solutions    *
     *    for microcontrollers - completely free of charge!                  *
     *                                                                       *
     *    >>> See http://www.FreeRTOS.org/Documentation for details. <<<     *
     *                                                                       *
     *    Thank you for using FreeRTOS, and thank you for your support!      *
     *                                                                       *
    ***************************************************************************


    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
    >>>NOTE<<< The modification to the GPL is included to allow you to
    distribute a combined work that includes FreeRTOS without being obliged to
    provide the source code for proprietary components outside of the FreeRTOS
    kernel.  FreeRTOS is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
    more details. You should have received a copy of the GNU General Public
    License and the FreeRTOS license exception along with FreeRTOS; if not it
    can be viewed here: http://www.freertos.org/a00114.html and also obtained
    by writing to Richard Barry, contact details for whom are available on the
    FreeRTOS WEB site.

    1 tab == 4 spaces!

    http://www.FreeRTOS.org - Documentation, latest information, license and
    contact details.

    http://www.SafeRTOS.com - A version that is certified for use in safety
    critical systems.

    http://www.OpenRTOS.com - Commercial support, development, porting,
    licensing and training services.
*/

    .text
    .arm

    .set SYS_MODE,    0x1f
    .set SVC_MODE,    0x13
    .set IRQ_MODE,    0x12

    /* Hardware registers. */
    .extern ulICCIAR
    .extern ulICCEOIR
    .extern ulICDABR0
    .extern ulICDIPR0
    .extern ulICCHPIR
    .extern ulICCPMR

    /* Variables and functions. */
    .extern ulMaxAPIPriorityMask
    .extern _freertos_vector_table
    .extern pxCurrentTCB
    .extern vTaskSwitchContext
    .extern vApplicationIRQHandler
    .extern ulPortInterruptNesting
    .extern ulPortTaskHasFPUContext

    .global FreeRTOS_IRQ_Handler
    .global FreeRTOS_SWI_Handler
    .global vPortRestoreTaskContext
    .global vPortInstallFreeRTOSVectorTable




.macro portSAVE_CONTEXT

    /* Save the LR and SPSR onto the system mode stack before switching to
    system mode to save the remaining system mode registers. */
    SRSDB    sp!, #SYS_MODE
    CPS        #SYS_MODE
    PUSH    {R0-R12, R14}

    /* Push the critical nesting count. */
    LDR        R2, ulCriticalNestingConst
    LDR        R1, [R2]
    PUSH    {R1}

    /* Does the task have a floating point context that needs saving?  If
    ulPortTaskHasFPUContext is 0 then no. */
    LDR        R2, ulPortTaskHasFPUContextConst
    LDR        R3, [R2]
    CMP        R3, #0

    /* Save the floating point context, if any. */
    FMRXNE  R1,  FPSCR
    VPUSHNE {D0-D15}
    VPUSHNE    {D16-D31}
    PUSHNE    {R1}

    /* Save ulPortTaskHasFPUContext itself. */
    PUSH    {R3}

    /* Save the stack pointer in the TCB. */
    LDR        R0, pxCurrentTCBConst
    LDR        R1, [R0]
    STR        SP, [R1]

    .endm

; /**********************************************************************/

.macro portRESTORE_CONTEXT

    /* Switch to system mode. */
    CPS        #SYS_MODE

    /* Set the SP to point to the stack of the task being restored. */
    LDR        R0, pxCurrentTCBConst
    LDR        R1, [R0]
    LDR        SP, [R1]

    /* Is there a floating point context to restore?  If the restored
    ulPortTaskHasFPUContext is zero then no. */
    LDR        R0, ulPortTaskHasFPUContextConst
    POP        {R1}
    STR        R1, [R0]
    CMP        R1, #0

    /* Restore the floating point context, if any. */
    POPNE     {R0}
    VPOPNE    {D16-D31}
    VPOPNE    {D0-D15}
    VMSRNE  FPSCR, R0

    /* Restore the critical section nesting depth. */
    LDR        R0, ulCriticalNestingConst
    POP        {R1}
    STR        R1, [R0]

    /* Ensure the priority mask is correct for the critical nesting depth. */
    LDR        R2, ulICCPMRConst
    LDR        R2, [R2]
    CMP        R1, #0
    MOVEQ    R4, #255
    LDRNE    R4, ulMaxAPIPriorityMaskConst
    LDRNE    R4, [R4]
    STR        R4, [R2]

    /* Restore all system mode registers other than the SP (which is already
    being used). */
    POP        {R0-R12, R14}

    /* Return to the task code, loading CPSR on the way. */
    RFEIA    sp!

    .endm




/******************************************************************************
 * SVC handler is used to start the scheduler and yield a task.
 *****************************************************************************/
.align 4
.type FreeRTOS_SWI_Handler, %function
FreeRTOS_SWI_Handler:
    /* Save the context of the current task and select a new task to run. */
    portSAVE_CONTEXT
    LDR R0, vTaskSwitchContextConst
    BLX    R0

.type vPortRestoreTaskContext, %function
vPortRestoreTaskContext:
    portRESTORE_CONTEXT

.align 4
.type FreeRTOS_IRQ_Handler, %function
FreeRTOS_IRQ_Handler:
    /* Return to the interrupted instruction. */
    SUB        lr, lr, #4

    /* Push the return address and SPSR. */
    PUSH    {lr}
    MRS        lr, SPSR
    PUSH    {lr}

    /* Change to supervisor mode to allow reentry. */
    CPS        #SVC_MODE

    /* Push used registers. */
    PUSH    {r0-r6, r12}

    /* Increment nesting count.  r5 holds the address of ulPortInterruptNesting
    for future use.  r6 holds the original ulPortInterruptNesting value for
    future use. */
    LDR        r5, ulPortInterruptNestingConst
    LDR        r6, [r5]
    ADD        r4, r6, #1
    STR        r4, [r5]

    /* First read ICCHPIR the highest pending interrupt register (before ICCIAR).
    If reading is not in this order, ICCIAR may be read before it completely reflects
    processing through the CPU interface. Consequently, wrong interrupt IDs may be read.
    (See RZ's documentation, "Flow of Interrupt Operations, note 1) */
    LDR        r1, ulICCHPIRConst
    LDR        r1, [r1]
    LDR        r3, [r1]

    /* Read value from ICCIAR the interrupt acknowledge register, which is stored in r0
    for future parameter and interrupt clearing use. */
    LDR     r2, ulICCIARConst
    LDR        r2, [r2]
    LDR        r0, [r2]

    /* Check the value of ICCIAR before processing.
    (See RZ's documentation, "Notes on Reading Interrupt ID Values from Interrupt Acknowledge
    Register (ICCIAR)" */
    LDR        r2, =0x000003FF
    AND        r3, r0, r2
    LDR        r1, =0x000003FE
    CMP        r3, r1
    BPL        int_not_active
    CMP        r3, #0
    BEQ        int_check

int_active:
    /* Interrupt has been confirmed as active, process it */

    /* Ensure bit 2 of the stack pointer is clear.  r2 holds the bit 2 value for
    future use. */
    MOV        r2, sp
    AND        r2, r2, #4
    SUB        sp, sp, r2

    /* Call the interrupt handler. */
    PUSH    {r0-r3, lr}
    LDR        r1, vApplicationIRQHandlerConst
    BLX        r1
    POP        {r0-r3, lr}
    ADD        sp, sp, r2

    CPSID    i
    DSB
    ISB

    /* Write the value read from ICCIAR to ICCEOIR. */
    LDR     r4, ulICCEOIRConst
    LDR        r4, [r4]
    STR        r0, [r4]

end_of_handler:
    /* Restore the old nesting count. */
    STR        r6, [r5]

    /* A context switch is never performed if the nesting count is not 0. */
    CMP        r6, #0
    BNE        exit_without_switch

    /* Did the interrupt request a context switch?  r1 holds the address of
    ulPortYieldRequired and r0 the value of ulPortYieldRequired for future
    use. */
    LDR        r1, =ulPortYieldRequired
    LDR        r0, [r1]
    CMP        r0, #0
    BNE        switch_before_exit

exit_without_switch:
    /* No context switch.  Restore used registers, LR_irq and SPSR before
    returning. */
    POP        {r0-r6, r12}
    CPS        #IRQ_MODE
    POP        {LR}
    MSR        SPSR_cxsf, LR
    POP        {LR}
    MOVS    PC, LR

switch_before_exit:
    /* A context swtich is to be performed.  Clear the context switch pending
    flag. */
    MOV        r0, #0
    STR        r0, [r1]

    /* Restore used registers, LR-irq and SPSR before saving the context
    to the task stack. */
    POP        {r0-r6, r12}
    CPS        #IRQ_MODE
    POP        {LR}
    MSR        SPSR_cxsf, LR
    POP        {LR}
    portSAVE_CONTEXT

    /* Call the function that selects the new task to execute.
    vTaskSwitchContext() if vTaskSwitchContext() uses LDRD or STRD
    instructions, or 8 byte aligned stack allocated data.  LR does not need
    saving as a new LR will be loaded by portRESTORE_CONTEXT anyway. */
    LDR        R0, vTaskSwitchContextConst
    BLX        R0

    /* Restore the context of, and branch to, the task selected to execute
    next. */
    portRESTORE_CONTEXT

int_check:
    /* Interrupt needs to be confirmed. ICDABR0 is used for this.
    (See RZ's documentation, "Notes on Reading Interrupt ID Values from Interrupt Acknowledge
    Register (ICCIAR)" */
    LDR     r2, =ulICDABR0Const
    LDR     r2, [r2]
    LDR     r3, [r2]
    AND     r3, r3, #0x00000001
    CMP     r3, #0
    BNE     int_active

int_not_active:
    /* Interrupt is not active and must be cleared. ICDIPR0 is used for this.
    (See RZ's documentation, "Notes on Reading Interrupt ID Values from Interrupt Acknowledge
    Register (ICCIAR)" */
    LDR     r2, =ulICDIPR0Const
    LDR     r2, [r2]
    LDR     r3, [r2]
    STR     r3, [r2]
    B       end_of_handler

ulICCIARConst:    .word ulICCIAR
ulICCEOIRConst:    .word ulICCEOIR
ulICDABR0Const:    .word ulICDABR0
ulICDIPR0Const:    .word ulICDIPR0
ulICCHPIRConst: .word ulICCHPIR
ulICCPMRConst: .word ulICCPMR
pxCurrentTCBConst: .word pxCurrentTCB
ulCriticalNestingConst: .word ulCriticalNesting
ulPortTaskHasFPUContextConst: .word ulPortTaskHasFPUContext
ulMaxAPIPriorityMaskConst: .word ulMaxAPIPriorityMask
vTaskSwitchContextConst: .word vTaskSwitchContext
vApplicationIRQHandlerConst: .word vApplicationIRQHandler
ulPortInterruptNestingConst: .word ulPortInterruptNesting

.end


portmacro.h:

/*
    FreeRTOS V8.0.0 - Copyright (C) 2014 Real Time Engineers Ltd.
    All rights reserved

    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS provides completely free yet professionally developed,    *
     *    robust, strictly quality controlled, supported, and cross          *
     *    platform software that has become a de facto standard.             *
     *                                                                       *
     *    Help yourself get started quickly and support the FreeRTOS         *
     *    project by purchasing a FreeRTOS tutorial book, reference          *
     *    manual, or both from: http://www.FreeRTOS.org/Documentation        *
     *                                                                       *
     *    Thank you!                                                         *
     *                                                                       *
    ***************************************************************************

    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.

    >>! NOTE: The modification to the GPL is included to allow you to distribute
    >>! a combined work that includes FreeRTOS without being obliged to provide
    >>! the source code for proprietary components outside of the FreeRTOS
    >>! kernel.

    FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  Full license text is available from the following
    link: http://www.freertos.org/a00114.html

    1 tab == 4 spaces!

    ***************************************************************************
     *                                                                       *
     *    Having a problem?  Start by reading the FAQ "My application does   *
     *    not run, what could be wrong?"                                     *
     *                                                                       *
     *    http://www.FreeRTOS.org/FAQHelp.html                               *
     *                                                                       *
    ***************************************************************************

    http://www.FreeRTOS.org - Documentation, books, training, latest versions,
    license and Real Time Engineers Ltd. contact details.

    http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
    including FreeRTOS+Trace - an indispensable productivity tool, a DOS
    compatible FAT file system, and our tiny thread aware UDP/IP stack.

    http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High
    Integrity Systems to sell under the OpenRTOS brand.  Low cost OpenRTOS
    licenses offer ticketed support, indemnification and middleware.

    http://www.SafeRTOS.com - High Integrity Systems also provide a safety
    engineered and independently SIL3 certified version for use in safety and
    mission critical applications that require provable dependability.

    1 tab == 4 spaces!
*/

#ifndef PORTMACRO_H
#define PORTMACRO_H

#ifdef __cplusplus
    extern "C" {
#endif

/*-----------------------------------------------------------
 * Port specific definitions.
 *
 * The settings in this file configure FreeRTOS correctly for the given hardware
 * and compiler.
 *
 * These settings should not be altered.
 *-----------------------------------------------------------
 */

/* Type definitions. */
#define portCHAR        char
#define portFLOAT        float
#define portDOUBLE        double
#define portLONG        long
#define portSHORT        short
#define portSTACK_TYPE    uint32_t
#define portBASE_TYPE    long

typedef portSTACK_TYPE StackType_t;
typedef long BaseType_t;
typedef unsigned long UBaseType_t;

typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t ) 0xffffffffUL

/*-----------------------------------------------------------*/

/* Hardware specifics. */
#define portSTACK_GROWTH            ( -1 )
#define portTICK_PERIOD_MS            ( ( TickType_t ) 1000 / configTICK_RATE_HZ )
#define portBYTE_ALIGNMENT            8

/*-----------------------------------------------------------*/

/* Task utilities. */

/* Called at the end of an ISR that can cause a context switch. */
#define portEND_SWITCHING_ISR( xSwitchRequired )\
{                                                \
extern uint32_t ulPortYieldRequired;            \
                                                \
    if( xSwitchRequired != pdFALSE )            \
    {                                            \
        ulPortYieldRequired = pdTRUE;            \
    }                                            \
}

#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
#define portYIELD() __asm( "SWI 0" );


/*-----------------------------------------------------------
 * Critical section control
 *----------------------------------------------------------*/

extern void vPortEnterCritical( void );
extern void vPortExitCritical( void );
extern uint32_t ulPortSetInterruptMask( void );
extern void vPortClearInterruptMask( uint32_t ulNewMaskValue );

/* These macros do not globally disable/enable interrupts.  They do mask off
interrupts that have a priority below configMAX_API_CALL_INTERRUPT_PRIORITY. */
#define portENTER_CRITICAL()        vPortEnterCritical();
#define portEXIT_CRITICAL()            vPortExitCritical();
#define portDISABLE_INTERRUPTS()    ulPortSetInterruptMask()
#define portENABLE_INTERRUPTS()        vPortClearInterruptMask( 0 )
#define portSET_INTERRUPT_MASK_FROM_ISR()        ulPortSetInterruptMask()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)    vPortClearInterruptMask(x)

/*-----------------------------------------------------------*/

/* Task function macros as described on the FreeRTOS.org WEB site.  These are
not required for this port but included in case common demo code that uses these
macros is used. */
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters )    void vFunction( void *pvParameters )
#define portTASK_FUNCTION( vFunction, pvParameters )    void vFunction( void *pvParameters )

/* Prototype of the FreeRTOS tick handler.  This must be installed as the
handler for whichever peripheral is used to generate the RTOS tick. */
void FreeRTOS_Tick_Handler( void );

/* Any task that uses the floating point unit MUST call vPortTaskUsesFPU()
before any floating point instructions are executed. */
void vPortTaskUsesFPU( void );
#define portTASK_USES_FLOATING_POINT() vPortTaskUsesFPU()

#define portLOWEST_INTERRUPT_PRIORITY ( ( ( uint32_t ) configUNIQUE_INTERRUPT_PRIORITIES ) - 1UL )
#define portLOWEST_USABLE_INTERRUPT_PRIORITY ( portLOWEST_INTERRUPT_PRIORITY - 1UL )

/* Architecture specific optimisations. */
#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1

    /* Store/clear the ready priorities in a bit map. */
    #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
    #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )

    /*-----------------------------------------------------------*/

    #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31 - __builtin_clz( uxReadyPriorities ) )

#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */

#ifdef configASSERT
    void vPortValidateInterruptPriority( void );
    #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID()     vPortValidateInterruptPriority()
#endif /* configASSERT */

#define portNOP() __asm volatile( "NOP" )


#ifdef __cplusplus
    } /* extern C */
#endif


/* The number of bits to shift for an interrupt priority is dependent on the
number of bits implemented by the interrupt controller. */
#if configUNIQUE_INTERRUPT_PRIORITIES == 16
    #define portPRIORITY_SHIFT 4
    #define portMAX_BINARY_POINT_VALUE    3
#elif configUNIQUE_INTERRUPT_PRIORITIES == 32
    #define portPRIORITY_SHIFT 3
    #define portMAX_BINARY_POINT_VALUE    2
#elif configUNIQUE_INTERRUPT_PRIORITIES == 64
    #define portPRIORITY_SHIFT 2
    #define portMAX_BINARY_POINT_VALUE    1
#elif configUNIQUE_INTERRUPT_PRIORITIES == 128
    #define portPRIORITY_SHIFT 1
    #define portMAX_BINARY_POINT_VALUE    0
#elif configUNIQUE_INTERRUPT_PRIORITIES == 256
    #define portPRIORITY_SHIFT 0
    #define portMAX_BINARY_POINT_VALUE    0
#else
    #error Invalid configUNIQUE_INTERRUPT_PRIORITIES setting.  configUNIQUE_INTERRUPT_PRIORITIES must be set to the number of unique priorities implemented by the target hardware
#endif

/* Interrupt controller access addresses. */
#define portICDABR0_ACTIVE_BIT_0_OFFSET                                    ( 0x300 )
#define portICDIPR0_INTERRUPT_PRORITY_0_OFFSET                    ( 0x400 )

#define portICDABR0_ACTIVE_BIT_REGISTER_0_ADDRESS                        ( configINTERRUPT_CONTROLLER_BASE_ADDRESS + portICDABR0_ACTIVE_BIT_0_OFFSET )
#define portICDIPR0_INTERRUPT_PRORITY_REGISTER_0_ADDRESS        ( configINTERRUPT_CONTROLLER_BASE_ADDRESS + portICDIPR0_INTERRUPT_PRORITY_0_OFFSET )

#define portICCPMR_PRIORITY_MASK_OFFSET                          ( 0x04 )
#define portICCIAR_INTERRUPT_ACKNOWLEDGE_OFFSET                 ( 0x0C )
#define portICCEOIR_END_OF_INTERRUPT_OFFSET                     ( 0x10 )
#define portICCBPR_BINARY_POINT_OFFSET                            ( 0x08 )
#define portICCRPR_RUNNING_PRIORITY_OFFSET                        ( 0x14 )
#define portICCHPIR_HIGHEST_PENDING_INTERRUPT_OFFSET            ( 0x18 )

#define portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS         ( configINTERRUPT_CONTROLLER_BASE_ADDRESS + configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET )
#define portICCPMR_PRIORITY_MASK_REGISTER                     ( *( ( volatile uint8_t * ) ( portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS + portICCPMR_PRIORITY_MASK_OFFSET ) ) )
#define portICCIAR_INTERRUPT_ACKNOWLEDGE_REGISTER_ADDRESS     ( portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS + portICCIAR_INTERRUPT_ACKNOWLEDGE_OFFSET )
#define portICCEOIR_END_OF_INTERRUPT_REGISTER_ADDRESS         ( portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS + portICCEOIR_END_OF_INTERRUPT_OFFSET )
#define portICCPMR_PRIORITY_MASK_REGISTER_ADDRESS             ( portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS + portICCPMR_PRIORITY_MASK_OFFSET )
#define portICCBPR_BINARY_POINT_REGISTER                     ( *( ( const volatile uint32_t * ) ( portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS + portICCBPR_BINARY_POINT_OFFSET ) ) )
#define portICCRPR_RUNNING_PRIORITY_REGISTER                 ( *( ( const volatile uint8_t * ) ( portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS + portICCRPR_RUNNING_PRIORITY_OFFSET ) ) )
#define portICCHPIR_HIGHEST_PENDING_INTERRUPT_ADDRESS         ( portINTERRUPT_CONTROLLER_CPU_INTERFACE_ADDRESS + portICCHPIR_HIGHEST_PENDING_INTERRUPT_OFFSET )

#endif /* PORTMACRO_H */

Regs,

Guillaume

0 comments

Please sign in to leave a comment.