#include "FreeRTOS.h"
#include "task.h"
+/*
+ * Two test tasks that fill the CPU registers with known values before
+ * continuously looping round checking that each register still contains its
+ * expected value. Both tasks use a separate set of values, with an incorrect
+ * value being found at any time being indicative of an error in the context
+ * switch mechanism. One of the tasks uses a yield instruction to increase the
+ * test coverage. The nature of these tasks necessitates that they are written
+ * in assembly code.
+ */
static void vRegTest1( void *pvParameters );
static void vRegTest2( void *pvParameters );
-static volatile unsigned long ulRegTest1Counter = 0UL, ulRegTest2Counter = 0UL;
-
+/*
+ * A task that tests the management of the Interrupt Controller (IC) during a
+ * context switch. The state of the IC current mask level must be maintained
+ * across context switches. Also, yields must be able to be performed when the
+ * interrupt controller mask is not zero. This task tests both these
+ * requirements.
+ */
+static void prvICCheck1Task( void *pvParameters );
+
+/* Counters used to ensure the tasks are still running. */
+static volatile unsigned long ulRegTest1Counter = 0UL, ulRegTest2Counter = 0UL, ulICTestCounter = 0UL;
+
+/* Handle to the task that checks the interrupt controller behaviour. This is
+used by the traceTASK_SWITCHED_OUT() macro, which is defined in
+FreeRTOSConfig.h and can be removed - it is just for the purpose of this test. */
+xTaskHandle xICTestTask = NULL;
+
+/* Variable that gets set to pdTRUE by traceTASK_SWITCHED_OUT each time
+is switched out. */
+volatile unsigned long ulTaskSwitchedOut;
/*-----------------------------------------------------------*/
void vStartRegTestTasks( void )
{
xTaskCreate( vRegTest1, ( signed char * ) "RTest1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
xTaskCreate( vRegTest2, ( signed char * ) "RTest1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+ xTaskCreate( prvICCheck1Task, ( signed char * ) "ICCheck", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, &xICTestTask );
}
/*-----------------------------------------------------------*/
}
/*-----------------------------------------------------------*/
+static void prvICCheck1Task( void *pvParameters )
+{
+long lICCheckStatus = pdPASS;
+
+ for( ;; )
+ {
+ /* At this point the interrupt mask should be zero. */
+ if( ic->cpl != 0 )
+ {
+ lICCheckStatus = pdFAIL;
+ }
+
+ /* If we yield here, it should still be 0 when the task next runs.
+ ulTaskSwitchedOut is just used to check that a switch does actually
+ happen. */
+ ulTaskSwitchedOut = pdFALSE;
+ taskYIELD();
+ if( ( ulTaskSwitchedOut != pdTRUE ) || ( ic->cpl != 0 ) )
+ {
+ lICCheckStatus = pdFAIL;
+ }
+
+ /* Set the interrupt mask to portSYSTEM_INTERRUPT_PRIORITY_LEVEL + 1,
+ before checking it is as expected. */
+ taskENTER_CRITICAL();
+ if( ic->cpl != ( portSYSTEM_INTERRUPT_PRIORITY_LEVEL + 1 ) )
+ {
+ lICCheckStatus = pdFAIL;
+ }
+
+ /* If we yield here, it should still be
+ portSYSTEM_INTERRUPT_PRIORITY_LEVEL + 10 when the task next runs. */
+ ulTaskSwitchedOut = pdFALSE;
+ taskYIELD();
+ if( ( ulTaskSwitchedOut != pdTRUE ) || ( ic->cpl != ( portSYSTEM_INTERRUPT_PRIORITY_LEVEL + 1 ) ) )
+ {
+ lICCheckStatus = pdFAIL;
+ }
+
+ /* Return the interrupt mask to its default state. */
+ taskEXIT_CRITICAL();
+
+ /* Just increment a loop counter so the check task knows if this task
+ is still running or not. */
+ if( lICCheckStatus == pdPASS )
+ {
+ ulICTestCounter++;
+ }
+ }
+}
+/*-----------------------------------------------------------*/
+
portBASE_TYPE xAreRegTestTasksStillRunning( void )
{
-static unsigned long ulLastCounter1 = 0UL, ulLastCounter2 = 0UL;
+static unsigned long ulLastCounter1 = 0UL, ulLastCounter2 = 0UL, ulLastICTestCounter = 0UL;
long lReturn;
/* Check that both loop counters are still incrementing, indicating that
{
lReturn = pdFAIL;
}
+ else if( ulLastICTestCounter == ulICTestCounter )
+ {
+ lReturn = pdFAIL;
+ }
else
{
lReturn = pdPASS;
ulLastCounter1 = ulRegTest1Counter;
ulLastCounter2 = ulRegTest2Counter;
+ ulLastICTestCounter = ulICTestCounter;
return lReturn;
}
#define comBLOCK_RETRY_TIME 10
/*-----------------------------------------------------------*/
+/* The interrupt handlers are naked functions that call C handlers. The C
+handlers are marked as noinline to ensure they work correctly when the
+optimiser is on. */
+void interrupt5_handler( void ) __attribute__((naked));
+static void prvTxHandler( void ) __attribute__((noinline));
+void interrupt6_handler( void ) __attribute__((naked));
+static void prvRxHandler( void ) __attribute__((noinline));
+
+/*-----------------------------------------------------------*/
+
/* Queues used to hold received characters, and characters waiting to be
transmitted. */
static xQueueHandle xRxedChars;
return pdFALSE;
}
}
-
/*-----------------------------------------------------------*/
void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength )
while( xSerialPutChar( pxPort, *pChNext, comBLOCK_RETRY_TIME ) != pdTRUE ); pChNext++;
}
}
-
/*-----------------------------------------------------------*/
signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, portTickType xBlockTime )
}
/*-----------------------------------------------------------*/
-void interrupt_handler( IRQ_UART1_TX )
+/* UART Tx interrupt handler. */
+void interrupt5_handler( void )
{
-static signed char cChar;
-static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
+ /* This is a naked function. */
portSAVE_CONTEXT();
+ prvTxHandler();
+ portRESTORE_CONTEXT();
+}
+/*-----------------------------------------------------------*/
+
+static void prvTxHandler( void )
+{
+signed char cChar;
+portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
/* The interrupt was caused by the transmit fifo having space for at least one
character. Are there any more characters to transmit? */
ensure that the unblocked task is the task that executes when the interrupt
completes if the unblocked task has a priority higher than the interrupted
task. */
- if( xHigherPriorityTaskWoken )
- {
- portYIELD_FROM_ISR();
- }
- portRESTORE_CONTEXT();
+ portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*/
-void interrupt_handler( IRQ_UART1_RX )
+/* UART Rx interrupt. */
+void interrupt6_handler( void )
{
-static signed char cChar;
-static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
-
portSAVE_CONTEXT();
+ prvRxHandler();
+ portRESTORE_CONTEXT();
+}
+/*-----------------------------------------------------------*/
+
+static void prvRxHandler( void )
+{
+signed char cChar;
+portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
/* The interrupt was caused by the receiver getting data. */
cChar = uart1->rx_data;
- (void)xQueueSendFromISR(xRxedChars, &cChar, &xHigherPriorityTaskWoken);
+ xQueueSendFromISR(xRxedChars, &cChar, &xHigherPriorityTaskWoken );
/* If an event caused a task to unblock then we call "Yield from ISR" to
ensure that the unblocked task is the task that executes when the interrupt
completes if the unblocked task has a priority higher than the interrupted
task. */
- if( xHigherPriorityTaskWoken )
- {
- portYIELD_FROM_ISR();
- }
-
- portRESTORE_CONTEXT();
+ portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*/