From 15e6e29c53d14221a7c16231644ae3c019705043 Mon Sep 17 00:00:00 2001 From: Stefan Petersen Date: Thu, 2 Jul 2015 23:57:10 +0200 Subject: [PATCH 1/6] atomevent implementation, see kernel/atomevent.c for documentation. This implements a simple event notification system which can signal up to implementation defined number of bits, suitable for signalling to threads from ISRs and callbacks. Proper tested ports are avr. --- kernel/atom.h | 2 + kernel/atomevent.c | 368 ++++++++++++++++++++++++++++++++++++++ kernel/atomevent.h | 46 +++++ ports/avr/Makefile | 1 + ports/avr/atomport.h | 7 + ports/cortex-m/atomport.h | 7 + tests/event1.c | 135 ++++++++++++++ tests/event2.c | 152 ++++++++++++++++ tests/event3.c | 160 +++++++++++++++++ 9 files changed, 878 insertions(+) create mode 100644 kernel/atomevent.c create mode 100644 kernel/atomevent.h create mode 100644 tests/event1.c create mode 100644 tests/event2.c create mode 100644 tests/event3.c diff --git a/kernel/atom.h b/kernel/atom.h index 2ee75a54..b6f5fa68 100755 --- a/kernel/atom.h +++ b/kernel/atom.h @@ -78,6 +78,8 @@ typedef struct atom_tcb uint8_t suspend_wake_status; /* Status returned to woken suspend calls */ ATOM_TIMER *suspend_timo_cb; /* Callback registered for suspension timeouts */ + /* Event bits */ + ATOM_EVENTS events; /* Mask of bits events are waiting for */ /* Details used if thread stack-checking is required */ #ifdef ATOM_STACK_CHECKING POINTER stack_bottom; /* Pointer to bottom of stack allocation */ diff --git a/kernel/atomevent.c b/kernel/atomevent.c new file mode 100644 index 00000000..f5f03df7 --- /dev/null +++ b/kernel/atomevent.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2015, Stefan Petersen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +/** + * \file + * Event library. + * + * + * This module implements event handling, which is a very simple way of + * waiting for events, for instance from a timer or an interrupt. + * + * \par Zero size cost + * The event register is stored in the TCB of every thread in atomThreads. + * Disadvantage is that every TCB will contain the event register whether + * it is used or not. There is also no queue to the event, therefore the + * signaller must know the TCB of the thread to call. + * + * \par No initialization before use + * Having the event register int the TCB means you don't have to allocate + * and initialize anything before using event. + * + * \par Interrupt-safe calls + * atomEventWait() for events generally blocks while waiting for + * atomEventSignal() to signal, but this can be controlled at call. + * atomEventSignal() to does not block at all. If the thread is not waiting + * for an event, the events signalled is stored in the event register of the + * TCB. + * + * \par Thread to thread signalling + * Signalling can only go to one task at a time, so several tasks can not lock + * on a particual event. + * + * \par Signalling events can not lock + * When event(s) are signalled with atomEventSignal() two things can happen. + * -# If the thread is not waiting for an event, the event mask will be stored + * in the threads event register + * -# If the thread is waiting for an event the thread will be put back on the + * ready queue and will be scheduled in as soon as its priority allows. + * Please note that when atomEventWait() returns it will clear the event + * register in the TCB. + * + * \par Events + * Events is a bitfields with implementation defined size and every + * bit in that word can signify an event. That makes it possible to wait for + * several events at the same time. + * Signalling of events is per thread, so the signaller must know the TCB of the + * thread to signal before hand (not usually a problem). + * + * \n Usage instructions: \n + * + * The size of this bitfield is implementation dependent and is defined in + * atomport.h. Since the only data that is needed is stored in the TCB it + * is not necessary to allocate any memory nor special initalizing. + * + * The TCB of a waiting process is removed from the ready queue, but then not + * stored in any new queue while waiting for the event. The only way to get + * the TCB back into the ready queue when signalling is to actually send the + * TCB in the signalling call. + * + */ + +#include "atomevent.h" + +/* Forward declarations */ + +static void atomEventTimerCallback (POINTER cb_data); + +/** + * \b atomEventSignal + * + * Signal events to a particual thread. + * + * Signals an event to a thread given by the TCB pointer waiting in + * an atomEventWait() with the same bits set in the event mask. + * If the thread is not waiting, then the events signalled will be stored + * int the event register of the TCB until the atomEventWait() is called. + * + * @param[in] curr_tcb_ptr Pointer to TCB which is to be signalled + * @param[in] events Bits to signal to thread. + * + * @retval ATOM_OK Success + * @retval ATOM_ERR_PARAM Bad parameter + * @retval ATOM_ERR_QUEUE Problem putting the thread back on the ready queue + */ +uint8_t atomEventSignal (ATOM_TCB *curr_tcb_ptr, ATOM_EVENTS events) +{ + uint8_t status; + CRITICAL_STORE; + + /* Check parameters */ + if (curr_tcb_ptr == NULL) + { + /* Bad TCB pointer */ + status = ATOM_ERR_PARAM; + } + else + { + /* Protect access to the tcb object and OS queues */ + CRITICAL_START (); + + if (curr_tcb_ptr->suspended) + { + if (curr_tcb_ptr->events & events) + { + /* Set the events to be signalled when waiting thread is to be + * scheduled in again. */ + curr_tcb_ptr->events = curr_tcb_ptr->events & events; + /* Put this thread on the ready queue */ + if (tcbEnqueuePriority (&tcbReadyQ, curr_tcb_ptr) != ATOM_OK) + { + /* Queue-related error */ + status = ATOM_ERR_QUEUE; + } + else + { + curr_tcb_ptr->suspended = FALSE; + status = curr_tcb_ptr->suspend_wake_status; /* FIX Check this */ + + /* If there's a timeout on this suspension, cancel it */ + if (curr_tcb_ptr->suspend_timo_cb) + { + /* Cancel the callback */ + if (atomTimerCancel (curr_tcb_ptr->suspend_timo_cb) != ATOM_OK) + { + /* Return timer error */ + status = ATOM_ERR_TIMER; + } + /* Flag has no timeout registered */ + curr_tcb_ptr->suspend_timo_cb = NULL; + } + } + } + else + { + /* No requested bits signalled which probably should be + considered an error in parameters. */ + status = ATOM_ERR_PARAM; + } + if (status == ATOM_OK) + { + if (atomCurrentContext()) + { + atomSched(FALSE); + } + } + } + else + { + /* TCB is not waiting for events, we mask in signalled events */ + curr_tcb_ptr->events = events; + status = ATOM_OK; + } + + /* Exit critical region */ + CRITICAL_END (); + } + return status; +} + + +/** + * \b atomEventWait + * + * Wait for events to be signalled. + * + * Depending on the \c timeout value specified the call will do one of + * the following: + * + * \c timeout == 0 : Call will block until it is signalled \n + * \c timeout > 0 : Call will block until available up to the specified timeout \n + * + * If the call needs to block and \c timeout is zero, it will block + * indefinitely until the someone will call atomEventSignal() with one + * or several of the event mask bits set. + * + * If the call needs to block and \c timeout is non-zero, the call will o1nly + * block for the specified number of system ticks after which time, if the + * thread was not already woken, the call will return with \c 0. + * + * WaitEvent clears the event memory after an event has occurred. + * WaitSingleEvent does not clear event memory. + * + * @param[in] event_mask Bits to signal to thread. + * @param[out] events Pointer to bits in the event that is signalled or + * zero if timed out. + * @param[in] timeout Max system ticks to block (0 = forever / -1 = non-blocking) + * + * @retval ATOM_OK Success + * @retval ATOM_TIMEOUT Event timed out before being woken + * @retval ATOM_WOULDBLOCK Called with timeout == -1 but no event awaits + * @retval ATOM_ERR_CONTEXT Not called in thread context and attempted to block + * @retval ATOM_ERR_PARAM Bad parameter + * @retval ATOM_ERR_QUEUE Problem putting the thread on the suspend queue + * @retval ATOM_ERR_TIMER Problem registering the timeout + */ +uint8_t atomEventWait (ATOM_EVENTS event_mask, ATOM_EVENTS *events, int32_t timeout) +{ + uint8_t status; + ATOM_TCB *curr_tcb_ptr; + ATOM_TIMER timer_cb; + CRITICAL_STORE; + + /* Get the current TCB */ + curr_tcb_ptr = atomCurrentContext(); + + if (event_mask == 0L) + { + status = ATOM_ERR_PARAM; + } + else + { + CRITICAL_START(); + /* If already event bits are set in the TCBs event register */ + if (event_mask & curr_tcb_ptr->events) + { + *events = curr_tcb_ptr->events & event_mask; + CRITICAL_END(); + status = ATOM_OK; + } + else + { + if (timeout < 0) + { + CRITICAL_END(); + *events = 0; + status = ATOM_WOULDBLOCK; + } + else + { + /* Remember the event mask we are waiting for */ + curr_tcb_ptr->events = event_mask; + /* Suspend ourselves */ + curr_tcb_ptr->suspended = TRUE; + curr_tcb_ptr->suspend_wake_status = ATOM_OK; + if (timeout) + { + /* Fill out the timer callback request structure */ + timer_cb.cb_func = atomEventTimerCallback; + timer_cb.cb_data = (POINTER)curr_tcb_ptr; + timer_cb.cb_ticks = timeout; + + /** + * Store the timer details in the TCB so that we can + * cancel the timer callback if a event is signalled + * before the timeout occurs. + */ + curr_tcb_ptr->suspend_timo_cb = &timer_cb; + + /* Register a callback on timeout */ + if (atomTimerRegister (&timer_cb) != ATOM_OK) + { + /* Timer registration failed */ + status = ATOM_ERR_TIMER; + + /* Clean up and return to the caller */ + curr_tcb_ptr->suspended = FALSE; + curr_tcb_ptr->suspend_timo_cb = NULL; + } + else + { + status = ATOM_OK; + } + } + + /* Set no timeout requested */ + else + { + /* No need to cancel timeouts on this one */ + curr_tcb_ptr->suspend_timo_cb = NULL; + status = ATOM_OK; + } + /** + * Only call the scheduler if we are in thread context, + * otherwise it will be called on exiting the ISR by + * atomIntExit(). + */ + if ((status == ATOM_OK) && atomCurrentContext()) + { + atomSched (FALSE); + /* Restore returned events, clears event memory and update + status after context switch */ + *events = curr_tcb_ptr->events; + curr_tcb_ptr->events = 0; + status = curr_tcb_ptr->suspend_wake_status; + } + else + { + status = ATOM_ERR_CONTEXT; + } + CRITICAL_END(); + } + } + } + return status; +} + + +/** + * \b atomEventTimerCallback + * + * This is an internal function not for use by application code. + * + * Timeouts on suspended threads are notified by the timer system through + * this generic callback. The timer system calls us back with a pointer to + * the relevant \c ATOM_TCB object which is used to retrieve the + * event details. + * + * @param[in] cb_data Pointer to a ATOM_TCB object + */ +static void atomEventTimerCallback (POINTER cb_data) +{ + ATOM_TCB *curr_tcb_ptr; + CRITICAL_STORE; + + /* Get the pointer */ + curr_tcb_ptr = (ATOM_TCB *)cb_data; + + /* Check parameter is valid */ + if (curr_tcb_ptr) + { + /* Enter critical region */ + CRITICAL_START (); + + /* Set status to indicate to the waiting thread that it timed out */ + curr_tcb_ptr->suspend_wake_status = ATOM_TIMEOUT; + + /* Flag as no timeout registered */ + curr_tcb_ptr->suspend_timo_cb = NULL; + + /* Put the thread on the ready queue */ + (void)tcbEnqueuePriority (&tcbReadyQ, curr_tcb_ptr); + + /* Exit critical region */ + CRITICAL_END (); + + /** + * Note that we don't call the scheduler now as it will be called + * when we exit the ISR by atomIntExit(). + */ + } +} diff --git a/kernel/atomevent.h b/kernel/atomevent.h new file mode 100644 index 00000000..b98cd1d5 --- /dev/null +++ b/kernel/atomevent.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, Stefan Petersen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __ATOM_EVENT_H +#define __ATOM_EVENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "atom.h" + +extern uint8_t atomEventSignal(ATOM_TCB *tcb, ATOM_EVENTS events); +extern uint8_t atomEventWait(ATOM_EVENTS event_mask, ATOM_EVENTS *events, + int32_t timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOM_EVENT_H */ diff --git a/ports/avr/Makefile b/ports/avr/Makefile index 6578ab15..718dd2d3 100644 --- a/ports/avr/Makefile +++ b/ports/avr/Makefile @@ -41,6 +41,7 @@ APP_ASM_OBJECTS = atomport-asm.o # Kernel object files KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o +KERNEL_OBJECTS += atomevent.o # Collection of built objects (excluding test applications) ALL_OBJECTS = $(APP_OBJECTS) $(APP_ASM_OBJECTS) $(KERNEL_OBJECTS) diff --git a/ports/avr/atomport.h b/ports/avr/atomport.h index 875bd159..4592610d 100644 --- a/ports/avr/atomport.h +++ b/ports/avr/atomport.h @@ -53,6 +53,13 @@ */ #define POINTER void * +/** + * Architecture-specific definition of atom event size. + * It is best selected as the size of the architecture, but can be + * reduced or increased depending on requirements. + */ +typedef uint8_t ATOM_EVENTS; + /** * Critical region protection: this should disable interrupts diff --git a/ports/cortex-m/atomport.h b/ports/cortex-m/atomport.h index d3337ea5..59fc2543 100644 --- a/ports/cortex-m/atomport.h +++ b/ports/cortex-m/atomport.h @@ -57,6 +57,13 @@ enum { assert_static__ = 1/(e) }; \ } while (0) +/** + * Architecture-specific definition of atom event size. + * It is best selected as the size of the architecture, but can be + * reduced or increased depending on requirements. + */ +typedef uint32_t ATOM_EVENTS; + /** * Critical region protection: this should disable interrupts * to protect OS data structures during modification. It must diff --git a/tests/event1.c b/tests/event1.c new file mode 100644 index 00000000..745f7c9e --- /dev/null +++ b/tests/event1.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2015, Stefan Petersen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "atom.h" +#include "atomtests.h" +#include "atomevent.h" + + +/* Number of test threads */ +#define NUM_TEST_THREADS 1 + + +/* Test OS objects */ +static ATOM_TCB tcb[NUM_TEST_THREADS]; +static uint8_t test_thread_stack[NUM_TEST_THREADS][TEST_THREAD_STACK_SIZE]; + + +/* Test result tracking */ +static volatile int g_result; + + +/* Forward declarations */ +static void test1_thread_func (uint32_t param); + + +/* Signaling event masks used */ +#define EXPECTED_MASK 0x03L +#define EXPECTING_MASK 0x01L + + +/** + * \b test_start + * + * Start test. + * + * @retval Number of failures + */ +uint32_t test_start (void) +{ + int failures; + + /* Default to zero failures */ + failures = 0; + + /* Run test and update "failures" count */ + 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 + { + /* Give the other thread some time to start */ + atomTimerDelay(20); + atomEventSignal(&tcb[0], EXPECTING_MASK); + if (g_result == 0) + { + failures++; + } + } + + /* If threads are created, check for thread stack overflow */ + + /* Quit */ + return failures; + +} + + +/** + * \b test1_thread_func + * + * Entry point for test thread 1. + * + * @param[in] param Unused (optional thread entry parameter) + * + * @return None + */ +static void test1_thread_func (uint32_t param) +{ + uint8_t status; + ATOM_EVENTS events; + int failures; + + /* Compiler warnings */ + param = param; + + /* Default to zero failures */ + failures = 0; + + /* Wait eternally for event to be signalled */ + status = atomEventWait(EXPECTING_MASK, &events, 0); + if (status != ATOM_OK) { + failures++; + } + + if (events == EXPECTING_MASK) { + g_result = 1; + } + + /* Wait forever */ + while (1) + { + atomTimerDelay (SYSTEM_TICKS_PER_SEC); + } +} diff --git a/tests/event2.c b/tests/event2.c new file mode 100644 index 00000000..d8e44693 --- /dev/null +++ b/tests/event2.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, Stefan Petersen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "atom.h" +#include "atomtests.h" +#include "atomevent.h" + + +/* Number of test threads */ +#define NUM_TEST_THREADS 1 + + +/* Test OS objects */ +static ATOM_TCB tcb[NUM_TEST_THREADS]; +static uint8_t test_thread_stack[NUM_TEST_THREADS][TEST_THREAD_STACK_SIZE]; + + +/* Test result tracking */ +static volatile int g_result; + +/* Number of tested single events */ +#define NUMBER_OF_EVENTS (sizeof(ATOM_EVENTS) * 8) + +/* Forward declarations */ +static void test1_thread_func (uint32_t param); + + +/** + * \b test_start + * + * Start test. + * + * @retval Number of failures + */ +uint32_t test_start (void) +{ + uint8_t status; + int failures; + int test; + + /* Default to zero failures */ + failures = 0; + + /* Run test and update "failures" count */ + 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 + { + /* Give the other thread some time to start */ + atomTimerDelay(20); + /* Send some events to see if we can do this repeatedly */ + for (test = 0; test < NUMBER_OF_EVENTS; test++) { + status = atomEventSignal(&tcb[0], (1L << test)); + if (status != ATOM_OK) { + failures++; + } + atomTimerDelay(10); + } + + if (g_result != 1) + { + failures++; + } + } + + /* If threads are created, check for thread stack overflow */ + + /* Quit */ + return failures; +} + + +/** + * \b test1_thread_func + * + * Entry point for test thread 1. + * + * @param[in] param Unused (optional thread entry parameter) + * + * @return None + */ +static void test1_thread_func (uint32_t param) +{ + uint8_t status; + ATOM_EVENTS events; + int failures; + int test; + + /* Compiler warnings */ + param = param; + + /* Default to zero failures */ + failures = 0; + g_result = 0; + + /* Wait eternally for event to be signalled */ + for (test = 0; test < NUMBER_OF_EVENTS; test++) { + status = atomEventWait((1L << test), &events, 0); + if (status != ATOM_OK) { + failures++; + } + if (events != (1L << test)) { + failures++; + } + atomTimerDelay(10); + g_result++; + } + + if ((g_result == NUMBER_OF_EVENTS) && (failures == 0)) { + g_result = 1; + } else { + g_result = 0; + } + + /* Wait forever */ + while (1) + { + atomTimerDelay (SYSTEM_TICKS_PER_SEC); + } +} diff --git a/tests/event3.c b/tests/event3.c new file mode 100644 index 00000000..9b9407d5 --- /dev/null +++ b/tests/event3.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2015, Stefan Petersen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. No personal names or organizations' names associated with the + * Atomthreads project may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "atom.h" +#include "atomtests.h" +#include "atomevent.h" + + +/* Number of test threads */ +#define NUM_TEST_THREADS 1 + + +/* Test OS objects */ +static ATOM_TCB tcb[NUM_TEST_THREADS]; +static uint8_t test_thread_stack[NUM_TEST_THREADS][TEST_THREAD_STACK_SIZE]; + + +/* Test result tracking */ +static volatile int g_result; + +/* Forward declarations */ +static void test1_thread_func (uint32_t param); + + +/** + * \b test_start + * + * Start test. + * + * @retval Number of failures + */ +uint32_t test_start (void) +{ + uint8_t status; + int failures; + + /* Default to zero failures */ + failures = 0; + + /* Run test and update "failures" count */ + 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 + { + /* Give the other thread some time to start */ + atomTimerDelay(20); + /* Send some events to see if we can do this repeatedly */ + status = atomEventSignal(&tcb[0], 0x02); + if (status != ATOM_OK) { + failures++; + } + + if (g_result != 1) + { + failures++; + } + } + + /* If threads are created, check for thread stack overflow */ + + /* Quit */ + return failures; +} + + +/** + * \b test1_thread_func + * + * Entry point for test thread 1. + * + * @param[in] param Unused (optional thread entry parameter) + * + * @return None + */ +static void test1_thread_func (uint32_t param) +{ + ATOM_EVENTS events; + int failures; + + /* Compiler warnings */ + param = param; + + /* Default to zero failures */ + failures = 0; + g_result = 0; + + /* Just check without locking and no signal */ + if (atomEventWait(1, &events, -1) != ATOM_WOULDBLOCK) { + failures++; + } + /* Check that the event register is cleared after each atomEventWait() */ + if (tcb[0].events != 0) { + failures++; + } + + /* Just check with timeout and no signal */ + if (atomEventWait(1, &events, 10) != ATOM_TIMEOUT) { + failures++; + } + /* Check that the event register is cleared after each atomEventWait() */ + if (tcb[0].events != 0) { + failures++; + } + + /* Wait timed for event to actually be signalled */ + if (atomEventWait(0x02, &events, 15) != ATOM_OK) { + failures++; + } + if (events != 0x02) { + failures++; + } + /* Check that the event register is cleared after each atomEventWait() */ + if (tcb[0].events != 0) { + failures++; + } + + if (failures == 0) { + g_result = 1; + } else { + g_result = 0; + } + + /* Wait forever */ + while (1) + { + atomTimerDelay (SYSTEM_TICKS_PER_SEC); + } +} From e766690317ad49e689effe4f9f51777d18c16ea9 Mon Sep 17 00:00:00 2001 From: Stefan Petersen Date: Thu, 2 Jul 2015 23:36:51 +0200 Subject: [PATCH 2/6] Introduces changes to other ports to support events. Completely untested. This introduces changes in some ports to support the events. The ATOM_EVENTS type is added in atomports.h and the compilation of kernel/atomevent.c is added to the Makefiles. --- ports/arm/atomport.h | 7 +++++++ ports/arm/platforms/dm36x/Makefile | 1 + ports/arm/platforms/qemu_integratorcp/Makefile | 1 + ports/armv7a/Makefile | 1 + ports/armv7a/atomport.h | 7 +++++++ ports/mips/Makefile | 1 + ports/mips/atomport.h | 6 ++++++ ports/stm8/atomport.h | 6 ++++++ ports/stm8/cosmic.mak | 1 + ports/stm8/iar.mak | 1 + ports/stm8/raisonance.mak | 1 + 11 files changed, 33 insertions(+) diff --git a/ports/arm/atomport.h b/ports/arm/atomport.h index a2286153..c999ae29 100644 --- a/ports/arm/atomport.h +++ b/ports/arm/atomport.h @@ -52,6 +52,13 @@ */ #define POINTER void * +/** + * Architecture-specific definition of atom event size. + * It is best selected as the size of the architecture, but can be + * reduced or increased depending on requirements. + */ +typedef uint32_t ATOM_EVENTS; + /** * Hardware timer functions (optional, not available on all ports) */ diff --git a/ports/arm/platforms/dm36x/Makefile b/ports/arm/platforms/dm36x/Makefile index 39cc7dbb..497fa5d2 100644 --- a/ports/arm/platforms/dm36x/Makefile +++ b/ports/arm/platforms/dm36x/Makefile @@ -51,6 +51,7 @@ PORT_ASM_OBJECTS = atomport-asm.o # Kernel object files KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o +KERNEL_OBJECTS += atomevent.o # Collection of built objects (excluding test applications) ALL_OBJECTS = $(PLATFORM_OBJECTS) $(PLATFORM_ASM_OBJECTS) $(PORT_OBJECTS) $(PORT_ASM_OBJECTS) $(KERNEL_OBJECTS) diff --git a/ports/arm/platforms/qemu_integratorcp/Makefile b/ports/arm/platforms/qemu_integratorcp/Makefile index d60c4b85..dcf7d8cb 100644 --- a/ports/arm/platforms/qemu_integratorcp/Makefile +++ b/ports/arm/platforms/qemu_integratorcp/Makefile @@ -36,6 +36,7 @@ PORT_ASM_OBJECTS = atomport-asm.o # Kernel object files KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o +KERNEL_OBJECTS += atomevent.o # Collection of built objects (excluding test applications) ALL_OBJECTS = $(PLATFORM_OBJECTS) $(PLATFORM_ASM_OBJECTS) $(PORT_OBJECTS) $(PORT_ASM_OBJECTS) $(KERNEL_OBJECTS) diff --git a/ports/armv7a/Makefile b/ports/armv7a/Makefile index b95991f1..8560e1b1 100644 --- a/ports/armv7a/Makefile +++ b/ports/armv7a/Makefile @@ -71,6 +71,7 @@ objs += atomsem.o objs += atommutex.o objs += atomtimer.o objs += atomqueue.o +objs += atomevent.o # Collection of built objects (excluding test applications) build_objs = $(foreach obj,$(objs),$(build_dir)/$(obj)) diff --git a/ports/armv7a/atomport.h b/ports/armv7a/atomport.h index 0730e216..ff5ce3ee 100644 --- a/ports/armv7a/atomport.h +++ b/ports/armv7a/atomport.h @@ -59,6 +59,13 @@ #define POINTER void * #define UINT32 uint32_t +/** + * Architecture-specific definition of atom event size. + * It is best selected as the size of the architecture, but can be + * reduced or increased depending on requirements. + */ +typedef uint32_t ATOM_EVENTS; + /** * Critical region protection: this should disable interrupts * to protect OS data structures during modification. It must diff --git a/ports/mips/Makefile b/ports/mips/Makefile index 2254250a..6a107036 100644 --- a/ports/mips/Makefile +++ b/ports/mips/Makefile @@ -45,6 +45,7 @@ APP_ASM_OBJECTS = atomport-entry.o atomport-asm.o # Kernel object files KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o +KERNEL_OBJECTS += atomevent.o # Collection of built objects (excluding test applications) ALL_OBJECTS = $(APP_ASM_OBJECTS) $(APP_OBJECTS) $(KERNEL_OBJECTS) diff --git a/ports/mips/atomport.h b/ports/mips/atomport.h index 6145df14..4041443a 100644 --- a/ports/mips/atomport.h +++ b/ports/mips/atomport.h @@ -58,6 +58,12 @@ #define POINTER void * #define UINT32 uint32_t +/** + * Architecture-specific definition of atom event size. + * It is best selected as the size of the architecture, but can be + * reduced or increased depending on requirements. + */ +typedef uint32_t ATOM_EVENTS; /** * Critical region protection: this should disable interrupts diff --git a/ports/stm8/atomport.h b/ports/stm8/atomport.h index 2063bfb3..4d170e24 100644 --- a/ports/stm8/atomport.h +++ b/ports/stm8/atomport.h @@ -59,6 +59,12 @@ #define uint32_t u32 #define POINTER void * +/** + * Architecture-specific definition of atom event size. + * It is best selected as the size of the architecture, but can be + * reduced or increased depending on requirements. + */ +typedef uint8_t ATOM_EVENTS; /** * Critical region protection: this should disable interrupts diff --git a/ports/stm8/cosmic.mak b/ports/stm8/cosmic.mak index 56e148d9..1b50149b 100644 --- a/ports/stm8/cosmic.mak +++ b/ports/stm8/cosmic.mak @@ -38,6 +38,7 @@ PERIPH_OBJECTS = stm8s_gpio.o stm8s_tim1.o stm8s_clk.o stm8s_uart2.o # Kernel object files KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o +KERNEL_OBJECTS += atomevent.o # Collection of built objects (excluding test applications) ALL_OBJECTS = $(APP_OBJECTS) $(APP_ASM_OBJECTS) $(PERIPH_OBJECTS) $(KERNEL_OBJECTS) diff --git a/ports/stm8/iar.mak b/ports/stm8/iar.mak index ae1f7fd8..bb0d9407 100644 --- a/ports/stm8/iar.mak +++ b/ports/stm8/iar.mak @@ -38,6 +38,7 @@ PERIPH_OBJECTS = stm8s_gpio.o stm8s_tim1.o stm8s_clk.o stm8s_uart2.o # Kernel object files KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o +KERNEL_OBJECTS += atomevent.o # Collection of built objects (excluding test applications) ALL_OBJECTS = $(APP_OBJECTS) $(APP_ASM_OBJECTS) $(PERIPH_OBJECTS) $(KERNEL_OBJECTS) diff --git a/ports/stm8/raisonance.mak b/ports/stm8/raisonance.mak index 96ad9d0d..c161f50d 100644 --- a/ports/stm8/raisonance.mak +++ b/ports/stm8/raisonance.mak @@ -38,6 +38,7 @@ PERIPH_OBJECTS = stm8s_gpio.o stm8s_tim1.o stm8s_clk.o stm8s_uart2.o # Kernel object files KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o +KERNEL_OBJECTS += atomevent.o # Collection of built objects (excluding test applications) ALL_OBJECTS = $(APP_OBJECTS) $(APP_ASM_OBJECTS) $(PERIPH_OBJECTS) $(KERNEL_OBJECTS) From 6ceb49fb837d75a903c543a43f2a9ee028483ce1 Mon Sep 17 00:00:00 2001 From: Stefan Petersen Date: Thu, 16 Jul 2015 22:54:33 +0200 Subject: [PATCH 3/6] Moved around CRITICAL_START/END to make scheduling in atomevent to work. --- kernel/atomevent.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/kernel/atomevent.c b/kernel/atomevent.c index f5f03df7..cdfb092e 100644 --- a/kernel/atomevent.c +++ b/kernel/atomevent.c @@ -163,6 +163,8 @@ uint8_t atomEventSignal (ATOM_TCB *curr_tcb_ptr, ATOM_EVENTS events) considered an error in parameters. */ status = ATOM_ERR_PARAM; } + /* Exit critical region */ + CRITICAL_END (); if (status == ATOM_OK) { if (atomCurrentContext()) @@ -175,11 +177,11 @@ uint8_t atomEventSignal (ATOM_TCB *curr_tcb_ptr, ATOM_EVENTS events) { /* TCB is not waiting for events, we mask in signalled events */ curr_tcb_ptr->events = events; + /* Exit critical region */ + CRITICAL_END (); status = ATOM_OK; } - /* Exit critical region */ - CRITICAL_END (); } return status; } @@ -301,7 +303,8 @@ uint8_t atomEventWait (ATOM_EVENTS event_mask, ATOM_EVENTS *events, int32_t time * otherwise it will be called on exiting the ISR by * atomIntExit(). */ - if ((status == ATOM_OK) && atomCurrentContext()) + CRITICAL_END (); + if ((status == ATOM_OK) && atomCurrentContext()) { atomSched (FALSE); /* Restore returned events, clears event memory and update @@ -314,7 +317,6 @@ uint8_t atomEventWait (ATOM_EVENTS event_mask, ATOM_EVENTS *events, int32_t time { status = ATOM_ERR_CONTEXT; } - CRITICAL_END(); } } } From f47d87faf6f1752def26eec576530994b6d413d2 Mon Sep 17 00:00:00 2001 From: Stefan Petersen Date: Thu, 16 Jul 2015 22:57:53 +0200 Subject: [PATCH 4/6] Consistency fixups in event library. --- kernel/atomevent.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/atomevent.c b/kernel/atomevent.c index cdfb092e..11291302 100644 --- a/kernel/atomevent.c +++ b/kernel/atomevent.c @@ -238,19 +238,19 @@ uint8_t atomEventWait (ATOM_EVENTS event_mask, ATOM_EVENTS *events, int32_t time } else { - CRITICAL_START(); + CRITICAL_START (); /* If already event bits are set in the TCBs event register */ if (event_mask & curr_tcb_ptr->events) { *events = curr_tcb_ptr->events & event_mask; - CRITICAL_END(); + CRITICAL_END (); status = ATOM_OK; } else { if (timeout < 0) { - CRITICAL_END(); + CRITICAL_END (); *events = 0; status = ATOM_WOULDBLOCK; } From 24b512a3aa625f0fda90f569183d7a48416ff29c Mon Sep 17 00:00:00 2001 From: Stefan Petersen Date: Tue, 5 Jan 2016 14:25:46 +0100 Subject: [PATCH 5/6] Improved doxygen comments for atomevents slightly. --- kernel/atomevent.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/kernel/atomevent.c b/kernel/atomevent.c index 11291302..dd2afed2 100644 --- a/kernel/atomevent.c +++ b/kernel/atomevent.c @@ -43,35 +43,35 @@ * signaller must know the TCB of the thread to call. * * \par No initialization before use - * Having the event register int the TCB means you don't have to allocate - * and initialize anything before using event. + * Having the event register in the TCB means you don't have to allocate + * and initialize anything before using event(s). * * \par Interrupt-safe calls - * atomEventWait() for events generally blocks while waiting for - * atomEventSignal() to signal, but this can be controlled at call. - * atomEventSignal() to does not block at all. If the thread is not waiting - * for an event, the events signalled is stored in the event register of the - * TCB. + * atomEventWait() generally blocks while waiting for atomEventSignal() to + * signal, but this can be controlled when calling the function. + * atomEventSignal() does not block at all. If the thread is not + * waiting for an event, the events signalled are stored in the event register + * of the TCB. * * \par Thread to thread signalling - * Signalling can only go to one task at a time, so several tasks can not lock + * Signals can only go to one task at a time, so several tasks can not lock * on a particual event. * * \par Signalling events can not lock - * When event(s) are signalled with atomEventSignal() two things can happen. + * When event(s) are signalled with atomEventSignal() two things can happen: * -# If the thread is not waiting for an event, the event mask will be stored * in the threads event register - * -# If the thread is waiting for an event the thread will be put back on the + * -# If the thread is waiting for an event, the thread will be put back on the * ready queue and will be scheduled in as soon as its priority allows. * Please note that when atomEventWait() returns it will clear the event * register in the TCB. * * \par Events - * Events is a bitfields with implementation defined size and every + * Events is a bitfield with implementation defined size and every * bit in that word can signify an event. That makes it possible to wait for * several events at the same time. * Signalling of events is per thread, so the signaller must know the TCB of the - * thread to signal before hand (not usually a problem). + * thread to signal before hand (usually not a problem). * * \n Usage instructions: \n * @@ -100,7 +100,7 @@ static void atomEventTimerCallback (POINTER cb_data); * Signals an event to a thread given by the TCB pointer waiting in * an atomEventWait() with the same bits set in the event mask. * If the thread is not waiting, then the events signalled will be stored - * int the event register of the TCB until the atomEventWait() is called. + * in the event register of the TCB until the atomEventWait() is called. * * @param[in] curr_tcb_ptr Pointer to TCB which is to be signalled * @param[in] events Bits to signal to thread. @@ -192,19 +192,21 @@ uint8_t atomEventSignal (ATOM_TCB *curr_tcb_ptr, ATOM_EVENTS events) * * Wait for events to be signalled. * - * Depending on the \c timeout value specified the call will do one of + * Depending on the \c timeout value specified, the call will do one of * the following: + * - If \c timeout == 0 : Call will block until it is signalled \n + * - If \c timeout > 0 : Call will block until available up to the specified timeout \n * - * \c timeout == 0 : Call will block until it is signalled \n - * \c timeout > 0 : Call will block until available up to the specified timeout \n * * If the call needs to block and \c timeout is zero, it will block - * indefinitely until the someone will call atomEventSignal() with one + * indefinitely until someone will call atomEventSignal() with one * or several of the event mask bits set. * - * If the call needs to block and \c timeout is non-zero, the call will o1nly - * block for the specified number of system ticks after which time, if the - * thread was not already woken, the call will return with \c 0. + * If the call needs to block and \c timeout is positive, the call will only + * block for the specified number of system ticks after which, if the + * thread was not already woken, the call will return with \c ATOM_TIMEOUT. + * If the call needs to block and \c timeout is -1, the function will return + * with \c ATOM_WOULDBLOCK. * * WaitEvent clears the event memory after an event has occurred. * WaitSingleEvent does not clear event memory. From eb2f5ea2f16ca82e022f03ce061d407794196ff7 Mon Sep 17 00:00:00 2001 From: Stefan Petersen Date: Tue, 19 Apr 2016 18:10:42 +0200 Subject: [PATCH 6/6] Include event impl. in cortex-m port when building for test. --- ports/cortex-m/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/cortex-m/Makefile b/ports/cortex-m/Makefile index 4ba03e92..93a2334d 100644 --- a/ports/cortex-m/Makefile +++ b/ports/cortex-m/Makefile @@ -98,6 +98,7 @@ objs += atomsem.o objs += atommutex.o objs += atomtimer.o objs += atomqueue.o +objs += atomevent.o # Collection of built objects (excluding test applications) build_objs = $(foreach obj,$(objs),$(build_dir)/$(obj))