diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gos/gos.mk | 4 | ||||
| -rw-r--r-- | src/gos/gos_arduino.c | 492 | ||||
| -rw-r--r-- | src/gos/gos_arduino.h | 64 | ||||
| -rw-r--r-- | src/gos/gos_chibios.c | 2 | ||||
| -rw-r--r-- | src/gos/gos_ecos.c | 3 | ||||
| -rw-r--r-- | src/gos/gos_freertos.c | 3 | ||||
| -rw-r--r-- | src/gos/gos_mk.c | 2 | ||||
| -rw-r--r-- | src/gos/gos_options.h | 42 | ||||
| -rw-r--r-- | src/gos/gos_raw32.c | 789 | ||||
| -rw-r--r-- | src/gos/gos_raw32.h | 96 | ||||
| -rw-r--r-- | src/gos/gos_rawrtos.c | 3 | ||||
| -rw-r--r-- | src/gos/gos_x_heap.c | 195 | ||||
| -rw-r--r-- | src/gos/gos_x_heap.h | 62 | ||||
| -rw-r--r-- | src/gos/gos_x_threads.c | 672 | ||||
| -rw-r--r-- | src/gos/gos_x_threads.h | 103 | 
15 files changed, 1121 insertions, 1411 deletions
| 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 <string.h>				// 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 <stdlib.h>				// 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();  } - -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 <setjmp.h> /* 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; +systemticks_t gfxMillisecondsToTicks(delaytime_t ms) { +	return ms;  } -#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 @@ -109,6 +132,16 @@   		#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 <string.h>				// 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 <stdlib.h>				// 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 <setjmp.h> /* 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 @@ -26,37 +26,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 <string.h>				// Prototype for memcpy() + + +#if GFX_OS_HEAP_SIZE == 0 +	#include <stdlib.h>				// 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 <string.h>				// Prototype for memcpy() +	#include <setjmp.h> + +	/** +	 * 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 */ | 
