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.
This commit is contained in:
Richard Barry 2010-03-29 14:04:11 +00:00
parent f4d8802850
commit a2b1a64ba1
3 changed files with 139 additions and 27 deletions

View file

@ -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 #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 */ #endif /* FREERTOS_CONFIG_H */
// Local Variables:
// tab-width:4
// End:

View file

@ -54,17 +54,45 @@
#include "FreeRTOS.h" #include "FreeRTOS.h"
#include "task.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 vRegTest1( void *pvParameters );
static void vRegTest2( 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 ) void vStartRegTestTasks( void )
{ {
xTaskCreate( vRegTest1, ( signed char * ) "RTest1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL ); 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( 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 ) portBASE_TYPE xAreRegTestTasksStillRunning( void )
{ {
static unsigned long ulLastCounter1 = 0UL, ulLastCounter2 = 0UL; static unsigned long ulLastCounter1 = 0UL, ulLastCounter2 = 0UL, ulLastICTestCounter = 0UL;
long lReturn; long lReturn;
/* Check that both loop counters are still incrementing, indicating that /* Check that both loop counters are still incrementing, indicating that
@ -208,6 +288,10 @@ long lReturn;
{ {
lReturn = pdFAIL; lReturn = pdFAIL;
} }
else if( ulLastICTestCounter == ulICTestCounter )
{
lReturn = pdFAIL;
}
else else
{ {
lReturn = pdPASS; lReturn = pdPASS;
@ -215,6 +299,7 @@ long lReturn;
ulLastCounter1 = ulRegTest1Counter; ulLastCounter1 = ulRegTest1Counter;
ulLastCounter2 = ulRegTest2Counter; ulLastCounter2 = ulRegTest2Counter;
ulLastICTestCounter = ulICTestCounter;
return lReturn; return lReturn;
} }

View file

@ -71,6 +71,16 @@
#define comBLOCK_RETRY_TIME 10 #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 /* Queues used to hold received characters, and characters waiting to be
transmitted. */ transmitted. */
static xQueueHandle xRxedChars; static xQueueHandle xRxedChars;
@ -116,7 +126,6 @@ signed portBASE_TYPE xSerialGetChar( xComPortHandle pxPort, signed char *pcRxedC
return pdFALSE; return pdFALSE;
} }
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
void vSerialPutString( xComPortHandle pxPort, const signed char * const pcString, unsigned short usStringLength ) 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++; while( xSerialPutChar( pxPort, *pChNext, comBLOCK_RETRY_TIME ) != pdTRUE ); pChNext++;
} }
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
signed portBASE_TYPE xSerialPutChar( xComPortHandle pxPort, signed char cOutChar, portTickType xBlockTime ) 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; /* This is a naked function. */
static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
portSAVE_CONTEXT(); 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 /* The interrupt was caused by the transmit fifo having space for at least one
character. Are there any more characters to transmit? */ 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 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 completes if the unblocked task has a priority higher than the interrupted
task. */ task. */
if( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
{
portYIELD_FROM_ISR();
} }
/*-----------------------------------------------------------*/
/* UART Rx interrupt. */
void interrupt6_handler( void )
{
portSAVE_CONTEXT();
prvRxHandler();
portRESTORE_CONTEXT(); portRESTORE_CONTEXT();
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
void interrupt_handler( IRQ_UART1_RX ) static void prvRxHandler( void )
{ {
static signed char cChar; signed char cChar;
static portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE; portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
portSAVE_CONTEXT();
/* The interrupt was caused by the receiver getting data. */ /* The interrupt was caused by the receiver getting data. */
cChar = uart1->rx_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 /* 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 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 completes if the unblocked task has a priority higher than the interrupted
task. */ task. */
if( xHigherPriorityTaskWoken ) portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
{
portYIELD_FROM_ISR();
}
portRESTORE_CONTEXT();
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/