From c1d239bbdaef9ae08948ad2b61510ac1cd240947 Mon Sep 17 00:00:00 2001 From: inmarket Date: Thu, 16 Jul 2015 19:02:59 +1000 Subject: [PATCH] Significant improvements in alternative scheduler. Isolate the generic thread and heap code Tidyup's. Generic threading now working for x86, cortex-m0->m7. --- boards/base/STM32F746-Discovery/board.mk | 2 +- .../example_raw32/Makefile | 2 +- .../stm32f746g_raw32_ugfx.c | 1 - boards/base/Win32/board.mk | 4 + gfxconf.example.h | 4 +- src/gos/gos.mk | 4 +- src/gos/gos_arduino.c | 494 +---------- src/gos/gos_arduino.h | 64 +- src/gos/gos_chibios.c | 2 +- src/gos/gos_ecos.c | 3 +- src/gos/gos_freertos.c | 3 +- src/gos/gos_mk.c | 2 + src/gos/gos_options.h | 42 +- src/gos/gos_raw32.c | 789 +----------------- src/gos/gos_raw32.h | 96 +-- src/gos/gos_rawrtos.c | 3 +- src/gos/gos_x_heap.c | 195 +++++ src/gos/gos_x_heap.h | 62 ++ src/gos/gos_x_threads.c | 672 +++++++++++++++ src/gos/gos_x_threads.h | 103 +++ 20 files changed, 1131 insertions(+), 1416 deletions(-) create mode 100644 src/gos/gos_x_heap.c create mode 100644 src/gos/gos_x_heap.h create mode 100644 src/gos/gos_x_threads.c create mode 100644 src/gos/gos_x_threads.h diff --git a/boards/base/STM32F746-Discovery/board.mk b/boards/base/STM32F746-Discovery/board.mk index dd1f8441..35e62757 100644 --- a/boards/base/STM32F746-Discovery/board.mk +++ b/boards/base/STM32F746-Discovery/board.mk @@ -19,7 +19,7 @@ ifeq ($(OPT_OS),raw32) $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c \ $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_system.c \ $(GFXLIB)/boards/base/STM32F746-Discovery/stm32f746g_raw32_interrupts.c - GFXDEFS += GFX_OS_EXTRA_INIT_FUNCTION=Raw32OSInit + GFXDEFS += GFX_OS_EXTRA_INIT_FUNCTION=Raw32OSInit GFX_OS_INIT_NO_WARNING=TRUE SRCFLAGS+= -std=c99 GFXINC += $(CMSIS)/Device/ST/STM32F7xx/Include \ $(CMSIS)/Include \ diff --git a/boards/base/STM32F746-Discovery/example_raw32/Makefile b/boards/base/STM32F746-Discovery/example_raw32/Makefile index 298d74bf..dee0f01a 100644 --- a/boards/base/STM32F746-Discovery/example_raw32/Makefile +++ b/boards/base/STM32F746-Discovery/example_raw32/Makefile @@ -55,7 +55,7 @@ LDFLAGS = SRC = OBJS = -DEFS = GOS_RAW_HEAP_SIZE=40960 +DEFS = GFX_OS_HEAP_SIZE=40960 LIBS = INCPATH = diff --git a/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c b/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c index 3d493e5c..5fde982e 100644 --- a/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c +++ b/boards/base/STM32F746-Discovery/stm32f746g_raw32_ugfx.c @@ -13,7 +13,6 @@ systemticks_t gfxMillisecondsToTicks(delaytime_t ms) static void SystemClock_Config(void); static void CPU_CACHE_Enable(void); -static void LCD_Config(void); void Raw32OSInit(void) { RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; diff --git a/boards/base/Win32/board.mk b/boards/base/Win32/board.mk index adcbaeaf..b4577927 100644 --- a/boards/base/Win32/board.mk +++ b/boards/base/Win32/board.mk @@ -3,3 +3,7 @@ GFXSRC += GFXLIBS += include $(GFXLIB)/drivers/multiple/Win32/driver.mk include $(GFXLIB)/drivers/gaudio/Win32/driver.mk + +ifeq ($(OPT_OS),win32.raw32) + GFXDEFS += GFX_OS_INIT_NO_WARNING=TRUE +endif diff --git a/gfxconf.example.h b/gfxconf.example.h index 52c129ef..d46bbae2 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -35,13 +35,15 @@ //#define GFX_USE_OS_ECOS FALSE //#define GFX_USE_OS_RAWRTOS FALSE //#define GFX_USE_OS_RAW32 FALSE -// #define GOS_RAW_HEAP_SIZE 0 // #define INTERRUPTS_OFF() optional_code // #define INTERRUPTS_ON() optional_code // Options that (should where relevant) apply to all operating systems // #define GFX_COMPILER GFX_COMPILER_UNKNOWN +// #define GFX_CPU GFX_CPU_UNKNOWN +// #define GFX_OS_HEAP_SIZE 0 // #define GFX_NO_OS_INIT FALSE +// #define GFX_OS_INIT_NO_WARNING FALSE // #define GFX_OS_EXTRA_INIT_FUNCTION myOSInitRoutine // #define GFX_OS_EXTRA_DEINIT_FUNCTION myOSDeInitRoutine diff --git a/src/gos/gos.mk b/src/gos/gos.mk index 7db246d2..1c3a86a5 100644 --- a/src/gos/gos.mk +++ b/src/gos/gos.mk @@ -11,5 +11,7 @@ GFXSRC += $(GFXLIB)/src/gos/gos_chibios.c \ $(GFXLIB)/src/gos/gos_raw32.c \ $(GFXLIB)/src/gos/gos_ecos.c \ $(GFXLIB)/src/gos/gos_rawrtos.c \ - $(GFXLIB)/src/gos/gos_arduino.c + $(GFXLIB)/src/gos/gos_arduino.c \ + $(GFXLIB)/src/gos/gos_x_threads.c \ + $(GFXLIB)/src/gos/gos_x_heap.c diff --git a/src/gos/gos_arduino.c b/src/gos/gos_arduino.c index 380d528a..c565abb9 100644 --- a/src/gos/gos_arduino.c +++ b/src/gos/gos_arduino.c @@ -11,7 +11,8 @@ #include // Prototype for memcpy() -static void _gosThreadsInit(void); +void _gosHeapInit(void); +void _gosThreadsInit(void); /********************************************************* * Initialise @@ -23,7 +24,12 @@ void _gosInit(void) * On the other hand the C runtime should still already be initialized before * getting here! */ - #warning "GOS: Arduino - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #if !GFX_OS_INIT_NO_WARNING + #warning "GOS: Arduino - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #endif + + // Start the heap allocator + _gosHeapInit(); // Start the scheduler _gosThreadsInit(); @@ -53,488 +59,16 @@ void gfxExit(void) { dummy++; } -/********************************************************* - * Head allocation functions - *********************************************************/ - -#include // Prototype for malloc(), realloc() and free() - -void *gfxAlloc(size_t sz) { - return malloc(sz); -} - -void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) { - (void) oldsz; - return realloc(ptr, newsz); -} - -void gfxFree(void *ptr) { - free(ptr); -} - -/********************************************************* - * Semaphores and critical region functions - *********************************************************/ - -#if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON) - #define INTERRUPTS_OFF() - #define INTERRUPTS_ON() -#endif - -void gfxSystemLock(void) { - INTERRUPTS_OFF(); -} - -void gfxSystemUnlock(void) { - INTERRUPTS_ON(); -} - -void gfxMutexInit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxMutexEnter(gfxMutex *pmutex) { - INTERRUPTS_OFF(); - while (pmutex[0]) { - INTERRUPTS_ON(); - gfxYield(); - INTERRUPTS_OFF(); - } - pmutex[0] = 1; - INTERRUPTS_ON(); -} - -void gfxMutexExit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { - psem->cnt = val; - psem->limit = limit; -} - -bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { - systemticks_t starttm, delay; - - // Convert our delay to ticks - switch (ms) { - case TIME_IMMEDIATE: - delay = TIME_IMMEDIATE; - break; - case TIME_INFINITE: - delay = TIME_INFINITE; - break; - default: - delay = gfxMillisecondsToTicks(ms); - if (!delay) delay = 1; - starttm = gfxSystemTicks(); - } - - INTERRUPTS_OFF(); - while (psem->cnt <= 0) { - INTERRUPTS_ON(); - // Check if we have exceeded the defined delay - switch (delay) { - case TIME_IMMEDIATE: - return FALSE; - case TIME_INFINITE: - break; - default: - if (gfxSystemTicks() - starttm >= delay) - return FALSE; - break; - } - gfxYield(); - INTERRUPTS_OFF(); - } - psem->cnt--; - INTERRUPTS_ON(); - return TRUE; -} - -bool_t gfxSemWaitI(gfxSem *psem) { - if (psem->cnt <= 0) - return FALSE; - psem->cnt--; - return TRUE; -} - -void gfxSemSignal(gfxSem *psem) { - INTERRUPTS_OFF(); - gfxSemSignalI(psem); - INTERRUPTS_ON(); -} - -void gfxSemSignalI(gfxSem *psem) { - if (psem->cnt < psem->limit) - psem->cnt++; -} /********************************************************* * Sleep functions *********************************************************/ -void gfxSleepMilliseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); +systemticks_t gfxSystemTicks(void) { + return millis(); +} +systemticks_t gfxMillisecondsToTicks(delaytime_t ms) { + return ms; } -void gfxSleepMicroseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms/1000); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); -} - -/********************************************************* - * Threading functions - *********************************************************/ - -/** - * There are some compilers we know how they store the jmpbuf. For those - * we can use the constant macro definitions. For others we have to "auto-detect". - * Auto-detection is hairy and there is no guarantee it will work on all architectures. - * For those it doesn't - read the compiler manuals and the library source code to - * work out the correct macro values. - * You can use the debugger to work out the values for your compiler and put them here. - * Defining these macros as constant values makes the system behavior guaranteed but also - * makes your code compiler and cpu architecture dependent. It also saves a heap of code - * and a few bytes of RAM. - */ -#if GFX_COMPILER == GFX_COMPILER_MINGW32 - #define AUTO_DETECT_MASK FALSE - #define STACK_DIR_UP FALSE - #define MASK1 0x00000011 - #define MASK2 0x00000000 - #define STACK_BASE 12 -#else - // Use auto-detection of the stack frame format - // Assumes all the relevant stuff to be relocated is in the first 256 bytes of the jmpbuf. - #define AUTO_DETECT_MASK TRUE - #define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down - #define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation - #define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation - #define STACK_BASE stackbase // The base of the stack frame relative to the local variables - static bool_t stackdirup; - static uint32_t jmpmask1; - static uint32_t jmpmask2; - static size_t stackbase; -#endif - -#include /* jmp_buf, setjmp(), longjmp() */ - -/** - * Some compilers define a _setjmp() and a setjmp(). - * The difference between them is that setjmp() saves the signal masks. - * That is of no use to us so prefer to use the _setjmp() methods. - * If they don't exist compile them to be the standard setjmp() function. - * Similarly for longjmp(). - */ -#if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__) - #define _setjmp setjmp -#endif -#if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__) - #define _longjmp longjmp -#endif - -typedef struct thread { - struct thread * next; // Next thread - int flags; // Flags - #define FLG_THD_ALLOC 0x0001 - #define FLG_THD_MAIN 0x0002 - #define FLG_THD_DEAD 0x0004 - #define FLG_THD_WAIT 0x0008 - size_t size; // Size of the thread stack (including this structure) - threadreturn_t (*fn)(void *param); // Thread function - void * param; // Parameter for the thread function - jmp_buf cxt; // The current thread context. -} thread; - -typedef struct threadQ { - thread *head; - thread *tail; -} threadQ; - -static threadQ readyQ; // The list of ready threads -static threadQ deadQ; // Where we put threads waiting to be deallocated -static thread * current; // The current running thread -static thread mainthread; // The main thread context - -static void Qinit(threadQ * q) { - q->head = q->tail = 0; -} - -static void Qadd(threadQ * q, thread *t) { - t->next = 0; - if (q->head) { - q->tail->next = t; - q->tail = t; - } else - q->head = q->tail = t; -} - -static thread *Qpop(threadQ * q) { - struct thread * t; - - if (!q->head) - return 0; - t = q->head; - q->head = t->next; - return t; -} - -#if AUTO_DETECT_MASK - // The structure for the saved stack frame information - typedef struct saveloc { - char * localptr; - jmp_buf cxt; - } saveloc; - - // A pointer to our auto-detection buffer. - static saveloc *pframeinfo; - - /* These functions are not static to prevent the compiler removing them as functions */ - - void get_stack_state(void) { - char* c; - pframeinfo->localptr = (char *)&c; - _setjmp(pframeinfo->cxt); - } - - void get_stack_state_in_fn(void) { - pframeinfo++; - get_stack_state(); - pframeinfo--; - } -#endif - -static void _gosThreadsInit(void) { - Qinit(&readyQ); - current = &mainthread; - current->next = 0; - current->size = sizeof(thread); - current->flags = FLG_THD_MAIN; - current->fn = 0; - current->param = 0; - - #if AUTO_DETECT_MASK - { - uint32_t i; - char ** pout; - char ** pin; - size_t diff; - char * framebase; - - // Allocate a buffer to store our test data - pframeinfo = gfxAlloc(sizeof(saveloc)*2); - - // Get details of the stack frame from within a function - get_stack_state_in_fn(); - - // Get details of the stack frame outside the function - get_stack_state(); - - /* Work out the frame entries to relocate by treating the jump buffer as an array of pointers */ - stackdirup = pframeinfo[1].localptr > pframeinfo[0].localptr; - pout = (char **)pframeinfo[0].cxt; - pin = (char **)pframeinfo[1].cxt; - diff = pframeinfo[0].localptr - pframeinfo[1].localptr; - framebase = pframeinfo[0].localptr; - jmpmask1 = jmpmask2 = 0; - for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) { - if ((size_t)(*pout - *pin) == diff) { - if (i < 32) - jmpmask1 |= 1 << i; - else - jmpmask2 |= 1 << (i-32); - - if (stackdirup) { - if (framebase > *pout) - framebase = *pout; - } else { - if (framebase < *pout) - framebase = *pout; - } - } - } - stackbase = stackdirup ? (pframeinfo[0].localptr - framebase) : (framebase - pframeinfo[0].localptr); - - // Clean up - gfxFree(pframeinfo); - } - #endif -} - -gfxThreadHandle gfxThreadMe(void) { - return (gfxThreadHandle)current; -} - -void gfxYield(void) { - if (!_setjmp(current->cxt)) { - // Add us back to the Queue - Qadd(&readyQ, current); - - // Check if there are dead processes to deallocate - while ((current = Qpop(&deadQ))) - gfxFree(current); - - // Run the next process - current = Qpop(&readyQ); - _longjmp(current->cxt, 1); - } -} - -// This routine is not currently public - but it could be. -void gfxThreadExit(threadreturn_t ret) { - // Save the results - current->param = (void *)ret; - current->flags |= FLG_THD_DEAD; - - // Add us to the dead list if we need deallocation as we can't free ourselves. - // If someone is waiting on the thread they will do the cleanup. - if ((current->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC) - Qadd(&deadQ, current); - - // Switch to the next task - current = Qpop(&readyQ); - if (!current) - gfxExit(); // Oops - this should never happen! - _longjmp(current->cxt, 1); -} - -gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) { - thread * t; - (void) prio; - - // Ensure we have a minimum stack size - if (stacksz < sizeof(thread)+64) { - stacksz = sizeof(thread)+64; - stackarea = 0; - } - - if (stackarea) { - t = (thread *)stackarea; - t->flags = 0; - } else { - t = (thread *)gfxAlloc(stacksz); - if (!t) - return 0; - t->flags = FLG_THD_ALLOC; - } - t->size = stacksz; - t->fn = fn; - t->param = param; - if (_setjmp(t->cxt)) { - // This is the new thread - call the function! - gfxThreadExit(current->fn(current->param)); - - // We never get here - return 0; - } - - // Move the stack frame and relocate the context data - { - char ** s; - char * nf; - int diff; - uint32_t i; - - // Copy the stack frame - #if AUTO_DETECT_MASK - if (STACK_DIR_UP) { // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); - } else { // Stack grows down - nf = (char *)(t) + stacksz - (stackbase + sizeof(char *)); - memcpy(nf, &t, stackbase+sizeof(char *)); - } - #elif STACK_DIR_UP - // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&t - stackbase, stackbase+sizeof(char *)); - #else - // Stack grows down - nf = (char *)(t) + size - (stackbase + sizeof(char *)); - memcpy(nf, &t, stackbase+sizeof(char *)); - #endif - - // Relocate the context data - s = (char **)(t->cxt); - diff = nf - (char *)&t; - - // Relocate the elements we know need to be relocated - for (i = 1; i && i < MASK1; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #ifdef MASK2 - for (i = 1; i && i < MASK2; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #endif - } - - // Add this thread to the ready queue - Qadd(&readyQ, t); - return t; -} - -threadreturn_t gfxThreadWait(gfxThreadHandle th) { - thread * t; - - t = th; - if (t == current) - return -1; - - // Mark that we are waiting - t->flags |= FLG_THD_WAIT; - - // Wait for the thread to die - while(!(t->flags & FLG_THD_DEAD)) - gfxYield(); - - // Unmark - t->flags &= ~FLG_THD_WAIT; - - // Clean up resources if needed - if (t->flags & FLG_THD_ALLOC) - gfxFree(t); - - // Return the status left by the dead process - return (threadreturn_t)t->param; -} - -#endif /* GFX_USE_OS_RAW32 */ +#endif /* GFX_USE_OS_ARDUINO */ diff --git a/src/gos/gos_arduino.h b/src/gos/gos_arduino.h index fc9e7073..6a18aaec 100644 --- a/src/gos/gos_arduino.h +++ b/src/gos/gos_arduino.h @@ -44,70 +44,26 @@ typedef bool bool_t; typedef uint32_t size_t; #endif -typedef uint32_t delaytime_t; -typedef uint32_t systemticks_t; -typedef short semcount_t; -typedef int threadreturn_t; -typedef int threadpriority_t; - -#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) -#define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; - -#define TIME_IMMEDIATE 0 -#define TIME_INFINITE ((delaytime_t)-1) -#define MAX_SEMAPHORE_COUNT 0x7FFF -#define LOW_PRIORITY 0 -#define NORMAL_PRIORITY 1 -#define HIGH_PRIORITY 2 - -typedef struct { - semcount_t cnt; - semcount_t limit; -} gfxSem; - -typedef uint32_t gfxMutex; -typedef void * gfxThreadHandle; - -#define gfxThreadClose(thread) -#define gfxMutexDestroy(pmutex) -#define gfxSemDestroy(psem) -#define gfxSemCounter(psem) ((psem)->cnt) -#define gfxSemCounterI(psem) ((psem)->cnt) - -#define gfxSystemTicks() millis() -#define gfxMillisecondsToTicks(ms) (ms) - #ifdef __cplusplus extern "C" { #endif void gfxHalt(const char *msg); void gfxExit(void); - void *gfxAlloc(size_t sz); - void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); - void gfxFree(void *ptr); - void gfxYield(void); - void gfxSleepMilliseconds(delaytime_t ms); - void gfxSleepMicroseconds(delaytime_t ms); - //systemticks_t gfxSystemTicks(void); - //systemticks_t gfxMillisecondsToTicks(delaytime_t ms); - void gfxSystemLock(void); - void gfxSystemUnlock(void); - void gfxMutexInit(gfxMutex *pmutex); - void gfxMutexEnter(gfxMutex *pmutex); - void gfxMutexExit(gfxMutex *pmutex); - void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); - bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); - bool_t gfxSemWaitI(gfxSem *psem); - void gfxSemSignal(gfxSem *psem); - void gfxSemSignalI(gfxSem *psem); - gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); - threadreturn_t gfxThreadWait(gfxThreadHandle thread); - gfxThreadHandle gfxThreadMe(void); #ifdef __cplusplus } #endif +/*===========================================================================*/ +/* Use the generic thread handling and heap handling */ +/*===========================================================================*/ + +#define GOS_NEED_X_THREADS TRUE +#define GOS_NEED_X_HEAP TRUE + +#include "gos_x_threads.h" +#include "gos_x_heap.h" + #endif /* GFX_USE_OS_ARDUINO */ #endif /* _GOS_ARDUINO_H */ diff --git a/src/gos/gos_chibios.c b/src/gos/gos_chibios.c index 62f5d8ee..4c79bd3d 100644 --- a/src/gos/gos_chibios.c +++ b/src/gos/gos_chibios.c @@ -48,7 +48,7 @@ void _gosInit(void) chSysInit(); } #endif - #else + #elif !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call halInit() and chSysInit() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_ecos.c b/src/gos/gos_ecos.c index 16ce821b..cdad86da 100644 --- a/src/gos/gos_ecos.c +++ b/src/gos/gos_ecos.c @@ -13,7 +13,8 @@ void _gosInit(void) { #if !GFX_NO_OS_INIT #error "GOS: Operating System initialization for eCos is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" - #else + #endif + #if !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call cyg_scheduler_start() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_freertos.c b/src/gos/gos_freertos.c index dbdfd22e..7b978738 100644 --- a/src/gos/gos_freertos.c +++ b/src/gos/gos_freertos.c @@ -26,7 +26,8 @@ void _gosInit(void) { #if !GFX_NO_OS_INIT #error "GOS: Operating System initialization for FreeRTOS is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" - #else + #endif + #if !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call vTaskStartScheduler() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_mk.c b/src/gos/gos_mk.c index 71267233..1b033206 100644 --- a/src/gos/gos_mk.c +++ b/src/gos/gos_mk.c @@ -14,3 +14,5 @@ #include "gos_raw32.c" #include "gos_rawrtos.c" #include "gos_win32.c" +#include "gos_x_threads.c" +#include "gos_x_heap.c" diff --git a/src/gos/gos_options.h b/src/gos/gos_options.h index 8fb4f51b..600c3085 100644 --- a/src/gos/gos_options.h +++ b/src/gos/gos_options.h @@ -74,7 +74,7 @@ * @details Defaults to FALSE */ #ifndef GFX_USE_OS_ARDUINO - #define GFX_USE_OS_ARDUINO FALSE + #define GFX_USE_OS_ARDUINO FALSE #endif /** * @} @@ -88,12 +88,35 @@ * @note This is setting enables optimisations that are compiler specific. It does * not need to be specified as reasonable defaults and various auto-detection * will happen as required. + * @note Currently only used by ugfx generic thread handling (GOS_USE_OS_RAW32 and GOS_USE_OS_ARDUINO) */ #ifndef GFX_COMPILER #define GFX_COMPILER GFX_COMPILER_UNKNOWN #endif #define GFX_COMPILER_UNKNOWN 0 // Unknown compiler #define GFX_COMPILER_MINGW32 1 // MingW32 (x86) compiler for windows + /** + * @brief Enable cpu specific code + * @details Defaults to GFX_CPU_UNKNOWN + * @note This is setting enables optimisations that are cpu specific. It does + * not need to be specified as reasonable defaults and various auto-detection + * will happen as required. + * @note Currently only used by ugfx generic thread handling (GOS_USE_OS_RAW32 and GOS_USE_OS_ARDUINO) + * @{ + */ + #ifndef GFX_CPU + #define GFX_CPU GFX_CPU_UNKNOWN + #endif + #define GFX_CPU_UNKNOWN 0 //**< Unknown cpu + #define GFX_CPU_CORTEX_M0 1 //**< Cortex M0 + #define GFX_CPU_CORTEX_M1 2 //**< Cortex M1 + #define GFX_CPU_CORTEX_M2 3 //**< Cortex M2 + #define GFX_CPU_CORTEX_M3 4 //**< Cortex M3 + #define GFX_CPU_CORTEX_M4 5 //**< Cortex M4 + #define GFX_CPU_CORTEX_M4_FP 6 //**< Cortex M4 with hardware floating point + #define GFX_CPU_CORTEX_M7 7 //**< Cortex M7 + #define GFX_CPU_CORTEX_M7_FP 8 //**< Cortex M7 with hardware floating point + /** @} */ /** * @brief Should uGFX avoid initializing the operating system * @details Defaults to FALSE @@ -108,6 +131,16 @@ #ifndef GFX_NO_OS_INIT #define GFX_NO_OS_INIT FALSE #endif + /** + * @brief Turn off warnings about initializing the operating system + * @details Defaults to FALSE + * @note This is only relevant where GOS cannot initialise the operating + * system automatically or the operating system initialisation has been + * explicitly turned off. + */ + #ifndef GFX_OS_INIT_NO_WARNING + #define GFX_OS_INIT_NO_WARNING FALSE + #endif /** * @brief Should uGFX stuff be added to the FreeRTOS+Tracer * @details Defaults to FALSE @@ -117,7 +150,8 @@ #endif /** * @brief How much RAM should uGFX use for the heap - * @details Defaults to 0. Only valid with GFX_USE_OS_RAW32 + * @details Defaults to 0. + * @note Only used when the generic ugfx heap code is used (GFX_USE_OS_RAW32 and GFX_USE_OS_ARDUINO) * @note If 0 then the standard C runtime malloc(), free() and realloc() * are used. * @note If it is non-zero then this is the number of bytes of RAM @@ -125,8 +159,8 @@ * runtime routines will be used and a new routine @p gfxAddHeapBlock() * is added allowing the user to add extra memory blocks to the heap. */ - #ifndef GOS_RAW_HEAP_SIZE - #define GOS_RAW_HEAP_SIZE 0 + #ifndef GFX_OS_HEAP_SIZE + #define GFX_OS_HEAP_SIZE 0 #endif /** @} */ diff --git a/src/gos/gos_raw32.c b/src/gos/gos_raw32.c index 7d19717d..4e61feb9 100644 --- a/src/gos/gos_raw32.c +++ b/src/gos/gos_raw32.c @@ -12,14 +12,8 @@ #if GFX_USE_OS_RAW32 -#include // Prototype for memcpy() - -#if GOS_RAW_HEAP_SIZE != 0 - static void _gosHeapInit(void); -#else - #define _gosHeapInit() -#endif -static void _gosThreadsInit(void); +void _gosHeapInit(void); +void _gosThreadsInit(void); /********************************************************* * Initialise @@ -31,7 +25,9 @@ void _gosInit(void) * On the other hand the C runtime should still already be initialized before * getting here! */ - #warning "GOS: Raw32 - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #if !GFX_OS_INIT_NO_WARNING + #warning "GOS: Raw32 - Make sure you initialize your hardware and the C runtime before calling gfxInit() in your application!" + #endif // Set up the heap allocator _gosHeapInit(); @@ -46,7 +42,7 @@ void _gosDeinit(void) } /********************************************************* - * For WIn32 emulation - automatically add the tick functions + * For Win32 emulation - automatically add the tick functions * the user would normally have to provide for bare metal. *********************************************************/ @@ -98,777 +94,4 @@ void gfxExit(void) { #endif } -/********************************************************* - * Head allocation functions - *********************************************************/ - -#if GOS_RAW_HEAP_SIZE == 0 - #include // Prototype for malloc(), realloc() and free() - - void *gfxAlloc(size_t sz) { - return malloc(sz); - } - - void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) { - (void) oldsz; - return realloc(ptr, newsz); - } - - void gfxFree(void *ptr) { - free(ptr); - } - -#else - - // Slot structure - user memory follows - typedef struct memslot { - struct memslot *next; // The next memslot - size_t sz; // Includes the size of this memslot. - } memslot; - - // Free Slot - immediately follows the memslot structure - typedef struct freeslot { - memslot *nextfree; // The next free slot - } freeslot; - - #define GetSlotSize(sz) ((((sz) + (sizeof(freeslot) - 1)) & ~(sizeof(freeslot) - 1)) + sizeof(memslot)) - #define NextFree(pslot) ((freeslot *)Slot2Ptr(pslot))->nextfree - #define Ptr2Slot(p) ((memslot *)(p) - 1) - #define Slot2Ptr(pslot) ((pslot)+1) - - static memslot * firstSlot; - static memslot * lastSlot; - static memslot * freeSlots; - static char heap[GOS_RAW_HEAP_SIZE]; - - static void _gosHeapInit(void) { - lastSlot = 0; - gfxAddHeapBlock(heap, GOS_RAW_HEAP_SIZE); - } - - void gfxAddHeapBlock(void *ptr, size_t sz) { - if (sz < sizeof(memslot)+sizeof(freeslot)) - return; - - if (lastSlot) - lastSlot->next = (memslot *)ptr; - else - firstSlot = lastSlot = freeSlots = (memslot *)ptr; - - lastSlot->next = 0; - lastSlot->sz = sz; - NextFree(lastSlot) = 0; - } - - void *gfxAlloc(size_t sz) { - register memslot *prev, *p, *new; - - if (!sz) return 0; - sz = GetSlotSize(sz); - for (prev = 0, p = freeSlots; p != 0; prev = p, p = NextFree(p)) { - // Loop till we have a block big enough - if (p->sz < sz) - continue; - // Can we save some memory by splitting this block? - if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { - new = (memslot *)((char *)p + sz); - new->next = p->next; - p->next = new; - new->sz = p->sz - sz; - p->sz = sz; - if (lastSlot == p) - lastSlot = new; - NextFree(new) = NextFree(p); - NextFree(p) = new; - } - // Remove it from the free list - if (prev) - NextFree(prev) = NextFree(p); - else - freeSlots = NextFree(p); - // Return the result found - return Slot2Ptr(p); - } - // No slots large enough - return 0; - } - - void *gfxRealloc(void *ptr, size_t oldsz, size_t sz) { - register memslot *prev, *p, *new; - (void) oldsz; - - if (!ptr) - return gfxAlloc(sz); - if (!sz) { - gfxFree(ptr); - return 0; - } - - p = Ptr2Slot(ptr); - sz = GetSlotSize(sz); - - // If the next slot is free (and contiguous) merge it into this one - if ((char *)p + p->sz == (char *)p->next) { - for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { - if (new == p->next) { - p->next = new->next; - p->sz += new->sz; - if (prev) - NextFree(prev) = NextFree(new); - else - freeSlots = NextFree(new); - if (lastSlot == new) - lastSlot = p; - break; - } - } - } - - // If this block is large enough we are nearly done - if (sz < p->sz) { - // Can we save some memory by splitting this block? - if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { - new = (memslot *)((char *)p + sz); - new->next = p->next; - p->next = new; - new->sz = p->sz - sz; - p->sz = sz; - if (lastSlot == p) - lastSlot = new; - NextFree(new) = freeSlots; - freeSlots = new; - } - return Slot2Ptr(p); - } - - // We need to do this the hard way - if ((new = gfxAlloc(sz))) - return 0; - memcpy(new, ptr, p->sz - sizeof(memslot)); - gfxFree(ptr); - return new; - } - - void gfxFree(void *ptr) { - register memslot *prev, *p, *new; - - if (!ptr) - return; - - p = Ptr2Slot(ptr); - - // If the next slot is free (and contiguous) merge it into this one - if ((char *)p + p->sz == (char *)p->next) { - for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { - if (new == p->next) { - p->next = new->next; - p->sz += new->sz; - if (prev) - NextFree(prev) = NextFree(new); - else - freeSlots = NextFree(new); - if (lastSlot == new) - lastSlot = p; - break; - } - } - } - - // Add it into the free chain - NextFree(p) = freeSlots; - freeSlots = p; - } -#endif - -/********************************************************* - * Semaphores and critical region functions - *********************************************************/ - -#if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON) - #define INTERRUPTS_OFF() - #define INTERRUPTS_ON() -#endif - -void gfxSystemLock(void) { - INTERRUPTS_OFF(); -} - -void gfxSystemUnlock(void) { - INTERRUPTS_ON(); -} - -void gfxMutexInit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxMutexEnter(gfxMutex *pmutex) { - INTERRUPTS_OFF(); - while (pmutex[0]) { - INTERRUPTS_ON(); - gfxYield(); - INTERRUPTS_OFF(); - } - pmutex[0] = 1; - INTERRUPTS_ON(); -} - -void gfxMutexExit(gfxMutex *pmutex) { - pmutex[0] = 0; -} - -void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { - psem->cnt = val; - psem->limit = limit; -} - -bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { - systemticks_t starttm, delay; - - // Convert our delay to ticks - switch (ms) { - case TIME_IMMEDIATE: - delay = TIME_IMMEDIATE; - break; - case TIME_INFINITE: - delay = TIME_INFINITE; - break; - default: - delay = gfxMillisecondsToTicks(ms); - if (!delay) delay = 1; - starttm = gfxSystemTicks(); - } - - INTERRUPTS_OFF(); - while (psem->cnt <= 0) { - INTERRUPTS_ON(); - // Check if we have exceeded the defined delay - switch (delay) { - case TIME_IMMEDIATE: - return FALSE; - case TIME_INFINITE: - break; - default: - if (gfxSystemTicks() - starttm >= delay) - return FALSE; - break; - } - gfxYield(); - INTERRUPTS_OFF(); - } - psem->cnt--; - INTERRUPTS_ON(); - return TRUE; -} - -bool_t gfxSemWaitI(gfxSem *psem) { - if (psem->cnt <= 0) - return FALSE; - psem->cnt--; - return TRUE; -} - -void gfxSemSignal(gfxSem *psem) { - INTERRUPTS_OFF(); - gfxSemSignalI(psem); - INTERRUPTS_ON(); -} - -void gfxSemSignalI(gfxSem *psem) { - if (psem->cnt < psem->limit) - psem->cnt++; -} - -/********************************************************* - * Sleep functions - *********************************************************/ - -void gfxSleepMilliseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); -} - -void gfxSleepMicroseconds(delaytime_t ms) { - systemticks_t starttm, delay; - - // Safety first - switch (ms) { - case TIME_IMMEDIATE: - return; - case TIME_INFINITE: - while(1) - gfxYield(); - return; - } - - // Convert our delay to ticks - delay = gfxMillisecondsToTicks(ms/1000); - starttm = gfxSystemTicks(); - - do { - gfxYield(); - } while (gfxSystemTicks() - starttm < delay); -} - -/********************************************************* - * Threading functions - *********************************************************/ - -#if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP - - /** - * There are some compilers we know how they store the jmpbuf. For those - * we can use the constant macro definitions. For others we have to "auto-detect". - * Auto-detection is hairy and there is no guarantee it will work on all architectures. - * For those it doesn't - read the compiler manuals and the library source code to - * work out the correct macro values. - * You can use the debugger to work out the values for your compiler and put them here. - * Defining these macros as constant values makes the system behavior guaranteed but also - * makes your code compiler and cpu architecture dependent. It also saves a heap of code - * and a few bytes of RAM. - */ - #if GFX_COMPILER == GFX_COMPILER_MINGW32 - #define AUTO_DETECT_MASK FALSE - #define STACK_DIR_UP FALSE - #define MASK1 0x00000011 - #define MASK2 0x00000000 - #define STACK_BASE 12 - #else - // Use auto-detection of the stack frame format - // Assumes all the relevant stuff to be relocated is in the first 256 bytes of the jmpbuf. - #define AUTO_DETECT_MASK TRUE - #define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down - #define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation - #define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation - #define STACK_BASE stackbase // The base of the stack frame relative to the local variables - static bool_t stackdirup; - static uint32_t jmpmask1; - static uint32_t jmpmask2; - static size_t stackbase; - #endif - - #include /* jmp_buf, setjmp(), longjmp() */ - - /** - * Some compilers define a _setjmp() and a setjmp(). - * The difference between them is that setjmp() saves the signal masks. - * That is of no use to us so prefer to use the _setjmp() methods. - * If they don't exist compile them to be the standard setjmp() function. - * Similarly for longjmp(). - */ - #if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__) - #define _setjmp setjmp - #endif - #if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__) - #define _longjmp longjmp - #endif - - typedef jmp_buf threadcxt; - -#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M0 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M1 - typedef void * threadcxt; -#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M3 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M4 - typedef void * threadcxt; -#else - #error "GOS RAW32: Unsupported Scheduler. Try setting GOS_RAW_SCHEDULER = SCHED_USE_SETJMP" -#endif - -typedef struct thread { - struct thread * next; // Next thread - int flags; // Flags - #define FLG_THD_ALLOC 0x0001 - #define FLG_THD_MAIN 0x0002 - #define FLG_THD_DEAD 0x0004 - #define FLG_THD_WAIT 0x0008 - size_t size; // Size of the thread stack (including this structure) - threadreturn_t (*fn)(void *param); // Thread function - void * param; // Parameter for the thread function - threadcxt cxt; // The current thread context. -} thread; - -typedef struct threadQ { - thread *head; - thread *tail; -} threadQ; - -static threadQ readyQ; // The list of ready threads -static threadQ deadQ; // Where we put threads waiting to be deallocated -static thread * current; // The current running thread -static thread mainthread; // The main thread context - -static void Qinit(threadQ * q) { - q->head = q->tail = 0; -} - -static void Qadd(threadQ * q, thread *t) { - t->next = 0; - if (q->head) { - q->tail->next = t; - q->tail = t; - } else - q->head = q->tail = t; -} - -static thread *Qpop(threadQ * q) { - struct thread * t; - - if (!q->head) - return 0; - t = q->head; - q->head = t->next; - return t; -} - -#if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP && AUTO_DETECT_MASK - - // The structure for the saved stack frame information - typedef struct saveloc { - char * localptr; - jmp_buf cxt; - } saveloc; - - // A pointer to our auto-detection buffer. - static saveloc *pframeinfo; - - /* These functions are not static to prevent the compiler removing them as functions */ - - void get_stack_state(void) { - char *c; - pframeinfo->localptr = (char *)&c; - _setjmp(pframeinfo->cxt); - } - - void get_stack_state_in_fn(void) { - pframeinfo++; - get_stack_state(); - pframeinfo--; - } -#endif - -static void _gosThreadsInit(void) { - Qinit(&readyQ); - current = &mainthread; - current->next = 0; - current->size = sizeof(thread); - current->flags = FLG_THD_MAIN; - current->fn = 0; - current->param = 0; - - #if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP && AUTO_DETECT_MASK - { - uint32_t i; - char ** pout; - char ** pin; - size_t diff; - char * framebase; - - // Allocate a buffer to store our test data - pframeinfo = gfxAlloc(sizeof(saveloc)*2); - - // Get details of the stack frame from within a function - get_stack_state_in_fn(); - - // Get details of the stack frame outside the function - get_stack_state(); - - /* Work out the frame entries to relocate by treating the jump buffer as an array of pointers */ - stackdirup = pframeinfo[1].localptr > pframeinfo[0].localptr; - pout = (char **)pframeinfo[0].cxt; - pin = (char **)pframeinfo[1].cxt; - diff = pframeinfo[0].localptr - pframeinfo[1].localptr; - framebase = pframeinfo[0].localptr; - jmpmask1 = jmpmask2 = 0; - for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) { - if ((size_t)(*pout - *pin) == diff) { - if (i < 32) - jmpmask1 |= 1 << i; - else - jmpmask2 |= 1 << (i-32); - - if (stackdirup) { - if (framebase > *pout) - framebase = *pout; - } else { - if (framebase < *pout) - framebase = *pout; - } - } - } - stackbase = stackdirup ? (pframeinfo[0].localptr - framebase) : (framebase - pframeinfo[0].localptr); - - // Clean up - gfxFree(pframeinfo); - } - #endif -} - -gfxThreadHandle gfxThreadMe(void) { - return (gfxThreadHandle)current; -} - -// Check if there are dead processes to deallocate -static void cleanUpDeadThreads(void) { - thread *p; - - while ((p = Qpop(&deadQ))) - gfxFree(p); -} - -#if GOS_RAW_SCHEDULER == SCHED_USE_SETJMP - // Move the stack frame and relocate the context data - void _gfxAdjustCxt(thread *t) { - char ** s; - char * nf; - int diff; - uint32_t i; - - // Copy the stack frame - #if AUTO_DETECT_MASK - if (STACK_DIR_UP) { // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *)); - } else { // Stack grows down - nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); - memcpy(nf, &s, stackbase+sizeof(char *)); - } - #elif STACK_DIR_UP - // Stack grows up - nf = (char *)(t) + sizeof(thread) + stackbase; - memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *)); - #else - // Stack grows down - nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); - memcpy(nf, &s, stackbase+sizeof(char *)); - #endif - - // Relocate the context data - s = (char **)(t->cxt); - diff = nf - (char *)&s; - - // Relocate the elements we know need to be relocated - for (i = 1; i && i < MASK1; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #ifdef MASK2 - for (i = 1; i && i < MASK2; i <<= 1, s++) { - if ((MASK1 & i)) - *s += diff; - } - #endif - } - #define CXT_SET(t) { \ - if (!_setjmp(t->cxt)) { \ - _gfxAdjustCxt(t); \ - current = t; \ - _longjmp(current->cxt, 1); \ - } \ - } - #define CXT_SAVE() if (_setjmp(current->cxt)) return - #define CXT_RESTORE() _longjmp(current->cxt, 1) - -#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M0 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M1 - - // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 - - #define CXT_SET(t) { \ - register void* r13 __asm__("r13"); \ - current = t; \ - r13 = (char*)current + current->size; \ - } - - /** - * Save the current thread context. - * - * Automatically returns the calling function when this thread gets restarted - * with the thread handle as the return value - */ - // - #define CXT_SAVE() { \ - register void* r13 __asm__("r13"); \ - __asm__ volatile ( "push {r4, r5, r6, r7, lr} \n\t" \ - "mov r4, r8 \n\t" \ - "mov r5, r9 \n\t" \ - "mov r6, r10 \n\t" \ - "mov r7, r11 \n\t" \ - "push {r4, r5, r6, r7}" : : : "memory"); \ - current->cxt = r13; \ - } - #define CXT_RESTORE() { \ - register void* r13 __asm__ ("r13"); \ - r13 = current->cxt; \ - __asm__ volatile ( "pop {r4, r5, r6, r7} \n\t" \ - "mov r8, r4 \n\t" \ - "mov r9, r5 \n\t" \ - "mov r10, r6 \n\t" \ - "mov r11, r7 \n\t" \ - "pop {r4, r5, r6, r7, pc}" : : "r" (r13) : "memory"); \ - } - -#elif GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M3 || GOS_RAW_SCHEDULER == SCHED_USE_CORTEX_M4 - - // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 and floating point if needed - - #define CXT_SET(t) { \ - register void* r13 __asm__("r13"); \ - current = t; \ - r13 = (char *)current + current->size; \ - } - - #if CORTEX_USE_FPU - #define CXT_SAVE() { \ - register void* r13 __asm__("r13"); \ - __asm__ volatile ("push {r4, r5, r6, r7, r8, r9, r10, r11, lr}" : : : "memory");\ - __asm__ volatile ("vpush {s16-s31}" : : : "memory"); \ - current->cxt = r13; \ - } - #define CXT_RESTORE() { \ - register void* r13 __asm__("r13"); \ - r13 = current->cxt; \ - __asm__ volatile ("vpop {s16-s31}" : : : "memory"); \ - __asm__ volatile ("pop {r4, r5, r6, r7, r8, r9, r10, r11, pc}" : : : "memory"); \ - } - #else - #define CXT_SAVE() { \ - register void* r13 __asm__("r13"); \ - __asm__ volatile ("push {r4, r5, r6, r7, r8, r9, r10, r11, lr}" : : : "memory");\ - current->cxt = r13; \ - } - #define CXT_RESTORE() { \ - register void* r13 __asm__("r13"); \ - r13 = current->cxt; \ - __asm__ volatile ("pop {r4, r5, r6, r7, r8, r9, r10, r11, pc}" : : : "memory"); \ - } - #endif -#endif - -void gfxYield(void) { - - // Clean up zombies - cleanUpDeadThreads(); - - // Is there another thread to run? - if (!readyQ.head) - return; - - // Save the current thread (automatically returns when this thread gets re-executed) - CXT_SAVE(); - - // Add us back to the Queue - Qadd(&readyQ, current); - - // Run the next process - current = Qpop(&readyQ); - CXT_RESTORE(); -} - -// This routine is not currently public - but it could be. -void gfxThreadExit(threadreturn_t ret) { - // Save the results in case someone is waiting - current->param = (void *)ret; - current->flags |= FLG_THD_DEAD; - - // Add us to the dead list if we need deallocation as we can't free ourselves. - // If someone is waiting on the thread they will do the cleanup. - if ((current->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC) - Qadd(&deadQ, current); - - // Set the next thread - current = Qpop(&readyQ); - - // Was that the last thread? If so exit - if (!current) - gfxExit(); - - // Switch to the new thread - CXT_RESTORE(); -} - -void _gfxStartThread(thread *t) { - - // Save the current thread (automatically returns when this thread gets re-executed) - CXT_SAVE(); - - // Add the current thread to the queue because we are starting a new thread. - Qadd(&readyQ, current); - - // Change to the new thread and the new stack - CXT_SET(t); - - // Run the users function - gfxThreadExit(current->fn(current->param)); - - // We never get here! -} - -gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) { - thread * t; - (void) prio; - - // Ensure we have a minimum stack size - if (stacksz < sizeof(thread)+64) { - stacksz = sizeof(thread)+64; - stackarea = 0; - } - - if (stackarea) { - t = (thread *)stackarea; - t->flags = 0; - } else { - t = (thread *)gfxAlloc(stacksz); - if (!t) - return 0; - t->flags = FLG_THD_ALLOC; - } - t->size = stacksz; - t->fn = fn; - t->param = param; - - _gfxStartThread(t); - - // Return the new thread handle - return t; -} - -threadreturn_t gfxThreadWait(gfxThreadHandle th) { - thread * t; - - t = th; - if (t == current) - return -1; - - // Mark that we are waiting - t->flags |= FLG_THD_WAIT; - - // Wait for the thread to die - while(!(t->flags & FLG_THD_DEAD)) - gfxYield(); - - // Unmark - t->flags &= ~FLG_THD_WAIT; - - // Clean up resources if needed - if (t->flags & FLG_THD_ALLOC) - gfxFree(t); - - // Return the status left by the dead process - return (threadreturn_t)t->param; -} - #endif /* GFX_USE_OS_RAW32 */ diff --git a/src/gos/gos_raw32.h b/src/gos/gos_raw32.h index 0661bd37..0fca9223 100644 --- a/src/gos/gos_raw32.h +++ b/src/gos/gos_raw32.h @@ -25,37 +25,6 @@ #if GFX_USE_OS_RAW32 -/*===========================================================================*/ -/* Special Macros just for a Raw implementation */ -/*===========================================================================*/ - -/** - * @brief Set the maximum size of the heap. - * @note If set to 0 then the C runtime library malloc() and free() are used. - */ -#ifndef GOS_RAW_HEAP_SIZE - #define GOS_RAW_HEAP_SIZE 0 -#endif - -/** - * @brief Scheduler cpu support - */ -#define SCHED_USE_SETJMP 0 -#define SCHED_USE_CORTEX_M0 1 -#define SCHED_USE_CORTEX_M1 2 -#define SCHED_USE_CORTEX_M2 3 -#define SCHED_USE_CORTEX_M3 4 -#define SCHED_USE_CORTEX_M4 5 - -/** - * @brief Set the preferred scheduler method - * @note If not defined the SCHED_USE_SETJMP is used which should work for most platforms. - */ -#ifndef GOS_RAW_SCHEDULER - #define GOS_RAW_SCHEDULER SCHED_USE_SETJMP -#endif - - /*===========================================================================*/ /* Type definitions */ /*===========================================================================*/ @@ -79,71 +48,26 @@ typedef unsigned char bool_t; typedef uint32_t size_t; #endif -typedef uint32_t delaytime_t; -typedef uint32_t systemticks_t; -typedef short semcount_t; -typedef int threadreturn_t; -typedef int threadpriority_t; - -#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) -#define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; - -#define TIME_IMMEDIATE 0 -#define TIME_INFINITE ((delaytime_t)-1) -#define MAX_SEMAPHORE_COUNT 0x7FFF -#define LOW_PRIORITY 0 -#define NORMAL_PRIORITY 1 -#define HIGH_PRIORITY 2 - -typedef struct { - semcount_t cnt; - semcount_t limit; -} gfxSem; - -typedef uint32_t gfxMutex; -typedef void * gfxThreadHandle; - -#define gfxThreadClose(thread) -#define gfxMutexDestroy(pmutex) -#define gfxSemDestroy(psem) -#define gfxSemCounter(psem) ((psem)->cnt) -#define gfxSemCounterI(psem) ((psem)->cnt) - #ifdef __cplusplus extern "C" { #endif - #if GOS_RAW_HEAP_SIZE != 0 - void gfxAddHeapBlock(void *ptr, size_t sz); - #endif - void gfxHalt(const char *msg); void gfxExit(void); - void *gfxAlloc(size_t sz); - void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); - void gfxFree(void *ptr); - void gfxYield(void); - void gfxSleepMilliseconds(delaytime_t ms); - void gfxSleepMicroseconds(delaytime_t ms); - systemticks_t gfxSystemTicks(void); - systemticks_t gfxMillisecondsToTicks(delaytime_t ms); - void gfxSystemLock(void); - void gfxSystemUnlock(void); - void gfxMutexInit(gfxMutex *pmutex); - void gfxMutexEnter(gfxMutex *pmutex); - void gfxMutexExit(gfxMutex *pmutex); - void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); - bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); - bool_t gfxSemWaitI(gfxSem *psem); - void gfxSemSignal(gfxSem *psem); - void gfxSemSignalI(gfxSem *psem); - gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); - threadreturn_t gfxThreadWait(gfxThreadHandle thread); - gfxThreadHandle gfxThreadMe(void); #ifdef __cplusplus } #endif +/*===========================================================================*/ +/* Use the generic thread handling and heap handling */ +/*===========================================================================*/ + +#define GOS_NEED_X_THREADS TRUE +#define GOS_NEED_X_HEAP TRUE + +#include "gos_x_threads.h" +#include "gos_x_heap.h" + #endif /* GFX_USE_OS_RAW32 */ #endif /* _GOS_RAW32_H */ diff --git a/src/gos/gos_rawrtos.c b/src/gos/gos_rawrtos.c index c47c85bf..8efe2235 100644 --- a/src/gos/gos_rawrtos.c +++ b/src/gos/gos_rawrtos.c @@ -26,7 +26,8 @@ void _gosInit(void) { #if !GFX_NO_OS_INIT #error "GOS: Operating System initialization for RawRTOS is not yet implemented in uGFX. Please set GFX_NO_OS_INIT to TRUE in your gfxconf.h" - #else + #endif + #if !GFX_OS_INIT_NO_WARNING #warning "GOS: Operating System initialization has been turned off. Make sure you call raw_os_start() before gfxInit() in your application!" #endif } diff --git a/src/gos/gos_x_heap.c b/src/gos/gos_x_heap.c new file mode 100644 index 00000000..94b74d37 --- /dev/null +++ b/src/gos/gos_x_heap.c @@ -0,0 +1,195 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GOS_NEED_X_HEAP + +#include // Prototype for memcpy() + + +#if GFX_OS_HEAP_SIZE == 0 + #include // Prototype for malloc(), realloc() and free() + + void _gosHeapInit(void) { + } + void *gfxAlloc(size_t sz) { + return malloc(sz); + } + + void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz) { + (void) oldsz; + return realloc(ptr, newsz); + } + + void gfxFree(void *ptr) { + free(ptr); + } + +#else + + // Slot structure - user memory follows + typedef struct memslot { + struct memslot *next; // The next memslot + size_t sz; // Includes the size of this memslot. + } memslot; + + // Free Slot - immediately follows the memslot structure + typedef struct freeslot { + memslot *nextfree; // The next free slot + } freeslot; + + #define GetSlotSize(sz) ((((sz) + (sizeof(freeslot) - 1)) & ~(sizeof(freeslot) - 1)) + sizeof(memslot)) + #define NextFree(pslot) ((freeslot *)Slot2Ptr(pslot))->nextfree + #define Ptr2Slot(p) ((memslot *)(p) - 1) + #define Slot2Ptr(pslot) ((pslot)+1) + + static memslot * firstSlot; + static memslot * lastSlot; + static memslot * freeSlots; + static char heap[GFX_OS_HEAP_SIZE]; + + void _gosHeapInit(void) { + lastSlot = 0; + gfxAddHeapBlock(heap, GFX_OS_HEAP_SIZE); + } + + void gfxAddHeapBlock(void *ptr, size_t sz) { + if (sz < sizeof(memslot)+sizeof(freeslot)) + return; + + if (lastSlot) + lastSlot->next = (memslot *)ptr; + else + firstSlot = lastSlot = freeSlots = (memslot *)ptr; + + lastSlot->next = 0; + lastSlot->sz = sz; + NextFree(lastSlot) = 0; + } + + void *gfxAlloc(size_t sz) { + register memslot *prev, *p, *new; + + if (!sz) return 0; + sz = GetSlotSize(sz); + for (prev = 0, p = freeSlots; p != 0; prev = p, p = NextFree(p)) { + // Loop till we have a block big enough + if (p->sz < sz) + continue; + // Can we save some memory by splitting this block? + if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { + new = (memslot *)((char *)p + sz); + new->next = p->next; + p->next = new; + new->sz = p->sz - sz; + p->sz = sz; + if (lastSlot == p) + lastSlot = new; + NextFree(new) = NextFree(p); + NextFree(p) = new; + } + // Remove it from the free list + if (prev) + NextFree(prev) = NextFree(p); + else + freeSlots = NextFree(p); + // Return the result found + return Slot2Ptr(p); + } + // No slots large enough + return 0; + } + + void *gfxRealloc(void *ptr, size_t oldsz, size_t sz) { + register memslot *prev, *p, *new; + (void) oldsz; + + if (!ptr) + return gfxAlloc(sz); + if (!sz) { + gfxFree(ptr); + return 0; + } + + p = Ptr2Slot(ptr); + sz = GetSlotSize(sz); + + // If the next slot is free (and contiguous) merge it into this one + if ((char *)p + p->sz == (char *)p->next) { + for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { + if (new == p->next) { + p->next = new->next; + p->sz += new->sz; + if (prev) + NextFree(prev) = NextFree(new); + else + freeSlots = NextFree(new); + if (lastSlot == new) + lastSlot = p; + break; + } + } + } + + // If this block is large enough we are nearly done + if (sz < p->sz) { + // Can we save some memory by splitting this block? + if (p->sz >= sz + sizeof(memslot)+sizeof(freeslot)) { + new = (memslot *)((char *)p + sz); + new->next = p->next; + p->next = new; + new->sz = p->sz - sz; + p->sz = sz; + if (lastSlot == p) + lastSlot = new; + NextFree(new) = freeSlots; + freeSlots = new; + } + return Slot2Ptr(p); + } + + // We need to do this the hard way + if ((new = gfxAlloc(sz))) + return 0; + memcpy(new, ptr, p->sz - sizeof(memslot)); + gfxFree(ptr); + return new; + } + + void gfxFree(void *ptr) { + register memslot *prev, *p, *new; + + if (!ptr) + return; + + p = Ptr2Slot(ptr); + + // If the next slot is free (and contiguous) merge it into this one + if ((char *)p + p->sz == (char *)p->next) { + for (prev = 0, new = freeSlots; new != 0; prev = new, new = NextFree(new)) { + if (new == p->next) { + p->next = new->next; + p->sz += new->sz; + if (prev) + NextFree(prev) = NextFree(new); + else + freeSlots = NextFree(new); + if (lastSlot == new) + lastSlot = p; + break; + } + } + } + + // Add it into the free chain + NextFree(p) = freeSlots; + freeSlots = p; + } +#endif + +#endif /* GOS_NEED_X_HEAP */ diff --git a/src/gos/gos_x_heap.h b/src/gos/gos_x_heap.h new file mode 100644 index 00000000..3612989c --- /dev/null +++ b/src/gos/gos_x_heap.h @@ -0,0 +1,62 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * The raw32 GOS implementation supports any 32 bit processor with or without an + * underlying operating system. It uses cooperative multi-tasking. Be careful + * when writing device drivers not to disturb the assumptions this creates by performing + * call-backs to uGFX code unless you define the INTERRUPTS_OFF() and INTERRUPTS_ON() macros. + * It still requires some C runtime library support... + * enough startup to initialise the stack, interrupts, static data etc and call main(). + * setjmp() and longjmp() - for threading + * memcpy() - for heap and threading + * malloc(), realloc and free() - if GFX_OS_HEAP_SIZE == 0 + * + * You must also define the following routines in your own code so that timing functions will work... + * systemticks_t gfxSystemTicks(void); + * systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + */ +#ifndef _GOS_X_HEAP_H +#define _GOS_X_HEAP_H + +#if GOS_NEED_X_HEAP + + +/*===========================================================================*/ +/* Special Macros */ +/*===========================================================================*/ + +/** + * @brief Set the maximum size of the heap. + * @note If set to 0 then the C runtime library malloc() and free() are used. + */ +#ifndef GFX_OS_HEAP_SIZE + #define GFX_OS_HEAP_SIZE 0 +#endif + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + #if GFX_OS_HEAP_SIZE != 0 + void gfxAddHeapBlock(void *ptr, size_t sz); + #endif + + void *gfxAlloc(size_t sz); + void *gfxRealloc(void *ptr, size_t oldsz, size_t newsz); + void gfxFree(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* GOS_NEED_X_HEAP */ +#endif /* _GOS_X_HEAP_H */ diff --git a/src/gos/gos_x_threads.c b/src/gos/gos_x_threads.c new file mode 100644 index 00000000..8a781b21 --- /dev/null +++ b/src/gos/gos_x_threads.c @@ -0,0 +1,672 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GOS_NEED_X_THREADS + +/********************************************************* + * Semaphores and critical region functions + *********************************************************/ + +#if !defined(INTERRUPTS_OFF) || !defined(INTERRUPTS_ON) + #define INTERRUPTS_OFF() + #define INTERRUPTS_ON() +#endif + +void gfxSystemLock(void) { + INTERRUPTS_OFF(); +} + +void gfxSystemUnlock(void) { + INTERRUPTS_ON(); +} + +void gfxMutexInit(gfxMutex *pmutex) { + pmutex[0] = 0; +} + +void gfxMutexEnter(gfxMutex *pmutex) { + INTERRUPTS_OFF(); + while (pmutex[0]) { + INTERRUPTS_ON(); + gfxYield(); + INTERRUPTS_OFF(); + } + pmutex[0] = 1; + INTERRUPTS_ON(); +} + +void gfxMutexExit(gfxMutex *pmutex) { + pmutex[0] = 0; +} + +void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit) { + psem->cnt = val; + psem->limit = limit; +} + +bool_t gfxSemWait(gfxSem *psem, delaytime_t ms) { + systemticks_t starttm, delay; + + // Convert our delay to ticks + starttm = 0; + switch (ms) { + case TIME_IMMEDIATE: + delay = TIME_IMMEDIATE; + break; + case TIME_INFINITE: + delay = TIME_INFINITE; + break; + default: + delay = gfxMillisecondsToTicks(ms); + if (!delay) delay = 1; + starttm = gfxSystemTicks(); + } + + INTERRUPTS_OFF(); + while (psem->cnt <= 0) { + INTERRUPTS_ON(); + // Check if we have exceeded the defined delay + switch (delay) { + case TIME_IMMEDIATE: + return FALSE; + case TIME_INFINITE: + break; + default: + if (gfxSystemTicks() - starttm >= delay) + return FALSE; + break; + } + gfxYield(); + INTERRUPTS_OFF(); + } + psem->cnt--; + INTERRUPTS_ON(); + return TRUE; +} + +bool_t gfxSemWaitI(gfxSem *psem) { + if (psem->cnt <= 0) + return FALSE; + psem->cnt--; + return TRUE; +} + +void gfxSemSignal(gfxSem *psem) { + INTERRUPTS_OFF(); + gfxSemSignalI(psem); + INTERRUPTS_ON(); +} + +void gfxSemSignalI(gfxSem *psem) { + if (psem->cnt < psem->limit) + psem->cnt++; +} + +/********************************************************* + * Sleep functions + *********************************************************/ + +void gfxSleepMilliseconds(delaytime_t ms) { + systemticks_t starttm, delay; + + // Safety first + switch (ms) { + case TIME_IMMEDIATE: + return; + case TIME_INFINITE: + while(1) + gfxYield(); + return; + } + + // Convert our delay to ticks + delay = gfxMillisecondsToTicks(ms); + starttm = gfxSystemTicks(); + + do { + gfxYield(); + } while (gfxSystemTicks() - starttm < delay); +} + +void gfxSleepMicroseconds(delaytime_t ms) { + systemticks_t starttm, delay; + + // Safety first + switch (ms) { + case TIME_IMMEDIATE: + return; + case TIME_INFINITE: + while(1) + gfxYield(); + return; + } + + // Convert our delay to ticks + delay = gfxMillisecondsToTicks(ms/1000); + starttm = gfxSystemTicks(); + + do { + gfxYield(); + } while (gfxSystemTicks() - starttm < delay); +} + +/********************************************************* + * Threading functions + *********************************************************/ + +/** For each scheduler the following need to be defined... + * + * void _gfxThreadsInit(void); - Initialise the scheduler + * void _gfxStartThread(thread *oldt, thread *newt); - Start a new thread + * void _gfxTaskSwitch(thread *oldt, thread *newt); - Switch to a different thread + * + */ + +typedef struct thread { + struct thread * next; // Next thread + int flags; // Flags + #define FLG_THD_ALLOC 0x0001 + #define FLG_THD_MAIN 0x0002 + #define FLG_THD_DEAD 0x0004 + #define FLG_THD_WAIT 0x0008 + size_t size; // Size of the thread stack (including this structure) + threadreturn_t (*fn)(void *param); // Thread function + void * param; // Parameter for the thread function + void * cxt; // The current thread context. + } thread; + +typedef struct threadQ { + thread *head; + thread *tail; +} threadQ; + +static threadQ readyQ; // The list of ready threads +static threadQ deadQ; // Where we put threads waiting to be deallocated +static thread * current; // The current running thread +static thread mainthread; // The main thread context + +#if GFX_CPU == GFX_CPU_UNKNOWN + + #include // Prototype for memcpy() + #include + + /** + * Some compilers define a _setjmp() and a setjmp(). + * The difference between them is that setjmp() saves the signal masks. + * That is of no use to us so we prefer to use the _setjmp() methods. + * If they don't exist compile them to be the standard setjmp() function. + * Similarly for longjmp(). + */ + #if (!defined(setjmp) && !defined(_setjmp)) || defined(__KEIL__) || defined(__C51__) + #define CXT_SAVE setjmp + #else + #define CXT_SAVE _setjmp + #endif + #if (!defined(longjmp) && !defined(_longjmp)) || defined(__KEIL__) || defined(__C51__) + #define CXT_RESTORE longjmp + #else + #define CXT_RESTORE _longjmp + #endif + + // A place to store the main thread context. + // All other threads will store the context directly after the thread structure (as part of the stack space). + static jmp_buf maincxt; + + /** + * There are some compilers we know how they store the jmpbuf. For those + * we can use the constant macro definitions. For others we have to "auto-detect". + * Auto-detection is hairy and there is no guarantee it will work on all architectures. + * For those it doesn't - read the compiler manuals and the library source code to + * work out the correct macro values. + * You can use the debugger to work out the values for your compiler and put them here. + * Defining these macros as constant values makes the system behaviour guaranteed but also + * makes your code compiler and cpu architecture dependent. It also saves a heap of code + * and a few bytes of RAM. + * + * MACROS: + * + * AUTO_DETECT_STACKFRAME TRUE/FALSE - TRUE to auto-detect stack frame structure + * STACK_DIR_UP Macro/bool_t - TRUE if the stack grows up instead of down + * MASK1 Macro/uint32_t - The 1st mask of jmp_buf elements that need relocation + * MASK2 Macro/uint32_t - The 2nd mask of jmp_buf elements that need relocation + * STACK_BASE Macro/size_t - The base of the stack frame relative to the local variables + * _gfxThreadsInit() Macro/Function - Initialise the scheduler + * + */ + #if GFX_COMPILER == GFX_COMPILER_MINGW32 + + #define AUTO_DETECT_STACKFRAME FALSE + #define STACK_DIR_UP FALSE + #define MASK1 0x00000011 + #define MASK2 0x00000000 + #define STACK_BASE 12 + #define _gfxThreadsInit() mainthread.cxt = maincxt + + #else + + // Use auto-detection of the stack frame format + // Assumes all the relevant stuff to be relocated is in the first 256 bytes of the jmpbuf. + #define AUTO_DETECT_STACKFRAME TRUE + #define STACK_DIR_UP stackdirup // TRUE if the stack grow up instead of down + #define MASK1 jmpmask1 // The 1st mask of jmp_buf elements that need relocation + #define MASK2 jmpmask2 // The 2nd mask of jmp_buf elements that need relocation + #define STACK_BASE stackbase // The base of the stack frame relative to the local variables + + // The structure for the saved stack frame information + typedef struct saveloc { + char * localptr; + jmp_buf cxt; + } saveloc; + + static bool_t stackdirup; + static uint32_t jmpmask1; + static uint32_t jmpmask2; + static size_t stackbase; + static saveloc *pframeinfo; + + // These two functions are not static to prevent the compiler removing them as functions + void _gfxGetStackState(void) { + char *c; + pframeinfo->localptr = (char *)&c; + CXT_SAVE(pframeinfo->cxt); + } + void _gfxGetStackStateInFn(void) { + pframeinfo++; + _gfxGetStackState(); + pframeinfo--; + } + static void _gfxThreadsInit(void) { + uint32_t i; + char ** pout; + char ** pin; + size_t diff; + char * framebase; + saveloc tmpsaveloc[2]; + + // Create the main thread context + mainthread.cxt = maincxt; + + // Allocate a buffer to store our test data + pframeinfo = tmpsaveloc; + + // Get details of the stack frame from within a function + _gfxGetStackStateInFn(); + + // Get details of the stack frame outside the function + _gfxGetStackState(); + + /* Work out the frame entries to relocate by treating the jump buffer as an array of pointers */ + stackdirup = pframeinfo[1].localptr > pframeinfo[0].localptr; + pout = (char **)pframeinfo[0].cxt; + pin = (char **)pframeinfo[1].cxt; + diff = pframeinfo[0].localptr - pframeinfo[1].localptr; + framebase = pframeinfo[0].localptr; + jmpmask1 = jmpmask2 = 0; + for (i = 0; i < sizeof(jmp_buf)/sizeof(char *); i++, pout++, pin++) { + if ((size_t)(*pout - *pin) == diff) { + if (i < 32) + jmpmask1 |= 1 << i; + else + jmpmask2 |= 1 << (i-32); + + if (stackdirup) { + if (framebase > *pout) + framebase = *pout; + } else { + if (framebase < *pout) + framebase = *pout; + } + } + } + stackbase = stackdirup ? (pframeinfo[0].localptr - framebase) : (framebase - pframeinfo[0].localptr); + } + + #endif + + // Move the stack frame and relocate the context data + static void _gfxAdjustCxt(thread *t) { + char ** s; + char * nf; + int diff; + uint32_t i; + + // Copy the stack frame + #if AUTO_DETECT_STACKFRAME + if (STACK_DIR_UP) { // Stack grows up + nf = (char *)(t) + sizeof(thread) + sizeof(jmp_buf) + stackbase; + memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *)); + } else { // Stack grows down + nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); + memcpy(nf, &s, stackbase+sizeof(char *)); + } + #elif STACK_DIR_UP + // Stack grows up + nf = (char *)(t) + sizeof(thread) + sizeof(jmp_buf) + stackbase; + memcpy(t+1, (char *)&s - stackbase, stackbase+sizeof(char *)); + #else + // Stack grows down + nf = (char *)(t) + t->size - (stackbase + sizeof(char *)); + memcpy(nf, &s, stackbase+sizeof(char *)); + #endif + + // Relocate the context data + s = (char **)(t->cxt); + diff = nf - (char *)&s; + + // Relocate the elements we know need to be relocated + for (i = MASK1; i ; i >>= 1, s++) { + if ((i & 1)) + *s += diff; + } + #ifdef MASK2 + s = (char **)(t->cxt)+32; + for (i = MASK2; i ; i >>= 1, s++) { + if ((i & 1)) + *s += diff; + } + #endif + } + static void _gfxXSwitch(thread *oldt, thread *newt, bool_t doBuildFrame) { + + // Save the old context + if (CXT_SAVE(oldt->cxt)) return; + + // Do we need to build a new context? + if (doBuildFrame) { + + // Save our existing context as a starting point for the new context + newt->cxt = newt+1; + if (CXT_SAVE(newt->cxt)) { + + // We are now running the new thread + + // We can't use any of the above function parameters here + // as we are on a different stack. + + // Run the users function. + gfxThreadExit(current->fn(current->param)); + + // We never get here as gfxThreadExit() never returns + } + + // Adjust the new context so the stack references are correct + _gfxAdjustCxt(newt); + } + + // Start the new context + CXT_RESTORE(newt->cxt, 1); + } + + #define _gfxTaskSwitch(oldt, newt) _gfxXSwitch(oldt, newt, FALSE) + #define _gfxStartThread(oldt, newt) _gfxXSwitch(oldt, newt, TRUE) + +#elif GFX_CPU == GFX_CPU_CORTEX_M0 || GFX_CPU == GFX_CPU_CORTEX_M1 + + // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 + // The context is saved at the current stack location and a pointer is maintained in the thread structure. + + #define _gfxThreadsInit() + + static __attribute__((pcs("aapcs"),naked)) void _gfxTaskSwitch(thread *oldt, thread *newt) { + __asm__ volatile ( "push {r4, r5, r6, r7, lr} \n\t" + "mov r4, r8 \n\t" + "mov r5, r9 \n\t" + "mov r6, r10 \n\t" + "mov r7, r11 \n\t" + "push {r4, r5, r6, r7} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + "pop {r4, r5, r6, r7} \n\t" + "mov r8, r4 \n\t" + "mov r9, r5 \n\t" + "mov r10, r6 \n\t" + "mov r11, r7 \n\t" + "pop {r4, r5, r6, r7, pc} \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + } + + static __attribute__((pcs("aapcs"),naked)) void _gfxStartThread(thread *oldt, thread *newt) { + newt->cxt = (char *)newt + newt->size; + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" // save current context + "str sp, %[oldtcxt] \n\t" // save context pointer + "ldr sp, %[newtcxt] \n\t" // load new context pointer + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + + // Run the users function + gfxThreadExit(current->fn(current->param)); + } + +#elif GFX_CPU == GFX_CPU_CORTEX_M3 || GFX_CPU == GFX_CPU_CORTEX_M4 || GFX_CPU == GFX_CPU_CORTEX_M7 + + // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 + // The context is saved at the current stack location and a pointer is maintained in the thread structure. + + #if CORTEX_USE_FPU + #warning "GOS Threads: You have specified GFX_CPU=GFX_CPU_CORTX_M? with no hardware floating point support but CORTEX_USE_FPU is TRUE. Try using GFX_CPU_GFX_CPU_CORTEX_M?_FP instead" + #endif + + #define _gfxThreadsInit() + + static __attribute__((pcs("aapcs"),naked)) void _gfxTaskSwitch(thread *oldt, thread *newt) { + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + "pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + } + + static __attribute__((pcs("aapcs"),naked)) void _gfxStartThread(thread *oldt, thread *newt) { + newt->cxt = (char *)newt + newt->size; + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + + // Run the users function + gfxThreadExit(current->fn(current->param)); + } + +#elif GFX_CPU == GFX_CPU == GFX_CPU_CORTEX_M4_FP || GFX_CPU == GFX_CPU_CORTEX_M7_FP + + // Use the EABI calling standard (ARM's AAPCS) - Save r4 - r11 and floating point + // The context is saved at the current stack location and a pointer is maintained in the thread structure. + + #if !CORTEX_USE_FPU + #warning "GOS Threads: You have specified GFX_CPU=GFX_CPU_CORTX_M?_FP with hardware floating point support but CORTEX_USE_FPU is FALSE. Try using GFX_CPU_GFX_CPU_CORTEX_M? instead" + #endif + + #define _gfxThreadsInit() + + static __attribute__((pcs("aapcs-vfp"),naked)) void _gfxTaskSwitch(thread *oldt, thread *newt) { + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "vpush {s16-s31} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + "vpop {s16-s31} \n\t" + "pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + } + + static __attribute__((pcs("aapcs-vfp"),naked)) void _gfxStartThread(thread *oldt, thread *newt) { + newt->cxt = (char *)newt + newt->size; + __asm__ volatile ( "push {r4, r5, r6, r7, r8, r9, r10, r11, lr} \n\t" + "vpush {s16-s31} \n\t" + "str sp, %[oldtcxt] \n\t" + "ldr sp, %[newtcxt] \n\t" + : [newtcxt] "=m" (newt->cxt) + : [oldtcxt] "m" (oldt->cxt) + : "memory"); + + // Run the users function + gfxThreadExit(current->fn(current->param)); + } + +#else + #error "GOS Threads: Unsupported Scheduler. Try setting GFX_CPU = GFX_CPU_UNKNOWN" +#endif + +static void Qinit(threadQ * q) { + q->head = q->tail = 0; +} + +static void Qadd(threadQ * q, thread *t) { + t->next = 0; + if (q->head) { + q->tail->next = t; + q->tail = t; + } else + q->head = q->tail = t; +} + +static thread *Qpop(threadQ * q) { + struct thread * t; + + if (!q->head) + return 0; + t = q->head; + q->head = t->next; + return t; +} + +void _gosThreadsInit(void) { + Qinit(&readyQ); + + mainthread.next = 0; + mainthread.size = sizeof(thread); + mainthread.flags = FLG_THD_MAIN; + mainthread.fn = 0; + mainthread.param = 0; + + _gfxThreadsInit(); + + current = &mainthread; +} + +gfxThreadHandle gfxThreadMe(void) { + return (gfxThreadHandle)current; +} + +// Check if there are dead processes to deallocate +static void cleanUpDeadThreads(void) { + thread *p; + + while ((p = Qpop(&deadQ))) + gfxFree(p); +} + +void gfxYield(void) { + thread *me; + + // Clean up zombies + cleanUpDeadThreads(); + + // Is there another thread to run? + if (!readyQ.head) + return; + + Qadd(&readyQ, me = current); + current = Qpop(&readyQ); + _gfxTaskSwitch(me, current); +} + +// This routine is not currently public - but it could be. +void gfxThreadExit(threadreturn_t ret) { + thread *me; + + // Save the results in case someone is waiting + me = current; + me->param = (void *)ret; + me->flags |= FLG_THD_DEAD; + + // Add us to the dead list if we need deallocation as we can't free ourselves. + // If someone is waiting on the thread they will do the cleanup. + if ((me->flags & (FLG_THD_ALLOC|FLG_THD_WAIT)) == FLG_THD_ALLOC) + Qadd(&deadQ, me); + + // Set the next thread. Exit if it was the last thread + if (!(current = Qpop(&readyQ))) + gfxExit(); + + // Switch to the new thread + _gfxTaskSwitch(me, current); + + // We never get back here as we didn't re-queue ourselves +} + +gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param) { + thread * t; + thread * me; + (void) prio; + + // Ensure we have a minimum stack size + if (stacksz < sizeof(thread)+64) { + stacksz = sizeof(thread)+64; + stackarea = 0; + } + + if (stackarea) { + t = (thread *)stackarea; + t->flags = 0; + } else { + t = (thread *)gfxAlloc(stacksz); + if (!t) + return 0; + t->flags = FLG_THD_ALLOC; + } + t->size = stacksz; + t->fn = fn; + t->param = param; + + // Add the current thread to the queue because we are starting a new thread. + me = current; + Qadd(&readyQ, me); + current = t; + + _gfxStartThread(me, t); + + // Return the new thread handle + return t; +} + +threadreturn_t gfxThreadWait(gfxThreadHandle th) { + thread * t; + + t = th; + if (t == current) + return -1; + + // Mark that we are waiting + t->flags |= FLG_THD_WAIT; + + // Wait for the thread to die + while(!(t->flags & FLG_THD_DEAD)) + gfxYield(); + + // Unmark + t->flags &= ~FLG_THD_WAIT; + + // Clean up resources if needed + if (t->flags & FLG_THD_ALLOC) + gfxFree(t); + + // Return the status left by the dead process + return (threadreturn_t)t->param; +} + +#endif /* GFX_USE_OS_RAW32 */ diff --git a/src/gos/gos_x_threads.h b/src/gos/gos_x_threads.h new file mode 100644 index 00000000..78c30ac4 --- /dev/null +++ b/src/gos/gos_x_threads.h @@ -0,0 +1,103 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This threading implementation supports most 32 bit processors with or without an + * underlying operating system. It uses cooperative multi-tasking. Be careful + * when writing device drivers not to disturb the assumptions this creates by performing + * call-backs from interrupt handlers to uGFX code unless you define the INTERRUPTS_OFF() + * and INTERRUPTS_ON() macros. + * It still requires some C runtime library support for the setjmp implementation... + * setjmp() and longjmp() - for threading + * memcpy() - for heap and threading + * + * You must also define the following routines in your own code so that timing functions will work... + * systemticks_t gfxSystemTicks(void); + * systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + */ +#ifndef _GOS_X_THREADS_H +#define _GOS_X_THREADS_H + +#if GOS_NEED_X_THREADS + +typedef uint32_t delaytime_t; +typedef uint32_t systemticks_t; +typedef short semcount_t; +typedef int threadreturn_t; +typedef int threadpriority_t; + +#define DECLARE_THREAD_FUNCTION(fnName, param) threadreturn_t fnName(void *param) +#define DECLARE_THREAD_STACK(name, sz) uint8_t name[sz]; + +#define TIME_IMMEDIATE 0 +#define TIME_INFINITE ((delaytime_t)-1) +#define MAX_SEMAPHORE_COUNT 0x7FFF +#define LOW_PRIORITY 0 +#define NORMAL_PRIORITY 1 +#define HIGH_PRIORITY 2 + +typedef struct { + semcount_t cnt; + semcount_t limit; +} gfxSem; + +typedef uint32_t gfxMutex; +typedef void * gfxThreadHandle; + +#ifdef __cplusplus +extern "C" { +#endif + + // Required timing functions - supplied by the user or the operating system + systemticks_t gfxSystemTicks(void); + systemticks_t gfxMillisecondsToTicks(delaytime_t ms); + + // Sleep Functions + void gfxSleepMilliseconds(delaytime_t ms); + void gfxSleepMicroseconds(delaytime_t ms); + void gfxYield(void); + + // System Locking + void gfxSystemLock(void); + void gfxSystemUnlock(void); + + // Mutexes + void gfxMutexInit(gfxMutex *pmutex); + #define gfxMutexDestroy(pmutex) + void gfxMutexEnter(gfxMutex *pmutex); + void gfxMutexExit(gfxMutex *pmutex); + + // Semaphores + void gfxSemInit(gfxSem *psem, semcount_t val, semcount_t limit); + #define gfxSemDestroy(psem) + bool_t gfxSemWait(gfxSem *psem, delaytime_t ms); + bool_t gfxSemWaitI(gfxSem *psem); + void gfxSemSignal(gfxSem *psem); + void gfxSemSignalI(gfxSem *psem); + + // Deprecated Semaphore functions (they still work here) + #define gfxSemCounter(psem) ((psem)->cnt) + #define gfxSemCounterI(psem) ((psem)->cnt) + + // Threads + gfxThreadHandle gfxThreadCreate(void *stackarea, size_t stacksz, threadpriority_t prio, DECLARE_THREAD_FUNCTION((*fn),p), void *param); + #define gfxThreadClose(thread) + threadreturn_t gfxThreadWait(gfxThreadHandle thread); + gfxThreadHandle gfxThreadMe(void); + + /** The following is not part of the public ugfx API has some operating systems + * simply do not provide this capability. + * For RAW32 we need it anyway so we might as well declare it here. + */ + void gfxThreadExit(threadreturn_t ret); + +#ifdef __cplusplus +} +#endif + +#endif /* GOS_NEED_X_THREADS */ +#endif /* _GOS_X_THREADS_H */