This is a port of the mruby/c tutorial Chapter 03 to the mbed environment.
For details, refer to the following.
http://www.s-itoc.jp/activity/research/mrubyc/mrubyc_tutorial/436
Note:There is a change in rtt0.h from the original source in the mruby/c. It was necessary for inclusion in C ++ source.
mrubyc/rrt0.c
- Committer:
- tk_takateku
- Date:
- 2017-02-15
- Revision:
- 0:33feccbba3ff
File content as of revision 0:33feccbba3ff:
/*! @file @brief Realtime multitask monitor for mruby/c <pre> Copyright (C) 2016 Kyushu Institute of Technology. Copyright (C) 2016 Shimane IT Open-Innovation Center. This file is distributed under BSD 3-Clause License. </pre> */ /***** Feature test switches ************************************************/ /***** System headers *******************************************************/ #include <stdint.h> #include <string.h> #include <assert.h> /***** Local headers ********************************************************/ #include "alloc.h" #include "static.h" #include "load.h" #include "class.h" #include "vm.h" #include "rrt0.h" #include "hal/hal.h" /***** Constat values *******************************************************/ const int TIMESLICE_TICK = 10; // 10 * 1ms(HardwareTimer) 255 max /***** Macros ***************************************************************/ /***** Typedefs *************************************************************/ /***** Function prototypes **************************************************/ /***** Local variables ******************************************************/ static MrbcTcb *q_domant_; static MrbcTcb *q_ready_; static MrbcTcb *q_waiting_; static MrbcTcb *q_suspended_; static volatile uint32_t tick_; /***** Global variables *****************************************************/ /***** Signal catching functions ********************************************/ /***** Local functions ******************************************************/ //================================================================ /*! Insert to task queue @param Pointer of target TCB 引数で指定されたタスク(TCB)を、状態別Queueに入れる。 TCBはフリーの状態でなければならない。(別なQueueに入っていてはならない) Queueはpriority_preemption順にソート済みとなる。 挿入するTCBとQueueに同じpriority_preemption値がある場合は、同値の最後に挿入される。 */ static void q_insert_task(MrbcTcb *p_tcb) { MrbcTcb **pp_q; switch( p_tcb->state ) { case TASKSTATE_DOMANT: pp_q = &q_domant_; break; case TASKSTATE_READY: case TASKSTATE_RUNNING: pp_q = &q_ready_; break; case TASKSTATE_WAITING: pp_q = &q_waiting_; break; case TASKSTATE_SUSPENDED: pp_q = &q_suspended_; break; default: assert(!"Wrong task state."); return; } // case insert on top. if((*pp_q == NULL) || (p_tcb->priority_preemption < (*pp_q)->priority_preemption)) { p_tcb->next = *pp_q; *pp_q = p_tcb; assert(p_tcb->next != p_tcb); return; } // find insert point in sorted linked list. MrbcTcb *p = *pp_q; while( 1 ) { if((p->next == NULL) || (p_tcb->priority_preemption < p->next->priority_preemption)) { p_tcb->next = p->next; p->next = p_tcb; assert(p->next != p); return; } p = p->next; } } //================================================================ /*! Delete from task queue @param Pointer of target TCB Queueからタスク(TCB)を取り除く。 */ static void q_delete_task(MrbcTcb *p_tcb) { MrbcTcb **pp_q; switch( p_tcb->state ) { case TASKSTATE_DOMANT: pp_q = &q_domant_; break; case TASKSTATE_READY: case TASKSTATE_RUNNING: pp_q = &q_ready_; break; case TASKSTATE_WAITING: pp_q = &q_waiting_; break; case TASKSTATE_SUSPENDED: pp_q = &q_suspended_; break; default: assert(!"Wrong task state."); return; } if( *pp_q == NULL ) return; if( *pp_q == p_tcb ) { *pp_q = p_tcb->next; p_tcb->next = NULL; return; } MrbcTcb *p = *pp_q; while( p ) { if( p->next == p_tcb ) { p->next = p_tcb->next; p_tcb->next = NULL; return; } p = p->next; } } //================================================================ /*! Find requested task @param Pointer of vm @return Pointer of MrbcTcb. zero is not found. */ static inline MrbcTcb* find_requested_task(mrb_vm *vm) { MrbcTcb *tcb; for( tcb = q_ready_; tcb != NULL; tcb = tcb->next ) { if( tcb->vm == vm ) break; } return tcb; } //================================================================ /*! 一定時間停止(cruby互換) */ static void c_sleep(mrb_vm *vm, mrb_value *v) { MrbcTcb *tcb = find_requested_task(vm); if( tcb == NULL ) return; switch( v[1].tt ) { case MRB_TT_FIXNUM: mrbc_sleep_ms(tcb, GET_INT_ARG(0) * 1000); break; case MRB_TT_FLOAT: mrbc_sleep_ms(tcb, (uint32_t)(GET_FLOAT_ARG(0) * 1000)); break; default: // TODO 引数なしの場合は永久停止 break; } } //================================================================ /*! 一定時間停止(ms単位) */ static void c_sleep_ms(mrb_vm *vm, mrb_value *v) { MrbcTcb *tcb = find_requested_task(vm); if( tcb == NULL ) return; mrbc_sleep_ms(tcb, GET_INT_ARG(0)); } //================================================================ /*! 実行権を手放す */ static void c_relinquish(mrb_vm *vm, mrb_value *v) { MrbcTcb *tcb = find_requested_task(vm); if( tcb == NULL ) return; mrbc_relinquish(tcb); } //================================================================ /*! プライオリティー変更 */ static void c_change_priority(mrb_vm *vm, mrb_value *v) { MrbcTcb *tcb = find_requested_task(vm); if( tcb == NULL ) return; mrbc_change_priority(tcb, GET_INT_ARG(0)); } //================================================================ /*! 実行停止 */ static void c_suspend_task(mrb_vm *vm, mrb_value *v) { MrbcTcb *tcb = find_requested_task(vm); if( tcb == NULL ) return; mrbc_suspend_task(tcb); } //================================================================ /*! 実行再開 */ static void c_resume_task(mrb_vm *vm, mrb_value *v) { MrbcTcb *tcb = find_requested_task(vm); if( tcb == NULL ) return; // TODO: 未デバグ。引数で与えられたTCBのタスクを実行再開する。 mrbc_resume_task(tcb); } //================================================================ /*! TCBを得る */ static void c_get_tcb(mrb_vm *vm, mrb_value *v) { MrbcTcb *tcb = find_requested_task(vm); if( tcb == NULL ) return; // TODO: 未実装。TCBポインタをオブジェクトとして返す。 } /***** Global functions *****************************************************/ //================================================================ /*! Tick timer interrupt handler. */ void mrbc_tick(void) { MrbcTcb *tcb; int flag_preemption = 0; tick_++; // 実行中タスクのタイムスライス値を減らす tcb = q_ready_; if((tcb != NULL) && (tcb->state == TASKSTATE_RUNNING) && (tcb->timeslice > 0)) { tcb->timeslice--; if( tcb->timeslice == 0 ) tcb->vm->flag_preemption = 1; } // 待ちタスクキューから、ウェイクアップすべきタスクを探す tcb = q_waiting_; while( tcb != NULL ) { MrbcTcb *t = tcb; tcb = tcb->next; if( t->wakeup_tick == tick_ ) { q_delete_task(t); t->state = TASKSTATE_READY; t->timeslice = TIMESLICE_TICK; q_insert_task(t); flag_preemption = 1; } } if( flag_preemption ) { tcb = q_ready_; while( tcb != NULL ) { if( tcb->state == TASKSTATE_RUNNING ) tcb->vm->flag_preemption = 1; tcb = tcb->next; } } } //================================================================ /*! initialize */ void mrbc_init(void) { mrbc_init_alloc(); init_static(); hal_init(); // TODO 関数呼び出しが、c_XXX => mrbc_XXX の daisy chain になっている。 // 不要な複雑さかもしれない。要リファクタリング。 mrbc_define_method(0, mrbc_class_object, "sleep", c_sleep); mrbc_define_method(0, mrbc_class_object, "sleep_ms", c_sleep_ms); mrbc_define_method(0, mrbc_class_object, "relinquish", c_relinquish); mrbc_define_method(0, mrbc_class_object, "change_priority", c_change_priority); mrbc_define_method(0, mrbc_class_object, "suspend_task", c_suspend_task); mrbc_define_method(0, mrbc_class_object, "resume_task", c_resume_task); } //================================================================ /*! specify running VM code. @param vm_code pointer of VM byte code. @param tcb Task control block with parameter, or NULL. @retval Pointer of MrbcTcb. @retval NULL is error. */ MrbcTcb* mrbc_create_task(const uint8_t *vm_code, MrbcTcb *tcb) { // allocate Task Control Block if( tcb == NULL ) { tcb = (MrbcTcb*)mrbc_raw_alloc( sizeof(MrbcTcb) ); if( tcb == NULL ) return NULL; // ENOMEM static const MrbcTcb init_val = MRBC_TCB_INITIALIZER; *tcb = init_val; } tcb->timeslice = TIMESLICE_TICK; tcb->priority_preemption = tcb->priority; // assign VM on TCB if( tcb->state != TASKSTATE_DOMANT ) { tcb->vm = vm_open(); if( !tcb->vm ) return 0; // error. can't open VM. // NOTE: memory leak MrbcTcb. but ignore. loca_mrb_array(tcb->vm, vm_code); vm_boot(tcb->vm); } hal_disable_irq(); q_insert_task(tcb); hal_enable_irq(); return tcb; } //================================================================ /*! execute */ int mrbc_run(void) { while( 1 ) { MrbcTcb *tcb = q_ready_; if( tcb == NULL ) { // 実行すべきタスクなし hal_idle_cpu(); continue; } // 実行開始 tcb->state = TASKSTATE_RUNNING; int res = 0; #ifndef MRBC_NO_TIMER tcb->vm->flag_preemption = 0; res = vm_run(tcb->vm); #else while( tcb->timeslice > 0 ) { tcb->vm->flag_preemption = 1; res = vm_run(tcb->vm); tcb->timeslice--; if( res < 0 ) break; if( tcb->state != TASKSTATE_RUNNING ) break; } mrbc_tick(); #endif /* ifndef MRBC_NO_TIMER */ // タスク終了? if( res < 0 ) { hal_disable_irq(); q_delete_task(tcb); tcb->state = TASKSTATE_DOMANT; q_insert_task(tcb); hal_enable_irq(); vm_close(tcb->vm); tcb->vm = 0; if( q_ready_ == NULL && q_waiting_ == NULL && q_suspended_ == NULL ) break; continue; } // タスク切り替え hal_disable_irq(); if( tcb->state == TASKSTATE_RUNNING ) { tcb->state = TASKSTATE_READY; // タイムスライス終了? if( tcb->timeslice == 0 ) { q_delete_task(tcb); tcb->timeslice = TIMESLICE_TICK; q_insert_task(tcb); // insert task on queue last. } } hal_enable_irq(); } return 0; } //================================================================ /*! 実行一時停止 */ void mrbc_sleep_ms(MrbcTcb *tcb, uint32_t ms) { hal_disable_irq(); q_delete_task(tcb); tcb->timeslice = 0; tcb->state = TASKSTATE_WAITING; tcb->wakeup_tick = tick_ + ms; q_insert_task(tcb); hal_enable_irq(); tcb->vm->flag_preemption = 1; } //================================================================ /*! 実行権を手放す */ void mrbc_relinquish(MrbcTcb *tcb) { tcb->timeslice = 0; tcb->vm->flag_preemption = 1; } //================================================================ /*! プライオリティーの変更 TODO: No check, yet. */ void mrbc_change_priority(MrbcTcb *tcb, int priority) { tcb->priority = (uint8_t)priority; tcb->priority_preemption = (uint8_t)priority; tcb->timeslice = 0; tcb->vm->flag_preemption = 1; } //================================================================ /*! 実行停止 */ void mrbc_suspend_task(MrbcTcb *tcb) { hal_disable_irq(); q_delete_task(tcb); tcb->state = TASKSTATE_SUSPENDED; q_insert_task(tcb); hal_enable_irq(); tcb->vm->flag_preemption = 1; } //================================================================ /*! 実行再開 */ void mrbc_resume_task(MrbcTcb *tcb) { hal_disable_irq(); MrbcTcb *t = q_ready_; while( t != NULL ) { if( t->state == TASKSTATE_RUNNING ) t->vm->flag_preemption = 1; t = t->next; } q_delete_task(tcb); tcb->state = TASKSTATE_READY; q_insert_task(tcb); hal_enable_irq(); } #ifdef MRBC_DEBUG #include "console.h" //================================================================ /*! DEBUG print queue */ void pq(MrbcTcb *p_tcb) { MrbcTcb *p; p = p_tcb; while( p != NULL ) { console_printf("%08x ", (int)((uint64_t)p & 0xffffffff)); p = p->next; } console_printf("\n"); p = p_tcb; while( p != NULL ) { console_printf(" pri: %2d ", p->priority_preemption); p = p->next; } console_printf("\n"); p = p_tcb; while( p != NULL ) { console_printf(" nx:%04x ", (int)((uint64_t)p->next & 0xffff)); p = p->next; } console_printf("\n"); } void pqall(void) { // console_printf("<<<<< DOMANT >>>>>\n"); // pq(q_domant_); console_printf("<<<<< READY >>>>>\n"); pq(q_ready_); console_printf("<<<<< WAITING >>>>>\n"); pq(q_waiting_); console_printf("<<<<< SUSPENDED >>>>>\n"); pq(q_suspended_); } #endif