diff --git a/Makefile b/Makefile index 55a19c0c2da48fec3be2ffdd4547a533c0cd8d36..4af847029ee7b2667fb9c14747e3825eb1196688 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ SUBDIRS := miosix ## List here your source files (both .s, .c and .cpp) ## SRC := \ -testsuite.cpp +main.cpp ## ## List here additional static libraries with relative path diff --git a/Readme.txt b/Readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..ce3f37b6466d8ef7ecba7544a81ef0ba43089116 --- /dev/null +++ b/Readme.txt @@ -0,0 +1,9 @@ + +Welcome to the Miosix kernel +============================ + +You can find information on how to configure and use the kernel +at the following url: http://www.webalice.it/fede.tft/miosix + +The testsuite has been moved in the miosix/testsuite directory. +The examples have been moved in the miosix/examples directory. diff --git a/main.cpp b/main.cpp index 6bb0660a6eafb6fca7389f0156f7395a0e918788..8764b9fabb7ead786e8a9eae3d8bd03873dc6ec4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,5 @@ #include <cstdio> -#include <cstdlib> -#include <vector> #include "miosix.h" using namespace std; diff --git a/miosix/doc/textdoc/Changelog.txt b/miosix/doc/textdoc/Changelog.txt index 2726089217157df4fabf21ec578448d5b21e7e18..66c9034d4b9201535d1d9f26e554cbd82d37bc4a 100644 --- a/miosix/doc/textdoc/Changelog.txt +++ b/miosix/doc/textdoc/Changelog.txt @@ -1,6 +1,12 @@ Changelog for Miosix np embedded OS v1.59 +- Added test to testsuite for BufferQueue +- Refactored top level directory, now the testsuite and examples have + been moved in their directories within the miosix directory +- Improved Queue class +- Added class BufferQueue to simplify the development of double buffered + data transfers between threads and interrupt routines - Some const-ness fixes in util - Extended boot improvements to strive mini board - Forgot to add util/crc16.cpp to the makefile buid rules diff --git a/miosix/examples/thread_native/Readme.txt b/miosix/examples/thread_native/Readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..9f0e03314e4a4f29d91b5b929a8f7582fbfd5260 --- /dev/null +++ b/miosix/examples/thread_native/Readme.txt @@ -0,0 +1,11 @@ + +To run this example, copy the content of this directory into +the top level directory, and modify the Makefile from + +SRC := \ +main.cpp + +to + +SRC := \ +native_thread_example.cpp \ No newline at end of file diff --git a/native_thread_example.cpp b/miosix/examples/thread_native/native_thread_example.cpp similarity index 100% rename from native_thread_example.cpp rename to miosix/examples/thread_native/native_thread_example.cpp diff --git a/miosix/examples/thread_pthread/Readme.txt b/miosix/examples/thread_pthread/Readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..870fa05dd5600020a211b7ee072872745f3789bf --- /dev/null +++ b/miosix/examples/thread_pthread/Readme.txt @@ -0,0 +1,11 @@ + +To run this example, copy the content of this directory into +the top level directory, and modify the Makefile from + +SRC := \ +main.cpp + +to + +SRC := \ +pthread_example.cpp \ No newline at end of file diff --git a/pthread_example.cpp b/miosix/examples/thread_pthread/pthread_example.cpp similarity index 100% rename from pthread_example.cpp rename to miosix/examples/thread_pthread/pthread_example.cpp diff --git a/miosix/kernel/buffer_queue.h b/miosix/kernel/buffer_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..4c94f33d06a6ce5b65ffb124d946efbc92abd1e5 --- /dev/null +++ b/miosix/kernel/buffer_queue.h @@ -0,0 +1,182 @@ +/*************************************************************************** + * Copyright (C) 2011 by Terraneo Federico * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * As a special exception, if other files instantiate templates or use * + * macros or inline functions from this file, or you compile this file * + * and link it with other works to produce a work based on this file, * + * this file does not by itself cause the resulting work to be covered * + * by the GNU General Public License. However the source code for this * + * file must still be made available in accordance with the GNU General * + * Public License. This exception does not invalidate any other reasons * + * why a work based on this file might be covered by the GNU General * + * Public License. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see <http://www.gnu.org/licenses/> * + ***************************************************************************/ + +#include "kernel.h" +#include "error.h" + +#ifndef BUFFER_QUEUE_H +#define BUFFER_QUEUE_H + +namespace miosix { + +/** + * \addtogroup Sync + * \{ + */ + +/** + * A class to handle double buffering, but also triple buffering and in general + * N-buffering. Works between two threads but is especially suited to + * synchronize between a thread and an interrupt routine.<br> + * Note that unlike Queue, this class is only a data structure and not a + * synchronization primitive. The synchronization between the thread and + * the IRQ (or the other thread) must be done by the caller. Therefore, to + * avoid race conditions all the member function are on purpose marked IRQ + * meaning that can only be called with interrupt disabled or within an IRQ.<br> + * The internal implementation treats the buffers as a circular queue of N + * elements, hence the name. + * \tparam T type of elements of the buffer, usually char or unsigned char + * \tparam size maximum size of a buffer + * \tparam numbuf number of buffers, the default is two resulting in a + * double buffering scheme. Values 0 and 1 are forbidden + */ +template<typename T, unsigned int size, unsigned char numbuf=2> +class BufferQueue +{ +public: + /** + * Constructor, all buffers are empty + */ + BufferQueue() : put(0), get(0), cnt(0) {} + + ///Maximum size of a buffer + static const unsigned int bufferMaxSize=size; + + ///Number of buffers available + static const unsigned char numberOfBuffers=numbuf; + + /** + * \return true if no buffer is available for reading + */ + bool IRQisEmpty() const { return cnt==0; } + + /** + * \return true if no buffer is available for writing + */ + bool IRQisFull() const { return cnt==numbuf; } + + /** + * This member function allows to retrieve a buffer ready to be written, + * if available. + * \param buffer the available buffer will be assigned here if available + * \return true if a writable buffer has been found, false otherwise. + * In this case the buffer parameter is not modified + */ + bool IRQgetWritableBuffer(T *&buffer) + { + if(cnt==numbuf) return false; + buffer=buf[put]; + return true; + } + + /** + * After having called getWritableBuffer() or IRQgetWritableBuffer() to + * retrieve a buffer and having filled it, this member function allows + * to mark the buffer as available on the reader side. + * \param actualSize actual size of buffer. It usually equals bufferMaxSize + * but can be a lower value in case there is less available data + */ + void IRQbufferFilled(unsigned int actualSize) + { + if(++cnt>numbuf) errorHandler(UNEXPECTED); + bufSize[put]=actualSize; + put++; + put%=numbuf; + } + + /** + * \return The number of buffers available for writing (0 to numbuf) + */ + unsigned char IRQavailableForWriting() const { return numbuf-cnt; } + + /** + * This member function allows to retrieve a buffer ready to be read, + * if available. + * \param buffer the available buffer will be assigned here if available + * \param actualSize the actual size of the buffer, as reported by the + * writer side + * \return true if a readable buffer has been found, false otherwise. + * In this case the buffer and actualSize parameters are not modified + */ + bool IRQgetReadableBuffer(const T *&buffer, unsigned int& actualSize) + { + if(cnt==0) return false; + buffer=buf[get]; + actualSize=bufSize[get]; + return true; + } + + /** + * After having called getReadableBuffer() or IRQgetReadableBuffer() to + * retrieve a buffer and having read it, this member function allows + * to mark the buffer as available on the writer side. + */ + void IRQbufferEmptied() + { + if(--cnt<0) errorHandler(UNEXPECTED); + get++; + get%=numbuf; + } + + /** + * \return The number of buffers available for reading (0, to numbuf) + */ + unsigned char IRQavailableForReading() const { return cnt; } + + /** + * Reset the buffers. As a consequence, the queue becomes empty. + */ + void IRQreset() + { + put=get=cnt=0; + } + +private: + //Unwanted methods + BufferQueue(const BufferQueue&); + BufferQueue& operator=(const BufferQueue&); + + T buf[numbuf][size]; // The buffers + unsigned int bufSize[numbuf]; //To handle partially empty buffers + unsigned char put; //Put pointer, either 0 or 1 + unsigned char get; //Get pointer, either 0 or 1 + volatile unsigned char cnt; //Number of filled buffers, either (0 to numbuf) +}; + +//These two partial specialization are meant to produce compiler errors in case +//an attempt is made to allocate a BufferQueue with zero or one buffer, as it +//is forbidden +template<typename T, unsigned int size> class BufferQueue<T,size,0> {}; +template<typename T, unsigned int size> class BufferQueue<T,size,1> {}; + +/** + * \} + */ + +} //namespace miosix + +#endif //BUFFER_QUEUE_H diff --git a/miosix/kernel/sync.h b/miosix/kernel/sync.h index fe96991c491cecf78166bb0fb445a303c1a37c00..1f484c14cc94f6e91e726e92fe0f4a48871cc860 100644 --- a/miosix/kernel/sync.h +++ b/miosix/kernel/sync.h @@ -510,7 +510,7 @@ private: * queue with a waiting thread, and to avoid situations where a thread tries to * access a deleted queue. * \tparam T the type of elements in the queue - * \tparam len the length of the Queue + * \tparam len the length of the Queue. Value 0 is forbidden */ template <typename T, unsigned int len> class Queue @@ -519,27 +519,20 @@ public: /** * Constructor, create a new empty queue. */ - Queue() - { - put_pos=get_pos=num_elem=0; - waiting=NULL; - } + Queue() : waiting(0), num_elem(0), put_pos(0), get_pos(0) {} + + ///Size of the queue + static const unsigned int queueSize=len; /** * \return true if the queue is empty */ - bool isEmpty() const - { - return (put_pos==get_pos)&&(num_elem==0); - } + bool isEmpty() const { return num_elem==0; } /** * \return true if the queue is full */ - bool isFull() const - { - return (put_pos==get_pos)&&(num_elem!=0); - } + bool isFull() const { return num_elem==len; } /** * If a queue is empty, waits until the queue is not empty. @@ -766,6 +759,10 @@ private: volatile unsigned int get_pos; ///< index of buffer where to put next element }; +//This partial specialization is meant to to produce compiler errors in case an +//attempt is made to instantiate a Queue with zero size, as it is forbidden +template<typename T> class Queue<T,0> {}; + /** * \} */ diff --git a/miosix/testsuite/Readme.txt b/miosix/testsuite/Readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca5e8a5c5389d936b063d69a28845e9298a67423 --- /dev/null +++ b/miosix/testsuite/Readme.txt @@ -0,0 +1,11 @@ + +To run the testsuite, copy the content of this directory into +the top level directory, and modify the Makefile from + +SRC := \ +main.cpp + +to + +SRC := \ +testsuite.cpp \ No newline at end of file diff --git a/testsuite.cpp b/miosix/testsuite/testsuite.cpp similarity index 93% rename from testsuite.cpp rename to miosix/testsuite/testsuite.cpp index 2eff786fa9d2d1f5f246891f2f76333fadaea383..5075d55f8dfe52964417a4822ab2934c63e5d0b7 100644 --- a/testsuite.cpp +++ b/miosix/testsuite/testsuite.cpp @@ -41,6 +41,7 @@ #include <errno.h> #include "miosix.h" +#include "miosix/kernel/buffer_queue.h" #include "config/miosix_settings.h" #include "interfaces/console.h" #include "board_settings.h" @@ -79,6 +80,7 @@ static void test_15(); static void test_16(); static void test_17(); static void test_18(); +static void test_19(); //Filesystem test functions #ifdef WITH_FILESYSTEM static void fs_test_1(); @@ -136,6 +138,7 @@ int main() test_16(); test_17(); test_18(); + test_19(); ledOff(); Thread::sleep(500);//Ensure all threads are deleted. @@ -2330,6 +2333,196 @@ static void test_18() pass(); } +// +// Test 19 +// +/* +tests: +class BufferQueue +*/ + +BufferQueue<char,10,3> bq; +Thread *t19_v1; + +static const char b1c[]="b1c----"; +static const char b2c[]="b2c----x"; +static const char b3c[]="b3c----xx"; +static const char b4c[]=""; + +static char *IRQgbw(FastInterruptDisableLock& dLock) +{ + char *buffer=0; + if(bq.IRQgetWritableBuffer(buffer)==false) + { + FastInterruptEnableLock eLock(dLock); + fail("BufferQueue::get"); + } + return buffer; +} + +static void gbr(const char *&buffer, unsigned int& size) +{ + FastInterruptDisableLock dLock; + while(bq.IRQgetReadableBuffer(buffer,size)==false) + { + Thread::IRQwait(); + { + FastInterruptEnableLock eLock(dLock); + Thread::yield(); + } + } +} + +static void be() +{ + FastInterruptDisableLock dLock; + bq.IRQbufferEmptied(); +} + +static void t19_p1(void *argv) +{ + Thread::sleep(50); + { + FastInterruptDisableLock dLock; + char *buffer=IRQgbw(dLock); + strcpy(buffer,b1c); + bq.IRQbufferFilled(strlen(b1c)); + t19_v1->IRQwakeup(); + { + FastInterruptEnableLock eLock(dLock); + Thread::sleep(10); + } + buffer=IRQgbw(dLock); + strcpy(buffer,b2c); + bq.IRQbufferFilled(strlen(b2c)); + t19_v1->IRQwakeup(); + { + FastInterruptEnableLock eLock(dLock); + Thread::sleep(10); + } + buffer=IRQgbw(dLock); + strcpy(buffer,b3c); + bq.IRQbufferFilled(strlen(b3c)); + t19_v1->IRQwakeup(); + { + FastInterruptEnableLock eLock(dLock); + Thread::sleep(10); + } + buffer=IRQgbw(dLock); + strcpy(buffer,b4c); + bq.IRQbufferFilled(strlen(b4c)); + t19_v1->IRQwakeup(); + } +} + +static void test_19() +{ + test_name("BufferQueue"); + if(bq.bufferMaxSize!=10) fail("bufferMaxSize"); + if(bq.numberOfBuffers!=3) fail("numberOfBuffers"); + //NOTE: in theory we should disable interrupts before calling these, but + //since we are accessing it from one thread only, for now it isn't required + if(bq.IRQisEmpty()==false) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + + //Test filling only one slot + char *buf=0; + const char *buffer=0; + unsigned int size; + if(bq.IRQgetReadableBuffer(buffer,size)==true) fail("IRQgetReadableBuffer"); + if(bq.IRQgetWritableBuffer(buf)==false) fail("IRQgetWritableBuffer"); + const char b1a[]="b1a"; + strcpy(buf,b1a); + bq.IRQbufferFilled(strlen(b1a)); + buf=0; + if(bq.IRQisEmpty()==true) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + if(bq.IRQgetReadableBuffer(buffer,size)==false) fail("IRQgetReadableBuffer"); + if(size!=strlen(b1a)) fail("returned size"); + if(strcmp(buffer,b1a)!=0) fail("returned buffer"); + bq.IRQbufferEmptied(); + if(bq.IRQisEmpty()==false) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + if(bq.IRQgetReadableBuffer(buffer,size)==true) fail("IRQgetReadableBuffer"); + if(bq.IRQgetWritableBuffer(buf)==false) fail("IRQgetWritableBuffer"); + + //Test filling all three slots + const char b1b[]="b1b0"; + strcpy(buf,b1b); + bq.IRQbufferFilled(strlen(b1b)); + buf=0; + if(bq.IRQisEmpty()==true) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + if(bq.IRQgetWritableBuffer(buf)==false) fail("IRQgetWritableBuffer"); + const char b2b[]="b2b01"; + strcpy(buf,b2b); + bq.IRQbufferFilled(strlen(b2b)); + buf=0; + if(bq.IRQisEmpty()==true) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + if(bq.IRQgetWritableBuffer(buf)==false) fail("IRQgetWritableBuffer"); + const char b3b[]="b2b012"; + strcpy(buf,b3b); + bq.IRQbufferFilled(strlen(b3b)); + buf=0; + if(bq.IRQisEmpty()==true) fail("IRQisEmpty"); + if(bq.IRQisFull()==false) fail("IRQisFull"); + if(bq.IRQgetWritableBuffer(buf)==true) fail("IRQgetWritableBuffer"); + buf=0; + //Filled entirely, now emptying + if(bq.IRQgetReadableBuffer(buffer,size)==false) fail("IRQgetReadableBuffer"); + if(size!=strlen(b1b)) fail("returned size"); + if(strcmp(buffer,b1b)!=0) fail("returned buffer"); + bq.IRQbufferEmptied(); + if(bq.IRQisEmpty()==true) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + if(bq.IRQgetReadableBuffer(buffer,size)==false) fail("IRQgetReadableBuffer"); + if(size!=strlen(b2b)) fail("returned size"); + if(strcmp(buffer,b2b)!=0) fail("returned buffer"); + bq.IRQbufferEmptied(); + if(bq.IRQisEmpty()==true) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + if(bq.IRQgetReadableBuffer(buffer,size)==false) fail("IRQgetReadableBuffer"); + if(size!=strlen(b3b)) fail("returned size"); + if(strcmp(buffer,b3b)!=0) fail("returned buffer"); + bq.IRQbufferEmptied(); + if(bq.IRQgetReadableBuffer(buffer,size)==true) fail("IRQgetReadableBuffer"); + if(bq.IRQisEmpty()==false) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + + //Now real multithreaded test + t19_v1=Thread::getCurrentThread(); + Thread *t=Thread::create(t19_p1,STACK_MIN,0,0,Thread::JOINABLE); + gbr(buffer,size); + if(size!=strlen(b1c)) fail("returned size"); + if(strcmp(buffer,b1c)!=0) fail("returned buffer"); + be(); + gbr(buffer,size); + if(size!=strlen(b2c)) fail("returned size"); + if(strcmp(buffer,b2c)!=0) fail("returned buffer"); + be(); + gbr(buffer,size); + if(size!=strlen(b3c)) fail("returned size"); + if(strcmp(buffer,b3c)!=0) fail("returned buffer"); + be(); + gbr(buffer,size); + if(size!=strlen(b4c)) fail("returned size"); + if(strcmp(buffer,b4c)!=0) fail("returned buffer"); + be(); + t->join(); + if(bq.IRQisEmpty()==false) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + + //Last, check Reset (again, single thread mode) + if(bq.IRQgetWritableBuffer(buf)==false) fail("IRQgetWritableBuffer"); + strcpy(buf,b1a); + bq.IRQbufferFilled(strlen(b1a)); + bq.IRQreset(); + if(bq.IRQisEmpty()==false) fail("IRQisEmpty"); + if(bq.IRQisFull()==true) fail("IRQisFull"); + pass(); +} + #ifdef WITH_FILESYSTEM // // Filesystem test 1