From 359a3d4550b31f6d03352d67382c336af5bf5148 Mon Sep 17 00:00:00 2001 From: richardbarry Date: Mon, 29 Mar 2010 14:04:11 +0000 Subject: [PATCH] Finish off the Cortus demo application: + Add a traceTASK_SWITCHED_OUT macro as part of the RegTest.c. + Add task to test the saving and restoring of the interrupt mask. + Change the serial port interrupt handlers to use naked functions. git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@1010 1d2547de-c912-0410-9cb9-b8ca96c0e9e2 --- Demo/CORTUS_APS3_GCC/Demo/FreeRTOSConfig.h | 19 ++++- Demo/CORTUS_APS3_GCC/Demo/RegTest.c | 91 +++++++++++++++++++++- Demo/CORTUS_APS3_GCC/Demo/serial.c | 58 ++++++++------ 3 files changed, 140 insertions(+), 28 deletions(-) diff --git a/Demo/CORTUS_APS3_GCC/Demo/FreeRTOSConfig.h b/Demo/CORTUS_APS3_GCC/Demo/FreeRTOSConfig.h index 33110da7c..d3b6a2851 100644 --- a/Demo/CORTUS_APS3_GCC/Demo/FreeRTOSConfig.h +++ b/Demo/CORTUS_APS3_GCC/Demo/FreeRTOSConfig.h @@ -103,8 +103,21 @@ We use --gc-sections when linking, so there is no harm is setting all of these t #define BLOCKQ_1 1 + + + +/* A task is created to test the behaviour of the interrupt controller during +context switches. This macro is just used to set a variable to true each time +the test task is switched out - the task itself needs to know when this happens +in order to complete its tests. This macro will slow down the context switch +and can normally be removed (just delete the whole macro, although doing so will +cause the test task to indicate an error). */ +extern void *xICTestTask; +extern volatile unsigned long ulTaskSwitchedOut; +#define traceTASK_SWITCHED_OUT() if( pxCurrentTCB == xICTestTask ) ulTaskSwitchedOut = pdTRUE + + + #endif /* FREERTOS_CONFIG_H */ -// Local Variables: -// tab-width:4 -// End: + diff --git a/Demo/CORTUS_APS3_GCC/Demo/RegTest.c b/Demo/CORTUS_APS3_GCC/Demo/RegTest.c index 21a4040ce..7719e9a3a 100644 --- a/Demo/CORTUS_APS3_GCC/Demo/RegTest.c +++ b/Demo/CORTUS_APS3_GCC/Demo/RegTest.c @@ -54,17 +54,45 @@ #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 ); } /*-----------------------------------------------------------*/ @@ -193,9 +221,61 @@ static void vRegTest2( void *pvParameters ) } /*-----------------------------------------------------------*/ +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 @@ -208,6 +288,10 @@ long lReturn; { lReturn = pdFAIL; } + else if( ulLastICTestCounter == ulICTestCounter ) + { + lReturn = pdFAIL; + } else { lReturn = pdPASS; @@ -215,6 +299,7 @@ long lReturn; ulLastCounter1 = ulRegTest1Counter; ulLastCounter2 = ulRegTest2Counter; + ulLastICTestCounter = ulICTestCounter; return lReturn; } diff --git a/Demo/CORTUS_APS3_GCC/Demo/serial.c b/Demo/CORTUS_APS3_GCC/Demo/serial.c index 90cf9528a..95dd142b1 100644 --- a/Demo/CORTUS_APS3_GCC/Demo/serial.c +++ b/Demo/CORTUS_APS3_GCC/Demo/serial.c @@ -71,6 +71,16 @@ #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; @@ -116,7 +126,6 @@ signed portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, signed char *pcRxedC return pdFALSE; } } - /*-----------------------------------------------------------*/ void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength ) @@ -132,7 +141,6 @@ void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString while( xSerialPutChar( pxPort, *pChNext, comBLOCK_RETRY_TIME ) != pdTRUE ); pChNext++; } } - /*-----------------------------------------------------------*/ signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, portTickType xBlockTime ) @@ -162,12 +170,20 @@ void vSerialClose( xComPortHandle xPort ) } /*-----------------------------------------------------------*/ -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? */ @@ -186,36 +202,34 @@ static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; 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 ); } /*-----------------------------------------------------------*/ -- 2.39.5