/** * \b main * * Initialize atomthreads and start a test_thread to run the Atomthreads test suite. * */ int main (void) { uint32_t failures ; printf ("atomthreads starting %s... ", ATOMTHREADS_TEST) ; atomOSInit(&idle_stack[0], IDLE_STACK_BYTE_SIZE, TRUE) ; atomThreadCreate ((ATOM_TCB *)&test_tcb, TEST_THREAD_PRIO, test_thread, 0, &test_stack[0], TEST_STACK_BYTE_SIZE, TRUE); atomOSStart() ; return 0 ; }
/** * \b main * * Program entry point. * * Sets up the MicroBlaze hardware resources (system tick timer interrupt) necessary * for the OS to be started. Creates an application thread and starts the OS. */ int main ( void ) { int8_t status; ATOMLOG("\n\nAtomThreads MicroBlaze Starting\n\n"); /** * Initialise the OS before creating our threads. */ status = atomOSInit(&idle_thread_stack[0], IDLE_STACK_SIZE_BYTES, TRUE); if (status == ATOM_OK) { /* Enable the system tick timer */ microblazeInitSystemTickTimer(); /* Create an application thread */ status = atomThreadCreate(&main_tcb, TEST_THREAD_PRIO, main_thread_func, 0, &main_thread_stack[0], MAIN_STACK_SIZE_BYTES, TRUE); if (status == ATOM_OK) { /** * First application thread successfully created. It is * now possible to start the OS. Execution will not return * from atomOSStart(), which will restore the context of * our application thread and start executing it. * * Note that interrupts are still disabled at this point. * They will be enabled as we restore and execute our first * thread in archFirstThreadRestore(). */ atomOSStart(); } } while (1) ; /* There was an error starting the OS if we reach here */ return (0); }
/** * \b atomOSInit * * Initialise the atomthreads OS. * * Must be called before any application code uses the atomthreads APIs. No * threads are actually started until the application calls atomOSStart(). * * Callers must provide a pointer to some storage for the idle thread stack. * The caller is responsible for calculating the appropriate space required * for their particular architecture. * * Applications should use the following initialisation sequence: * * \li Call atomOSInit() before calling any atomthreads APIs * \li Arrange for a timer to call atomTimerTick() periodically * \li Create one or more application threads using atomThreadCreate() * \li Start the OS using atomOSStart(). At this point the highest * priority application thread created will be started. * * Interrupts should be disabled until the first thread restore is complete, * to avoid any complications due to interrupts occurring while crucial * operating system facilities are being initialised. They are normally * enabled by the archFirstThreadRestore() routine in the architecture port. * * @param[in] idle_thread_stack_top Ptr to top of stack area for idle thread * @param[in] idle_thread_stack_size Size of idle thread stack in bytes * * @retval ATOM_OK Success * @retval ATOM_ERROR Initialisation error */ uint8_t atomOSInit (void *idle_thread_stack_top, uint32_t idle_thread_stack_size) { uint8_t status; /* Initialise data */ curr_tcb = NULL; tcbReadyQ = NULL; atomOSStarted = FALSE; /* Create the idle thread */ status = atomThreadCreate(&idle_tcb, IDLE_THREAD_PRIORITY, atomIdleThread, 0, idle_thread_stack_top, idle_thread_stack_size); /* Return status */ return (status); }
int Sensordrv::start_thread() { uint8_t status; if(is_running) { return -EBUSY; } status = atomThreadCreate(&(this -> tcb), DEFAULT_THREAD_PRIO, this -> thread_func, 0, thread_stack, SENSORDRV_STACK_SIZE_BYTES, TRUE); if(ATOM_OK == status) { is_running = true; return 0; } else { return atom_err_to_errno(status); } }
/** * \b test_start * * Start mutex test. * * Stress-tests mutex Get and Put operations. Four threads are created which are * continually Getting and Putting the same mutex, with no time delays between * each Get/Put. * * @retval Number of g_failures */ uint32_t test_start (void) { CRITICAL_STORE; int finish_cnt; /* Default to zero g_failures */ g_failures = 0; /* Create mutex to stress */ if (atomMutexCreate (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error creating mutex\n")); g_failures++; } /* Create sem to receive thread-finished notification */ else if (atomSemCreate (&sem1, 0) != ATOM_OK) { ATOMLOG (_STR("Error creating sem\n")); g_failures++; } else { /* Take ownership of the mutex to ensure all threads wait for now */ if (atomMutexGet (&mutex1, 0) != ATOM_OK) { ATOMLOG (_STR("Error taking mutex\n")); g_failures++; } /* Create Thread 1 */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); CRITICAL_START (); g_failures++; CRITICAL_END (); } /* Create Thread 2 */ if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO, test_thread_func, 2, &test_thread_stack[1][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); CRITICAL_START (); g_failures++; CRITICAL_END (); } /* Create Thread 3 */ if (atomThreadCreate(&tcb[2], TEST_THREAD_PRIO, test_thread_func, 3, &test_thread_stack[2][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); CRITICAL_START (); g_failures++; CRITICAL_END (); } /* Create Thread 4 */ if (atomThreadCreate(&tcb[3], TEST_THREAD_PRIO, test_thread_func, 4, &test_thread_stack[3][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); CRITICAL_START (); g_failures++; CRITICAL_END (); } /* Release ownership of the mutex to kick the threads off */ if (atomMutexPut (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error putting mutex\n")); g_failures++; } /* * All four threads will now be performing Gets/Puts on mutex1. * When they have finished they will post sem1, so we wait * until sem1 is posted four times. */ finish_cnt = 0; while (1) { /* * Attempt to Get sem1. When we have managed to get * the semaphore four times, it must have been posted * by all four threads. */ if (atomSemGet (&sem1, 0) == ATOM_OK) { /* Increment our count of finished threads */ finish_cnt++; /* Check if all four threads have now posted sem1 */ if (finish_cnt == 4) { break; } } } /* Delete OS objects, test finished */ if (atomMutexDelete (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); CRITICAL_START (); g_failures++; CRITICAL_END (); } if (atomSemDelete (&sem1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); CRITICAL_START (); g_failures++; CRITICAL_END (); } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); g_failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); g_failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return g_failures; }
/** * \b test_start * * Start queue test. * * With multiple threads blocking on a single queue, this test confirms that * they are woken in order when the queue is posted. The correct order for * waking is that the higher priority threads are woken first, followed by the * lower priority threads. Where multiple threads of the same priority are * waiting, the threads are woken in FIFO order (the order in which they started * waiting on the queue). * * To test this we create four threads which all wait on a single queue. * One pair of threads are running at high priority, with the other pair at a * lower priority: * * Thread 1: low prio thread A * Thread 2: low prio thread B * Thread 3: high prio thread A * Thread 4: high prio thread B * * The threads are forced to start blocking on the same queue in the * above order. * * We expect to see them woken up in the following order: * 3, 4, 1, 2 * * This proves the multiple blocking thread ordering in terms of both * the priority-queueing and same-priority-FIFO-queueing. * * @retval Number of failures */ uint32_t test_start (void) { int failures, count; uint8_t msg; /* Default to zero failures */ failures = 0; /* Create empty queue */ if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK) { ATOMLOG (_STR("Error creating test q1\n")); failures++; } /* Start the threads */ else { /* * The test threads all start by calling atomQueueGet() to receive * a message from the queue. Because the queue is empty, all test * threads should immediately block on the queue (until a message * is posted to it). */ /* Create Thread 1 (lower priority thread A) */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO+1, test_thread_func, 1, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the queue */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 2 (lower priority thread B) */ if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO+1, test_thread_func, 2, &test_thread_stack[1][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the queue */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 3 (higher priority thread A) */ if (atomThreadCreate(&tcb[2], TEST_THREAD_PRIO, test_thread_func, 3, &test_thread_stack[2][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the queue */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 4 (higher priority thread B) */ if (atomThreadCreate(&tcb[3], TEST_THREAD_PRIO, test_thread_func, 4, &test_thread_stack[3][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the queue */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* All four threads will now be blocking on queue1 */ /* * Initialise wake count, used by threads to determine * what order they were woken in. */ wake_cnt = 0; /* Loop waking all four threads */ for (count = 0; count < 4; count++) { /* * Post a message to the queue. This will wake up one of the threads * blocking on it (because it is currently empty). That thread will * wake up, note the order at which it was woken, then go to sleep * forever leaving the queue empty again. This is done four times so * that all four threads are woken, noting their wake order. */ msg = 0x66; if (atomQueuePut (&queue1, 0, &msg) != ATOM_OK) { ATOMLOG (_STR("Post fail\n")); failures++; } /* * Sleep to give the thread time to wake up and modify the shared * global data wake_cnt and wake_order[]. We deliberately do not * use a mutex for protecting access to this shared data, as we * are testing the queue module in isolation here. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC / 4); } /* All four threads now woken up, check they woke in correct order */ if ((wake_order[0] != 3) && (wake_order[1] != 4) && (wake_order[2] != 1) && (wake_order[3] != 2)) { ATOMLOG (_STR("Bad order %d,%d,%d,%d\n"), wake_order[0], wake_order[1], wake_order[2], wake_order[3]); failures++; } /* Delete queue, test finished */ if (atomQueueDelete (&queue1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
int main ( void ) { int8_t status; /** * Reuse part of the idle thread's stack for the stack required * during this startup function. */ SP = (int)&idle_thread_stack[(IDLE_STACK_SIZE_BYTES/2) - 1]; /** * Note: to protect OS structures and data during initialisation, * interrupts must remain disabled until the first thread * has been restored. They are reenabled at the very end of * the first thread restore, at which point it is safe for a * reschedule to take place. */ /** * Initialise the OS before creating our threads. * * Note that we tell the OS that the idle stack is half its actual * size. This prevents it prefilling the bottom half with known * values for stack-checkig purposes, which we cannot allow because * we are temporarily using it for our own stack. The remainder will * still be available once the OS is started, this only prevents the * OS from prefilling it. * * If you are not reusing the idle thread's stack during startup then * you should pass in the correct size here. */ status = atomOSInit(&idle_thread_stack[IDLE_STACK_SIZE_BYTES - 1], (IDLE_STACK_SIZE_BYTES/2)); if (status == ATOM_OK) { /* Enable the system tick timer */ avrInitSystemTickTimer(); /* Create an application thread */ status = atomThreadCreate(&main_tcb, 16, main_thread_func, 0, &main_thread_stack[MAIN_STACK_SIZE_BYTES - 1], MAIN_STACK_SIZE_BYTES); if (status == ATOM_OK) { /** * First application thread successfully created. It is * now possible to start the OS. Execution will not return * from atomOSStart(), which will restore the context of * our application thread and start executing it. * * Note that interrupts are still disabled at this point. * They will be enabled as we restore and execute our first * thread in archFirstThreadRestore(). */ atomOSStart(); } } while (1) ; /* There was an error starting the OS if we reach here */ return (0); }
/** * \b test_start * * Start queue test. * * This test exercises queue deletion, waking threads blocking on a queue * in atomQueuePut() if the queue is deleted. * * Deletion wakeups are tested twice: once for a thread which is blocking * in atomQueuePut() with a timeout and once for a thread which is * blocking in atomQueuePut() with no timeout. * * Deletion of threads blocking in atomQueueGet() are tested in queue2.c. * * @retval Number of failures */ uint32_t test_start (void) { int failures, i; uint8_t msg; /* Default to zero failures */ failures = 0; /* Set a test value for posting to the queue */ msg = 0x66; /* Test wakeup of threads on queue deletion (thread blocking with no timeout) */ g_result = 0; if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK) { ATOMLOG (_STR("Error creating test queue\n")); failures++; } /* Successful queue creation */ else { /* Fill up all entries */ for (i = 0; i < QUEUE_ENTRIES; i++) { if (atomQueuePut (&queue1, 0, &msg) != ATOM_OK) { ATOMLOG (_STR("Error filling queue\n")); failures++; } } /* Create a test thread that will block because the queue is full */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test1_thread_func, 0, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 1\n")); failures++; } else { /* * We have created and filled a queue. We want to see that the other * thread is woken up if its queue is deleted. This is indicated * through g_result being set. */ /* Wait for the other thread to start blocking on queue1 */ if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Failed timer delay\n")); failures++; } else { /* The other thread will be blocking on queue1 now, delete queue1 */ if (atomQueueDelete(&queue1) != ATOM_OK) { ATOMLOG (_STR("Failed queue1 delete\n")); failures++; } else { /* Queue1 deleted. The thread should now wake up and set g_result. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC); if (g_result == 0) { ATOMLOG (_STR("Notify fail\n")); failures++; } else { /* Success */ } } } } } /* Test wakeup of threads on semaphore deletion (thread blocking with timeout) */ g_result = 0; if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(uint8_t), QUEUE_ENTRIES) != ATOM_OK) { ATOMLOG (_STR("Error creating test queue\n")); failures++; } /* Successful queue creation */ else { /* Fill up all entries */ for (i = 0; i < QUEUE_ENTRIES; i++) { if (atomQueuePut (&queue1, 0, &msg) != ATOM_OK) { ATOMLOG (_STR("Error filling queue\n")); failures++; } } /* Create a test thread that will block because the queue is full */ if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO, test2_thread_func, 0, &test_thread_stack[1][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 2\n")); failures++; } else { /* * We have created and filled a queue. We want to see that the other * thread is woken up if its queue is deleted. This is indicated * through g_result being set. */ /* Wait for the other thread to start blocking on queue1 */ if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Failed timer delay\n")); failures++; } else { /* The other thread will be blocking on queue1 now, delete queue1 */ if (atomQueueDelete(&queue1) != ATOM_OK) { ATOMLOG (_STR("Failed queue1 delete\n")); failures++; } else { /* Queue1 deleted. The thread should now wake up and set g_result. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC); if (g_result == 0) { ATOMLOG (_STR("Notify fail\n")); failures++; } else { /* Success */ } } } } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start mutex test. * * This tests the lock count of a mutex. The mutex object should * count the number of times a thread has locked the mutex and * not fully release it for use by another thread until it has * been released the same number of times it was locked. * * @retval Number of failures */ uint32_t test_start (void) { int failures; int i; /* Default to zero failures */ failures = 0; /* Create mutex */ if (atomMutexCreate (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error creating mutex\n")); failures++; } else { /* Initialise the shared_data to zero */ shared_data = 0; /* Take the mutex several times */ for (i = 0; i < TEST_LOCK_CNT; i++) { if (atomMutexGet (&mutex1, 0) != ATOM_OK) { ATOMLOG (_STR("Error taking mutex\n")); failures++; break; } } /* Create second thread */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* * The second thread has now been created and should block on * the mutex until we release it. We wait a while and check that * shared_data has not been modified. */ for (i = 0; i < 4; i++) { /* * Sleep for a while to give the second thread a chance to * modify shared_data, thought it shouldn't until we * release the mutex enough times. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Check shared data. The second thread always sets it to one. */ if (shared_data != 0) { ATOMLOG (_STR("Shared data modified\n")); failures++; break; } } /* Check successful so far */ if (failures == 0) { /* * Release the mutex TEST_LOCK_CNT-1 times, after which we * should still own the mutex (until we release one more time). */ for (i = 0; i < TEST_LOCK_CNT-1; i++) { if (atomMutexPut (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Failed release\n")); failures++; } } /* * Wait a little while then check that shared_data has * not been modified (we should still own the mutex). */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); if (shared_data != 0) { ATOMLOG (_STR("Expected unmodified\n")); failures++; } /* * Release the mutex one more time, after which we should no * longer own the mutex (and wake up the second thread). */ if (atomMutexPut (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Failed release\n")); failures++; } /* * Wait a little while then check that shared_data has * been modified by the second thread. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); if (shared_data != 1) { ATOMLOG (_STR("Expected modified\n")); failures++; } } /* * Finally attempt to release the mutex one more time, while * we no longer own the mutex. Either the second thread will * have ownership of it, or no thread will have ownership. * In both cases we expect to get an ownership error when we * attempt to release it. */ if (atomMutexPut (&mutex1) != ATOM_ERR_OWNERSHIP) { ATOMLOG (_STR("Failed locked+1 release\n")); failures++; } /* Delete mutex, test finished */ if (atomMutexDelete (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start timer test. * * Tests that atomTimerDelay() delays for the correct time * period when used by three threads simultanously. * * @retval Number of failures */ uint32_t test_start (void) { int failures; /* Default to zero failures */ failures = 0; g_failure_cnt[0] = g_failure_cnt[1] = g_failure_cnt[2] = 0; /* Create Thread 1 */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Thread1\n")); failures++; } /* Create Thread 2 */ if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO, test_thread_func, 2, &test_thread_stack[1][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Thread2\n")); failures++; } /* Create Thread 3 */ if (atomThreadCreate(&tcb[2], TEST_THREAD_PRIO, test_thread_func, 3, &test_thread_stack[2][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Thread3\n")); failures++; } /* Sleep for 10 seconds allowing the three threads to run tests */ if (atomTimerDelay(TEST_PERIOD_SECS * SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Period\n")); failures++; } /* Add the per-thread failure count to the main count */ failures += g_failure_cnt[0] + g_failure_cnt[1] + g_failure_cnt[2]; /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start kernel test. * * This tests the scheduling of threads at different priorities, and * preemption of lower priority threads by higher priority threads. * * Much of this functionality is already tested implicitly by the * semaphore, mutex tests etc but we repeat it here within the kernel * tests for completeness. * * Two threads are created at different priorities, with each thread * setting a running flag whenever it runs. We check that when the * higher priority thread is ready to run, only the higher priority * thread's running flag is set (even though the lower priority * thread should also be setting it at this time). This checks that * the scheduler is correctly prioritising thread execution. * * The test also exercises preemption, by disabling setting of the * running flag in the higher priority thread for a period. During * this time the higher priority thread repeatedly sleeps for one * system tick then wakes up to check the sleep-request flag again. * Every time the higher priority thread wakes up, it has preempted * the lower priority thread (which is always running). By ensuring * that the higher priority thread is able to start running again * after one of these periods (through checking the running flag) * we prove that the preemption has worked. * * @retval Number of failures */ uint32_t test_start (void) { int failures; int i; /* Default to zero failures */ failures = 0; /* Initialise global data */ running_flag[0] = running_flag[1] = FALSE; sleep_request[0] = sleep_request[1] = FALSE; /* Create low priority thread */ if (atomThreadCreate (&tcb[0], 253, test_thread_func, 0, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { ATOMLOG (_STR("Bad thread create\n")); failures++; } /* Create high priority thread */ else if (atomThreadCreate (&tcb[1], 252, test_thread_func, 1, &test_thread_stack[1][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { ATOMLOG (_STR("Bad thread create\n")); failures++; } /* Repeat test a few times */ for (i = 0; i < 8; i++) { /* Make the higher priority thread sleep */ sleep_request[1] = TRUE; /* Sleep a little to make sure the thread sees the sleep request */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Reset the running flag for both threads */ running_flag[0] = running_flag[1] = FALSE; /* Sleep a little to give any running threads time to set their flag */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Check only the low priority thread has run since we reset the flags */ if ((running_flag[0] != TRUE) || (running_flag[1] != FALSE)) { ATOMLOG (_STR("Lo%d %d/%d\n"), i, running_flag[0], running_flag[1]); failures++; break; } else { /* * We have confirmed that only the ready thread has been running. * Now check that if we wake up the high priority thread, the * low priority one stops running and only the high priority one * does. */ /* Tell the higher priority thread to stop sleeping */ sleep_request[1] = FALSE; /* Reset the running flag for both threads */ running_flag[0] = running_flag[1] = FALSE; /* Sleep a little to give any running threads time to set their flag */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Check only the high priority thread has run since we reset the flags */ if ((running_flag[1] != TRUE) || (running_flag[0] != FALSE)) { ATOMLOG (_STR("Hi%d/%d\n"), running_flag[0], running_flag[1]); failures++; break; } else { /* * We have confirmed that the high priority thread has preempted the * low priority thread, and remain running while never scheduling * the lower one back in. */ } } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start kernel test. * * This tests the round-robin timeslicing of same priority threads. * * Four threads are created (over and above the main test thread). * The main test thread sleeps for the duration of the entire test. * While the test is ongoing, all four threads are running * continuously at the same priority. They each check that whenever * the system tick (atomTimeGet()) changes, it has moved on 1 tick * since the last time the tick was checked, and that the previous * thread is the one created before itself. In the case of the first * thread created, the previous thread should be the last thread * created. This proves that on every tick the four threads get a * schedule timeslice equally, and in the same order throughout the * duration of the test. * * @retval Number of failures */ uint32_t test_start (void) { int failures; /* Default to zero failures */ failures = 0; /* Initialise global data */ last_time = 0; last_thread_id = -1; failure_cnt[0] = failure_cnt[1] = failure_cnt[2] = failure_cnt[3] = 0; /* Set test as not started until all threads are ready to go */ test_started = FALSE; /* * Create all four threads at the same priority as each other. * They are given a lower priority than this thread, however, * to ensure that once this thread wakes up to stop the test it * can do so without confusing the scheduling tests by having * a spell in which this thread was run. */ if (atomThreadCreate (&tcb[0], TEST_THREAD_PRIO + 1, test_thread_func, 0, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { ATOMLOG (_STR("Bad thread create\n")); failures++; } else if (atomThreadCreate (&tcb[1], TEST_THREAD_PRIO + 1, test_thread_func, 1, &test_thread_stack[1][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { ATOMLOG (_STR("Bad thread create\n")); failures++; } else if (atomThreadCreate (&tcb[2], TEST_THREAD_PRIO + 1, test_thread_func, 2, &test_thread_stack[2][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { ATOMLOG (_STR("Bad thread create\n")); failures++; } else if (atomThreadCreate (&tcb[3], TEST_THREAD_PRIO + 1, test_thread_func, 3, &test_thread_stack[3][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { ATOMLOG (_STR("Bad thread create\n")); failures++; } /* Start the test */ test_started = TRUE; /* Sleep for 5 seconds during test */ atomTimerDelay (5 * SYSTEM_TICKS_PER_SEC); /* Stop the test */ test_started = FALSE; /* Sleep for tests to complete */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Count any failures from test threads */ failures += failure_cnt[0] + failure_cnt[1] + failure_cnt[2] + failure_cnt[3]; /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
int main ( void ) { int8_t status; uint32_t loop; /** * Brief delay to give the debugger a chance to stop the core before we * muck around with the chip's configuration. */ for(loop = 0; loop < 1000000; ++loop){ __asm__("nop"); } /** * Note: to protect OS structures and data during initialisation, * interrupts must remain disabled until the first thread * has been restored. They are reenabled at the very end of * the first thread restore, at which point it is safe for a * reschedule to take place. */ board_setup(); /** * Initialise the OS before creating our threads. * * Note that we cannot enable stack-checking on the idle thread on * this platform because we are already using part of the idle * thread's stack now as our startup stack. Prefilling for stack * checking would overwrite our current stack. * * If you are not reusing the idle thread's stack during startup then * you are free to enable stack-checking here. */ status = atomOSInit(&idle_thread_stack[0], IDLE_STACK_SIZE_BYTES, FALSE); if (status == ATOM_OK) { /* Create an application thread */ status = atomThreadCreate(&main_tcb, TEST_THREAD_PRIO, main_thread_func, 0, &main_thread_stack[0], MAIN_STACK_SIZE_BYTES, TRUE); if (status == ATOM_OK) { /** * First application thread successfully created. It is * now possible to start the OS. Execution will not return * from atomOSStart(), which will restore the context of * our application thread and start executing it. * * Note that interrupts are still disabled at this point. * They will be enabled as we restore and execute our first * thread in archFirstThreadRestore(). */ atomOSStart(); } } while (1) ; /* There was an error starting the OS if we reach here */ return (0); }
/** * \b test_start * * Start mutex test. * * This tests the ownership checks of the mutex library. Only threads * which own a mutex can release it. It should not be possible to * release a mutex if it is not owned by any thread, is owned by a * different thread, or at interrupt context. We test here that all * three cases are trapped. * * @retval Number of failures */ uint32_t test_start (void) { int failures; ATOM_TIMER timer_cb; /* Default to zero failures */ failures = 0; /* Create mutex */ if (atomMutexCreate (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error creating mutex\n")); failures++; } else { /* Initialise the shared_data to zero */ shared_data = 0; /* Attempt to release the mutex when not owned by any thread */ if (atomMutexPut (&mutex1) != ATOM_ERR_OWNERSHIP) { ATOMLOG (_STR("Release error\n")); failures++; } /* Create second thread */ else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* * The second thread has now been created and should take ownership * of the mutex. We wait a while and check that shared_data has been * modified, which proves to us that the thread has taken the mutex. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); if (shared_data != 1) { ATOMLOG (_STR("Shared data unmodified\n")); failures++; } /* Check successful so far */ if (failures == 0) { /* * Attempt to release the mutex again now that it is owned * by another thread. */ if (atomMutexPut (&mutex1) != ATOM_ERR_OWNERSHIP) { ATOMLOG (_STR("Release error 2\n")); failures++; } /* Finally check that the mutex cannot be released from an ISR */ /* Fill out the timer callback request structure */ timer_cb.cb_func = testCallback; timer_cb.cb_data = NULL; timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC; /* Request the timer callback to run in one second */ if (atomTimerRegister (&timer_cb) != ATOM_OK) { ATOMLOG (_STR("Error registering timer\n")); failures++; } /* * Wait two seconds for shared_date to be set to 2 * indicating success. This happens if the timer * callback received the expected ownership error * when attempting to release the mutex. */ else { atomTimerDelay (2 * SYSTEM_TICKS_PER_SEC); if (shared_data != 2) { ATOMLOG (_STR("Context check failed\n")); failures++; } } } /* Delete mutex, test finished */ if (atomMutexDelete (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start mutex test. * * This tests timeouts on a mutex. We make a thread block with timeout * on a mutex, and test that sufficient time has actually passed as * was requested by the timeout parameter. * * The main thread creates a second thread which will immediately take * ownership of the mutex. The test checks that the correct timeout * occurs when the first thread blocks on the mutex which is already * owned (by the second thread). * * @retval Number of failures */ uint32_t test_start (void) { int failures; uint32_t start_time, end_time; /* Default to zero failures */ failures = 0; /* Create mutex */ if (atomMutexCreate (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error creating mutex\n")); failures++; } else { /* Initialise the shared_data to zero */ shared_data = 0; /* Create second thread */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* * The second thread has now been created and should take ownership * of the mutex. We wait a while and check that shared_data has been * modified, which proves to us that the thread has taken the mutex. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); if (shared_data != 1) { ATOMLOG (_STR("Shared data unmodified\n")); failures++; } /* Check successful so far */ if (failures == 0) { /* Take note of the start time */ start_time = atomTimeGet(); /* Block on the mutex with two second timeout */ if (atomMutexGet (&mutex1, 2 * SYSTEM_TICKS_PER_SEC) != ATOM_TIMEOUT) { ATOMLOG (_STR("Failed get\n")); failures++; } /* Take note of the end time */ end_time = atomTimeGet(); /* Now check that two seconds have passed */ if ((end_time < (start_time + (2 * SYSTEM_TICKS_PER_SEC))) || (end_time > (start_time + (2 * SYSTEM_TICKS_PER_SEC) + 1))) { ATOMLOG (_STR("Bad time\n")); failures++; } } /* Delete mutex, test finished */ if (atomMutexDelete (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start queue test. * * This tests basic operation of queues. * * The main test thread creates a second thread and posts * a series of messages to the second thread. The message * values are checked against the expected values. * * We test using 4-byte messages. * * @retval Number of failures */ uint32_t test_start (void) { int failures, count; int num_entries; uint32_t msg; /* Default to zero failures */ failures = 0; g_result = 0; /* Create test queue */ if (atomQueueCreate (&queue1, (uint8_t *)&queue1_storage[0], sizeof(queue1_storage[0]), QUEUE_ENTRIES) != ATOM_OK) { ATOMLOG (_STR("Error creating test queue\n")); failures++; } /* Create a test thread that will block because the queue is empty */ else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO + 1, test1_thread_func, 0, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 1\n")); failures++; } else { /* * We have created an empty queue and a thread which should now * be blocking on the queue. The test thread is lower priority * than us. */ /* Wait for the other thread to start blocking on queue1 */ if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Failed timer delay\n")); failures++; } else { /* * Post all entries in the test array to the queue. * Because the second thread is lower priority than * us, we will post 8 messages until the queue is * full without waking up the second thread at all. * At that point, we will block and the second * thread will remove one message from the queue. * With a spare entry in the queue, this thread * will wake up again and post another message. * This will continue until this thread has posted * all messages, at which point the second thread * will drain all remaining messages from the * queue. * * Through this scheme we are able to test posting * to the queue at all possible fill levels. */ num_entries = sizeof(test_values) / sizeof(test_values[0]); for (count = 0; count < num_entries; count++) { /* Increment through and post all test values to the queue */ msg = test_values[count]; if (atomQueuePut (&queue1, 0, (uint8_t *)&msg) != ATOM_OK) { ATOMLOG (_STR("Failed post\n")); failures++; } } /* Sleep a while for the second thread to finish */ atomTimerDelay (SYSTEM_TICKS_PER_SEC); /* Check that the second thread has found all test values */ if (g_result != 1) { ATOMLOG (_STR("Bad test vals\n")); failures++; } } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start semaphore test. * * This tests usage of a semaphore for basic mutual exclusion type * operation. Note that Atomthreads has a more fully-featured real * mutex implementation in the mutex module. * * The semaphore sem1 is initialised with a count of 1. Whichever * thread holds this semaphore can then modify the global variable * "shared_data". * * The main thread first takes the "mutex" sem1, then creates a * second thread. The second thread should block on the sem1 mutex * until the main thread releases it. The test checks that the * global "shared_data" is not modified by the second thread * until the main thread releases the mutex. * * @retval Number of failures */ uint32_t test_start (void) { int failures; int i; /* Default to zero failures */ failures = 0; /* Create sem with count one for mutex purposes */ if (atomSemCreate (&sem1, 1) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 1\n")); failures++; } else { /* Initialise the shared_data to zero */ shared_data = 0; /* Take the mutex to ensure only this thread can modify shared_data */ if (atomSemGet (&sem1, 0) != ATOM_OK) { ATOMLOG (_STR("Error taking mutex\n")); failures++; } /* Create second thread */ else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* * The second thread has now been created and should block on * the "mutex" sem1 (which now has count zero) until we release * it. We wait a while and check that shared_data has not been * modified. */ for (i = 0; i < 4; i++) { /* * Sleep for a while to give the second thread a chance to * modify shared_data, thought it shouldn't until we * release the mutex. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Check shared data. The second thread always sets it to one. */ if (shared_data != 0) { ATOMLOG (_STR("Shared data modified\n")); failures++; break; } } /* Check successful so far */ if (failures == 0) { /* * Release the mutex, which will allow the second thread to * wake and start modifying shared_data. */ if (atomSemPut (&sem1) != ATOM_OK) { ATOMLOG (_STR("Failed release\n")); failures++; } /* * Wait a little while then check that shared_data has * been modified. */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); if (shared_data != 1) { ATOMLOG (_STR("Expected modify\n")); failures++; } /* * Release and take the mutex again a few times to ensure * that the mutex continues to protect shared_data. */ for (i = 0; i < 4; i++) { /* * Take the mutex again, to prevent second thread accessing * shared_data. */ if (atomSemGet (&sem1, SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Retake %d\n"), i); failures++; break; } else { /* * Set shared_data to 0 and wait to ensure that the * second thread doesn't modify it while we have the * mutex again. */ shared_data = 0; /* Wait a while to give second thread potential to run */ atomTimerDelay(SYSTEM_TICKS_PER_SEC/4); /* * Check that shared_data has not been modified while we * own the mutex. */ if (shared_data != 0) { /* Thread is still modifying the data */ ATOMLOG (_STR("Still modifying\n")); failures++; break; } /* * Release the mutex, which will allow the second thread to * wake and start modifying shared_data again. */ if (atomSemPut (&sem1) != ATOM_OK) { ATOMLOG (_STR("Failed release\n")); failures++; } } } } /* Delete semaphore, test finished */ if (atomSemDelete (&sem1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start queue test. * * This test verifies the queue deletion API, by deleting a queue * on which multiple threads are blocking, and checking that all three * are woken up with an appropriate error code. * * @retval Number of failures */ uint32_t test_start (void) { int failures; int i; /* Default to zero failures */ failures = 0; /* Initialise pass status for all three threads to FALSE */ for (i = 0; i < 3; i++) { pass_flag[i] = FALSE; } /* Test wakeup of three threads on queue deletion */ if (atomQueueCreate (&queue1, &queue1_storage[0], sizeof(queue1_storage[0]), QUEUE_ENTRIES) != ATOM_OK) { ATOMLOG (_STR("Error creating Q\n")); failures++; } else { /* The queue is empty so all three test threads will block */ /* Create test thread 1 */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 0, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 1\n")); failures++; } /* Create test thread 2 */ else if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[1][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 2\n")); failures++; } /* Create test thread 3 */ else if (atomThreadCreate(&tcb[2], TEST_THREAD_PRIO, test_thread_func, 2, &test_thread_stack[2][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 3\n")); failures++; } /* Test threads now created */ else { /* Wait a while for threads to start blocking on queue1 */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Delete queue1 now that all three threads should be blocking */ if (atomQueueDelete (&queue1) != ATOM_OK) { ATOMLOG (_STR("Delete fail\n")); failures++; } else { /* Wait a while for all three threads to wake up */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Check that all three threads have passed */ if ((pass_flag[0] != TRUE) || (pass_flag[1] != TRUE) || (pass_flag[2] != TRUE)) { ATOMLOG (_STR("Thread fail\n")); failures++; } } } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
int main ( void ) { int8_t status; sei(); SerialInit(MYUBRR); InitWatch(); /** * Reuse part of the idle thread's stack for the stack required * during this startup function. */ SP = (int)&idle_thread_stack[(IDLE_STACK_SIZE_BYTES/2) - 1]; /** * Note: to protect OS structures and data during initialisation, * interrupts must remain disabled until the first thread * has been restored. They are reenabled at the very end of * the first thread restore, at which point it is safe for a * reschedule to take place. */ /** * Initialise the OS before creating our threads. * * Note that we cannot enable stack-checking on the idle thread on * this platform because we are already using part of the idle * thread's stack now as our startup stack. Prefilling for stack * checking would overwrite our current stack. * * If you are not reusing the idle thread's stack during startup then * you are free to enable stack-checking here. */ status = atomOSInit(&idle_thread_stack[0], IDLE_STACK_SIZE_BYTES, FALSE); if (status == ATOM_OK) { /* Enable the system tick timer */ avrInitSystemTickTimer(); /* Create the main thread */ status = atomThreadCreate(&main_tcb, MAIN_THREAD_PRIO, main_thread_func, 0, &main_thread_stack[0], MAIN_STACK_SIZE_BYTES, FALSE); if (status == ATOM_OK) { /** * Application threads successfully created. It is * now possible to start the OS. Execution will not return * from atomOSStart(), which will restore the context of * our application thread and start executing it. * * Note that interrupts are still disabled at this point. * They will be enabled as we restore and execute our first * thread in archFirstThreadRestore(). */ atomOSStart(); } } while (1) { atomTimerDelay (2 * SYSTEM_TICKS_PER_SEC); // wait 2 sec } /* There was an error starting the OS if we reach here */ return (0); }
/** * \b test_start * * Start semaphore test. * * With multiple threads blocking on a single semaphore, this test confirms that * they are woken in order when the semaphore is posted. The correct order for * waking is that the higher priority threads are woken first, followed by the * lower priority threads. Where multiple threads of the same priority are * waiting, the threads are woken in FIFO order (the order in which they started * waiting on the semaphore). * * To test this we create four threads which all wait on a single semaphore. * One pair of threads are running at high priority, with the other pair at a * lower priority: * * Thread 1: low prio thread A * Thread 2: low prio thread B * Thread 3: high prio thread A * Thread 4: high prio thread B * * The threads are forced to start blocking on the same semaphore in the * above order (the semaphore is initialised with count 0 to ensure any * threads calling atomSemGet() will block). * * We expect to see them woken up in the following order: * 3, 4, 1, 2 * * This proves the multiple blocking thread ordering in terms of both * the priority-queueing and same-priority-FIFO-queueing. * * @retval Number of failures */ uint32_t test_start (void) { int failures; int i; /* Default to zero failures */ failures = 0; /* Create sem with count zero (so that all threads will block) */ if (atomSemCreate (&sem1, 0) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 1\n")); failures++; } else { /* Create Thread 1 (lower priority thread A) */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO+1, test_thread_func, 1, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the semaphore */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 2 (lower priority thread B) */ if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO+1, test_thread_func, 2, &test_thread_stack[1][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the semaphore */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 3 (higher priority thread A) */ if (atomThreadCreate(&tcb[2], TEST_THREAD_PRIO, test_thread_func, 3, &test_thread_stack[2][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the semaphore */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 4 (higher priority thread B) */ if (atomThreadCreate(&tcb[3], TEST_THREAD_PRIO, test_thread_func, 4, &test_thread_stack[3][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the semaphore */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* All four threads will now be blocking on sem1 */ /* * Initialise wake count, used by threads to determine * what order they were woken in. */ wake_cnt = 0; /* * Wake the four threads up in order, leaving some time between * each wake up for them to deal with global data in a * thread-safe fashion. */ for (i = 0; i < 4; i++) { /* Post semaphore to wake one of the threads up */ if (atomSemPut (&sem1) != ATOM_OK) { ATOMLOG (_STR("Post fail\n")); failures++; } /* Sleep to give the thread time to manipulate global data */ atomTimerDelay (SYSTEM_TICKS_PER_SEC / 4); } /* All four threads now woken up, check they woke in correct order */ if ((wake_order[0] != 3) || (wake_order[1] != 4) || (wake_order[2] != 1) || (wake_order[3] != 2)) { ATOMLOG (_STR("Bad order %d,%d,%d,%d\n"), wake_order[0], wake_order[1], wake_order[2], wake_order[3]); failures++; } /* Delete semaphore, test finished */ if (atomSemDelete (&sem1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start semaphore test. * * This test exercises the semaphore creation and deletion APIs, including * waking threads blocking on a semaphore if the semaphore is deleted. * Deletion wakeups are tested twice: once for a thread which is blocking * with a timeout and once for a thread which is blocking with no timeout. * * @retval Number of failures */ uint32_t test_start (void) { int failures; uint32_t i; uint8_t status; /* Default to zero failures */ failures = 0; /* Test creation and deletion of semaphores: good values */ for (i = 0; i < 1000; i++) { if (atomSemCreate (&sem1, 0) == ATOM_OK) { if (atomSemDelete (&sem1) == ATOM_OK) { /* Success */ } else { /* Fail */ ATOMLOG (_STR("Error deleting semaphore\n")); failures++; break; } } else { /* Fail */ ATOMLOG (_STR("Error creating semaphore\n")); failures++; break; } } /* Test creation and deletion of semaphores: creation checks */ if (atomSemCreate (NULL, 0) != ATOM_OK) { /* Success */ } else { /* Fail */ ATOMLOG (_STR("Bad semaphore creation checks\n")); failures++; } /* Test creation and deletion of semaphores: deletion checks */ if (atomSemDelete (NULL) != ATOM_OK) { /* Success */ } else { /* Fail */ ATOMLOG (_STR("Bad semaphore deletion checks\n")); failures++; } /* Test wakeup of threads on semaphore deletion (thread blocking with no timeout) */ if (atomSemCreate (&sem1, 0) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 1\n")); failures++; } else if (atomSemCreate (&sem2, 0) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 2\n")); failures++; } else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test1_thread_func, 0, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 1\n")); failures++; } else { /* * We have created two semaphores. sem1 is for the other thread * to wait on, which we will delete from this thread. We want * to see that the other thread is woken up if its semaphore * is deleted. This is indicated through sem2 being posted * back to us. */ /* Wait for the other thread to start blocking on sem1 */ if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Failed timer delay\n")); failures++; } else { /* The other thread will be blocking on sem1 now, delete sem1 */ if (atomSemDelete(&sem1) != ATOM_OK) { ATOMLOG (_STR("Failed sem1 delete\n")); failures++; } else { /* Sem1 deleted. The thread should now wake up and post sem2. */ if ((status = atomSemGet (&sem2, (5*SYSTEM_TICKS_PER_SEC))) != ATOM_OK) { ATOMLOG (_STR("Notify fail (%d)\n"), status); failures++; } else { /* Success */ /* Clean up the last remaining semaphore */ if (atomSemDelete (&sem2) != ATOM_OK) { ATOMLOG (_STR("Failed sem2 delete\n")); failures++; } } } } } /* Test wakeup of threads on semaphore deletion (thread blocking with timeout) */ if (atomSemCreate (&sem1, 0) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 1\n")); failures++; } else if (atomSemCreate (&sem2, 0) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 2\n")); failures++; } else if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO, test2_thread_func, 0, &test_thread_stack[1][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 2\n")); failures++; } else { /* * We have created two semaphores. sem1 is for the other thread * to wait on, which we will delete from this thread. We want * to see that the other thread is woken up if its semaphore * is deleted. This is indicated through sem2 being posted * back to us. */ /* Wait for the other thread to start blocking on sem1 */ if (atomTimerDelay(SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Failed timer delay\n")); failures++; } else { /* The other thread will be blocking on sem1 now, delete sem1 */ if (atomSemDelete(&sem1) != ATOM_OK) { ATOMLOG (_STR("Failed sem1 delete\n")); failures++; } else { /* Sem1 deleted. The thread should now wake up and post sem2. */ if ((status = atomSemGet (&sem2, (5*SYSTEM_TICKS_PER_SEC))) != ATOM_OK) { ATOMLOG (_STR("Notify fail (%d)\n"), status); failures++; } else { /* Success */ /* Clean up the last remaining semaphore */ if (atomSemDelete (&sem2) != ATOM_OK) { ATOMLOG (_STR("Failed sem2 delete\n")); failures++; } } } } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start mutex test. * * This test exercises the atomMutexGet() and atomMutexPut() APIs including * forcing the various error indications which can be returned from the * APIs to ensure that handling for these corner cases have been correctly * implemented. * * @retval Number of failures */ uint32_t test_start (void) { int failures; uint8_t status; ATOM_TIMER timer_cb; int count; /* Default to zero failures */ failures = 0; /* Test parameter checks */ if (atomMutexGet (NULL, 0) != ATOM_ERR_PARAM) { ATOMLOG (_STR("Get param failed\n")); failures++; } if (atomMutexPut (NULL) != ATOM_ERR_PARAM) { ATOMLOG (_STR("Put param failed\n")); failures++; } /* Test atomMutexGet() can not be called from interrupt context */ g_result = 0; if (atomMutexCreate (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error creating test mutex1\n")); failures++; } else { /* Fill out the timer callback request structure */ timer_cb.cb_func = testCallback; timer_cb.cb_data = NULL; timer_cb.cb_ticks = SYSTEM_TICKS_PER_SEC; /* Request the timer callback to run in one second */ if (atomTimerRegister (&timer_cb) != ATOM_OK) { ATOMLOG (_STR("Error registering timer\n")); failures++; } /* Wait two seconds for g_result to be set indicating success */ else { atomTimerDelay (2 * SYSTEM_TICKS_PER_SEC); if (g_result != 1) { ATOMLOG (_STR("Context check failed\n")); failures++; } } /* Delete the test mutex */ if (atomMutexDelete (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Mutex1 delete failed\n")); failures++; } } /* Create mutex1 which will be owned by us */ if (atomMutexCreate (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error creating test mutex 1\n")); failures++; } /* Create mutex2 which will be owned by another thread */ else if (atomMutexCreate (&mutex2) != ATOM_OK) { ATOMLOG (_STR("Error creating test mutex 2\n")); failures++; } /* Create a test thread, the sole purpose of which is to own mutex2 */ g_owned = 0; if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 0, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread 1\n")); failures++; } /* Sleep until the test thread owns mutex2 */ atomTimerDelay (SYSTEM_TICKS_PER_SEC); if (g_owned == 0) { ATOMLOG (_STR("Thread own fail\n")); failures++; } /* Test wait on mutex with timeout - should timeout while owned by another thread */ if ((status = atomMutexGet (&mutex2, SYSTEM_TICKS_PER_SEC)) != ATOM_TIMEOUT) { ATOMLOG (_STR("Get %d\n"), status); failures++; } else { /* Success */ } /* Test wait on mutex with no blocking - should return that owned by another thread */ if ((status = atomMutexGet (&mutex2, -1)) != ATOM_WOULDBLOCK) { ATOMLOG (_STR("Wouldblock err %d\n"), status); failures++; } /* Test wait on mutex with no blocking when mutex is available */ if (atomMutexGet (&mutex1, -1) != ATOM_OK) { ATOMLOG (_STR("Error taking mutex1\n")); failures++; } else { /* Relinquish ownership of mutex1 */ if (atomMutexPut (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error posting mutex\n")); failures++; } } /* Test for lock count overflows with too many gets */ count = 255; while (count--) { if (atomMutexGet (&mutex1, 0) != ATOM_OK) { ATOMLOG (_STR("Error getting mutex1\n")); failures++; break; } } /* The lock count should overflow this time */ if (atomMutexGet (&mutex1, 0) != ATOM_ERR_OVF) { ATOMLOG (_STR("Error tracking overflow\n")); failures++; } else { /* Success */ } /* Delete the test mutexes */ if (atomMutexDelete (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error deleting mutex1\n")); failures++; } if (atomMutexDelete (&mutex2) != ATOM_OK) { ATOMLOG (_STR("Error deleting mutex2\n")); failures++; } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start semaphore test. * * This tests basic counting semaphore operation between two threads. * * A semaphore is created with a count of 10. A second thread then * ensures that it can decrement the semaphore 10 times before * it can no longer be decremented. * * @retval Number of failures */ uint32_t test_start (void) { int failures; /* Default to zero failures */ failures = 0; /* Create sem with count ten for second thread to decrement */ if (atomSemCreate (&sem1, INITIAL_SEM_COUNT) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 1\n")); failures++; } /* Create sem to receive test-passed notification */ else if (atomSemCreate (&sem2, 0) != ATOM_OK) { ATOMLOG (_STR("Error creating test semaphore 1\n")); failures++; } else { /* Check that sem2 doesn't already have a positive count */ if (atomSemGet (&sem2, -1) != ATOM_WOULDBLOCK) { ATOMLOG (_STR("Sem2 already put\n")); failures++; } /* Create second thread */ else if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO, test_thread_func, 1, &test_thread_stack[0][TEST_THREAD_STACK_SIZE - 1], TEST_THREAD_STACK_SIZE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* * The second thread has now been created and will attempt to * decrement sem1 ten times, then finally check that it cannot * decrement it any further. If this passes then the second * thread will post sem2 to notify us that the test has passed. */ else { /* Give the second thread one second to post sem2 */ if (atomSemGet (&sem2, SYSTEM_TICKS_PER_SEC) != ATOM_OK) { ATOMLOG (_STR("Sem2 not posted\n")); failures++; } } /* Delete semaphores, test finished */ if (atomSemDelete (&sem1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } if (atomSemDelete (&sem2) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }
/** * \b test_start * * Start mutex test. * * With multiple threads blocking on a single mutex, this test confirms that * they are woken in order when the mutex is released. The correct order for * waking is that the higher priority threads are woken first, followed by the * lower priority threads. Where multiple threads of the same priority are * waiting, the threads are woken in FIFO order (the order in which they started * waiting on the mutex). * * To test this we create four threads which all wait on a single mutex. * One pair of threads are running at high priority, with the other pair at a * lower priority: * * Thread 1: low prio thread A * Thread 2: low prio thread B * Thread 3: high prio thread A * Thread 4: high prio thread B * * The threads are forced to start blocking on the same mutex in the * above order. * * We expect to see them woken up in the following order: * 3, 4, 1, 2 * * This proves the multiple blocking thread ordering in terms of both * the priority-queueing and same-priority-FIFO-queueing. * * @retval Number of failures */ uint32_t test_start (void) { int failures; /* Default to zero failures */ failures = 0; /* Create mutex */ if (atomMutexCreate (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Error creating test mutex 1\n")); failures++; } /* Take ownership of the mutex so all threads will block to begin with */ else if (atomMutexGet (&mutex1, 0) != ATOM_OK) { ATOMLOG (_STR("Get error\n")); failures++; } /* Start the threads */ else { /* Create Thread 1 (lower priority thread A) */ if (atomThreadCreate(&tcb[0], TEST_THREAD_PRIO+1, test_thread_func, 1, &test_thread_stack[0][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the mutex */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 2 (lower priority thread B) */ if (atomThreadCreate(&tcb[1], TEST_THREAD_PRIO+1, test_thread_func, 2, &test_thread_stack[1][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the mutex */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 3 (higher priority thread A) */ if (atomThreadCreate(&tcb[2], TEST_THREAD_PRIO, test_thread_func, 3, &test_thread_stack[2][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the mutex */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* Create Thread 4 (higher priority thread B) */ if (atomThreadCreate(&tcb[3], TEST_THREAD_PRIO, test_thread_func, 4, &test_thread_stack[3][0], TEST_THREAD_STACK_SIZE, TRUE) != ATOM_OK) { /* Fail */ ATOMLOG (_STR("Error creating test thread\n")); failures++; } /* Delay to ensure the thread will start blocking on the mutex */ atomTimerDelay (SYSTEM_TICKS_PER_SEC/4); /* All four threads will now be blocking on mutex1 */ /* * Initialise wake count, used by threads to determine * what order they were woken in. */ wake_cnt = 0; /* * Release the mutex. This will wake up one of the threads blocking * on it. That thread will take ownership of the mutex, and note the * order at which it was woken, before releasing the mutex. This in * turn will wake up the next thread blocking on the mutex until all * four test threads have taken and released the mutex, noting their * wake order. */ if (atomMutexPut (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Post fail\n")); failures++; } /* Sleep to give all four threads time to complete */ atomTimerDelay (SYSTEM_TICKS_PER_SEC / 4); /* All four threads now woken up, check they woke in correct order */ if ((wake_order[0] != 3) && (wake_order[1] != 4) && (wake_order[2] != 1) && (wake_order[3] != 2)) { ATOMLOG (_STR("Bad order %d,%d,%d,%d\n"), wake_order[0], wake_order[1], wake_order[2], wake_order[3]); failures++; } /* Delete mutex, test finished */ if (atomMutexDelete (&mutex1) != ATOM_OK) { ATOMLOG (_STR("Delete failed\n")); failures++; } } /* Check thread stack usage (if enabled) */ #ifdef ATOM_STACK_CHECKING { uint32_t used_bytes, free_bytes; int thread; /* Check all threads */ for (thread = 0; thread < NUM_TEST_THREADS; thread++) { /* Check thread stack usage */ if (atomThreadStackCheck (&tcb[thread], &used_bytes, &free_bytes) != ATOM_OK) { ATOMLOG (_STR("StackCheck\n")); failures++; } else { /* Check the thread did not use up to the end of stack */ if (free_bytes == 0) { ATOMLOG (_STR("StackOverflow %d\n"), thread); failures++; } /* Log the stack usage */ #ifdef TESTS_LOG_STACK_USAGE ATOMLOG (_STR("StackUse:%d\n"), (int)used_bytes); #endif } } } #endif /* Quit */ return failures; }