/************************************************************************/ /* */ /* File: core.c */ /* Description: kernel entry point */ /* Version: 1.0 */ /* Author: Otto Mattik */ /* */ /* (C)Copyright Otto Mattik 2014-2021. */ /* */ /* This file is a part of 'armen' (a tiny operating system). */ /* 'armen' is distributed under the CeCILL-V2.1 licence. For more */ /* details about this licence, please visit the website cecill.info */ /* */ /************************************************************************/ #include "core.h" static uint32_t _time; extern void* main( void* ); /* * called from armen_start (see assembly code) * * input: * ram_start beginning of free ram * ram_end end of ram * output: */ void _sys_init( void* ram_start, void* ram_end ) { register uint8_t i; register uint16_t stack_per_thread; register void* stack; /* * initialize stacks in table of threads */ stack_per_thread = (ram_end - ram_start - IDLE_STACK_SIZE) / (ARMEN_THREADS - 1); stack = ram_end; for( i = 1; i < ARMEN_THREADS; i++ ) { _threads[i].istack = stack; stack -= stack_per_thread; } /* * thread0 = idle */ _threads[0].istack = stack; _threads[0].cstack = _init_context( _idle, 0, stack ); _threads[0].rank = _threads[0].order = 255; _threads[0].entry = _idle; _threads[0].status = TS_RUNNING; /* * thread1 = main */ _threads[1].cstack = _threads[1].istack; _threads[1].rank = 127; _threads[1].entry = main; _threads[1].status = TS_RUNNING; _cur_thread = 1; #if defined(UART) && (UART != 0) _uart_init( ); #endif } /* * called by timer every millisecond (see assembly code) * * input: * output: */ void _sys_clock( void ) { register uint8_t i; register _thread_t* pthread; ++_time; /* * update priorities */ for( i = 1; i < ARMEN_THREADS; i++ ) { pthread = &_threads[i]; if( pthread->status == TS_UNUSED ) continue; if( pthread->status == TS_RUNNING ) { if( i == _cur_thread ) { if( pthread->order != 254 ) ++pthread->order; } else { if( pthread->order != 0 ) --pthread->order; } } /* * update thread timer(s) */ else { if( pthread->timer1 != 0 && --pthread->timer1 == 0 && (pthread->events & EVENT_TIMER1) != 0 ) { pthread->events &= ~EVENT_TIMER1; pthread->event |= EVENT_TIMER1; pthread->order = 0; pthread->status = TS_RUNNING; } #if (ARMEN_TIMERS == 2) if( pthread->timer2 != 0 && --pthread->timer2 == 0 && (pthread->events & EVENT_TIMER2) != 0 ) { pthread->events &= ~EVENT_TIMER2; pthread->event |= EVENT_TIMER2; pthread->order = 0; pthread->status = TS_RUNNING; } #endif } } } /* * thread election * * input: * sp current value of stack pointer * output: */ void _scheduler( void* sp ) { register uint8_t i, rank, order, elected; register _thread_t* pthread; elected = 0; rank = order = 255; _threads[_cur_thread].cstack = sp; for( i = 1; i < ARMEN_THREADS; i++ ) { pthread = &_threads[i]; if( pthread->status != TS_RUNNING || pthread->order > order || (pthread->order == order && pthread->rank > rank ) ) continue; elected = i; rank = pthread->rank; order = pthread->order; } if( elected != _cur_thread ) { _cur_thread = elected; _restore_context( _threads[elected].cstack ); /* no return */ } } /* * get current time (milliseconds since start-up) * * input: * output: * count of milliseconds */ uint32_t get_time( void ) { return( _time ); } /* * suspend execution of the calling thread * * input: * events mask of events * output: * event occured */ uint16_t wait_event( uint16_t events ) { uint16_t ret = 0; if( events != 0 ) { do { _disable_intr( ); _threads[_cur_thread].events |= events; _threads[_cur_thread].status = TS_SUSPENDED; _schedule( ); } while( (_threads[_cur_thread].event & events) == 0 ) ; ret = _threads[_cur_thread].event & events; _threads[_cur_thread].event &= ~events; } return( ret ); } /* * current value of timer * * input: * timer timer identifier * output: * timer value if success, otherwise -1 */ timer_t get_timer( uint8_t timer ) { #ifdef CHECK_PARAM if( timer < 1 || timer > ARMEN_TIMERS ) return( (timer_t)-1 ); #endif #if (ARMEN_TIMERS == 2) if( timer == 2 ) return( _threads[_cur_thread].timer2 ); #endif return( _threads[_cur_thread].timer1 ); } /* * suspend current thread during a period * * input: * timer timer identifier * msecs period in milliseconds * output: * 0 if success, otherwise -1 */ int8_t set_timer( uint8_t timer, timer_t msecs ) { #ifdef CHECK_PARAM if( timer < 1 || timer > ARMEN_TIMERS ) return( -1 ); #endif #if (ARMEN_TIMERS == 2) if( timer == 2 ) { _threads[_cur_thread].timer2 = msecs; if( msecs != 0 ) _threads[_cur_thread].events &= ~EVENT_TIMER2; else _threads[_cur_thread].events |= EVENT_TIMER2; } else #endif { _threads[_cur_thread].timer1 = msecs; if( msecs != 0 ) _threads[_cur_thread].events &= ~EVENT_TIMER1; else _threads[_cur_thread].events |= EVENT_TIMER1; } return( 0 ); } void sleep( timer_t msecs ) { if( msecs != 0 ) { _threads[_cur_thread].events |= EVENT_TIMER1; _threads[_cur_thread].timer1 = msecs; wait_event( EVENT_TIMER1 ); } } /* * wake up thread(s) on event * * input: * event reason for wake up * param depends on event * output: */ void _wakeup( uint16_t event, void* param ) { register uint8_t i; register _thread_t* pthread; for( i = 1; i < ARMEN_THREADS; i++ ) { pthread = &_threads[i]; if( pthread->status != TS_SUSPENDED || (pthread->events & event) == 0 #if defined(USE_SEM) || (event == EVENT_SEM && pthread->sem != param) #endif || (event == EVENT_JOIN && pthread->joinid != _cur_thread) ) continue; if( event == EVENT_JOIN ) pthread->param = param; pthread->events &= ~event; pthread->event |= event; pthread->order = 0; pthread->status = TS_RUNNING; } } void send_user_event( void ) { register uint8_t iflag; iflag = _disable_intr( ); _wakeup( EVENT_USER, 0 ); _enable_intr( iflag ); }