_Benchmarking Real-Time Operating Systems_ by Eric McRae Listing One /* RTOS Benchmark Master Control Program ** The main() function here is invoked as the first user task after the RTOS ** has been configured and started. It is responsible for configuring the ** task set and operation mode based on the global test configuration ** parameters. The Dhrystone code is not included here due to its size. ** However its flow is modified to be as follows. ** Initialize data; ** while( 1 ) ** { ** Invoke callback routine supplied when this task started; ** Do normal Dhrystone computations; ** } ** When a Dhrystone task is started, it is supplied with a callback argument ** pointing to a function in this file. That callback is invoked at the end ** of every pass through the Dhrystone loop. Some RTOS may be very restrictive ** in what can be done during an interrupt. If task control cannot be exerted ** during that time parts of this code will have to be restructured. ** Caveat emptor: This code has not yet been tested on any RTOS. ** There are subtle differences between this code and the article text. ** The code is more recent. */ /* RTOS specific routines compiled separately */ extern void enableSliceModeV(); /* Enable RoundRobin multitasking */ extern void enableTaskModeV(); /* Enable multitasking */ /* Install a callback in the timer interrupt */ extern void installTimerHandlerV( void (*callbackPF)(void) ); /* Start a high priority task */ extern void startHighTaskV( void (*callbackPF)(void) ); extern void waitForSignalV( void ); /* For High Priority latency task */ extern void signalHighTask( void ); /* signal high priority task */ /* Install interrupt handler */ extern void installIntHandler( void (*callbackPF)(void) ); extern void killSelfV( void ); /* Stop current thread */ extern void suspendTaskV( int ); /* Suspend this task */ extern void suspendSelfV( void ); /* Suspend current task */ extern void resumeTaskV( int ); /* Resume a given task */ extern void createSemaphoreV( void *semaphorePV );/* construct a semaphore */ extern void releaseSemaphoreV( void * ); extern void * requestSemaphorePV( void ); /* Starts one task at a given priority, ** passes it the given callback, rtns ID */ extern int startTaskN( int priorityN, void (*callbackPF)(void) ); /* Starts countN tasks at same priority */ extern void startRRTasksV(int priorityN, int countN, void (*callbackPF)(void)); /* Starts countN tasks at different priority */ extern void startTasksV(int priorityN, int countN, void (*callbackPF)(void) ); extern void rtosFreeV( void * ); /* free a block */ extern void *rtosAllocPV( void ); /* allocate a block */ extern void waitMsgV( void ); /* wait for message from someone */ extern void sendMsgV( void ); /* send message to next task */ extern void sendFirstMsgV( void ); /* send message to first task */ /* CPU specific macros */ #define DISABLE_INTERRUPTS /* Assembler statement for disable */ #define ENABLE_INTERRUPTS /* Assembler statement for ensable */ /* Target board specific functions compiled separately */ extern void resetMeasureV( void ); /* de-asserts the Measure output */ extern void startMeasureV( void ); /* Asserts the Measure output */ extern void dhrypulseV( void ); /* Toggles the Dhrypulse output */ /* Compiler specific definitions */ #define interrupt #define MAXTASKS 20 /* Maximum number of tasks in list */ #define MAXALLOC 64 /* Maximum number of allocated blocks */ #define MINALLOC 16 /* Minimum number of allocated blocks */ enum TEST { BASELINE = 1, ROUNDROBIN = 2, PRIORITY = 3, SEMAPHORE = 4, MEMORY = 5, BASEINTLAT = 6, INTLATENCY = 7, MESSAGE = 8 }; /* Global test configuration values set by emulator at startup */ enum TEST testN; /* Determines which test to run */ int taskCountN; /* Number of tasks (where appropriate */ int highPriorityIsBig; /* Selects priority direction for OS */ int basePriN; /* Starting task priority */ enum STATE { NotStarted = 0, Running, Suspended }; enum MODE { Suspend, Resume }; struct TASKLISTENT { int idN; /* numeric task ID */ int priN; /* task priority (bigger is higher) */ enum STATE stateN; /* Current state */ } taskListAH[MAXTASKS]; static void *semaphorePV; /* generic semaphore pointer */ static int resumeN; /* resume flag for semaphore test */ static int taskN; /* Task ID */ static int allocN, allocFillN; /* allocation table indicies */ static void * semaphorePV; static int messageCountN; /* determines when and how many */ static void *allocAP[MAXALLOC]; /* Allocation table */ /* Gray Decode Table */ static int const grayTabAN[MAXALLOC] = { 0, 1, 3, 2, 6, 7, 5, 4, 12, 13, 15, 14, 10, 11, 9, 8, 24, 25, 27, 26, 30, 31, 29, 28, 20, 21, 23, 22, 18, 19, 17, 16, 48, 49, 51, 50, 54, 55, 53, 52, 60, 61, 63, 62, 58, 59, 57, 56, 40, 41, 43, 42, 46, 47, 45, 44, 36, 37, 39, 38, 34, 35, 33, 32 }; /* Function declarations */ static void performanceHandlerV( void ); static void nullCB( void ); static void semaSuspendV( void ); static void semaphoreV( void ); static void semaphoreHandlerV( void ); static void memoryHandlerV( void ); static void memoryCB( void ); static void latencyHandlerV( void ); static void interrupt intHandlerV( void ); static void interrupt latencyHandlerV( void ); static void latencyTaskV( void ); static void waitSendMsgV( void ); static void messageCB( void ); static void messageHandlerV( void ); static void messageCB( void ); /* The entry point for the first user task */ void mcp_main( void ) { int i; resetMeasureV(); /* Reset Measure output port */ switch(testN) { case BASELINE: startTaskN( basePriN, nullCB); /* Start 1 task, minimal callback */ DISABLE_INTERRUPTS; /* No more interrupts */ startMeasureV(); /* Begin measurement */ break; case ROUNDROBIN: /* Run with various timer tick rates */ enableSliceModeV(); /* Enable RR multitasking */ /* Start countN tasks, min. callback */ startRRTasksV(basePriN, taskCountN, nullCB); startMeasureV(); /* Begin measurement */ break; case PRIORITY: enableTaskModeV(); /* Enable multitasking */ /* Start countN tasks, min. callback */ startTasksV(basePriN, taskCountN, nullCB); /* Install routine in timer int. handler */ installTimerHandlerV( performanceHandlerV ); startMeasureV(); /* Begin measurement */ break; case SEMAPHORE: createSemaphoreV( &semaphorePV ); /* construct a semaphore */ /* Start a normal task that uses a semaphore */ enableTaskModeV(); /* Enable multitasking */ startTaskN( basePriN, semaphoreV ); /* Then start a higher priority task that suspends itself ** and then resumes after every timer tick. */ resumeN = 0; /* prevent next task from going far */ if( highPriorityIsBig ) taskN = startTaskN( basePriN + 1, semaSuspendV ); else taskN = startTaskN( basePriN - 1, semaSuspendV ); installTimerHandlerV( semaphoreHandlerV ); startMeasureV(); /* Begin measurement */ break; case MEMORY: for( i = 0; i < MAXALLOC; i++ ) allocAP[i] = 0; /* clear allocation table */ allocFillN = 0; /* initialize index */ allocN = -1; /* No allocations yet */ enableSliceModeV(); /* Enable RR multitasking */ startRRTasksV(basePriN, taskCountN, memoryCB); installTimerHandlerV( memoryHandlerV ); startMeasureV(); /* Begin measurement */ break; case BASEINTLAT: startTaskN( basePriN, nullCB ); installIntHandler( intHandlerV ); /* Handler pulses Measure */ break; case INTLATENCY: for( i = 0; i < MAXALLOC; i++ ) allocAP[i] = 0; /* clear allocation table */ allocFillN = 0; /* initialize index */ allocN = -1; /* No allocations yet */ enableSliceModeV(); /* Enable RR multitasking */ startRRTasksV(basePriN, taskCountN, memoryCB); installTimerHandlerV( memoryHandlerV ); /* At this point we have a good system load. Now ** set up the high priority task and the interrupt handler. */ startHighTaskV( latencyTaskV ); installIntHandler( latencyHandlerV ); break; case MESSAGE: startRRTasksV(basePriN, 9, waitSendMsgV ); /* start msg tasks */ /* start Dhrystone/initiator task */ startRRTasksV(basePriN, 1, messageCB ); installTimerHandlerV( messageHandlerV ); /* message trigger */ break; default: break; } killSelfV(); /* Remove this thread */ } /* Function: addNewTaskV ** Purpose: Callback from RTOS specific task start routines. This function ** updates the active task list. */ void addNewTaskV( int idN, int priorityN ) { static int startedTasksN = 0; taskListAH[startedTasksN].idN = idN; taskListAH[startedTasksN].priN = priorityN; taskListAH[startedTasksN].stateN = Running; startedTasksN++; } /* Function: performanceHandlerV ** Purpose: Called from the RTOS timer interrupt code. This routine ** suspends and resumes tasks in the task list. */ void performanceHandlerV( void ) { static enum MODE modeN = Suspend; int i, theTaskN, itsPriN, tasksN; if( taskCountN == 1 ) return; /* don't do anything if just 1 task */ tasksN = taskCountN; if( modeN == Suspend ) { /* search for the highest priority task that is still running */ if( highPriorityIsBig ) itsPriN = 0; else itsPriN = 32767; for( i = 0; i < taskCountN; i++ ) { if( taskListAH[i].stateN == Running ) { /* if the task is running, check it's priority */ if( highPriorityIsBig ) { if( taskListAH[i].priN > itsPriN ) { itsPriN = taskListAH[i].priN; theTaskN = i; /* remember which task is highest */ } } else { /* Low numbers are higher priority */ if( taskListAH[i].priN < itsPriN ) { itsPriN = taskListAH[i].priN; theTaskN = i; /* remember which task is highest */ } } } } suspendTaskV( taskListAH[theTaskN].idN ); /* suspend highest task */ taskListAH[i].stateN = Suspended; tasksN--; if( tasksN == 1 ) modeN = Resume; } /* End of mode == suspend */ else /* Mode must be Resume */ { if( highPriorityIsBig ) itsPriN = 32767; else itsPriN = 0; tasksN = 0; for( i = 0; i < taskCountN; i++ ) { if( taskListAH[i].stateN == Suspended ) { /* if the task is suspended, check it's priority */ if( highPriorityIsBig ) { if( taskListAH[i].priN < itsPriN ) { itsPriN = taskListAH[i].priN; theTaskN = i; /* remember which task is lowest */ } } else { /* Low numbers are higher priority */ if( taskListAH[i].priN > itsPriN ) { itsPriN = taskListAH[i].priN; theTaskN = i; /* remember which task is lowest */ } } } else tasksN++; /* Count number of running tasks */ } /* If there were no suspended tasks */ resumeTaskV( taskListAH[theTaskN].idN ); /* resume the task */ tasksN++; if( tasksN++ == taskCountN ) /* If all tasks are now running */ modeN = Suspend; /* Switch back to suspend mode */ } } /* Function: nullCB ** Purpose: Do nothing call back */ void nullCB( void ) { } /* Function: semaphoreV ** Purpose: Callback for normal priority semaphore task. Releases semaphore ** (if owned), then grabs it right back. If a higher priority task has ** requested it, then I guess we won't come right back from the release. */ static void semaphoreV( void ) { static int haveIt = 0; if( haveIt ) /* No semaphore to release the first time */ releaseSemaphoreV( semaphorePV ); semaphorePV = requestSemaphorePV(); haveIt = 1; } /* Function: semaSuspendV ** Purpose: Callback for high priority semaphore task. Releases semaphore ** (if owned) and then suspends itself. When resumed, it requests semaphore. */ static void semaSuspendV( void ) { static int haveIt = 0; if( haveIt ) { releaseSemaphoreV( semaphorePV ); suspendSelfV(); } semaphorePV = requestSemaphorePV(); haveIt = 1; } /* Function: semaphoreHandlerV ** Purpose: Called during a timer interrupt. Resumes the desired task. */ static void semaphoreHandlerV( void ) { resumeTaskV( taskN ); } /* Function: memoryHandlerV ** Purpose: Invoked from the timer interrupt handler. This routine places an ** index of an allocation table entry in a location that can be read by the ** memory callback routine. This routine has alloc and free modes. In alloc ** mode, it finds the index of the first non-allocated entry. In free mode, ** it uses a Gray code hash to get the index of an entry to be freed. */ static void memoryHandlerV( void ) { static int freeModeN = 0; int i; if( ! freeModeN ) { /* If allocating blocks */ for( i = 0; allocAP[i]; i++ ) ; /* Scan for unallocated slot */ allocN = i; /* set this for callback routine */ allocFillN++; /* track number of allocated blocks */ if( allocFillN == MAXALLOC ) freeModeN = 1; } else { /* Must be freeing blocks, use Gray code index */ allocN = grayTabAN[--allocFillN]; if( allocFillN == MINALLOC ) /* if we hit lower allocation limit */ freeModeN = 0; } } /* Function: memoryCB ** Purpose: Called from the dhrystone loop. Looks at the allocAP[allocN] entry ** and requests an alloc or free depending on whether the value is 0. */ static void memoryCB( void ) { if( allocAP[allocN] ) { /* if this entry is allocated */ rtosFreeV( allocAP[allocN] ); allocAP[allocN] = 0; } else /* entry was null, assign it a block */ allocAP[allocN] = rtosAllocPV(); } /* Function: intHandlerV ** Purpose: invoked directly by the discrete interrupt input ** Used for establishing baseline interrupt latency */ static void interrupt intHandlerV( void ) { startMeasureV(); /* Asserts the Measure output */ resetMeasureV(); /* de-asserts the Measure output */ } /* Function: latencyHandlerV ** Purpose: invoked directly by the discrete interrupt input. Used for ** establishing baseline interrupt latency. Signals task waiting for interrupt */ static void interrupt latencyHandlerV( void ) { signalHighTask(); /* assert signal to waiting task */ } /* Function: latencyTaskV ** Purpose: Runs as a thread, waits on a signal and then pulses the ** measure output. The test measures the time from the onset ** of the interrupt to the assertion of the measure output. */ static void latencyTaskV( void ) { while( 1 ) { waitForSignalV(); /* Hang here waiting for interrupt */ startMeasureV(); /* Asserts the Measure output */ resetMeasureV(); /* de-asserts the Measure output */ } } /* Function: waitSendMsgV ** Purpose: Self contained thread that lives for message exchange. The RTOS ** specific message functions determine the recipient of messages sent such ** that all running tasks send receive messages. Started from a normal ** Dhrystone loop but never return; don't contribute to Dhrystone rate. */ static void waitSendMsgV( void ) { while( 1 ) { waitMsgV(); /* wait for message from someone */ sendMsgV(); /* send message to next task */ } } /* start Dhrystone/initiator task */ /* Function: messageCB ** Purpose: Called from the dhrystone loop. If the messageCountN ** value has changed, initiate a message loop. */ static void messageCB( void ) { static int lastCountN = 0; if( messageCountN != lastCountN ) { /* if it's time to do some messages */ if( (messageCountN = lastCountN ) == 2 ) { /* if sending/receiving 2 messages */ sendFirstMsgV(); sendFirstMsgV(); waitMsgV(); waitMsgV(); } else { /* just sending one message */ sendFirstMsgV(); waitMsgV(); } } } /* Function: messageHandlerV ** Purpose: Called from the timer interrupt handler. Bounces the global ** message counter between 1 and 2. */ static void messageHandlerV( void ) { if( ++messageCountN == 3 ) messageCountN = 1; }