From 405a2ca22c901996c37434631a4ecbbc89f6835a Mon Sep 17 00:00:00 2001 From: Richard Barry Date: Sun, 7 Aug 2011 10:08:04 +0000 Subject: [PATCH] Add run time stats gathering to the MicroBlaze demo. --- .../RTOSDemoSource/FreeRTOSConfig.h | 14 +- .../SDKProjects/RTOSDemoSource/main-blinky.c | 12 - .../SDKProjects/RTOSDemoSource/main-full.c | 224 +++++++++++------- 3 files changed, 148 insertions(+), 102 deletions(-) diff --git a/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/FreeRTOSConfig.h b/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/FreeRTOSConfig.h index c4397d99c..6ea8f8db1 100644 --- a/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/FreeRTOSConfig.h +++ b/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/FreeRTOSConfig.h @@ -77,10 +77,10 @@ *----------------------------------------------------------*/ #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 1 -#define configUSE_TICK_HOOK 1 +#define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( XPAR_MICROBLAZE_CORE_CLOCK_FREQ_HZ ) /* Not actually used in this demo as the timer is set up in main() and uses the peripheral clock, not the CPU clock. */ #define configTICK_RATE_HZ ( ( portTickType ) 1000 ) -#define configMAX_PRIORITIES ( 6 ) +#define configMAX_PRIORITIES ( 7 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 64 * 1024 ) ) #define configMAX_TASK_NAME_LEN ( 10 ) #define configUSE_TRACE_FACILITY 1 @@ -114,7 +114,7 @@ designs will only include on interrupt controller. */ /* Software timer definitions. */ #define configUSE_TIMERS 1 -#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 4 ) #define configTIMER_QUEUE_LENGTH 10 #define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE ) @@ -127,6 +127,8 @@ to exclude the API function. */ #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelayUntil 1 #define INCLUDE_vTaskDelay 1 +#define INCLUDE_pcTaskNameGet 1 +#define INCLUDE_pcTaskNameGet 1 #define configASSERT( x ) if( ( x ) == 0 ) { portDISABLE_INTERRUPTS(); for( ;; ); } @@ -143,7 +145,11 @@ prevent the C syntax being included in assembly files. */ -#define configLWIP_TASK_PRIORITY ( configMAX_PRIORITIES - 2 ) + + +/* Networking configuration follows. */ + +#define configLWIP_TASK_PRIORITY ( configMAX_PRIORITIES - 4 ) /* MAC address configuration. */ #define configMAC_ADDR0 0x00 diff --git a/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-blinky.c b/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-blinky.c index a68b78657..73af0591f 100644 --- a/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-blinky.c +++ b/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-blinky.c @@ -448,18 +448,6 @@ volatile size_t xFreeHeapSpace; } /*-----------------------------------------------------------*/ -void vApplicationTickHook( void ) -{ - /* vApplicationTickHook() will only be called if configUSE_TICK_HOOK is set - to 1 in FreeRTOSConfig.h. It executes from an interrupt context so must - not use any FreeRTOS API functions that do not end in ...FromISR(). - - This simple blinky demo does not use the tick hook, but a tick hook is - required to be defined as the blinky and full demos share a - FreeRTOSConfig.h header file. */ -} -/*-----------------------------------------------------------*/ - /* This is an application defined callback function used to install the tick interrupt handler. It is provided as an application callback because the kernel will run on lots of different MicroBlaze and FPGA configurations - not all of diff --git a/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-full.c b/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-full.c index 36c415932..1862f372b 100644 --- a/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-full.c +++ b/Demo/MicroBlaze_Spartan-6_EthernetLite/SDKProjects/RTOSDemoSource/main-full.c @@ -70,13 +70,18 @@ * In addition to the standard demo tasks, the following tasks and tests are * defined and/or created within this file: * - * TCP/IP ("lwIP") task - TBD _RB_ + * TCP/IP ("lwIP") task - lwIP is used to create a basic web server. The web + * server uses server side includes (SSI) to generate tables of task statistics, + * and run time statistics (run time statistics show how much processing time + * each task has consumed). See + * http://www.FreeRTOS.org/Free-RTOS-for-Xilinx-MicroBlaze-on-Spartan-6-FPGA.html + * for details on setting up and using the embedded web server. * * "Reg test" tasks - These test the task context switch mechanism by first * filling the MicroBlaze registers with known values, before checking that each * register maintains the value that was written to it as the tasks are switched * in and out. The two register test tasks do not use the same values, and - * execute at a very low priority to ensure they are pre-empted regularly. + * execute at a very low priority, to ensure they are pre-empted regularly. * * "Check" timer - The check timer period is initially set to five seconds. * The check timer callback function checks that all the standard demo tasks, @@ -88,9 +93,15 @@ * indication of the system status: If the LED toggles every five seconds then * no issues have been discovered. If the LED toggles every 200ms then an issue * has been discovered with at least one task. The last reported issue is - * latched into the pcStatusMessage variable. + * latched into the pcStatusMessage variable, and can also be viewed at the + * bottom of the pages served by the embedded web server. * - * This file also includes example implementations of the vApplicationTickHook(), + * ***NOTE*** This demo uses the standard comtest tasks, which has special + * hardware requirements. See + * http://www.FreeRTOS.org/Free-RTOS-for-Xilinx-MicroBlaze-on-Spartan-6-FPGA.html + * for more information. + * + * This file also includes example implementations of the * vApplicationIdleHook(), vApplicationStackOverflowHook(), * vApplicationMallocFailedHook(), vApplicationClearTimerInterrupt(), and * vApplicationSetupTimerInterrupt() callback (hook) functions. @@ -131,11 +142,10 @@ /* Priorities at which the various tasks are created. */ #define mainQUEUE_POLL_PRIORITY ( tskIDLE_PRIORITY + 1 ) -#define mainSEM_TEST_PRIORITY ( tskIDLE_PRIORITY + 1 ) -#define mainBLOCK_Q_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define mainSEM_TEST_PRIORITY ( tskIDLE_PRIORITY + 2 ) +#define mainBLOCK_Q_PRIORITY ( tskIDLE_PRIORITY + 1 ) #define mainCREATOR_TASK_PRIORITY ( tskIDLE_PRIORITY + 3 ) #define mainFLASH_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) -#define mainuIP_TASK_PRIORITY ( tskIDLE_PRIORITY + 2 ) #define mainCOM_TEST_PRIORITY ( tskIDLE_PRIORITY + 2 ) #define mainINTEGER_TASK_PRIORITY ( tskIDLE_PRIORITY ) #define mainGEN_QUEUE_TASK_PRIORITY ( tskIDLE_PRIORITY ) @@ -210,6 +220,13 @@ static XTmrCtr xTimer0Instance; /* The 'check' timer, as described at the top of this file. */ static xTimerHandle xCheckTimer = NULL; +/* Used in the run time stats calculations. */ +static unsigned long ulClocksPer10thOfAMilliSecond = 0UL; + +/* Constants used to set up the AXI timer to generate ticks. */ +static const unsigned char ucTimerCounterNumber = ( unsigned char ) 0U; +static const unsigned long ulCounterReloadValue = ( ( XPAR_AXI_TIMER_0_CLOCK_FREQ_HZ / configTICK_RATE_HZ ) - 1UL ); + /*-----------------------------------------------------------*/ int main( void ) @@ -290,64 +307,68 @@ portTickType xExecutionRate = mainNO_ERROR_CHECK_TIMER_PERIOD; /* This is the callback function used by the 'check' timer, as described in the comments at the top of this file. */ - /* Check the standard demo tasks are running without error. */ - if( xAreGenericQueueTasksStillRunning() != pdTRUE ) + /* Don't overwrite any errors that have already been latched. */ + if( pcStatusMessage == NULL ) { - pcStatusMessage = "Error: GenQueue"; - } - else if( xAreQueuePeekTasksStillRunning() != pdTRUE ) - { - pcStatusMessage = "Error: QueuePeek\r\n"; - } - else if( xAreBlockingQueuesStillRunning() != pdTRUE ) - { - pcStatusMessage = "Error: BlockQueue\r\n"; - } - else if( xAreBlockTimeTestTasksStillRunning() != pdTRUE ) - { - pcStatusMessage = "Error: BlockTime\r\n"; - } - else if( xAreSemaphoreTasksStillRunning() != pdTRUE ) - { - pcStatusMessage = "Error: SemTest\r\n"; - } - else if( xArePollingQueuesStillRunning() != pdTRUE ) - { - pcStatusMessage = "Error: PollQueue\r\n"; - } - else if( xIsCreateTaskStillRunning() != pdTRUE ) - { - pcStatusMessage = "Error: Death\r\n"; - } - else if( xAreRecursiveMutexTasksStillRunning() != pdTRUE ) - { - pcStatusMessage = "Error: RecMutex\r\n"; - } - else if( xAreMathsTaskStillRunning() != pdPASS ) - { - pcStatusMessage = "Error: Flop\r\n"; - } - else if( xAreComTestTasksStillRunning() != pdPASS ) - { - pcStatusMessage = "Error: Comtest\r\n"; - } - else if( xAreDynamicPriorityTasksStillRunning() != pdPASS ) - { - pcStatusMessage = "Error: Dynamic\r\n"; - } - else if( xAreTimerDemoTasksStillRunning( xExecutionRate ) != pdTRUE ) - { - pcStatusMessage = "Error: TimerDemo"; - } - else if( ulRegTest1CycleCount == ulLastRegTest1CycleCount ) - { - /* Check the reg test tasks are still cycling. They will stop - incrementing their loop counters if they encounter an error. */ - pcStatusMessage = "Error: RegTest1\r\n"; - } - else if( ulRegTest2CycleCount == ulLastRegTest2CycleCount ) - { - pcStatusMessage = "Error: RegTest2\r\n"; + /* Check the standard demo tasks are running without error. */ + if( xAreGenericQueueTasksStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: GenQueue"; + } + else if( xAreQueuePeekTasksStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: QueuePeek\r\n"; + } + else if( xAreBlockingQueuesStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: BlockQueue\r\n"; + } + else if( xAreBlockTimeTestTasksStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: BlockTime\r\n"; + } + else if( xAreSemaphoreTasksStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: SemTest\r\n"; + } + else if( xArePollingQueuesStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: PollQueue\r\n"; + } + else if( xIsCreateTaskStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: Death\r\n"; + } + else if( xAreRecursiveMutexTasksStillRunning() != pdTRUE ) + { + pcStatusMessage = "Error: RecMutex\r\n"; + } + else if( xAreMathsTaskStillRunning() != pdPASS ) + { + pcStatusMessage = "Error: Flop\r\n"; + } + else if( xAreComTestTasksStillRunning() != pdPASS ) + { + pcStatusMessage = "Error: Comtest\r\n"; + } + else if( xAreDynamicPriorityTasksStillRunning() != pdPASS ) + { + pcStatusMessage = "Error: Dynamic\r\n"; + } + else if( xAreTimerDemoTasksStillRunning( xExecutionRate ) != pdTRUE ) + { + pcStatusMessage = "Error: TimerDemo"; + } + else if( ulRegTest1CycleCount == ulLastRegTest1CycleCount ) + { + /* Check the reg test tasks are still cycling. They will stop + incrementing their loop counters if they encounter an error. */ + pcStatusMessage = "Error: RegTest1\r\n"; + } + else if( ulRegTest2CycleCount == ulLastRegTest2CycleCount ) + { + pcStatusMessage = "Error: RegTest2\r\n"; + } } /* Store a local copy of the current reg test loop counters. If these have @@ -401,8 +422,6 @@ function below declares as an extern. */ void vApplicationSetupTimerInterrupt( void ) { portBASE_TYPE xStatus; -const unsigned char ucTimerCounterNumber = ( unsigned char ) 0U; -const unsigned long ulCounterValue = ( ( XPAR_AXI_TIMER_0_CLOCK_FREQ_HZ / configTICK_RATE_HZ ) - 1UL ); extern void vPortTickISR( void *pvUnused ); /* Initialise the timer/counter. */ @@ -427,7 +446,7 @@ extern void vPortTickISR( void *pvUnused ); XTmrCtr_SetHandler( &xTimer0Instance, ( void * ) vPortTickISR, NULL ); /* Set the correct period for the timer. */ - XTmrCtr_SetResetValue( &xTimer0Instance, ucTimerCounterNumber, ulCounterValue ); + XTmrCtr_SetResetValue( &xTimer0Instance, ucTimerCounterNumber, ulCounterReloadValue ); /* Enable the interrupts. Auto-reload mode is used to generate a periodic tick. Note that interrupts are disabled when this function is @@ -525,18 +544,6 @@ static long lCheckTimerStarted = pdFALSE; } /*-----------------------------------------------------------*/ -void vApplicationTickHook( void ) -{ - /* vApplicationTickHook() will only be called if configUSE_TICK_HOOK is set - to 1 in FreeRTOSConfig.h. It executes from an interrupt context so must - not use any FreeRTOS API functions that do not end in ...FromISR(). */ - - /* Call the periodic timer test, which tests the timer API functions that - can be called from an ISR. */ - vTimerPeriodicISRTests(); -} -/*-----------------------------------------------------------*/ - void vApplicationExceptionRegisterDump( xPortRegisterDump *xRegisterDump ) { ( void ) xRegisterDump; @@ -590,19 +597,53 @@ static void prvSetupHardware( void ) void vMainConfigureTimerForRunTimeStats( void ) { -unsigned long ulRunTimeStatsDivisor; - /* How many times does the counter counter increment in 10ms? */ - ulRunTimeStatsDivisor = 0UL / 1000UL; //_RB_ + ulClocksPer10thOfAMilliSecond = XPAR_AXI_TIMER_0_CLOCK_FREQ_HZ / 10000UL; } /*-----------------------------------------------------------*/ unsigned long ulMainGetRunTimeCounterValue( void ) { -unsigned long ulReturn, ulCurrentCount; +unsigned long ulTimerCounts1, ulTimerCounts2, ulTickCount, ulReturn; - ulCurrentCount = 0UL; - ulReturn = 0UL; + /* NOTE: This can get called from a yield, in which case interrupts are + disabled, or from a tick ISR, in which case the effect is the same as if + interrupts were disabled. In either case, it is going to run atomically. */ + + /* The timer is in down count mode. How many clocks have passed since it + was last reloaded? */ + ulTimerCounts1 = ulCounterReloadValue - XTmrCtr_GetValue( &xTimer0Instance, ucTimerCounterNumber ); + + /* How many times has it overflowed? */ + ulTickCount = xTaskGetTickCountFromISR(); + + /* If this is being called from a yield, has the counter overflowed since + it was read? If that is the case then ulTickCounts will need incrementing + again as it will not yet have been incremented from the tick interrupt. */ + ulTimerCounts2 = ulCounterReloadValue - XTmrCtr_GetValue( &xTimer0Instance, ucTimerCounterNumber ); + if( ulTimerCounts2 < ulTimerCounts1 ) + { + /* There is a tick interrupt pending but the tick count not yet + incremented. */ + ulTickCount++; + + /* Use the second timer reading. */ + ulTimerCounts1 = ulTimerCounts2; + } + + /* Convert the tick count into tenths of a millisecond. THIS ASSUMES + configTICK_RATE_HZ is 1000! */ + ulReturn = ( ulTickCount * 10UL ); + + /* Add on the number of tenths of a millisecond that have passed since the + tick count last got updated. */ + ulReturn += ( ulTimerCounts1 / ulClocksPer10thOfAMilliSecond ); + + /* Some crude rounding. */ + if( ( ulTimerCounts1 % ulClocksPer10thOfAMilliSecond ) > ( ulClocksPer10thOfAMilliSecond >> 1UL ) ) + { + ulReturn++; + } return ulReturn; } @@ -610,7 +651,18 @@ unsigned long ulReturn, ulCurrentCount; char *pcMainGetTaskStatusMessage( void ) { - return ( char * ) pcStatusMessage; +char * pcReturn; + + if( pcStatusMessage == NULL ) + { + pcReturn = ( char * ) "OK"; + } + else + { + pcReturn = ( char * ) pcStatusMessage; + } + + return pcReturn; }