From 13608b92d25debea25e3a8efb8334069b07e5f0e Mon Sep 17 00:00:00 2001 From: rtel Date: Thu, 30 Jan 2014 14:45:48 +0000 Subject: [PATCH] Add in interrupt nesting test. git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@2187 1d2547de-c912-0410-9cb9-b8ca96c0e9e2 --- .../CORTEX_A9_Zynq_ZC702/RTOSDemo/.project | 5 + .../RTOSDemo/src/FreeRTOSConfig.h | 25 +- .../RTOSDemo/src/FreeRTOS_tick_config.c | 21 +- .../RTOSDemo/src/IntQueueTimer.c | 323 ++++++++---------- .../RTOSDemo/src/main_full.c | 5 + 5 files changed, 178 insertions(+), 201 deletions(-) diff --git a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/.project b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/.project index 1d9dcfbc6..4431c9a66 100644 --- a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/.project +++ b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/.project @@ -49,6 +49,11 @@ 1 FREERTOS_ROOT/FreeRTOS-Plus/Demo/Common/FreeRTOS_Plus_CLI_Demos/UARTCommandConsole.c + + src/Standard_Demo_Tasks/IntQueue.c + 1 + FREERTOS_ROOT/FreeRTOS/Demo/Common/Minimal/IntQueue.c + diff --git a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOSConfig.h b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOSConfig.h index e2d387664..9130dc805 100644 --- a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOSConfig.h +++ b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOSConfig.h @@ -132,14 +132,14 @@ #define configUSE_QUEUE_SETS 1 /* Co-routine definitions. */ -#define configUSE_CO_ROUTINES 0 -#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) /* Software timer definitions. */ -#define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) -#define configTIMER_QUEUE_LENGTH 5 -#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 5 +#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) /* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */ @@ -165,13 +165,14 @@ Zynq MPU. */ #define configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET ( -0xf00 ) #define configUNIQUE_INTERRUPT_PRIORITIES 32 -/* Run time stats gathering definitions. */ -unsigned long ulGetRunTimeCounterValue( void ); -void vInitialiseRunTimeStats( void ); - +/* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS is not required because the time base +comes from the ulHighFrequencyTimerCounts variable which is incremented in a +high frequency timer that is already being started as part of the interrupt +nesting test. */ #define configGENERATE_RUN_TIME_STATS 1 -#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vInitialiseRunTimeStats() -#define portGET_RUN_TIME_COUNTER_VALUE() ulGetRunTimeCounterValue() +extern volatile uint32_t ulHighFrequencyTimerCounts; +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() ulHighFrequencyTimerCounts /* The size of the global output buffer that is available for use when there are multiple command interpreters running at once (for example, one on a UART diff --git a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOS_tick_config.c b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOS_tick_config.c index 601311059..0134d31d7 100644 --- a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOS_tick_config.c +++ b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/FreeRTOS_tick_config.c @@ -88,6 +88,7 @@ BaseType_t xStatus; extern void FreeRTOS_Tick_Handler( void ); XScuTimer_Config *pxTimerConfig; XScuGic_Config *pxGICConfig; +const uint8_t ucRisingEdge = 3; /* This function is called with the IRQ interrupt disabled, and the IRQ interrupt should be left disabled. It is enabled automatically when the @@ -99,8 +100,11 @@ XScuGic_Config *pxGICConfig; xStatus = XScuGic_CfgInitialize( &xInterruptController, pxGICConfig, pxGICConfig->CpuBaseAddress ); configASSERT( xStatus == XST_SUCCESS ); + /* The priority must be the lowest possible. */ + XScuGic_SetPriorityTriggerType( &xInterruptController, XPAR_SCUTIMER_INTR, portLOWEST_USABLE_INTERRUPT_PRIORITY << portPRIORITY_SHIFT, ucRisingEdge ); + /* Install the FreeRTOS tick handler. */ - xStatus = XScuGic_Connect(&xInterruptController, XPAR_SCUTIMER_INTR, (Xil_ExceptionHandler) FreeRTOS_Tick_Handler, (void *)&xTimer); + xStatus = XScuGic_Connect( &xInterruptController, XPAR_SCUTIMER_INTR, (Xil_ExceptionHandler) FreeRTOS_Tick_Handler, ( void * ) &xTimer ); configASSERT( xStatus == XST_SUCCESS ); /* Initialise the timer. */ @@ -127,21 +131,6 @@ XScuGic_Config *pxGICConfig; } /*-----------------------------------------------------------*/ -/* - * Crude implementation of a run time counter used to measure how much time - * each task spends in the Running state. - */ -unsigned long ulGetRunTimeCounterValue( void ) -{ - return 0; -} -/*-----------------------------------------------------------*/ - -void vInitialiseRunTimeStats( void ) -{ -} -/*-----------------------------------------------------------*/ - void vClearTickInterrupt( void ) { XScuTimer_ClearInterruptStatus( &xTimer ); diff --git a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/IntQueueTimer.c b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/IntQueueTimer.c index c0bd4c077..1a24a4cba 100644 --- a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/IntQueueTimer.c +++ b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/IntQueueTimer.c @@ -63,6 +63,24 @@ 1 tab == 4 spaces! */ +/* + * This file initialises three timers as follows: + * + * Timer 0 and Timer 1 provide the interrupts that are used with the IntQ + * standard demo tasks, which test interrupt nesting and using queues from + * interrupts. Both these interrupts operate below the maximum syscall + * interrupt priority. + * + * Timer 2 is a much higher frequency timer that tests the nesting of interrupts + * that execute above the maximum syscall interrupt priority. + * + * All the timers can nest with the tick interrupt - creating a maximum + * interrupt nesting depth of 4. + * + * For convenience, the high frequency timer is also used to provide the time + * base for the run time stats. + */ + /* Scheduler includes. */ #include "FreeRTOS.h" @@ -71,214 +89,173 @@ #include "IntQueue.h" /* Xilinx includes. */ -#include "xstatus.h" -#include "xil_io.h" -#include "xil_exception.h" #include "xttcps.h" #include "xscugic.h" +/* The frequencies at which the first two timers expire are slightly offset to +ensure they don't remain synchronised. The frequency of the interrupt that +operates above the max syscall interrupt priority is 10 times faster so really +hammers the interrupt entry and exit code. */ +#define tmrTIMERS_USED 3 #define tmrTIMER_0_FREQUENCY ( 2000UL ) #define tmrTIMER_1_FREQUENCY ( 2001UL ) +#define tmrTIMER_2_FREQUENCY ( 20000UL ) -#define TTC_TICK_DEVICE_ID XPAR_XTTCPS_0_DEVICE_ID -#define TTC_TICK_INTR_ID XPAR_XTTCPS_0_INTR -#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID +/*-----------------------------------------------------------*/ /* - * Constants to set the basic operating parameters. - * PWM_DELTA_DUTY is critical to the running time of the test. Smaller values - * make the test run longer. + * The single interrupt service routines that is used to service all three + * timers. */ -#define TICK_TIMER_FREQ_HZ 100 /* Tick timer counter's output frequency */ +static void prvTimerHandler( void *CallBackRef ); -#define TICKS_PER_CHANGE_PERIOD TICK_TIMER_FREQ_HZ /* Tick signals per update */ +/*-----------------------------------------------------------*/ -#define TIMERS_USED 2 +/* Hardware constants. */ +static const BaseType_t xDeviceIDs[ tmrTIMERS_USED ] = { XPAR_XTTCPS_0_DEVICE_ID, XPAR_XTTCPS_1_DEVICE_ID, XPAR_XTTCPS_2_DEVICE_ID }; +static const BaseType_t xInterruptIDs[ tmrTIMERS_USED ] = { XPAR_XTTCPS_0_INTR, XPAR_XTTCPS_1_INTR, XPAR_XTTCPS_2_INTR }; -static void TickHandler(void *CallBackRef); +/* Timer configuration settings. */ +typedef struct +{ + uint32_t OutputHz; /* Output frequency. */ + uint16_t Interval; /* Interval value. */ + uint8_t Prescaler; /* Prescaler value. */ + uint16_t Options; /* Option settings. */ +} TmrCntrSetup; -static volatile uint8_t UpdateFlag; /* Flag to update the seconds counter */ -static uint32_t TickCount; /* Ticker interrupts between seconds change */ -static XTtcPs TtcPsInst[ TIMERS_USED ]; /* Timer counter instance */ +static TmrCntrSetup xTimerSettings[ tmrTIMERS_USED ] = +{ + { tmrTIMER_0_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE }, + { tmrTIMER_1_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE }, + { tmrTIMER_2_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE } +}; + +/* Lower priority number means higher logical priority, so +configMAX_API_CALL_INTERRUPT_PRIORITY - 1 is above the maximum system call +interrupt priority. */ +static const UBaseType_t uxInterruptPriorities[ tmrTIMERS_USED ] = +{ + configMAX_API_CALL_INTERRUPT_PRIORITY + 1, + configMAX_API_CALL_INTERRUPT_PRIORITY, + configMAX_API_CALL_INTERRUPT_PRIORITY - 1 +} +static XTtcPs xTimerInstances[ tmrTIMERS_USED ]; -typedef struct { - u32 OutputHz; /* Output frequency */ - u16 Interval; /* Interval value */ - u8 Prescaler; /* Prescaler value */ - u16 Options; /* Option settings */ -} TmrCntrSetup; +/* Used to provide a means of ensuring the intended interrupt nesting depth is +actually being reached. */ +extern uint32_t ulPortInterruptNesting; +static uint32_t ulMaxRecordedNesting = 0; -static const TmrCntrSetup SettingsTable[ TIMERS_USED ] = { { tmrTIMER_0_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE }, - { tmrTIMER_1_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE } }; +/* For convenience the high frequency timer increments a variable that is then +used as the time base for the run time stats. */ +volatile uint32_t ulHighFrequencyTimerCounts = 0; -BaseType_t DeviceIDs[ TIMERS_USED ] = { XPAR_XTTCPS_0_DEVICE_ID, XPAR_XTTCPS_1_DEVICE_ID }; -BaseType_t InterruptIDs[ TIMERS_USED ] = { XPAR_XTTCPS_0_INTR, XPAR_XTTCPS_1_INTR }; +/*-----------------------------------------------------------*/ void vInitialiseTimerForIntQueueTest( void ) { -int Status; -TmrCntrSetup *TimerSetup; -XTtcPs *TtcPsTick; +BaseType_t xStatus; +TmrCntrSetup *pxTimerSettings; extern XScuGic xInterruptController; BaseType_t xTimer; -XTtcPs *Timer; -XTtcPs_Config *Config; +XTtcPs *pxTimerInstance; +XTtcPs_Config *pxTimerConfiguration; +const uint8_t ucRisingEdge = 3; - for( xTimer = 0; xTimer < TIMERS_USED; xTimer++ ) + for( xTimer = 0; xTimer < tmrTIMERS_USED; xTimer++ ) { + /* Look up the timer's configuration. */ + pxTimerInstance = &( xTimerInstances[ xTimer ] ); + pxTimerConfiguration = XTtcPs_LookupConfig( xDeviceIDs[ xTimer ] ); + configASSERT( pxTimerConfiguration ); + + pxTimerSettings = &( xTimerSettings[ xTimer ] ); + + /* Initialise the device. */ + xStatus = XTtcPs_CfgInitialize( pxTimerInstance, pxTimerConfiguration, pxTimerConfiguration->BaseAddress ); + if( xStatus != XST_SUCCESS ) + { + /* Not sure how to do this before XTtcPs_CfgInitialize is called + as pxTimerInstance is set within XTtcPs_CfgInitialize(). */ + XTtcPs_Stop( pxTimerInstance ); + xStatus = XTtcPs_CfgInitialize( pxTimerInstance, pxTimerConfiguration, pxTimerConfiguration->BaseAddress ); + configASSERT( xStatus == XST_SUCCESS ); + } + + /* Set the options. */ + XTtcPs_SetOptions( pxTimerInstance, pxTimerSettings->Options ); + + /* The timer frequency is preset in the pxTimerSettings structure. + Derive the values for the other structure members. */ + XTtcPs_CalcIntervalFromFreq( pxTimerInstance, pxTimerSettings->OutputHz, &( pxTimerSettings->Interval ), &( pxTimerSettings->Prescaler ) ); + + /* Set the interval and prescale. */ + XTtcPs_SetInterval( pxTimerInstance, pxTimerSettings->Interval ); + XTtcPs_SetPrescaler( pxTimerInstance, pxTimerSettings->Prescaler ); + + /* The priority must be the lowest possible. */ + XScuGic_SetPriorityTriggerType( &xInterruptController, xInterruptIDs[ xTimer ], uxInterruptPriorities[ xTimer ] << portPRIORITY_SHIFT, ucRisingEdge ); - TimerSetup = &( SettingsTable[ xTimer ] ); - Timer = &TtcPsInst[ xTimer ]; - - /* - * Look up the configuration based on the device identifier - */ - Config = XTtcPs_LookupConfig(DeviceIDs[ xTimer ]); - configASSERT( Config ); - - /* - * Initialize the device - */ - Status = XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress); - configASSERT(Status == XST_SUCCESS); - - /* - * Stop the timer first - */ - XTtcPs_Stop( Timer ); - - /* - * Set the options - */ - XTtcPs_SetOptions(Timer, TimerSetup->Options); - - /* - * Timer frequency is preset in the TimerSetup structure, - * however, the value is not reflected in its other fields, such as - * IntervalValue and PrescalerValue. The following call will map the - * frequency to the interval and prescaler values. - */ - XTtcPs_CalcIntervalFromFreq(Timer, TimerSetup->OutputHz, - &(TimerSetup->Interval), &(TimerSetup->Prescaler)); - - /* - * Set the interval and prescale - */ - XTtcPs_SetInterval(Timer, TimerSetup->Interval); - XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler); - - - /* - * Connect to the interrupt controller - */ - Status = XScuGic_Connect(&xInterruptController, InterruptIDs[ xTimer ], (Xil_InterruptHandler)TickHandler, (void *)Timer); - configASSERT( Status == XST_SUCCESS); - - /* - * Enable the interrupt for the Timer counter - */ - XScuGic_Enable(&xInterruptController, InterruptIDs[ xTimer ]); - - /* - * Enable the interrupts for the tick timer/counter - * We only care about the interval timeout. - */ - XTtcPs_EnableInterrupts(Timer, XTTCPS_IXR_INTERVAL_MASK); - - /* - * Start the tick timer/counter - */ - XTtcPs_Start(Timer); + /* Connect to the interrupt controller. */ + xStatus = XScuGic_Connect( &xInterruptController, xInterruptIDs[ xTimer ], ( Xil_InterruptHandler ) prvTimerHandler, ( void * ) pxTimerInstance ); + configASSERT( xStatus == XST_SUCCESS); + + /* Enable the interrupt in the GIC. */ + XScuGic_Enable( &xInterruptController, xInterruptIDs[ xTimer ] ); + + /* Enable the interrupts in the timer. */ + XTtcPs_EnableInterrupts( pxTimerInstance, XTTCPS_IXR_INTERVAL_MASK ); + + /* Start the timer. */ + XTtcPs_Start( pxTimerInstance ); } } /*-----------------------------------------------------------*/ -void vT2InterruptHandler( void ) +static void prvTimerHandler( void *pvCallBackRef ) { - portEND_SWITCHING_ISR( xFirstTimerHandler() ); -} -/*-----------------------------------------------------------*/ +uint32_t ulInterruptStatus; +XTtcPs *pxTimer = ( XTtcPs * ) pvCallBackRef; +BaseType_t xYieldRequired; -void vT3InterruptHandler( void ) -{ - portEND_SWITCHING_ISR( xSecondTimerHandler() ); -} -/*-----------------------------------------------------------*/ + /* Read the interrupt status, then write it back to clear the interrupt. */ + ulInterruptStatus = XTtcPs_GetInterruptStatus( pxTimer ); + XTtcPs_ClearInterruptStatus( pxTimer, ulInterruptStatus ); -volatile uint32_t ulTimer1Count = 0, ulTimer2Count = 0; + /* Only one interrupt event type is expected. */ + configASSERT( ( XTTCPS_IXR_INTERVAL_MASK & ulInterruptStatus ) != 0 ); -static void TickHandler(void *CallBackRef) -{ -uint32_t StatusEvent; -XTtcPs *pxTtcPs = (XTtcPs *)CallBackRef; - /* - * Read the interrupt status, then write it back to clear the interrupt. - */ - StatusEvent = XTtcPs_GetInterruptStatus(pxTtcPs); - XTtcPs_ClearInterruptStatus(pxTtcPs, StatusEvent); - - if (0 != (XTTCPS_IXR_INTERVAL_MASK & StatusEvent)) { - if( pxTtcPs->Config.DeviceId == DeviceIDs[ 0 ] ) - { - ulTimer1Count++; - } - else + /* Check the device ID to know which IntQueue demo to call. */ + if( pxTimer->Config.DeviceId == xDeviceIDs[ 0 ] ) + { + xYieldRequired = xFirstTimerHandler(); + } + else if( pxTimer->Config.DeviceId == xDeviceIDs[ 1 ] ) + { + xYieldRequired = xSecondTimerHandler(); + } + else + { + /* The high frequency timer is also used to generate the time base for + the run time state. */ + ulHighFrequencyTimerCounts++; + + /* Latch the highest interrupt nesting count detected. */ + if( ulPortInterruptNesting > ulMaxRecordedNesting ) { - ulTimer2Count++; + ulMaxRecordedNesting = ulPortInterruptNesting; } - TickCount++; + + xYieldRequired = pdFALSE; } -} -#if 0 -int SetupTimer(int DeviceID) -{ - int Status; - XTtcPs_Config *Config; - XTtcPs *Timer; - TmrCntrSetup *TimerSetup; - - TimerSetup = &SettingsTable; - - Timer = &TtcPsInst; - /* - * Stop the timer first - */ - XTtcPs_Stop( &TtcPsInst ); - - /* - * Look up the configuration based on the device identifier - */ - Config = XTtcPs_LookupConfig(DeviceIDs[ DeviceID ]); - configASSERT( Config ); - - /* - * Initialize the device - */ - Status = XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress); - configASSERT(Status == XST_SUCCESS); - - /* - * Set the options - */ - XTtcPs_SetOptions(Timer, TimerSetup->Options); - - /* - * Timer frequency is preset in the TimerSetup structure, - * however, the value is not reflected in its other fields, such as - * IntervalValue and PrescalerValue. The following call will map the - * frequency to the interval and prescaler values. - */ - XTtcPs_CalcIntervalFromFreq(Timer, TimerSetup->OutputHz, - &(TimerSetup->Interval), &(TimerSetup->Prescaler)); - - /* - * Set the interval and prescale - */ - XTtcPs_SetInterval(Timer, TimerSetup->Interval); - XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler); - - return XST_SUCCESS; + /* If xYieldRequired is not pdFALSE then calling either xFirstTimerHandler() + or xSecondTimerHandler() resulted in a task leaving the blocked state and + the task that left the blocked state had a priority higher than the currently + running task (the task this interrupt interrupted) - so a context switch + should be performed so the interrupt returns directly to the higher priority + task. xYieldRequired is tested inside the following macro. */ + portYIELD_FROM_ISR( xYieldRequired ); } -#endif diff --git a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/main_full.c b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/main_full.c index 3019fc0c8..f6f632573 100644 --- a/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/main_full.c +++ b/FreeRTOS/Demo/CORTEX_A9_Zynq_ZC702/RTOSDemo/src/main_full.c @@ -324,6 +324,11 @@ unsigned long ulErrorFound = pdFALSE; /* Check all the demo tasks (other than the flash tasks) to ensure that they are all still running, and that none have detected an error. */ + if( xAreIntQueueTasksStillRunning() != pdTRUE ) + { + ulErrorFound = pdTRUE; + } + if( xAreMathsTaskStillRunning() != pdTRUE ) { ulErrorFound = pdTRUE; -- 2.39.5