1 tab == 4 spaces!\r
*/\r
\r
+/*\r
+ * This file initialises three timers as follows:\r
+ *\r
+ * Timer 0 and Timer 1 provide the interrupts that are used with the IntQ\r
+ * standard demo tasks, which test interrupt nesting and using queues from\r
+ * interrupts. Both these interrupts operate below the maximum syscall\r
+ * interrupt priority.\r
+ *\r
+ * Timer 2 is a much higher frequency timer that tests the nesting of interrupts\r
+ * that execute above the maximum syscall interrupt priority.\r
+ *\r
+ * All the timers can nest with the tick interrupt - creating a maximum\r
+ * interrupt nesting depth of 4.\r
+ *\r
+ * For convenience, the high frequency timer is also used to provide the time\r
+ * base for the run time stats.\r
+ */\r
+\r
/* Scheduler includes. */\r
#include "FreeRTOS.h"\r
\r
#include "IntQueue.h"\r
\r
/* Xilinx includes. */\r
-#include "xstatus.h"\r
-#include "xil_io.h"\r
-#include "xil_exception.h"\r
#include "xttcps.h"\r
#include "xscugic.h"\r
\r
+/* The frequencies at which the first two timers expire are slightly offset to\r
+ensure they don't remain synchronised. The frequency of the interrupt that\r
+operates above the max syscall interrupt priority is 10 times faster so really\r
+hammers the interrupt entry and exit code. */\r
+#define tmrTIMERS_USED 3\r
#define tmrTIMER_0_FREQUENCY ( 2000UL )\r
#define tmrTIMER_1_FREQUENCY ( 2001UL )\r
+#define tmrTIMER_2_FREQUENCY ( 20000UL )\r
\r
-#define TTC_TICK_DEVICE_ID XPAR_XTTCPS_0_DEVICE_ID\r
-#define TTC_TICK_INTR_ID XPAR_XTTCPS_0_INTR\r
-#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID\r
+/*-----------------------------------------------------------*/\r
\r
/*\r
- * Constants to set the basic operating parameters.\r
- * PWM_DELTA_DUTY is critical to the running time of the test. Smaller values\r
- * make the test run longer.\r
+ * The single interrupt service routines that is used to service all three\r
+ * timers.\r
*/\r
-#define TICK_TIMER_FREQ_HZ 100 /* Tick timer counter's output frequency */\r
+static void prvTimerHandler( void *CallBackRef );\r
\r
-#define TICKS_PER_CHANGE_PERIOD TICK_TIMER_FREQ_HZ /* Tick signals per update */\r
+/*-----------------------------------------------------------*/\r
\r
-#define TIMERS_USED 2\r
+/* Hardware constants. */\r
+static const BaseType_t xDeviceIDs[ tmrTIMERS_USED ] = { XPAR_XTTCPS_0_DEVICE_ID, XPAR_XTTCPS_1_DEVICE_ID, XPAR_XTTCPS_2_DEVICE_ID };\r
+static const BaseType_t xInterruptIDs[ tmrTIMERS_USED ] = { XPAR_XTTCPS_0_INTR, XPAR_XTTCPS_1_INTR, XPAR_XTTCPS_2_INTR };\r
\r
-static void TickHandler(void *CallBackRef);\r
+/* Timer configuration settings. */\r
+typedef struct\r
+{\r
+ uint32_t OutputHz; /* Output frequency. */\r
+ uint16_t Interval; /* Interval value. */\r
+ uint8_t Prescaler; /* Prescaler value. */\r
+ uint16_t Options; /* Option settings. */\r
+} TmrCntrSetup;\r
\r
-static volatile uint8_t UpdateFlag; /* Flag to update the seconds counter */\r
-static uint32_t TickCount; /* Ticker interrupts between seconds change */\r
-static XTtcPs TtcPsInst[ TIMERS_USED ]; /* Timer counter instance */\r
+static TmrCntrSetup xTimerSettings[ tmrTIMERS_USED ] =\r
+{\r
+ { tmrTIMER_0_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE },\r
+ { tmrTIMER_1_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE },\r
+ { tmrTIMER_2_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE }\r
+};\r
+\r
+/* Lower priority number means higher logical priority, so\r
+configMAX_API_CALL_INTERRUPT_PRIORITY - 1 is above the maximum system call\r
+interrupt priority. */\r
+static const UBaseType_t uxInterruptPriorities[ tmrTIMERS_USED ] =\r
+{\r
+ configMAX_API_CALL_INTERRUPT_PRIORITY + 1,\r
+ configMAX_API_CALL_INTERRUPT_PRIORITY,\r
+ configMAX_API_CALL_INTERRUPT_PRIORITY - 1\r
+}\r
+static XTtcPs xTimerInstances[ tmrTIMERS_USED ];\r
\r
-typedef struct {\r
- u32 OutputHz; /* Output frequency */\r
- u16 Interval; /* Interval value */\r
- u8 Prescaler; /* Prescaler value */\r
- u16 Options; /* Option settings */\r
-} TmrCntrSetup;\r
+/* Used to provide a means of ensuring the intended interrupt nesting depth is\r
+actually being reached. */\r
+extern uint32_t ulPortInterruptNesting;\r
+static uint32_t ulMaxRecordedNesting = 0;\r
\r
-static const TmrCntrSetup SettingsTable[ TIMERS_USED ] = { { tmrTIMER_0_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE },\r
- { tmrTIMER_1_FREQUENCY, 0, 0, XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE } };\r
+/* For convenience the high frequency timer increments a variable that is then\r
+used as the time base for the run time stats. */\r
+volatile uint32_t ulHighFrequencyTimerCounts = 0;\r
\r
-BaseType_t DeviceIDs[ TIMERS_USED ] = { XPAR_XTTCPS_0_DEVICE_ID, XPAR_XTTCPS_1_DEVICE_ID };\r
-BaseType_t InterruptIDs[ TIMERS_USED ] = { XPAR_XTTCPS_0_INTR, XPAR_XTTCPS_1_INTR };\r
+/*-----------------------------------------------------------*/\r
\r
void vInitialiseTimerForIntQueueTest( void )\r
{\r
-int Status;\r
-TmrCntrSetup *TimerSetup;\r
-XTtcPs *TtcPsTick;\r
+BaseType_t xStatus;\r
+TmrCntrSetup *pxTimerSettings;\r
extern XScuGic xInterruptController;\r
BaseType_t xTimer;\r
-XTtcPs *Timer;\r
-XTtcPs_Config *Config;\r
+XTtcPs *pxTimerInstance;\r
+XTtcPs_Config *pxTimerConfiguration;\r
+const uint8_t ucRisingEdge = 3;\r
\r
- for( xTimer = 0; xTimer < TIMERS_USED; xTimer++ )\r
+ for( xTimer = 0; xTimer < tmrTIMERS_USED; xTimer++ )\r
{\r
+ /* Look up the timer's configuration. */\r
+ pxTimerInstance = &( xTimerInstances[ xTimer ] );\r
+ pxTimerConfiguration = XTtcPs_LookupConfig( xDeviceIDs[ xTimer ] );\r
+ configASSERT( pxTimerConfiguration );\r
+\r
+ pxTimerSettings = &( xTimerSettings[ xTimer ] );\r
+\r
+ /* Initialise the device. */\r
+ xStatus = XTtcPs_CfgInitialize( pxTimerInstance, pxTimerConfiguration, pxTimerConfiguration->BaseAddress );\r
+ if( xStatus != XST_SUCCESS )\r
+ {\r
+ /* Not sure how to do this before XTtcPs_CfgInitialize is called\r
+ as pxTimerInstance is set within XTtcPs_CfgInitialize(). */\r
+ XTtcPs_Stop( pxTimerInstance );\r
+ xStatus = XTtcPs_CfgInitialize( pxTimerInstance, pxTimerConfiguration, pxTimerConfiguration->BaseAddress );\r
+ configASSERT( xStatus == XST_SUCCESS );\r
+ }\r
+\r
+ /* Set the options. */\r
+ XTtcPs_SetOptions( pxTimerInstance, pxTimerSettings->Options );\r
+\r
+ /* The timer frequency is preset in the pxTimerSettings structure.\r
+ Derive the values for the other structure members. */\r
+ XTtcPs_CalcIntervalFromFreq( pxTimerInstance, pxTimerSettings->OutputHz, &( pxTimerSettings->Interval ), &( pxTimerSettings->Prescaler ) );\r
+\r
+ /* Set the interval and prescale. */\r
+ XTtcPs_SetInterval( pxTimerInstance, pxTimerSettings->Interval );\r
+ XTtcPs_SetPrescaler( pxTimerInstance, pxTimerSettings->Prescaler );\r
+\r
+ /* The priority must be the lowest possible. */\r
+ XScuGic_SetPriorityTriggerType( &xInterruptController, xInterruptIDs[ xTimer ], uxInterruptPriorities[ xTimer ] << portPRIORITY_SHIFT, ucRisingEdge );\r
\r
- TimerSetup = &( SettingsTable[ xTimer ] );\r
- Timer = &TtcPsInst[ xTimer ];\r
-\r
- /*\r
- * Look up the configuration based on the device identifier\r
- */\r
- Config = XTtcPs_LookupConfig(DeviceIDs[ xTimer ]);\r
- configASSERT( Config );\r
-\r
- /*\r
- * Initialize the device\r
- */\r
- Status = XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);\r
- configASSERT(Status == XST_SUCCESS);\r
-\r
- /*\r
- * Stop the timer first\r
- */\r
- XTtcPs_Stop( Timer );\r
-\r
- /*\r
- * Set the options\r
- */\r
- XTtcPs_SetOptions(Timer, TimerSetup->Options);\r
-\r
- /*\r
- * Timer frequency is preset in the TimerSetup structure,\r
- * however, the value is not reflected in its other fields, such as\r
- * IntervalValue and PrescalerValue. The following call will map the\r
- * frequency to the interval and prescaler values.\r
- */\r
- XTtcPs_CalcIntervalFromFreq(Timer, TimerSetup->OutputHz,\r
- &(TimerSetup->Interval), &(TimerSetup->Prescaler));\r
-\r
- /*\r
- * Set the interval and prescale\r
- */\r
- XTtcPs_SetInterval(Timer, TimerSetup->Interval);\r
- XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler);\r
-\r
-\r
- /*\r
- * Connect to the interrupt controller\r
- */\r
- Status = XScuGic_Connect(&xInterruptController, InterruptIDs[ xTimer ], (Xil_InterruptHandler)TickHandler, (void *)Timer);\r
- configASSERT( Status == XST_SUCCESS);\r
-\r
- /*\r
- * Enable the interrupt for the Timer counter\r
- */\r
- XScuGic_Enable(&xInterruptController, InterruptIDs[ xTimer ]);\r
-\r
- /*\r
- * Enable the interrupts for the tick timer/counter\r
- * We only care about the interval timeout.\r
- */\r
- XTtcPs_EnableInterrupts(Timer, XTTCPS_IXR_INTERVAL_MASK);\r
-\r
- /*\r
- * Start the tick timer/counter\r
- */\r
- XTtcPs_Start(Timer);\r
+ /* Connect to the interrupt controller. */\r
+ xStatus = XScuGic_Connect( &xInterruptController, xInterruptIDs[ xTimer ], ( Xil_InterruptHandler ) prvTimerHandler, ( void * ) pxTimerInstance );\r
+ configASSERT( xStatus == XST_SUCCESS);\r
+\r
+ /* Enable the interrupt in the GIC. */\r
+ XScuGic_Enable( &xInterruptController, xInterruptIDs[ xTimer ] );\r
+\r
+ /* Enable the interrupts in the timer. */\r
+ XTtcPs_EnableInterrupts( pxTimerInstance, XTTCPS_IXR_INTERVAL_MASK );\r
+\r
+ /* Start the timer. */\r
+ XTtcPs_Start( pxTimerInstance );\r
}\r
}\r
/*-----------------------------------------------------------*/\r
\r
-void vT2InterruptHandler( void )\r
+static void prvTimerHandler( void *pvCallBackRef )\r
{\r
- portEND_SWITCHING_ISR( xFirstTimerHandler() );\r
-}\r
-/*-----------------------------------------------------------*/\r
+uint32_t ulInterruptStatus;\r
+XTtcPs *pxTimer = ( XTtcPs * ) pvCallBackRef;\r
+BaseType_t xYieldRequired;\r
\r
-void vT3InterruptHandler( void )\r
-{\r
- portEND_SWITCHING_ISR( xSecondTimerHandler() );\r
-}\r
-/*-----------------------------------------------------------*/\r
+ /* Read the interrupt status, then write it back to clear the interrupt. */\r
+ ulInterruptStatus = XTtcPs_GetInterruptStatus( pxTimer );\r
+ XTtcPs_ClearInterruptStatus( pxTimer, ulInterruptStatus );\r
\r
-volatile uint32_t ulTimer1Count = 0, ulTimer2Count = 0;\r
+ /* Only one interrupt event type is expected. */\r
+ configASSERT( ( XTTCPS_IXR_INTERVAL_MASK & ulInterruptStatus ) != 0 );\r
\r
-static void TickHandler(void *CallBackRef)\r
-{\r
-uint32_t StatusEvent;\r
-XTtcPs *pxTtcPs = (XTtcPs *)CallBackRef;\r
- /*\r
- * Read the interrupt status, then write it back to clear the interrupt.\r
- */\r
- StatusEvent = XTtcPs_GetInterruptStatus(pxTtcPs);\r
- XTtcPs_ClearInterruptStatus(pxTtcPs, StatusEvent);\r
-\r
- if (0 != (XTTCPS_IXR_INTERVAL_MASK & StatusEvent)) {\r
- if( pxTtcPs->Config.DeviceId == DeviceIDs[ 0 ] )\r
- {\r
- ulTimer1Count++;\r
- }\r
- else\r
+ /* Check the device ID to know which IntQueue demo to call. */\r
+ if( pxTimer->Config.DeviceId == xDeviceIDs[ 0 ] )\r
+ {\r
+ xYieldRequired = xFirstTimerHandler();\r
+ }\r
+ else if( pxTimer->Config.DeviceId == xDeviceIDs[ 1 ] )\r
+ {\r
+ xYieldRequired = xSecondTimerHandler();\r
+ }\r
+ else\r
+ {\r
+ /* The high frequency timer is also used to generate the time base for\r
+ the run time state. */\r
+ ulHighFrequencyTimerCounts++;\r
+\r
+ /* Latch the highest interrupt nesting count detected. */\r
+ if( ulPortInterruptNesting > ulMaxRecordedNesting )\r
{\r
- ulTimer2Count++;\r
+ ulMaxRecordedNesting = ulPortInterruptNesting;\r
}\r
- TickCount++;\r
+\r
+ xYieldRequired = pdFALSE;\r
}\r
-}\r
\r
-#if 0\r
-int SetupTimer(int DeviceID)\r
-{\r
- int Status;\r
- XTtcPs_Config *Config;\r
- XTtcPs *Timer;\r
- TmrCntrSetup *TimerSetup;\r
-\r
- TimerSetup = &SettingsTable;\r
-\r
- Timer = &TtcPsInst;\r
- /*\r
- * Stop the timer first\r
- */\r
- XTtcPs_Stop( &TtcPsInst );\r
-\r
- /*\r
- * Look up the configuration based on the device identifier\r
- */\r
- Config = XTtcPs_LookupConfig(DeviceIDs[ DeviceID ]);\r
- configASSERT( Config );\r
-\r
- /*\r
- * Initialize the device\r
- */\r
- Status = XTtcPs_CfgInitialize(Timer, Config, Config->BaseAddress);\r
- configASSERT(Status == XST_SUCCESS);\r
-\r
- /*\r
- * Set the options\r
- */\r
- XTtcPs_SetOptions(Timer, TimerSetup->Options);\r
-\r
- /*\r
- * Timer frequency is preset in the TimerSetup structure,\r
- * however, the value is not reflected in its other fields, such as\r
- * IntervalValue and PrescalerValue. The following call will map the\r
- * frequency to the interval and prescaler values.\r
- */\r
- XTtcPs_CalcIntervalFromFreq(Timer, TimerSetup->OutputHz,\r
- &(TimerSetup->Interval), &(TimerSetup->Prescaler));\r
-\r
- /*\r
- * Set the interval and prescale\r
- */\r
- XTtcPs_SetInterval(Timer, TimerSetup->Interval);\r
- XTtcPs_SetPrescaler(Timer, TimerSetup->Prescaler);\r
-\r
- return XST_SUCCESS;\r
+ /* If xYieldRequired is not pdFALSE then calling either xFirstTimerHandler()\r
+ or xSecondTimerHandler() resulted in a task leaving the blocked state and\r
+ the task that left the blocked state had a priority higher than the currently\r
+ running task (the task this interrupt interrupted) - so a context switch\r
+ should be performed so the interrupt returns directly to the higher priority\r
+ task. xYieldRequired is tested inside the following macro. */\r
+ portYIELD_FROM_ISR( xYieldRequired );\r
}\r
-#endif\r
\r