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.
Diff: mrubyc/rrt0.c
- Revision:
- 0:33feccbba3ff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrubyc/rrt0.c Wed Feb 15 01:03:35 2017 +0000 @@ -0,0 +1,574 @@ +/*! @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 +