From 4cdb9cf2aa8ceb29d761479367aa205081eaf525 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 18:35:25 +0400 Subject: [PATCH 01/55] changelog: added current dev version section --- stuff/doc_pages/changelog.dox | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 7846f84..03c489f 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -5,6 +5,10 @@ TNeoKernel changelog +\section changelog_current Current development version (BETA) + + - *No changes yet* + \section changelog_v1_02 v1.02 Release date: 2014-10-14. From 512077fc1935fbecf9e84e93c1c10c0e01c87fc7 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 20:24:30 +0400 Subject: [PATCH 02/55] improved comments in tn_sem.c --- src/core/tn_internal.h | 1 - src/core/tn_sem.c | 48 ++++++++++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index ce4d066..f54616c 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -148,7 +148,6 @@ extern struct TN_Task tn_idle_task; /** * Remove all tasks from wait queue, returning the TN_RC_DELETED code. - * Note: this function might sleep. */ void _tn_wait_queue_notify_deleted(struct TN_ListItem *wait_queue); diff --git a/src/core/tn_sem.c b/src/core/tn_sem.c index 19ee77e..b28b652 100644 --- a/src/core/tn_sem.c +++ b/src/core/tn_sem.c @@ -74,6 +74,9 @@ static inline enum TN_RCode _check_param_generic( return rc; } +/** + * Additional param checking when creating semaphore + */ static inline enum TN_RCode _check_param_create( struct TN_Sem *sem, int start_count, @@ -104,6 +107,13 @@ static inline enum TN_RCode _check_param_create( // }}} +/** + * Generic function that performs job from task context + * + * @param sem semaphore to perform job on + * @param p_worker pointer to actual worker function + * @param timeout see `#TN_Timeout` + */ static inline enum TN_RCode _sem_job_perform( struct TN_Sem *sem, int (p_worker)(struct TN_Sem *sem), @@ -114,20 +124,22 @@ static inline enum TN_RCode _sem_job_perform( enum TN_RCode rc = TN_RC_OK; BOOL waited_for_sem = FALSE; + //-- perform additional params checking (if enabled by TN_CHECK_PARAM) rc = _check_param_generic(sem); if (rc != TN_RC_OK){ goto out; } + //-- check that function is called from task context if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; goto out; } - TN_INT_DIS_SAVE(); - - rc = p_worker(sem); + TN_INT_DIS_SAVE(); //-- disable interrupts + rc = p_worker(sem); //-- call actual worker function + //-- if we should wait, put current task to wait if (rc == TN_RC_TIMEOUT && timeout != 0){ _tn_task_curr_to_wait_action( &(sem->wait_queue), TN_WAIT_REASON_SEM, timeout @@ -138,12 +150,13 @@ static inline enum TN_RCode _sem_job_perform( } #if TN_DEBUG + //-- if we're going to wait, _tn_need_context_switch() must return TRUE if (!_tn_need_context_switch() && waited_for_sem){ _TN_FATAL_ERROR(""); } #endif - TN_INT_RESTORE(); + TN_INT_RESTORE(); //-- restore previous interrupts state _tn_switch_context_if_needed(); if (waited_for_sem){ //-- get wait result @@ -154,6 +167,12 @@ static inline enum TN_RCode _sem_job_perform( return rc; } +/** + * Generic function that performs job from interrupt context + * + * @param sem semaphore to perform job on + * @param p_worker pointer to actual worker function + */ static inline enum TN_RCode _sem_job_iperform( struct TN_Sem *sem, int (p_worker)(struct TN_Sem *sem) @@ -162,21 +181,21 @@ static inline enum TN_RCode _sem_job_iperform( TN_INTSAVE_DATA_INT; enum TN_RCode rc = TN_RC_OK; + //-- perform additional params checking (if enabled by TN_CHECK_PARAM) rc = _check_param_generic(sem); if (rc != TN_RC_OK){ goto out; } + //-- check that function is called from interrupt context if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; goto out; } - TN_INT_IDIS_SAVE(); - - rc = p_worker(sem); - - TN_INT_IRESTORE(); + TN_INT_IDIS_SAVE(); //-- disable interrupts + rc = p_worker(sem); //-- call actual worker function + TN_INT_IRESTORE(); //-- restore previous interrupts state out: return rc; @@ -186,6 +205,7 @@ static inline enum TN_RCode _sem_signal(struct TN_Sem *sem) { enum TN_RCode rc = TN_RC_OK; + //-- wake up first (if any) task from the semaphore wait queue if ( !_tn_task_first_wait_complete( &sem->wait_queue, TN_RC_OK, NULL, NULL, NULL @@ -208,6 +228,9 @@ static inline enum TN_RCode _sem_wait(struct TN_Sem *sem) { enum TN_RCode rc = TN_RC_OK; + //-- decrement semaphore count if possible. + // If not, return TN_RC_TIMEOUT + // (it is handled in _sem_job_perform() / _sem_job_iperform()) if (sem->count > 0){ sem->count--; } else { @@ -236,6 +259,7 @@ enum TN_RCode tn_sem_create( { enum TN_RCode rc = TN_RC_OK; + //-- perform additional params checking (if enabled by TN_CHECK_PARAM) rc = _check_param_create(sem, start_count, max_count); if (rc != TN_RC_OK){ goto out; @@ -259,11 +283,13 @@ enum TN_RCode tn_sem_delete(struct TN_Sem * sem) TN_INTSAVE_DATA; enum TN_RCode rc = TN_RC_OK; + //-- perform additional params checking (if enabled by TN_CHECK_PARAM) rc = _check_param_generic(sem); if (rc != TN_RC_OK){ goto out; } + //-- check that function is called from task context if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; goto out; @@ -271,10 +297,10 @@ enum TN_RCode tn_sem_delete(struct TN_Sem * sem) TN_INT_DIS_SAVE(); + //-- Remove all tasks from wait queue, returning the TN_RC_DELETED code. _tn_wait_queue_notify_deleted(&(sem->wait_queue)); - sem->id_sem = 0; //-- Semaphore does not exist now - + sem->id_sem = 0; //-- Semaphore does not exist now TN_INT_RESTORE(); //-- we might need to switch context if _tn_wait_queue_notify_deleted() From c3153266ab058a251b696469615879b696c3626e Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 21:08:43 +0400 Subject: [PATCH 03/55] Interface for architecture-dependent code is cleaned up: - TN_STATE_FLAG__SYS_RUNNING flag is set by tn_sys_start(), so that _tn_arch_context_switch_exit() and _tn_arch_system_start() became identical and replaced by single function _tn_arch_context_switch_nosave() - Comments improved - TN_UWord is used instead of unsigned int --- src/arch/pic32/tn_arch_pic32.c | 72 +++++++++++++-------------- src/arch/pic32/tn_arch_pic32mx_xc32.S | 44 ++-------------- src/arch/tn_arch.h | 72 ++++++++++++--------------- src/core/tn_sys.c | 8 ++- src/core/tn_tasks.c | 8 +-- src/core/tn_tasks.h | 4 +- 6 files changed, 83 insertions(+), 125 deletions(-) diff --git a/src/arch/pic32/tn_arch_pic32.c b/src/arch/pic32/tn_arch_pic32.c index 6bb3576..1110a25 100644 --- a/src/arch/pic32/tn_arch_pic32.c +++ b/src/arch/pic32/tn_arch_pic32.c @@ -89,10 +89,10 @@ TN_UWord *_tn_arch_stack_top_get( // // sizeof(void*) = sizeof(int) //---------------------------------------------------------------------------- -unsigned int *_tn_arch_stack_init( - TN_TaskBody *task_func, - TN_UWord *stack_top, - void *param +TN_UWord *_tn_arch_stack_init( + TN_TaskBody *task_func, + TN_UWord *stack_top, + void *param ) { //-- filling register's position in the stack - for debugging only @@ -100,38 +100,38 @@ unsigned int *_tn_arch_stack_init( *(--stack_top) = 0; *(--stack_top) = 0; *(--stack_top) = 0; - *(--stack_top) = (unsigned int)task_func; //-- EPC - *(--stack_top) = 3; //-- Status: EXL and IE bits are set - *(--stack_top) = (unsigned int)tn_task_exit; //-- ra - *(--stack_top) = 0x30303030L; //-- fp - *(--stack_top) = (unsigned int)&_gp; //-- gp - provided by linker - *(--stack_top) = 0x25252525L; //-- t9 - *(--stack_top) = 0x24242424L; //-- t8 - *(--stack_top) = 0x23232323L; //-- s7 - *(--stack_top) = 0x22222222L; //-- s6 - *(--stack_top) = 0x21212121L; //-- s5 - *(--stack_top) = 0x20202020L; //-- s4 - *(--stack_top) = 0x19191919L; //-- s3 - *(--stack_top) = 0x18181818L; //-- s2 - *(--stack_top) = 0x17171717L; //-- s1 - *(--stack_top) = 0x16161616L; //-- s0 - *(--stack_top) = 0x15151515L; //-- t7 - *(--stack_top) = 0x14141414L; //-- t6 - *(--stack_top) = 0x13131313L; //-- t5 - *(--stack_top) = 0x12121212L; //-- t4 - *(--stack_top) = 0x11111111L; //-- t3 - *(--stack_top) = 0x10101010L; //-- t2 - *(--stack_top) = 0x09090909L; //-- t1 - *(--stack_top) = 0x08080808L; //-- t0 - *(--stack_top) = 0x07070707L; //-- a3 - *(--stack_top) = 0x06060606L; //-- a2 - *(--stack_top) = 0x05050505L; //-- a1 - *(--stack_top) = (unsigned int)param; //-- a0 - task's function argument - *(--stack_top) = 0x03030303L; //-- v1 - *(--stack_top) = 0x02020202L; //-- v0 - *(--stack_top) = 0x01010101L; //-- at - *(--stack_top) = 0x33333333L; //-- hi - *(--stack_top) = 0x32323232L; //-- lo + *(--stack_top) = (TN_UWord)task_func; //-- EPC + *(--stack_top) = 3; //-- Status: EXL and IE bits are set + *(--stack_top) = (TN_UWord)tn_task_exit; //-- ra + *(--stack_top) = 0x30303030L; //-- fp + *(--stack_top) = (TN_UWord)&_gp; //-- gp - provided by linker + *(--stack_top) = 0x25252525L; //-- t9 + *(--stack_top) = 0x24242424L; //-- t8 + *(--stack_top) = 0x23232323L; //-- s7 + *(--stack_top) = 0x22222222L; //-- s6 + *(--stack_top) = 0x21212121L; //-- s5 + *(--stack_top) = 0x20202020L; //-- s4 + *(--stack_top) = 0x19191919L; //-- s3 + *(--stack_top) = 0x18181818L; //-- s2 + *(--stack_top) = 0x17171717L; //-- s1 + *(--stack_top) = 0x16161616L; //-- s0 + *(--stack_top) = 0x15151515L; //-- t7 + *(--stack_top) = 0x14141414L; //-- t6 + *(--stack_top) = 0x13131313L; //-- t5 + *(--stack_top) = 0x12121212L; //-- t4 + *(--stack_top) = 0x11111111L; //-- t3 + *(--stack_top) = 0x10101010L; //-- t2 + *(--stack_top) = 0x09090909L; //-- t1 + *(--stack_top) = 0x08080808L; //-- t0 + *(--stack_top) = 0x07070707L; //-- a3 + *(--stack_top) = 0x06060606L; //-- a2 + *(--stack_top) = 0x05050505L; //-- a1 + *(--stack_top) = (TN_UWord)param; //-- a0 - task's function argument + *(--stack_top) = 0x03030303L; //-- v1 + *(--stack_top) = 0x02020202L; //-- v0 + *(--stack_top) = 0x01010101L; //-- at + *(--stack_top) = 0x33333333L; //-- hi + *(--stack_top) = 0x32323232L; //-- lo return stack_top; } diff --git a/src/arch/pic32/tn_arch_pic32mx_xc32.S b/src/arch/pic32/tn_arch_pic32mx_xc32.S index d3f3759..c1c122a 100644 --- a/src/arch/pic32/tn_arch_pic32mx_xc32.S +++ b/src/arch/pic32/tn_arch_pic32mx_xc32.S @@ -47,9 +47,8 @@ /* Public functions declared in this file */ - .global _tn_arch_context_switch_exit + .global _tn_arch_context_switch_nosave .global _tn_arch_context_switch - .global _tn_arch_system_start .global _tn_arch_inside_isr .global tn_arch_sr_save_int_dis .global tn_arch_sr_restore @@ -67,30 +66,9 @@ *----------------------------------------------------------------------------*/ .set noreorder .set noat - .ent _tn_arch_system_start + .ent _tn_arch_context_switch_nosave -_tn_arch_system_start: - - li $t0, 1 /* 1 -> TN_STATE_FLAG__SYS_RUNNING */ - la $t1, tn_sys_state - sb $t0, 0($t1) - - lw $t1, tn_next_task_to_run - lw $sp, 0($t1) /* get new task's sp */ - la $t0, tn_curr_run_task - j tn_restore_context - sw $t1, 0($t0) /* tn_curr_run_task = tn_next_task_to_run */ - - .end _tn_arch_system_start - -/*---------------------------------------------------------------------------- -* Interrupts should be disabled here -*----------------------------------------------------------------------------*/ - .set noreorder - .set noat - .ent _tn_arch_context_switch_exit - -_tn_arch_context_switch_exit: +_tn_arch_context_switch_nosave: /* get new task's sp */ @@ -102,7 +80,7 @@ _tn_arch_context_switch_exit: sw $t1, 0($t0) /* *t0 = t1; */ /* i.e. tn_curr_run_task = tn_next_task_to_run; */ - .end _tn_arch_context_switch_exit + .end _tn_arch_context_switch_nosave /*---------------------------------------------------------------------------- * @@ -113,20 +91,6 @@ _tn_arch_context_switch_exit: _tn_arch_context_switch: - /* check if task switch needed */ - - /* - la $t0, tn_curr_run_task - la $t1, tn_next_task_to_run - lw $t0, 0($t0) - lw $t1, 0($t1) - bne $t0, $t1, 1f - nop - - sdbbp 0 - -1: - */ /* pend CS0 interrupt */ lui $t0, %hi(IFS0SET) ori $t1, $zero, 2 diff --git a/src/arch/tn_arch.h b/src/arch/tn_arch.h index 8e12c59..9595eef 100644 --- a/src/arch/tn_arch.h +++ b/src/arch/tn_arch.h @@ -89,7 +89,7 @@ void tn_arch_int_en(void); * * @see `tn_arch_sr_restore()` */ -unsigned int tn_arch_sr_save_int_dis(void); +TN_UWord tn_arch_sr_save_int_dis(void); /** * Restore previously saved status register @@ -99,7 +99,7 @@ unsigned int tn_arch_sr_save_int_dis(void); * * @see `tn_arch_sr_save_int_dis()` */ -void tn_arch_sr_restore(unsigned int sr); +void tn_arch_sr_restore(TN_UWord sr); @@ -121,12 +121,21 @@ void tn_arch_sr_restore(unsigned int sr); * size of the stack in `#TN_UWord`-s, not in bytes. */ TN_UWord *_tn_arch_stack_top_get( - TN_UWord *stack_low_address, - int stack_size + TN_UWord *stack_low_address, + int stack_size ); /** - * Should initialize stack for new task and return current stack pointer. + * Should put initial CPU context to the provided stack pointer for new task + * and return current stack pointer. + * + * When resulting context gets restored by `_tn_arch_context_switch_nosave()` + * or `_tn_arch_context_switch()`, the following conditions should be met: + * + * - Interrupts are enabled; + * - Return address is set to `tn_task_exit()`, so that when task body function + * returns, `tn_task_exit()` gets automatially called; + * - Argument 0 contains `param` pointer * * @param task_func * Pointer to task body function. @@ -137,7 +146,7 @@ TN_UWord *_tn_arch_stack_top_get( * * @return current stack pointer (top of the stack) */ -unsigned int *_tn_arch_stack_init( +TN_UWord *_tn_arch_stack_init( TN_TaskBody *task_func, TN_UWord *stack_top, void *param @@ -149,19 +158,19 @@ unsigned int *_tn_arch_stack_init( int _tn_arch_inside_isr(void); /** - * Called whenever we need to switch context to other task. + * Called whenever we need to switch context from one task to another. * * **Preconditions:** * - * * interrupts are enabled; - * * `tn_curr_run_task` points to currently running (preempted) task; - * * `tn_next_task_to_run` points to new task to run. + * - interrupts are enabled; + * - `tn_curr_run_task` points to currently running (preempted) task; + * - `tn_next_task_to_run` points to new task to run. * * **Actions to perform:** * - * * save context of the preempted task to its stack; - * * set `tn_curr_run_task` to `tn_next_task_to_run`; - * * switch context to it. + * - save context of the preempted task to its stack; + * - set `tn_curr_run_task` to `tn_next_task_to_run`; + * - restore context of the newly activated task from its stack. * * @see `tn_curr_run_task` * @see `tn_next_task_to_run` @@ -169,44 +178,25 @@ int _tn_arch_inside_isr(void); void _tn_arch_context_switch(void); /** - * Called when some task calls `tn_task_exit()`. - * - * **Preconditions:** - * - * * interrupts are disabled; - * * `tn_next_task_to_run` is already set to other task. - * - * **Actions to perform:** - * - * * set `tn_curr_run_task` to `tn_next_task_to_run`; - * * switch context to it. - * - * @see `tn_curr_run_task` - * @see `tn_next_task_to_run` - */ -void _tn_arch_context_switch_exit(void); - -/** - * Should perform first context switch (to the task pointed to by - * `tn_next_task_to_run`). + * Called whenever we need to switch context to new task, but don't save + * current context. This happens: + * - At system start, inside `tn_sys_start()`; + * - At task exit, inside `tn_task_exit()` * * **Preconditions:** * - * * no interrupts are set up yet, so, it's like interrupts disabled - * * `tn_next_task_to_run` is already set to idle task. + * - interrupts are disabled; + * - `tn_next_task_to_run` is already set to needed task. * * **Actions to perform:** * - * * set `#TN_STATE_FLAG__SYS_RUNNING` flag in the `tn_sys_state` variable; - * * set `tn_curr_run_task` to `tn_next_task_to_run`; - * * switch context to it. + * - set `tn_curr_run_task` to `tn_next_task_to_run`; + * - restore context of the newly activated task from its stack. * - * @see `#TN_STATE_FLAG__SYS_RUNNING` - * @see `tn_sys_state` * @see `tn_curr_run_task` * @see `tn_next_task_to_run` */ -void _tn_arch_system_start(void); +void _tn_arch_context_switch_nosave(void); #ifdef __cplusplus } /* extern "C" */ diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 2cf67ae..0aa994d 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -238,7 +238,7 @@ void tn_sys_start( int_stack[i] = TN_FILL_STACK_VAL; } - //-- pre-decrement stack + //-- set interrupt's top of the stack tn_int_sp = _tn_arch_stack_top_get( int_stack, int_stack_size @@ -279,8 +279,12 @@ void tn_sys_start( // (by user-provided callback) cb_user_task_create(); + //-- set flag that system is running + // (well, it will be running soon actually) + tn_sys_state |= TN_STATE_FLAG__SYS_RUNNING; + //-- Run OS - first context switch - _tn_arch_system_start(); + _tn_arch_context_switch_nosave(); } diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index b177730..3e59f64 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -647,7 +647,7 @@ void tn_task_exit(enum TN_TaskExitOpt opts) //-- it is here only for TN_INT_DIS_SAVE() normal operation, // but actually we don't need to save current interrupt status: // this function never returns, and interrupt status is restored - // from different task's stack inside `_tn_arch_context_switch_exit()` + // from different task's stack inside `_tn_arch_context_switch_nosave()` // call. TN_INTSAVE_DATA; @@ -666,8 +666,8 @@ void tn_task_exit(enum TN_TaskExitOpt opts) _task_delete(task); } - //-- interrupts will be enabled inside _tn_arch_context_switch_exit() - _tn_arch_context_switch_exit(); + //-- interrupts will be enabled inside _tn_arch_context_switch_nosave() + _tn_arch_context_switch_nosave(); out: return; @@ -1071,7 +1071,7 @@ void _tn_task_clear_dormant(struct TN_Task *task) //--- Init task stack, save pointer to task top of stack, // when not running - task->task_stk = _tn_arch_stack_init( + task->stack_top = _tn_arch_stack_init( task->task_func_addr, task->base_stack_top, task->task_func_param diff --git a/src/core/tn_tasks.h b/src/core/tn_tasks.h index 85357ba..d7f4fdf 100644 --- a/src/core/tn_tasks.h +++ b/src/core/tn_tasks.h @@ -171,10 +171,10 @@ enum TN_TaskExitOpt { * Task */ struct TN_Task { - /// pointer to task's current stack pointer; + /// pointer to task's current top of the stack; /// Note that this field **must** be a first field in the struct, /// this fact is exploited by platform-specific routines. - unsigned int *task_stk; + TN_UWord *stack_top; /// /// queue is used to include task in ready/wait lists struct TN_ListItem task_queue; From 61f85c425288051254ca5ea1be75ff28d256b72f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Tue, 14 Oct 2014 21:23:19 +0400 Subject: [PATCH 04/55] comments improved --- src/arch/example/tn_arch_example.h | 6 +++++- src/arch/pic32/tn_arch_pic32.h | 6 ++++++ src/arch/pic32/tn_arch_pic32_int_vec1.S | 17 ++++++++++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/arch/example/tn_arch_example.h b/src/arch/example/tn_arch_example.h index b861c35..7954cfa 100644 --- a/src/arch/example/tn_arch_example.h +++ b/src/arch/example/tn_arch_example.h @@ -46,6 +46,7 @@ /** * FFS - find first set bit. Used in `_find_next_task_to_run()` function. + * Say, for `0xa8` it should return `3`. * * May be not defined: in this case, naive algorithm will be used. */ @@ -107,6 +108,9 @@ typedef unsigned int TN_UWord; * For compatibility with all platforms, it's recommended to use only values * from 1 to 14, inclusive. * + * TODO: rename to TN_PRIORITIES_MAX_CNT, and add configurable macro + * TN_PRIORITIES_CNT. + * * @see `#TN_INT_WIDTH` */ #define TN_PRIORITIES_CNT TN_INT_WIDTH @@ -117,7 +121,7 @@ typedef unsigned int TN_UWord; #define TN_WAIT_INFINITE 0xFFFFFFFF /** - * Value for initializing the task's stack + * Value for initializing the unused space of task's stack */ #define TN_FILL_STACK_VAL 0xFEEDFACE diff --git a/src/arch/pic32/tn_arch_pic32.h b/src/arch/pic32/tn_arch_pic32.h index 682cf33..0704bb8 100644 --- a/src/arch/pic32/tn_arch_pic32.h +++ b/src/arch/pic32/tn_arch_pic32.h @@ -71,6 +71,7 @@ extern "C" { /*}*/ /** * FFS - find first set bit. Used in `_find_next_task_to_run()` function. + * Say, for `0xa8` it should return `3`. * * May be not defined: in this case, naive algorithm will be used. */ @@ -139,6 +140,11 @@ typedef unsigned int TN_UWord; * Number of priorities available, this value usually matches `#TN_INT_WIDTH`. * For compatibility with all platforms, it's recommended to use only values * from 1 to 14, inclusive. + * + * TODO: rename to TN_PRIORITIES_MAX_CNT, and add configurable macro + * TN_PRIORITIES_CNT. + * + * @see `#TN_INT_WIDTH` */ #define TN_PRIORITIES_CNT TN_INT_WIDTH diff --git a/src/arch/pic32/tn_arch_pic32_int_vec1.S b/src/arch/pic32/tn_arch_pic32_int_vec1.S index 6dbc45d..7cf9462 100644 --- a/src/arch/pic32/tn_arch_pic32_int_vec1.S +++ b/src/arch/pic32/tn_arch_pic32_int_vec1.S @@ -34,6 +34,18 @@ * ******************************************************************************/ +/** + * \file + * + * This file contains ISR for core software interrupt 0 (which is used by the + * kernel internally). + * + * **NOTE:** This file must be included in the main project itself, not in the + * library project. + * + */ + + .section .vector_1,code .align 2 .set nomips16 @@ -42,7 +54,10 @@ .global __vector_dispatch_1 .ent __vector_dispatch_1 + __vector_dispatch_1: - j cs0_int_handler + + j cs0_int_handler .end __vector_dispatch_1 .size __vector_dispatch_1, .-__vector_dispatch_1 + From b05fdc03f64b07008acca6892214008669e0cd96 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 12:51:53 +0400 Subject: [PATCH 05/55] tnkernel_diff now contains info about mutex deadlock detection --- src/core/tn_sys.h | 3 ++- stuff/doc_pages/tnkernel_diff.dox | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/core/tn_sys.h b/src/core/tn_sys.h index eea4a15..f23f03f 100644 --- a/src/core/tn_sys.h +++ b/src/core/tn_sys.h @@ -306,7 +306,8 @@ unsigned int tn_sys_time_get(void); * $(TN_CALL_FROM_MAIN) * $(TN_LEGEND_LINK) * - * **Note:** this function should be called before `tn_sys_start()` + * **Note:** this function should be called from `main()`, before + * `tn_sys_start()`. * * @param cb * Pointer to user-provided callback function. diff --git a/stuff/doc_pages/tnkernel_diff.dox b/stuff/doc_pages/tnkernel_diff.dox index 90dce55..2c1aa4e 100644 --- a/stuff/doc_pages/tnkernel_diff.dox +++ b/stuff/doc_pages/tnkernel_diff.dox @@ -152,6 +152,14 @@ Sometimes I feel lack of mutexes that allow recursive locking. I know there are Sometimes they are really useful though (say, if you want to use some third-party library that requires locking primitives to be recursive), so I decided to implement an option for that: `#TN_MUTEX_REC`. If it is non-zero, mutexes allow recursive locking; otherwise you get `#TN_RC_ILLEGAL_USE` when you try to lock mutex that is already locked by this task. Default value: `1`. +\subsection tnkernel_diff_mutex_deadlock Mutex deadlock detection + +TNeoKernel can notify you by calling your callback function if deadlock occurs. See: + + - compile-time option `#TN_MUTEX_DEADLOCK_DETECT`, + - `tn_callback_deadlock_set()`, + - `#TN_CBDeadlock`. + \subsection tnkernel_diff_new_functions New system services added Several system services were added: From 232552a6b80aed2aae2cb064badc672046d7740a Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 13:22:51 +0400 Subject: [PATCH 06/55] priority 0 is now allowed to use --- src/core/tn_internal.h | 4 ++-- src/core/tn_mutex.c | 4 ++-- src/core/tn_sys.c | 10 +++++----- src/core/tn_tasks.c | 3 --- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index f54616c..300c339 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -88,10 +88,10 @@ extern volatile int tn_created_tasks_cnt; extern volatile enum TN_StateFlag tn_sys_state; /// task that runs now -extern struct TN_Task * tn_curr_run_task; +extern struct TN_Task *tn_curr_run_task; /// task that should run as soon as possible (after context switch) -extern struct TN_Task * tn_next_task_to_run; +extern struct TN_Task *tn_next_task_to_run; /// bitmask of priorities with runnable tasks. /// lowest priority bit (1 << (TN_PRIORITIES_CNT - 1)) should always be set, diff --git a/src/core/tn_mutex.c b/src/core/tn_mutex.c index 60d7054..e86c6ee 100644 --- a/src/core/tn_mutex.c +++ b/src/core/tn_mutex.c @@ -120,8 +120,8 @@ static inline enum TN_RCode _check_param_create( } else if (1 && protocol == TN_MUTEX_PROT_CEILING && (0 - || ceil_priority < 1 - || ceil_priority > (TN_PRIORITIES_CNT - 2) + || ceil_priority < 0 + || ceil_priority >= (TN_PRIORITIES_CNT - 1) ) ) { diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 0aa994d..9fe21b5 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -89,7 +89,7 @@ void *tn_int_sp; //-- System tasks -//-- idle task - priority (TN_PRIORITIES_CNT-1) - lowest +//-- idle task - priority (TN_PRIORITIES_CNT - 1) - lowest struct TN_Task tn_idle_task; static void _idle_task_body(void * par); @@ -171,7 +171,7 @@ static inline enum TN_RCode _idle_task_create( return tn_task_create( (struct TN_Task*)&tn_idle_task, //-- task TCB _idle_task_body, //-- task function - TN_PRIORITIES_CNT - 1, //-- task priority + TN_PRIORITIES_CNT - 1, //-- task priority idle_task_stack, //-- task stack idle_task_stack_size, //-- task stack size // (in int, not bytes) @@ -217,7 +217,7 @@ void tn_sys_start( //-- initial system flags: no flags set (see enum TN_StateFlag) tn_sys_state = (0); - //-- reset bitmask of prioritis with runnable tasks + //-- reset bitmask of priorities with runnable tasks tn_ready_to_run_bmp = 0; //-- reset system time @@ -332,8 +332,8 @@ enum TN_RCode tn_sys_tslice_set(int priority, int ticks) goto out; } - if ( priority <= 0 || priority >= (TN_PRIORITIES_CNT - 1) - || ticks < 0 || ticks > TN_MAX_TIME_SLICE) + if ( priority < 0 || priority >= (TN_PRIORITIES_CNT - 1) + || ticks < 0 || ticks > TN_MAX_TIME_SLICE) { rc = TN_RC_WPARAM; goto out; diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index 3e59f64..d80122b 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -383,9 +383,6 @@ enum TN_RCode tn_task_create( //-- Lightweight checking of system tasks recreation if (0 || (priority == (TN_PRIORITIES_CNT - 1) && !(opts & TN_TASK_CREATE_OPT_IDLE)) - || (priority == 0) //-- there's no more timer task in the kernel, - // but for a kind of compatibility - // it's better to disallow tasks with priority 0 ) { return TN_RC_WPARAM; From 4f5fef74fddaf3c4c552bd9aa1c536925f915881 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 13:59:03 +0400 Subject: [PATCH 07/55] Application is now available to specify how many priority levels does it need for, it helps to save a bit of RAM. --- src/arch/example/tn_arch_example.h | 12 ++++-------- src/arch/pic32/tn_arch_pic32.h | 12 ++++-------- src/tn_cfg_default.h | 22 ++++++++++++++++++++++ stuff/doc_pages/changelog.dox | 4 +++- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/arch/example/tn_arch_example.h b/src/arch/example/tn_arch_example.h index 7954cfa..afdcf9d 100644 --- a/src/arch/example/tn_arch_example.h +++ b/src/arch/example/tn_arch_example.h @@ -104,16 +104,12 @@ typedef unsigned int TN_UWord; /** - * Number of priorities available, this value usually matches `#TN_INT_WIDTH`. - * For compatibility with all platforms, it's recommended to use only values - * from 1 to 14, inclusive. + * Maximum number of priorities available, this value usually matches + * `#TN_INT_WIDTH`. * - * TODO: rename to TN_PRIORITIES_MAX_CNT, and add configurable macro - * TN_PRIORITIES_CNT. - * - * @see `#TN_INT_WIDTH` + * @see TN_PRIORITIES_CNT */ -#define TN_PRIORITIES_CNT TN_INT_WIDTH +#define TN_PRIORITIES_MAX_CNT TN_INT_WIDTH /** * Value for infinite waiting, usually matches `UINT_MAX` diff --git a/src/arch/pic32/tn_arch_pic32.h b/src/arch/pic32/tn_arch_pic32.h index 0704bb8..790c768 100644 --- a/src/arch/pic32/tn_arch_pic32.h +++ b/src/arch/pic32/tn_arch_pic32.h @@ -137,16 +137,12 @@ typedef unsigned int TN_UWord; /** - * Number of priorities available, this value usually matches `#TN_INT_WIDTH`. - * For compatibility with all platforms, it's recommended to use only values - * from 1 to 14, inclusive. + * Maximum number of priorities available, this value usually matches + * `#TN_INT_WIDTH`. * - * TODO: rename to TN_PRIORITIES_MAX_CNT, and add configurable macro - * TN_PRIORITIES_CNT. - * - * @see `#TN_INT_WIDTH` + * @see TN_PRIORITIES_CNT */ -#define TN_PRIORITIES_CNT TN_INT_WIDTH +#define TN_PRIORITIES_MAX_CNT TN_INT_WIDTH /** * Value for infinite waiting, usually matches `UINT_MAX` diff --git a/src/tn_cfg_default.h b/src/tn_cfg_default.h index c88e70e..d14d3d9 100644 --- a/src/tn_cfg_default.h +++ b/src/tn_cfg_default.h @@ -72,6 +72,28 @@ * USER-DEFINED OPTIONS ******************************************************************************/ +/** + * Number of priorities that can be used by application, plus one for idle task + * (which has the lowest priority). This value can't be higher than + * architecture-dependent value `#TN_PRIORITIES_MAX_CNT`, which typically + * equals to width of `int` type. So, for 32-bit systems, max number of + * priorities is 32. + * + * But usually, application needs much less: I can imagine **at most** 4-5 + * different priorities, plus one for the idle task. + * + * Do note also that each possible priority level takes RAM: two pointers for + * linked list and one `short` for time slice value, so on 32-bit system it + * takes 10 bytes. So, with default value of 32 priorities available, it takes + * 320 bytes. If you set it, say, to 5, you save `270` bytes, which might be + * notable. + * + * Default: `#TN_PRIORITIES_MAX_CNT`. + */ +#ifndef TN_PRIORITIES_CNT +# define TN_PRIORITIES_CNT TN_PRIORITIES_MAX_CNT +#endif + /** * Enables additional param checking for most of the system functions. * It's surely useful for debug, but probably better to remove in release. diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 03c489f..6e15d13 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -7,7 +7,9 @@ TNeoKernel changelog \section changelog_current Current development version (BETA) - - *No changes yet* + - Application is now available to specify how many priority levels does it + need for, it helps to save a bit of RAM. For details, refer to + `#TN_PRIORITIES_CNT`. \section changelog_v1_02 v1.02 From 63531e12862a690b9bf5ae27e73017454e0c67f9 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 14:02:58 +0400 Subject: [PATCH 08/55] changelog updated --- stuff/doc_pages/changelog.dox | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 6e15d13..44c7cae 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -7,6 +7,9 @@ TNeoKernel changelog \section changelog_current Current development version (BETA) + - Priority 0 is now allowed to use by application (in the original TNKernel, + it was reserved for the timer task, but TNeoKernel \ref + tnkernel_diff_timer_task "does not have timer task") - Application is now available to specify how many priority levels does it need for, it helps to save a bit of RAM. For details, refer to `#TN_PRIORITIES_CNT`. From 51008ed1d46f8794614ec94ab62c78b8af13f27f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 14:37:43 +0400 Subject: [PATCH 09/55] dev wiki: plans updated --- stuff/vimwiki/index.wiki | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index aabffa2..41261ec 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -11,8 +11,8 @@ == Plans == - * [ ] Make TN_PRIORITIES_CNT customizable. For 32-bit systems, TN_PRIORITIES_CNT is 32, and since we have struct TN_ListItem tn_ready_list[TN_PRIORITIES_CNT] and unsigned short tn_tslice_ticks[TN_PRIORITIES_CNT], it takes 10 bytes for each priority. So, overhead is 320 bytes, which is notable. We usually need about 5 priorities, so, it would take 50 bytes. * [ ] Event group connection (at least, it would be useful to connect event group to queue) + * [ ] Exchange object: see AVIX User Guide & Reference Guide, 6.6.8 * [ ] Event group: probably, add mask of flags that should be cleared automatically when any task `wait`s for it * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. * [ ] Examples From f5b63aef984d542134136fcc4db7e71e1859a89c Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 16:12:45 +0400 Subject: [PATCH 10/55] TN_TASK_STACK_DEF() -> TN_STACK_ARR_DEF() --- .../pic32/basic/src/tn_pic32_example_basic.c | 12 +++++------ src/core/tn_oldsymbols.h | 7 +++++++ src/core/tn_sys.h | 20 +++++++++++++++++-- src/core/tn_tasks.h | 19 +++--------------- stuff/doc_pages/quick_guide.dox | 2 +- stuff/doc_pages/tnkernel_diff.dox | 2 +- 6 files changed, 36 insertions(+), 26 deletions(-) diff --git a/examples/pic32/basic/src/tn_pic32_example_basic.c b/examples/pic32/basic/src/tn_pic32_example_basic.c index cddfc2f..cc0d2b3 100644 --- a/examples/pic32/basic/src/tn_pic32_example_basic.c +++ b/examples/pic32/basic/src/tn_pic32_example_basic.c @@ -92,14 +92,14 @@ // and for interrupts are the requirement of the kernel; // others are application-dependent. // -// We use convenience macro TN_TASK_STACK_DEF() for that. +// We use convenience macro TN_STACK_ARR_DEF() for that. -TN_TASK_STACK_DEF(idle_task_stack, IDLE_TASK_STACK_SIZE); -TN_TASK_STACK_DEF(interrupt_stack, INTERRUPT_STACK_SIZE); +TN_STACK_ARR_DEF(idle_task_stack, IDLE_TASK_STACK_SIZE); +TN_STACK_ARR_DEF(interrupt_stack, INTERRUPT_STACK_SIZE); -TN_TASK_STACK_DEF(task_a_stack, TASK_A_STK_SIZE); -TN_TASK_STACK_DEF(task_b_stack, TASK_B_STK_SIZE); -TN_TASK_STACK_DEF(task_c_stack, TASK_C_STK_SIZE); +TN_STACK_ARR_DEF(task_a_stack, TASK_A_STK_SIZE); +TN_STACK_ARR_DEF(task_b_stack, TASK_B_STK_SIZE); +TN_STACK_ARR_DEF(task_c_stack, TASK_C_STK_SIZE); diff --git a/src/core/tn_oldsymbols.h b/src/core/tn_oldsymbols.h index 9163955..320a19e 100644 --- a/src/core/tn_oldsymbols.h +++ b/src/core/tn_oldsymbols.h @@ -325,6 +325,13 @@ typedef struct TN_Sem TN_SEM; /// old TNKernel name for `sizeof(#TN_UWord)` #define TN_ALIG sizeof(TN_UWord) + + + +/// old name for `TN_STACK_ARR_DEF` +#define TN_TASK_STACK_DEF TN_STACK_ARR_DEF + + /******************************************************************************* * PUBLIC FUNCTION PROTOTYPES ******************************************************************************/ diff --git a/src/core/tn_sys.h b/src/core/tn_sys.h index f23f03f..a448802 100644 --- a/src/core/tn_sys.h +++ b/src/core/tn_sys.h @@ -73,6 +73,22 @@ struct TN_Mutex; * DEFINITIONS ******************************************************************************/ +/** + * Convenience macro for the definition of stack array. See + * `tn_task_create()` for the usage example. + * + * @param name + * C variable name of the array + * @param size + * size of the stack array in words (`#TN_UWord`), not in bytes. + */ +#define TN_STACK_ARR_DEF(name, size) \ + TN_ARCH_STK_ATTR_BEFORE \ + TN_UWord name[ (size) ] \ + TN_ARCH_STK_ATTR_AFTER + + + /******************************************************************************* * PUBLIC TYPES @@ -215,14 +231,14 @@ typedef void (TN_CBDeadlock)( * * @param idle_task_stack * Pointer to array for idle task stack. - * User must either use the macro `TN_TASK_STACK_DEF()` for the definition + * User must either use the macro `TN_STACK_ARR_DEF()` for the definition * of stack array, or allocate it manually as an array of `#TN_UWord` with * `#TN_ARCH_STK_ATTR_BEFORE` and `#TN_ARCH_STK_ATTR_AFTER` macros. * @param idle_task_stack_size * Size of idle task stack, in words (`#TN_UWord`) * @param int_stack * Pointer to array for interrupt stack. - * User must either use the macro `TN_TASK_STACK_DEF()` for the definition + * User must either use the macro `TN_STACK_ARR_DEF()` for the definition * of stack array, or allocate it manually as an array of `#TN_UWord` with * `#TN_ARCH_STK_ATTR_BEFORE` and `#TN_ARCH_STK_ATTR_AFTER` macros. * @param int_stack_size diff --git a/src/core/tn_tasks.h b/src/core/tn_tasks.h index d7f4fdf..cd372dd 100644 --- a/src/core/tn_tasks.h +++ b/src/core/tn_tasks.h @@ -298,19 +298,6 @@ struct TN_Task { * DEFINITIONS ******************************************************************************/ -/** - * Convenience macro for the definition of stack array. See - * `tn_task_create()` for the usage example. - * - * @param name - * C variable name of the array - * @param size - * size of the stack array in words (`#TN_UWord`), not in bytes. - */ -#define TN_TASK_STACK_DEF(name, size) \ - TN_ARCH_STK_ATTR_BEFORE \ - TN_UWord name[ (size) ] \ - TN_ARCH_STK_ATTR_AFTER @@ -331,9 +318,9 @@ struct TN_Task { * * struct TN_Task my_task; * - * //-- define stack array, we use convenience macro TN_TASK_STACK_DEF() + * //-- define stack array, we use convenience macro TN_STACK_ARR_DEF() * // for that - * TN_TASK_STACK_DEF(my_task_stack, MY_TASK_STACK_SIZE); + * TN_STACK_ARR_DEF(my_task_stack, MY_TASK_STACK_SIZE); * * void my_task_body(void *param) * { @@ -379,7 +366,7 @@ struct TN_Task { * Must be > `0` and < `(#TN_PRIORITIES_CNT - 1)`. * @param task_stack_low_addr * Pointer to the stack for task. - * User must either use the macro `TN_TASK_STACK_DEF()` for the definition + * User must either use the macro `TN_STACK_ARR_DEF()` for the definition * of stack array, or allocate it manually as an array of `#TN_UWord` with * `#TN_ARCH_STK_ATTR_BEFORE` and `#TN_ARCH_STK_ATTR_AFTER` macros. * @param task_stack_size diff --git a/stuff/doc_pages/quick_guide.dox b/stuff/doc_pages/quick_guide.dox index a8dcd3b..05d8ded 100644 --- a/stuff/doc_pages/quick_guide.dox +++ b/stuff/doc_pages/quick_guide.dox @@ -29,7 +29,7 @@ tn_soft_isr(_TIMER_5_VECTOR) ### Quick guide on startup process - You allocate arrays for idle task stack and interrupt stack, there is a - convenience macro `TN_TASK_STACK_DEF()` for that. It is good idea to + convenience macro `TN_STACK_ARR_DEF()` for that. It is good idea to consult the `#TN_MIN_STACK_SIZE` to determine stack sizes (see example below). - You provide callback function like `void init_task_create(void) { ... }`, diff --git a/stuff/doc_pages/tnkernel_diff.dox b/stuff/doc_pages/tnkernel_diff.dox index 2c1aa4e..81bb1c6 100644 --- a/stuff/doc_pages/tnkernel_diff.dox +++ b/stuff/doc_pages/tnkernel_diff.dox @@ -202,7 +202,7 @@ You can still use "manual" definition of stack arrays, like that: TN_ARCH_STK_ATTR_AFTER; \endcode -Although it is recommended to use convenience macro for that: `TN_TASK_STACK_DEF()`. See `tn_task_create()` for the usage example. +Although it is recommended to use convenience macro for that: `TN_STACK_ARR_DEF()`. See `tn_task_create()` for the usage example. \subsection tnkernel_new_api__convenience_macros_fmem Convenience macros for fixed memory block pool buffers definition From fe48a5df1516eb6d6c63e57c246679c07315033e Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 16:42:08 +0400 Subject: [PATCH 11/55] pic32 example changed a bit --- .../pic32/basic/src/tn_pic32_example_basic.c | 8 +-- .../nbproject/configurations.xml | 62 +------------------ 2 files changed, 6 insertions(+), 64 deletions(-) diff --git a/examples/pic32/basic/src/tn_pic32_example_basic.c b/examples/pic32/basic/src/tn_pic32_example_basic.c index cc0d2b3..661fafc 100644 --- a/examples/pic32/basic/src/tn_pic32_example_basic.c +++ b/examples/pic32/basic/src/tn_pic32_example_basic.c @@ -202,10 +202,6 @@ void hw_init(void) INTClearFlag(INT_CS0); INTEnable(INT_CS0, INT_ENABLED); - //-- configure LED port pins - mPORTESetPinsDigitalOut(BIT_0 | BIT_1 | BIT_2); - mPORTEClearBits(BIT_0 | BIT_1 | BIT_2); - //-- enable multi-vectored interrupt mode INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); } @@ -215,6 +211,10 @@ void hw_init(void) */ void appl_init(void) { + //-- configure LED port pins + mPORTESetPinsDigitalOut(BIT_0 | BIT_1 | BIT_2); + mPORTEClearBits(BIT_0 | BIT_1 | BIT_2); + //-- initialize various on-board peripherals, such as // flash memory, displays, etc. // (in this sample project there's nothing to init) diff --git a/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml b/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml index 9fe8435..6248059 100644 --- a/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml +++ b/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml @@ -182,66 +182,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + From 416d2ca0d8cb879df870ef234c08f4d901209058 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Wed, 15 Oct 2014 21:04:46 +0400 Subject: [PATCH 12/55] working on kernel usage examples, draft of queue example is added --- examples/arch/pic32/example_arch.h | 41 +++ examples/arch/pic32/pic32_arch.c | 198 ++++++++++++++ .../queue/example_queue_pic32.X/Makefile | 113 ++++++++ .../nbproject/configurations.xml | 255 ++++++++++++++++++ .../nbproject/project.properties | 0 .../nbproject/project.xml | 18 ++ examples/common/queue/queue_example.c | 65 +++++ examples/common/queue/queue_example.h | 100 +++++++ examples/common/queue/task_consumer.c | 137 ++++++++++ examples/common/queue/task_consumer.h | 46 ++++ examples/common/queue/task_producer.c | 105 ++++++++ examples/common/queue/task_producer.h | 38 +++ 12 files changed, 1116 insertions(+) create mode 100644 examples/arch/pic32/example_arch.h create mode 100644 examples/arch/pic32/pic32_arch.c create mode 100644 examples/arch/pic32/queue/example_queue_pic32.X/Makefile create mode 100644 examples/arch/pic32/queue/example_queue_pic32.X/nbproject/configurations.xml create mode 100644 examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.properties create mode 100644 examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.xml create mode 100644 examples/common/queue/queue_example.c create mode 100644 examples/common/queue/queue_example.h create mode 100644 examples/common/queue/task_consumer.c create mode 100644 examples/common/queue/task_consumer.h create mode 100644 examples/common/queue/task_producer.c create mode 100644 examples/common/queue/task_producer.h diff --git a/examples/arch/pic32/example_arch.h b/examples/arch/pic32/example_arch.h new file mode 100644 index 0000000..1c0d38d --- /dev/null +++ b/examples/arch/pic32/example_arch.h @@ -0,0 +1,41 @@ +/******************************************************************************* + * Description: TODO + * + ******************************************************************************/ + +#ifndef _EXAMPLE_ARCH_H +#define _EXAMPLE_ARCH_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +/******************************************************************************* + * GLOBAL VARIABLES + ******************************************************************************/ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +//-- instruction that causes debugger to halt +#define SOFTWARE_BREAK() __asm__ volatile ("sdbbp 0") + + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + + +#endif // _EXAMPLE_ARCH_H + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + diff --git a/examples/arch/pic32/pic32_arch.c b/examples/arch/pic32/pic32_arch.c new file mode 100644 index 0000000..318343d --- /dev/null +++ b/examples/arch/pic32/pic32_arch.c @@ -0,0 +1,198 @@ +/** + * TNeoKernel PIC32 basic example + */ + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include +#include +#include +#include "tn.h" + +#include "example_arch.h" + + + +/******************************************************************************* + * PIC32 HARDWARE CONFIGURATION + ******************************************************************************/ + +#pragma config FNOSC = PRIPLL // Oscillator Selection +#pragma config FPLLIDIV = DIV_4 // PLL Input Divider (PIC32 Starter Kit: use divide by 2 only) +#pragma config FPLLMUL = MUL_20 // PLL Multiplier +#pragma config FPLLODIV = DIV_1 // PLL Output Divider +#pragma config FPBDIV = DIV_2 // Peripheral Clock divisor +#pragma config FWDTEN = OFF // Watchdog Timer +#pragma config WDTPS = PS1 // Watchdog Timer Postscale +#pragma config FCKSM = CSDCMD // Clock Switching & Fail Safe Clock Monitor +#pragma config OSCIOFNC = OFF // CLKO Enable +#pragma config POSCMOD = HS // Primary Oscillator +#pragma config IESO = OFF // Internal/External Switch-over +#pragma config FSOSCEN = OFF // Secondary Oscillator Enable +#pragma config CP = OFF // Code Protect +#pragma config BWP = OFF // Boot Flash Write Protect +#pragma config PWP = OFF // Program Flash Write Protect +#pragma config ICESEL = ICS_PGx2 // ICE/ICD Comm Channel Select +#pragma config DEBUG = OFF // Debugger Disabled for Starter Kit + + + + +/******************************************************************************* + * MACROS + ******************************************************************************/ + +//-- system frequency +#define SYS_FREQ 80000000UL + +//-- peripheral bus frequency +#define PB_FREQ 40000000UL + +//-- kernel ticks (system timer) frequency +#define SYS_TMR_FREQ 1000 + +//-- system timer prescaler +#define SYS_TMR_PRESCALER T5_PS_1_8 +#define SYS_TMR_PRESCALER_VALUE 8 + +//-- system timer period (auto-calculated) +#define SYS_TMR_PERIOD \ + (PB_FREQ / SYS_TMR_PRESCALER_VALUE / SYS_TMR_FREQ) + + + + +//-- idle task stack size, in words +#define IDLE_TASK_STACK_SIZE (TN_MIN_STACK_SIZE + 16) + +//-- interrupt stack size, in words +#define INTERRUPT_STACK_SIZE (TN_MIN_STACK_SIZE + 64) + + + +/******************************************************************************* + * EXTERN FUNCTION PROTOTYPE + ******************************************************************************/ + +//-- defined by particular example: create first application task(s) +extern void init_task_create(void); + + + +/******************************************************************************* + * DATA + ******************************************************************************/ + +//-- Allocate arrays for stacks: stack for idle task +// and for interrupts are the requirement of the kernel; +// others are application-dependent. +// +// We use convenience macro TN_STACK_ARR_DEF() for that. + +TN_STACK_ARR_DEF(idle_task_stack, IDLE_TASK_STACK_SIZE); +TN_STACK_ARR_DEF(interrupt_stack, INTERRUPT_STACK_SIZE); + + + +/******************************************************************************* + * ISRs + ******************************************************************************/ + +/** + * system timer ISR + */ +tn_soft_isr(_TIMER_5_VECTOR) +{ + INTClearFlag(INT_T5); + tn_tick_int_processing(); +} + + + +/******************************************************************************* + * FUNCTIONS + ******************************************************************************/ + +/** + * Hardware init: called from main() with interrupts disabled + */ +void hw_init(void) +{ + SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE); + + //turn off ADC function for all pins + AD1PCFG = 0xffffffff; + + //-- enable timer5 interrupt + OpenTimer5((0 + | T5_ON + | T5_IDLE_STOP + | SYS_TMR_PRESCALER + | T5_SOURCE_INT + ), + (SYS_TMR_PERIOD - 1) + ); + + //-- set timer5 interrupt priority to 2, enable it + INTSetVectorPriority(INT_TIMER_5_VECTOR, INT_PRIORITY_LEVEL_2); + INTSetVectorSubPriority(INT_TIMER_5_VECTOR, INT_SUB_PRIORITY_LEVEL_0); + INTClearFlag(INT_T5); + INTEnable(INT_T5, INT_ENABLED); + + //-- TNeoKernel PIC32 requirement: + // set up the software interrupt 0 with a priority of 1, subpriority 0 + // + // NOTE: the ISR is declared in kernel-provided file + // tn_arch_pic32_int_vec1.S, which should be included in the application + // project itself, in order to dispatch vector correctly. + INTSetVectorPriority(INT_CORE_SOFTWARE_0_VECTOR, INT_PRIORITY_LEVEL_1); + INTSetVectorSubPriority(INT_CORE_SOFTWARE_0_VECTOR, INT_SUB_PRIORITY_LEVEL_0); + INTClearFlag(INT_CS0); + INTEnable(INT_CS0, INT_ENABLED); + + //-- enable multi-vectored interrupt mode + INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR); +} + +//-- idle callback that is called periodically from idle task +void idle_task_callback (void) +{ +} + +int32_t main(void) +{ +#ifndef PIC32_STARTER_KIT + /*The JTAG is on by default on POR. A PIC32 Starter Kit uses the JTAG, but + for other debug tool use, like ICD 3 and Real ICE, the JTAG should be off + to free up the JTAG I/O */ + DDPCONbits.JTAGEN = 0; +#endif + + //-- unconditionally disable interrupts + tn_arch_int_dis(); + + //-- init hardware + hw_init(); + + //-- call to tn_sys_start() never returns + tn_sys_start( + idle_task_stack, + IDLE_TASK_STACK_SIZE, + interrupt_stack, + INTERRUPT_STACK_SIZE, + init_task_create, + idle_task_callback + ); + + //-- unreachable + return 1; +} + +void __attribute__((naked, nomips16, noreturn)) _general_exception_handler(void) +{ + SOFTWARE_BREAK(); + for (;;) ; +} + diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/Makefile b/examples/arch/pic32/queue/example_queue_pic32.X/Makefile new file mode 100644 index 0000000..fca8e2c --- /dev/null +++ b/examples/arch/pic32/queue/example_queue_pic32.X/Makefile @@ -0,0 +1,113 @@ +# +# There exist several targets which are by default empty and which can be +# used for execution of your targets. These targets are usually executed +# before and after some main targets. They are: +# +# .build-pre: called before 'build' target +# .build-post: called after 'build' target +# .clean-pre: called before 'clean' target +# .clean-post: called after 'clean' target +# .clobber-pre: called before 'clobber' target +# .clobber-post: called after 'clobber' target +# .all-pre: called before 'all' target +# .all-post: called after 'all' target +# .help-pre: called before 'help' target +# .help-post: called after 'help' target +# +# Targets beginning with '.' are not intended to be called on their own. +# +# Main targets can be executed directly, and they are: +# +# build build a specific configuration +# clean remove built files from a configuration +# clobber remove all built files +# all build all configurations +# help print help mesage +# +# Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and +# .help-impl are implemented in nbproject/makefile-impl.mk. +# +# Available make variables: +# +# CND_BASEDIR base directory for relative paths +# CND_DISTDIR default top distribution directory (build artifacts) +# CND_BUILDDIR default top build directory (object files, ...) +# CONF name of current configuration +# CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) +# CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) +# CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) +# CND_PACKAGE_DIR_${CONF} directory of package (current configuration) +# CND_PACKAGE_NAME_${CONF} name of package (current configuration) +# CND_PACKAGE_PATH_${CONF} path to package (current configuration) +# +# NOCDDL + + +# Environment +MKDIR=mkdir +CP=cp +CCADMIN=CCadmin +RANLIB=ranlib + + +# build +build: .build-post + +.build-pre: +# Add your pre 'build' code here... + +.build-post: .build-impl +# Add your post 'build' code here... + + +# clean +clean: .clean-post + +.clean-pre: +# Add your pre 'clean' code here... +# WARNING: the IDE does not call this target since it takes a long time to +# simply run make. Instead, the IDE removes the configuration directories +# under build and dist directly without calling make. +# This target is left here so people can do a clean when running a clean +# outside the IDE. + +.clean-post: .clean-impl +# Add your post 'clean' code here... + + +# clobber +clobber: .clobber-post + +.clobber-pre: +# Add your pre 'clobber' code here... + +.clobber-post: .clobber-impl +# Add your post 'clobber' code here... + + +# all +all: .all-post + +.all-pre: +# Add your pre 'all' code here... + +.all-post: .all-impl +# Add your post 'all' code here... + + +# help +help: .help-post + +.help-pre: +# Add your pre 'help' code here... + +.help-post: .help-impl +# Add your post 'help' code here... + + + +# include project implementation makefile +include nbproject/Makefile-impl.mk + +# include project make variables +include nbproject/Makefile-variables.mk diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/configurations.xml b/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/configurations.xml new file mode 100644 index 0000000..6af8c03 --- /dev/null +++ b/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/configurations.xml @@ -0,0 +1,255 @@ + + + + + + + + + + ../../pic32_arch.c + ../../../../../src/arch/pic32/tn_arch_pic32_int_vec1.S + + + ../../../../common/queue/queue_example.c + ../../../../common/queue/task_consumer.c + ../../../../common/queue/task_producer.c + + + + Makefile + + + + ../.. + ../../../../common/queue + ../../../../../src/arch/pic32 + + Makefile + + + + localhost + PIC32MX440F512H + + + ICD3PlatformTool + XC32 + 1.21 + 2 + + + + + + + + + + + + false + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.properties b/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.properties new file mode 100644 index 0000000..e69de29 diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.xml b/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.xml new file mode 100644 index 0000000..f6b4300 --- /dev/null +++ b/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.xml @@ -0,0 +1,18 @@ + + com.microchip.mplab.nbide.embedded.makeproject + + + example_queue_pic32 + e6b3e7cb-7b49-4750-89c9-cf229d92ec7f + 0 + c + + + + UTF-8 + + ../../../../../src/arch/pic32/tneokernel.X + + + + diff --git a/examples/common/queue/queue_example.c b/examples/common/queue/queue_example.c new file mode 100644 index 0000000..dad963d --- /dev/null +++ b/examples/common/queue/queue_example.c @@ -0,0 +1,65 @@ +/******************************************************************************* + * Description: TODO + * + ******************************************************************************/ + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include "queue_example.h" + +#include "task_consumer.h" +#include "task_producer.h" + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE TYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +/** + * Each example should define this funtion: it creates first application task + */ +void init_task_create(void) +{ + task_consumer_create(); +} + +/******************************************************************************* + * end of file + ******************************************************************************/ + + + diff --git a/examples/common/queue/queue_example.h b/examples/common/queue/queue_example.h new file mode 100644 index 0000000..abca051 --- /dev/null +++ b/examples/common/queue/queue_example.h @@ -0,0 +1,100 @@ +/******************************************************************************* + * Description: TODO + * + ******************************************************************************/ + +#ifndef _QUEUE_EXAMPLE_H +#define _QUEUE_EXAMPLE_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include "example_arch.h" + + + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +/******************************************************************************* + * GLOBAL VARIABLES + ******************************************************************************/ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/** + * Macro for checking value returned from system service. + * + * If you ignore the value returned by any system service, it is almost always + * a BAD idea: if something goes wrong, the sooner you know it, the better. + * But, checking error for every system service is a kind of annoying, so, + * simple macro was invented for that. + * + * In most situations, any values other than `#TN_RC_OK` or `#TN_RC_TIMEOUT` + * are not allowed (i.e. should never happen in normal device operation). + * + * So, if error value is neither `#TN_RC_OK` nor `#TN_RC_TIMEOUT`, this macro + * issues a software break. + * + * If you need to allow `#TN_RC_OK` only, use `SYSRETVAL_CHECK()` instead (see + * below) + * + * Usage is as follows: + * + * \code{.c} + * enum TN_RCode rc + * = SYSRETVAL_CHECK_TO(tn_queue_send(&my_queue, p_data, MY_TIMEOUT)); + * + * switch (rc){ + * case TN_RC_OK: + * //-- handle successfull operation + * break; + * case TN_RC_TIMEOUT: + * //-- handle timeout + * break; + * default: + * //-- should never be here + * break; + * } + * \endcode + * + */ +#define SYSRETVAL_CHECK_TO(x) \ + ({int __rv = (x); if (__rv != TERR_NO_ERR && __rv != TERR_TIMEOUT){SOFTWARE_BREAK();} __rv;}) + +/** + * The same as `SYSRETVAL_CHECK_TO()`, but it allows `#TN_RC_OK` only. + * + * Since there is only one return code allowed, usage is simple: + * + * \code{.c} + * SYSRETVAL_CHECK(tn_queue_send(&my_queue, p_data, MY_TIMEOUT)); + * \endcode + */ +#define SYSRETVAL_CHECK(x) \ + ({int __rv = (x); if (__rv != TERR_NO_ERR){SOFTWARE_BREAK();} __rv;}) + + + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + +/** + * Each example should define this funtion: it creates first application task + */ +void init_task_create(void); + + +#endif // _QUEUE_EXAMPLE_H + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + diff --git a/examples/common/queue/task_consumer.c b/examples/common/queue/task_consumer.c new file mode 100644 index 0000000..44cb174 --- /dev/null +++ b/examples/common/queue/task_consumer.c @@ -0,0 +1,137 @@ +/******************************************************************************* + * Description: TODO + * + ******************************************************************************/ + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include +#include +#include "task_consumer.h" +#include "task_producer.h" +#include "queue_example.h" +#include "tn.h" + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +//-- task stack size +#define TASK_CONSUMER_STACK_SIZE (TN_MIN_STACK_SIZE + 96) + +//-- highest priority +#define TASK_CONSUMER_PRIORITY 0 + + + +/******************************************************************************* + * PRIVATE FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE TYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +//-- define array for task stack +TN_STACK_ARR_DEF(task_consumer_stack, TASK_CONSUMER_STACK_SIZE); + +//-- task descriptor: it's better to explicitly zero it +static struct TN_Task task_consumer = {}; + + + +/******************************************************************************* + * PUBLIC DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +/** + * Application init: called from the first created application task + */ +static void appl_init(void) +{ + //-- configure LED port pins + + //-- set output for needed pins + TRISECLR = (1 << TASK_CONS_PIN__0); + + //-- clear current port value + PORTE = 0; + + + //-- create all the rest application tasks: + // + // create the producer task + task_producer_create(); + + //TODO: use semaphore or event + SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); +} + +static void task_consumer_body(void *par) +{ + appl_init(); + + for (;;) + { + SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); + + //-- toggle bits + PORTEINV = (1 << 0); + } +} + + + + + + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +void task_consumer_create(void) +{ + + SYSRETVAL_CHECK( + tn_task_create( + &task_consumer, + task_consumer_body, + TASK_CONSUMER_PRIORITY, + task_consumer_stack, + TASK_CONSUMER_STACK_SIZE, + NULL, + (TN_TASK_CREATE_OPT_START) + ) + ); + +} + + + + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + + diff --git a/examples/common/queue/task_consumer.h b/examples/common/queue/task_consumer.h new file mode 100644 index 0000000..f898d03 --- /dev/null +++ b/examples/common/queue/task_consumer.h @@ -0,0 +1,46 @@ +/******************************************************************************* + * Description: TODO + * + ******************************************************************************/ + +#ifndef _TASK_CONSUMER_H +#define _TASK_CONSUMER_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +enum E_TaskConsPin { + TASK_CONS_PIN__0 = 0, +}; + + + + +/******************************************************************************* + * GLOBAL VARIABLES + ******************************************************************************/ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + +void task_consumer_create(void); + + +#endif // _TASK_CONSUMER_H + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + diff --git a/examples/common/queue/task_producer.c b/examples/common/queue/task_producer.c new file mode 100644 index 0000000..72fcae5 --- /dev/null +++ b/examples/common/queue/task_producer.c @@ -0,0 +1,105 @@ +/******************************************************************************* + * Description: TODO + * + ******************************************************************************/ + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include +#include +#include "task_producer.h" +#include "queue_example.h" +#include "tn.h" + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +//-- task stack size +#define TASK_PRODUCER_STACK_SIZE (TN_MIN_STACK_SIZE + 96) + +//-- highest priority +#define TASK_PRODUCER_PRIORITY 0 + + + +/******************************************************************************* + * PRIVATE FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE TYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +//-- define array for task stack +TN_STACK_ARR_DEF(task_producer_stack, TASK_PRODUCER_STACK_SIZE); + +//-- task descriptor: it's better to explicitly zero it +static struct TN_Task task_producer = {}; + + + +/******************************************************************************* + * PUBLIC DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +static void task_producer_body(void *par) +{ + for (;;) + { + SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); + } +} + + + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +void task_producer_create(void) +{ + + SYSRETVAL_CHECK( + tn_task_create( + &task_producer, + task_producer_body, + TASK_PRODUCER_PRIORITY, + task_producer_stack, + TASK_PRODUCER_STACK_SIZE, + NULL, + (TN_TASK_CREATE_OPT_START) + ) + ); + +} + + + + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + + diff --git a/examples/common/queue/task_producer.h b/examples/common/queue/task_producer.h new file mode 100644 index 0000000..62b663a --- /dev/null +++ b/examples/common/queue/task_producer.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Description: TODO + * + ******************************************************************************/ + +#ifndef _TASK_PRODUCER_H +#define _TASK_PRODUCER_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +/******************************************************************************* + * GLOBAL VARIABLES + ******************************************************************************/ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + +void task_producer_create(void); + +#endif // _TASK_PRODUCER_H + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + From 8d0bbe3f65b9075486724a5a3a9b0fa0e7ea30f3 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 00:43:51 +0400 Subject: [PATCH 13/55] working on queue example --- examples/common/queue/queue_example.c | 41 ++----- examples/common/queue/queue_example.h | 48 +++++--- examples/common/queue/task_consumer.c | 154 ++++++++++++++++++++++++-- examples/common/queue/task_consumer.h | 46 ++++++-- examples/common/queue/task_producer.c | 90 ++++++++------- examples/common/queue/task_producer.h | 14 ++- 6 files changed, 285 insertions(+), 108 deletions(-) diff --git a/examples/common/queue/queue_example.c b/examples/common/queue/queue_example.c index dad963d..ab265e4 100644 --- a/examples/common/queue/queue_example.c +++ b/examples/common/queue/queue_example.c @@ -1,7 +1,11 @@ -/******************************************************************************* - * Description: TODO +/** + * \file * - ******************************************************************************/ + * Data queue usage example. + * + * For general information about the pattern, refer to the top of + * queue_example.h file + */ /******************************************************************************* @@ -13,37 +17,6 @@ #include "task_consumer.h" #include "task_producer.h" -/******************************************************************************* - * DEFINITIONS - ******************************************************************************/ - -/******************************************************************************* - * PRIVATE FUNCTION PROTOTYPES - ******************************************************************************/ - -/******************************************************************************* - * PRIVATE TYPES - ******************************************************************************/ - -/******************************************************************************* - * PRIVATE DATA - ******************************************************************************/ - -/******************************************************************************* - * PUBLIC DATA - ******************************************************************************/ - -/******************************************************************************* - * EXTERNAL DATA - ******************************************************************************/ - -/******************************************************************************* - * EXTERNAL FUNCTION PROTOTYPES - ******************************************************************************/ - -/******************************************************************************* - * PRIVATE FUNCTIONS - ******************************************************************************/ /******************************************************************************* * PUBLIC FUNCTIONS diff --git a/examples/common/queue/queue_example.h b/examples/common/queue/queue_example.h index abca051..718b203 100644 --- a/examples/common/queue/queue_example.h +++ b/examples/common/queue/queue_example.h @@ -1,7 +1,17 @@ -/******************************************************************************* - * Description: TODO +/** + * \file * - ******************************************************************************/ + * When you need to build a queue of some data objects from one task to + * another, the common pattern in the TNeoKernel is to use fixed memory pool + * and queue together. Number of elements in the memory pool is equal to the + * number of elements in the queue. So when you need to send the message, + * you get block from the memory pool, fill it with data, and send pointer + * to it by means of the queue. + * + * When you receive the message, you handle the data, and free the memory + * back to the memory pool. + * + */ #ifndef _QUEUE_EXAMPLE_H #define _QUEUE_EXAMPLE_H @@ -10,18 +20,12 @@ * INCLUDED FILES ******************************************************************************/ +//-- include architecture-specific things, +// at least, there is SOFTWARE_BREAK macro #include "example_arch.h" -/******************************************************************************* - * PUBLIC TYPES - ******************************************************************************/ - -/******************************************************************************* - * GLOBAL VARIABLES - ******************************************************************************/ - /******************************************************************************* * DEFINITIONS ******************************************************************************/ @@ -63,8 +67,15 @@ * \endcode * */ -#define SYSRETVAL_CHECK_TO(x) \ - ({int __rv = (x); if (__rv != TERR_NO_ERR && __rv != TERR_TIMEOUT){SOFTWARE_BREAK();} __rv;}) +#define SYSRETVAL_CHECK_TO(x) \ + ({ \ + int __rv = (x); \ + if (__rv != TN_RC_OK && __rv != TN_RC_TIMEOUT){ \ + SOFTWARE_BREAK(); \ + } \ + /* like, return __rv */ \ + __rv; \ + }) /** * The same as `SYSRETVAL_CHECK_TO()`, but it allows `#TN_RC_OK` only. @@ -75,8 +86,15 @@ * SYSRETVAL_CHECK(tn_queue_send(&my_queue, p_data, MY_TIMEOUT)); * \endcode */ -#define SYSRETVAL_CHECK(x) \ - ({int __rv = (x); if (__rv != TERR_NO_ERR){SOFTWARE_BREAK();} __rv;}) +#define SYSRETVAL_CHECK(x) \ + ({ \ + int __rv = (x); \ + if (__rv != TN_RC_OK){ \ + SOFTWARE_BREAK(); \ + } \ + /* like, return __rv */ \ + __rv; \ + }) diff --git a/examples/common/queue/task_consumer.c b/examples/common/queue/task_consumer.c index 44cb174..732206f 100644 --- a/examples/common/queue/task_consumer.c +++ b/examples/common/queue/task_consumer.c @@ -1,15 +1,17 @@ -/******************************************************************************* - * Description: TODO +/** + * \file * - ******************************************************************************/ - + * Data queue usage example. + * + * For general information about the pattern, refer to the top of + * queue_example.h file + */ /******************************************************************************* * INCLUDED FILES ******************************************************************************/ #include -#include #include "task_consumer.h" #include "task_producer.h" #include "queue_example.h" @@ -22,9 +24,34 @@ //-- task stack size #define TASK_CONSUMER_STACK_SIZE (TN_MIN_STACK_SIZE + 96) -//-- highest priority +//-- priority of consumer task: the highest one #define TASK_CONSUMER_PRIORITY 0 +//-- number of items in the consumer message queue +#define CONS_QUE_BUF_SIZE 4 + +//-- max timeout for waiting for memory and free message +#define WAIT_TIMEOUT 10 + + + + + +//-- gpio register to work with, you might want to switch it to different port +// NOTE: in real programs, it's better to use some peripheral library. +// I strongly dislike plib as well as "brand new" Harmony by Microchip, +// I use my own peripheral library, but in order to write simple example, +// I decided to write it as simple as possible. +#define TRIS_REG TRISE +#define TRIS_REG_CLR TRISECLR +#define TRIS_REG_SET TRISESET +#define TRIS_REG_INV TRISEINV + +#define PORT_REG PORTE +#define PORT_REG_CLR PORTECLR +#define PORT_REG_SET PORTESET +#define PORT_REG_INV PORTEINV + /******************************************************************************* @@ -35,6 +62,13 @@ * PRIVATE TYPES ******************************************************************************/ +struct TaskConsumerMsg { + enum E_TaskConsCmd cmd; + enum E_TaskConsPin pin_num; +}; + + + /******************************************************************************* * PRIVATE DATA ******************************************************************************/ @@ -45,6 +79,14 @@ TN_STACK_ARR_DEF(task_consumer_stack, TASK_CONSUMER_STACK_SIZE); //-- task descriptor: it's better to explicitly zero it static struct TN_Task task_consumer = {}; +//-- define queue and buffer for it +struct TN_DQueue cons_que; +void *cons_que_buf[ CONS_QUE_BUF_SIZE ]; + +//-- define fixed memory pool and buffer for it +struct TN_FMem cons_fmem; +TN_FMEM_BUF_DEF(cons_fmem_buf, struct TaskConsumerMsg, CONS_QUE_BUF_SIZE); + /******************************************************************************* @@ -71,10 +113,10 @@ static void appl_init(void) //-- configure LED port pins //-- set output for needed pins - TRISECLR = (1 << TASK_CONS_PIN__0); + TRIS_REG_CLR = TASK_CONS_PIN_MASK; //-- clear current port value - PORTE = 0; + PORT_REG = TASK_CONS_PIN_MASK; //-- create all the rest application tasks: @@ -88,14 +130,60 @@ static void appl_init(void) static void task_consumer_body(void *par) { + //-- in this particular application, consumer task is the first application + // task that is started, so, we should perform all the app initialization + // here, and then start other tasks. All of this is done in the appl_init(). appl_init(); + + //-- init things specific to consumer task + + //-- create memory pool + SYSRETVAL_CHECK( + tn_fmem_create( + &cons_fmem, + cons_fmem_buf, + TN_MAKE_ALIG_SIZE(sizeof(struct TaskConsumerMsg)), + CONS_QUE_BUF_SIZE + ) + ); + + //-- create queue + SYSRETVAL_CHECK( + tn_queue_create(&cons_que, (void *)cons_que_buf, CONS_QUE_BUF_SIZE) + ); + + struct TaskConsumerMsg *p_msg; + + //-- enter endless loop in which we will receive and handle messages for (;;) { - SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); + //-- wait for message, potentially can wait forever + // (if there are no messages) + enum TN_RCode rc = SYSRETVAL_CHECK( + tn_queue_receive(&cons_que, (void *)&p_msg, TN_WAIT_INFINITE) + ); + + if (rc == TN_RC_OK){ + + //-- message successfully received, let's check command + switch (p_msg->cmd){ + + case TASK_CONS_CMD__PIN_TOGGLE: + //-- toggle specified bit + PORT_REG_INV = (1 << p_msg->pin_num); + break; + + default: + //-- should never be here + SOFTWARE_BREAK(); + break; + } + + //-- free memory + SYSRETVAL_CHECK(tn_fmem_release(&cons_fmem, (void *)p_msg)); + } - //-- toggle bits - PORTEINV = (1 << 0); } } @@ -108,6 +196,9 @@ static void task_consumer_body(void *par) * PUBLIC FUNCTIONS ******************************************************************************/ +/** + * See comments in the header file + */ void task_consumer_create(void) { @@ -125,6 +216,47 @@ void task_consumer_create(void) } +/** + * See comments in the header file + */ +int task_consumer_msg_send(enum E_TaskConsCmd cmd, enum E_TaskConsPin pin_num) +{ + int ret = 0; + + struct TaskConsumerMsg *p_msg; + enum TN_RCode tn_rc; + + //-- get memory block from memory pool + tn_rc = SYSRETVAL_CHECK_TO( + tn_fmem_get(&cons_fmem, (void *)&p_msg, WAIT_TIMEOUT) + ); + if (tn_rc == TN_RC_OK){ + + //-- put correct data to the allocated memory + p_msg->cmd = cmd; + p_msg->pin_num = pin_num; + + //-- send it to the consumer task + tn_rc = SYSRETVAL_CHECK_TO( + tn_queue_send(&cons_que, (void *)p_msg, WAIT_TIMEOUT) + ); + + if (tn_rc == TN_RC_OK){ + ret = 1/*success*/; + } else { + //-- there was some error while sending the message, + // so, we should free buffer that we've allocated + SYSRETVAL_CHECK(tn_fmem_release(&cons_fmem, (void *)p_msg)); + } + } else { + //-- there was some error while allocating memory, + // nothing to do here: ret is still 0, and it is + // going to be returned + } + + return ret; +} + diff --git a/examples/common/queue/task_consumer.h b/examples/common/queue/task_consumer.h index f898d03..d35204e 100644 --- a/examples/common/queue/task_consumer.h +++ b/examples/common/queue/task_consumer.h @@ -1,7 +1,12 @@ -/******************************************************************************* - * Description: TODO +/** + * \file * - ******************************************************************************/ + * Data queue usage example. + * + * For general information about the pattern, refer to the top of + * queue_example.h file + */ + #ifndef _TASK_CONSUMER_H #define _TASK_CONSUMER_H @@ -14,17 +19,28 @@ * PUBLIC TYPES ******************************************************************************/ -enum E_TaskConsPin { - TASK_CONS_PIN__0 = 0, +enum E_TaskConsCmd { + TASK_CONS_CMD__PIN_TOGGLE, }; - - +enum E_TaskConsPin { + TASK_CONS_PIN__0, + TASK_CONS_PIN__1, + TASK_CONS_PIN__2, +}; /******************************************************************************* * GLOBAL VARIABLES ******************************************************************************/ +#define TASK_CONS_PIN_MASK (0 \ + | (1 << TASK_CONS_PIN__0) \ + | (1 << TASK_CONS_PIN__1) \ + | (1 << TASK_CONS_PIN__2) \ + ) + + + /******************************************************************************* * DEFINITIONS ******************************************************************************/ @@ -33,9 +49,25 @@ enum E_TaskConsPin { * PUBLIC FUNCTION PROTOTYPES ******************************************************************************/ +/** + * Create consumer task. + */ void task_consumer_create(void); +/** + * Send a message to the consumer. + * In this simple example, consumer will just toggle given pin number. + * + * @param pin_num + * pin number to toggle. + * + * @return + * - 1 on success + * - 0 on failure + */ +int task_consumer_msg_send(enum E_TaskConsCmd cmd, enum E_TaskConsPin pin_num); + #endif // _TASK_CONSUMER_H diff --git a/examples/common/queue/task_producer.c b/examples/common/queue/task_producer.c index 72fcae5..a2c0477 100644 --- a/examples/common/queue/task_producer.c +++ b/examples/common/queue/task_producer.c @@ -1,19 +1,21 @@ -/******************************************************************************* - * Description: TODO +/** + * \file * - ******************************************************************************/ + * Data queue usage example. + * + * For general information about the pattern, refer to the top of + * queue_example.h file + */ -/******************************************************************************* - * INCLUDED FILES - ******************************************************************************/ - #include -#include #include "task_producer.h" +#include "task_consumer.h" #include "queue_example.h" #include "tn.h" + + /******************************************************************************* * DEFINITIONS ******************************************************************************/ @@ -26,14 +28,6 @@ -/******************************************************************************* - * PRIVATE FUNCTION PROTOTYPES - ******************************************************************************/ - -/******************************************************************************* - * PRIVATE TYPES - ******************************************************************************/ - /******************************************************************************* * PRIVATE DATA ******************************************************************************/ @@ -46,27 +40,53 @@ static struct TN_Task task_producer = {}; -/******************************************************************************* - * PUBLIC DATA - ******************************************************************************/ - -/******************************************************************************* - * EXTERNAL DATA - ******************************************************************************/ - -/******************************************************************************* - * EXTERNAL FUNCTION PROTOTYPES - ******************************************************************************/ - /******************************************************************************* * PRIVATE FUNCTIONS ******************************************************************************/ +/** + * Body function for producer task + */ static void task_producer_body(void *par) { for (;;) { - SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); + int i; + + for (i = 0; i < 3/*pins count*/;i++){ + //-- Wait before sending message + SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); + + enum E_TaskConsPin pin_num = 0; + + //-- determine pin_num + switch (i){ + case 0: + pin_num = TASK_CONS_PIN__0; + break; + case 1: + pin_num = TASK_CONS_PIN__1; + break; + case 2: + pin_num = TASK_CONS_PIN__2; + break; + default: + //-- should never be here + SOFTWARE_BREAK(); + break; + } + + //-- Send the message to consumer + if (!task_consumer_msg_send( + TASK_CONS_CMD__PIN_TOGGLE, + pin_num + ) + ) + { + //-- failed to send the message + } + } + } } @@ -76,6 +96,9 @@ static void task_producer_body(void *par) * PUBLIC FUNCTIONS ******************************************************************************/ +/** + * See comments in the header file + */ void task_producer_create(void) { @@ -94,12 +117,3 @@ void task_producer_create(void) } - - - -/******************************************************************************* - * end of file - ******************************************************************************/ - - - diff --git a/examples/common/queue/task_producer.h b/examples/common/queue/task_producer.h index 62b663a..dba61be 100644 --- a/examples/common/queue/task_producer.h +++ b/examples/common/queue/task_producer.h @@ -1,7 +1,12 @@ -/******************************************************************************* - * Description: TODO +/** + * \file * - ******************************************************************************/ + * Data queue usage example. + * + * For general information about the pattern, refer to the top of + * queue_example.h file + */ + #ifndef _TASK_PRODUCER_H #define _TASK_PRODUCER_H @@ -26,6 +31,9 @@ * PUBLIC FUNCTION PROTOTYPES ******************************************************************************/ +/** + * Create producer task. + */ void task_producer_create(void); #endif // _TASK_PRODUCER_H From 43db11fce2d90a4aae0f68e96ee83cb4d09e0fe6 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 01:12:29 +0400 Subject: [PATCH 14/55] eventgrp is used to sync tasks startup --- examples/common/queue/queue_example.c | 31 ++++++++++++++++ examples/common/queue/queue_example.h | 30 +++++++++++++++ examples/common/queue/task_consumer.c | 53 ++++++++++++++++++++------- examples/common/queue/task_consumer.h | 8 ++-- examples/common/queue/task_producer.c | 19 ++++++++++ 5 files changed, 124 insertions(+), 17 deletions(-) diff --git a/examples/common/queue/queue_example.c b/examples/common/queue/queue_example.c index ab265e4..1951f1b 100644 --- a/examples/common/queue/queue_example.c +++ b/examples/common/queue/queue_example.c @@ -17,6 +17,18 @@ #include "task_consumer.h" #include "task_producer.h" +#include "tn.h" + + + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +static struct TN_EventGrp que_example_events; + + + /******************************************************************************* * PUBLIC FUNCTIONS @@ -30,6 +42,25 @@ void init_task_create(void) task_consumer_create(); } + +/** + * See comments in the header file + */ +void queue_example_init(void) +{ + //-- create application events + // (see enum E_QueExampleFlag in the header) + SYSRETVAL_CHECK(tn_eventgrp_create(&que_example_events, (0))); +} + +/** + * See comments in the header file + */ +struct TN_EventGrp *queue_example_eventgrp_get(void) +{ + return &que_example_events; +} + /******************************************************************************* * end of file ******************************************************************************/ diff --git a/examples/common/queue/queue_example.h b/examples/common/queue/queue_example.h index 718b203..a3deb7a 100644 --- a/examples/common/queue/queue_example.h +++ b/examples/common/queue/queue_example.h @@ -25,6 +25,22 @@ #include "example_arch.h" +/******************************************************************************* + * EXTERN TYPES + ******************************************************************************/ + +struct TN_EventGrp; + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +enum E_QueExampleFlag { + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT = (1 << 0), + QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT = (1 << 1), +}; + + /******************************************************************************* * DEFINITIONS @@ -107,6 +123,20 @@ */ void init_task_create(void); +/** + * Initialization of application common objects, must be called once + * from the initial application task (which is created in init_task_create()) + */ +void queue_example_init(void); + +/** + * Returns pointer to the application event group. + * See `enum E_QueExampleFlag` + * + * Do note that you must call `queue_example_init()` before + * you can get eventgrp. + */ +struct TN_EventGrp *queue_example_eventgrp_get(void); #endif // _QUEUE_EXAMPLE_H diff --git a/examples/common/queue/task_consumer.c b/examples/common/queue/task_consumer.c index 732206f..5883915 100644 --- a/examples/common/queue/task_consumer.c +++ b/examples/common/queue/task_consumer.c @@ -62,9 +62,14 @@ * PRIVATE TYPES ******************************************************************************/ +/** + * Type of message that consumer receives. + */ struct TaskConsumerMsg { - enum E_TaskConsCmd cmd; - enum E_TaskConsPin pin_num; + enum E_TaskConsCmd cmd; //-- command to perform + // (well, now there's just one command) + enum E_TaskConsPin pin_num; //-- number of pin for which command + // should be performed }; @@ -110,22 +115,36 @@ TN_FMEM_BUF_DEF(cons_fmem_buf, struct TaskConsumerMsg, CONS_QUE_BUF_SIZE); */ static void appl_init(void) { - //-- configure LED port pins + //-- init common application objects + queue_example_init(); - //-- set output for needed pins - TRIS_REG_CLR = TASK_CONS_PIN_MASK; - - //-- clear current port value - PORT_REG = TASK_CONS_PIN_MASK; + //-- configure LED port pins {{{ + { + //-- set output for needed pins + TRIS_REG_CLR = TASK_CONS_PIN_MASK; + //-- clear current port value + PORT_REG = TASK_CONS_PIN_MASK; + } + // }}} //-- create all the rest application tasks: - // - // create the producer task - task_producer_create(); - //TODO: use semaphore or event - SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); + //-- create the producer task {{{ + { + task_producer_create(); + + //-- wait until producer task initialized + tn_eventgrp_wait( + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT, + TN_EVENTGRP_WMODE_AND, + NULL, + TN_WAIT_INFINITE + ); + } + // }}} + } static void task_consumer_body(void *par) @@ -153,6 +172,14 @@ static void task_consumer_body(void *par) tn_queue_create(&cons_que, (void *)cons_que_buf, CONS_QUE_BUF_SIZE) ); + //-- cry that consumer task has initialized + tn_eventgrp_modify( + queue_example_eventgrp_get(), + TN_EVENTGRP_OP_SET, + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT + ); + + struct TaskConsumerMsg *p_msg; //-- enter endless loop in which we will receive and handle messages diff --git a/examples/common/queue/task_consumer.h b/examples/common/queue/task_consumer.h index d35204e..807f55a 100644 --- a/examples/common/queue/task_consumer.h +++ b/examples/common/queue/task_consumer.h @@ -33,6 +33,10 @@ enum E_TaskConsPin { * GLOBAL VARIABLES ******************************************************************************/ +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + #define TASK_CONS_PIN_MASK (0 \ | (1 << TASK_CONS_PIN__0) \ | (1 << TASK_CONS_PIN__1) \ @@ -41,10 +45,6 @@ enum E_TaskConsPin { -/******************************************************************************* - * DEFINITIONS - ******************************************************************************/ - /******************************************************************************* * PUBLIC FUNCTION PROTOTYPES ******************************************************************************/ diff --git a/examples/common/queue/task_producer.c b/examples/common/queue/task_producer.c index a2c0477..95f9999 100644 --- a/examples/common/queue/task_producer.c +++ b/examples/common/queue/task_producer.c @@ -49,6 +49,25 @@ static struct TN_Task task_producer = {}; */ static void task_producer_body(void *par) { + //-- nothing special to initialize here + + //-- cry that producer task has initialized + tn_eventgrp_modify( + queue_example_eventgrp_get(), + TN_EVENTGRP_OP_SET, + QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT + ); + + //-- wait until consumer task initialized, since we are about to + // send messages to it + tn_eventgrp_wait( + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, + TN_EVENTGRP_WMODE_AND, + NULL, + TN_WAIT_INFINITE + ); + for (;;) { int i; From 3a81a1538ab3ed21ebac49353dbee921f1548a98 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 01:17:11 +0400 Subject: [PATCH 15/55] used SYSRETVAL_CHECK() for eventgrp calls --- examples/common/queue/task_consumer.c | 24 ++++++++++++++---------- examples/common/queue/task_producer.c | 24 ++++++++++++++---------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/examples/common/queue/task_consumer.c b/examples/common/queue/task_consumer.c index 5883915..76ea641 100644 --- a/examples/common/queue/task_consumer.c +++ b/examples/common/queue/task_consumer.c @@ -135,12 +135,14 @@ static void appl_init(void) task_producer_create(); //-- wait until producer task initialized - tn_eventgrp_wait( - queue_example_eventgrp_get(), - QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT, - TN_EVENTGRP_WMODE_AND, - NULL, - TN_WAIT_INFINITE + SYSRETVAL_CHECK( + tn_eventgrp_wait( + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT, + TN_EVENTGRP_WMODE_AND, + NULL, + TN_WAIT_INFINITE + ) ); } // }}} @@ -173,10 +175,12 @@ static void task_consumer_body(void *par) ); //-- cry that consumer task has initialized - tn_eventgrp_modify( - queue_example_eventgrp_get(), - TN_EVENTGRP_OP_SET, - QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT + SYSRETVAL_CHECK( + tn_eventgrp_modify( + queue_example_eventgrp_get(), + TN_EVENTGRP_OP_SET, + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT + ) ); diff --git a/examples/common/queue/task_producer.c b/examples/common/queue/task_producer.c index 95f9999..379975e 100644 --- a/examples/common/queue/task_producer.c +++ b/examples/common/queue/task_producer.c @@ -52,20 +52,24 @@ static void task_producer_body(void *par) //-- nothing special to initialize here //-- cry that producer task has initialized - tn_eventgrp_modify( - queue_example_eventgrp_get(), - TN_EVENTGRP_OP_SET, - QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT + SYSRETVAL_CHECK( + tn_eventgrp_modify( + queue_example_eventgrp_get(), + TN_EVENTGRP_OP_SET, + QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT + ) ); //-- wait until consumer task initialized, since we are about to // send messages to it - tn_eventgrp_wait( - queue_example_eventgrp_get(), - QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, - TN_EVENTGRP_WMODE_AND, - NULL, - TN_WAIT_INFINITE + SYSRETVAL_CHECK( + tn_eventgrp_wait( + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, + TN_EVENTGRP_WMODE_AND, + NULL, + TN_WAIT_INFINITE + ) ); for (;;) From 7978deeffdf24ef3a7cfe4a01cb4691f01ccda46 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 01:45:06 +0400 Subject: [PATCH 16/55] producer is the first created task --- examples/common/queue/queue_example.c | 2 +- examples/common/queue/queue_example.h | 62 ++++++++++++++++++++++++++- examples/common/queue/task_consumer.c | 41 ++---------------- examples/common/queue/task_producer.c | 49 +++++++++++++++------ 4 files changed, 101 insertions(+), 53 deletions(-) diff --git a/examples/common/queue/queue_example.c b/examples/common/queue/queue_example.c index 1951f1b..8c77b4a 100644 --- a/examples/common/queue/queue_example.c +++ b/examples/common/queue/queue_example.c @@ -39,7 +39,7 @@ static struct TN_EventGrp que_example_events; */ void init_task_create(void) { - task_consumer_create(); + task_producer_create(); } diff --git a/examples/common/queue/queue_example.h b/examples/common/queue/queue_example.h index a3deb7a..f08c3c0 100644 --- a/examples/common/queue/queue_example.h +++ b/examples/common/queue/queue_example.h @@ -8,9 +8,62 @@ * you get block from the memory pool, fill it with data, and send pointer * to it by means of the queue. * - * When you receive the message, you handle the data, and free the memory + * When you receive the message, you handle the data, and release the memory * back to the memory pool. * + * This example project demonstrates this technique: there are two tasks: + * + * - producer task (which sends messages to the consumer) + * - consumer task (which receives messages from the producer) + * + * Actual functionality is, of course, very simple: each message contains + * the following: + * + * - command to perform: currently there is just a single command: toggle pin + * - pin number: obviously, number of the pin for which command should + * be performed. + * + * For the message structure, refer to `struct TaskConsumerMsg` in the + * file `task_consumer.c`. Note, however, that it is a "private" structure: + * producer doesn't touch it directly; instead, it uses special function: + * `task_consumer_msg_send()`, which sends message to the queue. + * + * So the producer just sends new message each 100 ms, specifying + * one of three pins. Consumer, consequently, toggles the pin specified + * in the message. + * + * Before we can get to the business, we need to initialize things. + * How it is done: + * + * - `init_task_create()`, which is called from the `tn_sys_start()`, + * creates first application task: the producer task. + * - producer task gets executed and first of all it initializes the + * essential application stuff, by calling `appl_init()`: + * + * - `queue_example_init()` is called, in which common application + * objects are created: in this project, there's just a single + * object: event group that is used to synchronize task + * initialization. See `enum E_QueExampleFlag`. + * - create consumer task + * - wait until it is initialized (i.e. wait for the flag + * QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT in the event group returned + * by `queue_example_eventgrp_get()`) + * + * - consumer task gets executed + * - gpio is configured: we need to set up certain pins to output + * as well as initially clear them + * - consumer initializes its own objects: + * - fixed memory pool + * - queue + * - sets the flag QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT + * - enters endless loop in which it will receive and + * handle messages. Now, it is going to wait for first message. + * + * - producer task gets executed and enters its endless loop in which + * it will send messages to the consumer. + * + * At this point, everything is initialized. + * */ #ifndef _QUEUE_EXAMPLE_H @@ -29,14 +82,21 @@ * EXTERN TYPES ******************************************************************************/ +/// Application common event group (see `enum E_QueExampleFlag`) struct TN_EventGrp; + + /******************************************************************************* * PUBLIC TYPES ******************************************************************************/ enum E_QueExampleFlag { + /// + /// Flag indicating that consumer task is initialized QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT = (1 << 0), + /// + /// Flag indicating that producer task is initialized QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT = (1 << 1), }; diff --git a/examples/common/queue/task_consumer.c b/examples/common/queue/task_consumer.c index 76ea641..1348a39 100644 --- a/examples/common/queue/task_consumer.c +++ b/examples/common/queue/task_consumer.c @@ -110,13 +110,9 @@ TN_FMEM_BUF_DEF(cons_fmem_buf, struct TaskConsumerMsg, CONS_QUE_BUF_SIZE); * PRIVATE FUNCTIONS ******************************************************************************/ -/** - * Application init: called from the first created application task - */ -static void appl_init(void) +static void task_consumer_body(void *par) { - //-- init common application objects - queue_example_init(); + //-- init things specific to consumer task //-- configure LED port pins {{{ { @@ -124,41 +120,10 @@ static void appl_init(void) TRIS_REG_CLR = TASK_CONS_PIN_MASK; //-- clear current port value - PORT_REG = TASK_CONS_PIN_MASK; - } - // }}} - - //-- create all the rest application tasks: - - //-- create the producer task {{{ - { - task_producer_create(); - - //-- wait until producer task initialized - SYSRETVAL_CHECK( - tn_eventgrp_wait( - queue_example_eventgrp_get(), - QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT, - TN_EVENTGRP_WMODE_AND, - NULL, - TN_WAIT_INFINITE - ) - ); + PORT_REG_CLR = TASK_CONS_PIN_MASK; } // }}} -} - -static void task_consumer_body(void *par) -{ - //-- in this particular application, consumer task is the first application - // task that is started, so, we should perform all the app initialization - // here, and then start other tasks. All of this is done in the appl_init(). - appl_init(); - - - //-- init things specific to consumer task - //-- create memory pool SYSRETVAL_CHECK( tn_fmem_create( diff --git a/examples/common/queue/task_producer.c b/examples/common/queue/task_producer.c index 379975e..c70656a 100644 --- a/examples/common/queue/task_producer.c +++ b/examples/common/queue/task_producer.c @@ -44,12 +44,44 @@ static struct TN_Task task_producer = {}; * PRIVATE FUNCTIONS ******************************************************************************/ +/** + * Application init: called from the first created application task + */ +static void appl_init(void) +{ + //-- init common application objects + queue_example_init(); + + //-- create all the rest application tasks: + + //-- create the consumer task {{{ + { + task_consumer_create(); + + //-- wait until producer task initialized + SYSRETVAL_CHECK( + tn_eventgrp_wait( + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, + TN_EVENTGRP_WMODE_AND, + NULL, + TN_WAIT_INFINITE + ) + ); + } + // }}} + +} + /** * Body function for producer task */ static void task_producer_body(void *par) { - //-- nothing special to initialize here + //-- in this particular application, producer task is the first application + // task that is started, so, we should perform all the app initialization + // here, and then start other tasks. All of this is done in the appl_init(). + appl_init(); //-- cry that producer task has initialized SYSRETVAL_CHECK( @@ -60,18 +92,9 @@ static void task_producer_body(void *par) ) ); - //-- wait until consumer task initialized, since we are about to - // send messages to it - SYSRETVAL_CHECK( - tn_eventgrp_wait( - queue_example_eventgrp_get(), - QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, - TN_EVENTGRP_WMODE_AND, - NULL, - TN_WAIT_INFINITE - ) - ); - + //-- at this point, application is completely initialized, and we can + // get to business: enter endless loop and repeatedly send + // messages to the consumer for (;;) { int i; From 6ba8a999ea3e0b6e6ce6e70b3478c86feded14f8 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 01:54:50 +0400 Subject: [PATCH 17/55] examples/pic32/basic project is moved to examples/arch/pic32/basic --- examples/{ => arch}/pic32/basic/.vimprj/.indexer_files | 0 examples/{ => arch}/pic32/basic/.vimprj/my.vim | 0 examples/{ => arch}/pic32/basic/src/tn_cfg_appl.h | 0 examples/{ => arch}/pic32/basic/src/tn_pic32_example_basic.c | 0 .../{ => arch}/pic32/basic/tn_pic32_example_basic.X/Makefile | 0 .../tn_pic32_example_basic.X/nbproject/configurations.xml | 0 .../tn_pic32_example_basic.X/nbproject/project.properties | 0 .../basic/tn_pic32_example_basic.X/nbproject/project.xml | 0 examples/common/queue/queue_example.h | 2 ++ examples/common/queue/readme.txt | 4 ++++ stuff/doc_pages/quick_guide.dox | 4 ++-- 11 files changed, 8 insertions(+), 2 deletions(-) rename examples/{ => arch}/pic32/basic/.vimprj/.indexer_files (100%) rename examples/{ => arch}/pic32/basic/.vimprj/my.vim (100%) rename examples/{ => arch}/pic32/basic/src/tn_cfg_appl.h (100%) rename examples/{ => arch}/pic32/basic/src/tn_pic32_example_basic.c (100%) rename examples/{ => arch}/pic32/basic/tn_pic32_example_basic.X/Makefile (100%) rename examples/{ => arch}/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml (100%) rename examples/{ => arch}/pic32/basic/tn_pic32_example_basic.X/nbproject/project.properties (100%) rename examples/{ => arch}/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml (100%) create mode 100644 examples/common/queue/readme.txt diff --git a/examples/pic32/basic/.vimprj/.indexer_files b/examples/arch/pic32/basic/.vimprj/.indexer_files similarity index 100% rename from examples/pic32/basic/.vimprj/.indexer_files rename to examples/arch/pic32/basic/.vimprj/.indexer_files diff --git a/examples/pic32/basic/.vimprj/my.vim b/examples/arch/pic32/basic/.vimprj/my.vim similarity index 100% rename from examples/pic32/basic/.vimprj/my.vim rename to examples/arch/pic32/basic/.vimprj/my.vim diff --git a/examples/pic32/basic/src/tn_cfg_appl.h b/examples/arch/pic32/basic/src/tn_cfg_appl.h similarity index 100% rename from examples/pic32/basic/src/tn_cfg_appl.h rename to examples/arch/pic32/basic/src/tn_cfg_appl.h diff --git a/examples/pic32/basic/src/tn_pic32_example_basic.c b/examples/arch/pic32/basic/src/tn_pic32_example_basic.c similarity index 100% rename from examples/pic32/basic/src/tn_pic32_example_basic.c rename to examples/arch/pic32/basic/src/tn_pic32_example_basic.c diff --git a/examples/pic32/basic/tn_pic32_example_basic.X/Makefile b/examples/arch/pic32/basic/tn_pic32_example_basic.X/Makefile similarity index 100% rename from examples/pic32/basic/tn_pic32_example_basic.X/Makefile rename to examples/arch/pic32/basic/tn_pic32_example_basic.X/Makefile diff --git a/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml b/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml similarity index 100% rename from examples/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml rename to examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml diff --git a/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/project.properties b/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.properties similarity index 100% rename from examples/pic32/basic/tn_pic32_example_basic.X/nbproject/project.properties rename to examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.properties diff --git a/examples/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml b/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml similarity index 100% rename from examples/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml rename to examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml diff --git a/examples/common/queue/queue_example.h b/examples/common/queue/queue_example.h index f08c3c0..1adebc6 100644 --- a/examples/common/queue/queue_example.h +++ b/examples/common/queue/queue_example.h @@ -1,6 +1,8 @@ /** * \file * + * This is an usage example of queues in TNeoKernel. + * * When you need to build a queue of some data objects from one task to * another, the common pattern in the TNeoKernel is to use fixed memory pool * and queue together. Number of elements in the memory pool is equal to the diff --git a/examples/common/queue/readme.txt b/examples/common/queue/readme.txt new file mode 100644 index 0000000..cf3692f --- /dev/null +++ b/examples/common/queue/readme.txt @@ -0,0 +1,4 @@ + +This is an usage example of queues in TNeoKernel. +For detailed explanation, see comments at the top of the file queue_example.h + diff --git a/stuff/doc_pages/quick_guide.dox b/stuff/doc_pages/quick_guide.dox index 05d8ded..87c6bce 100644 --- a/stuff/doc_pages/quick_guide.dox +++ b/stuff/doc_pages/quick_guide.dox @@ -68,7 +68,7 @@ tn_soft_isr(_TIMER_5_VECTOR) ### Basic example for PIC32 This example project can be found in the TNeoKernel repository, in the -`examples/pic32/basic` directory. +`examples/arch/pic32/basic` directory. \attention Before trying to build examples, please read \ref building page carefully: you need to copy configuration file in the tneokernel directory to @@ -76,7 +76,7 @@ build it. Each example has `tn_cfg_appl.h` file, and you should either create a symbolic link to this file from `tneokernel/src/tn_cfg.h` or just copy this file as `tneokernel/src/tn_cfg.h`. -\include pic32/basic/src/tn_pic32_example_basic.c +\include arch/pic32/basic/src/tn_pic32_example_basic.c \section round_robin Round-robin scheduling From a2869d84ead35b59a3d591a733f56ebe53ddfe71 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 02:09:33 +0400 Subject: [PATCH 18/55] refactor in queue example project organization --- examples/queue/arch/example_arch.h | 4 ++++ .../arch/pic32}/example_queue_pic32.X/Makefile | 0 .../example_queue_pic32.X/nbproject/configurations.xml | 10 ++++------ .../example_queue_pic32.X/nbproject/project.properties | 0 .../pic32}/example_queue_pic32.X/nbproject/project.xml | 0 examples/queue/arch/pic32_arch.c | 4 ++++ examples/{common => }/queue/queue_example.c | 0 examples/{common => }/queue/queue_example.h | 0 examples/{common => }/queue/readme.txt | 0 examples/{common => }/queue/task_consumer.c | 0 examples/{common => }/queue/task_consumer.h | 0 examples/{common => }/queue/task_producer.c | 0 examples/{common => }/queue/task_producer.h | 0 13 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 examples/queue/arch/example_arch.h rename examples/{arch/pic32/queue => queue/arch/pic32}/example_queue_pic32.X/Makefile (100%) rename examples/{arch/pic32/queue => queue/arch/pic32}/example_queue_pic32.X/nbproject/configurations.xml (97%) rename examples/{arch/pic32/queue => queue/arch/pic32}/example_queue_pic32.X/nbproject/project.properties (100%) rename examples/{arch/pic32/queue => queue/arch/pic32}/example_queue_pic32.X/nbproject/project.xml (100%) create mode 100644 examples/queue/arch/pic32_arch.c rename examples/{common => }/queue/queue_example.c (100%) rename examples/{common => }/queue/queue_example.h (100%) rename examples/{common => }/queue/readme.txt (100%) rename examples/{common => }/queue/task_consumer.c (100%) rename examples/{common => }/queue/task_consumer.h (100%) rename examples/{common => }/queue/task_producer.c (100%) rename examples/{common => }/queue/task_producer.h (100%) diff --git a/examples/queue/arch/example_arch.h b/examples/queue/arch/example_arch.h new file mode 100644 index 0000000..5bede5c --- /dev/null +++ b/examples/queue/arch/example_arch.h @@ -0,0 +1,4 @@ + +//-- Include common pic32 header for all examples +#include "../../arch/pic32/example_arch.h" + diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/Makefile b/examples/queue/arch/pic32/example_queue_pic32.X/Makefile similarity index 100% rename from examples/arch/pic32/queue/example_queue_pic32.X/Makefile rename to examples/queue/arch/pic32/example_queue_pic32.X/Makefile diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/configurations.xml b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml similarity index 97% rename from examples/arch/pic32/queue/example_queue_pic32.X/nbproject/configurations.xml rename to examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml index 6af8c03..c1f5ccf 100644 --- a/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/configurations.xml +++ b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml @@ -16,11 +16,9 @@ ../../pic32_arch.c ../../../../../src/arch/pic32/tn_arch_pic32_int_vec1.S - - ../../../../common/queue/queue_example.c - ../../../../common/queue/task_consumer.c - ../../../../common/queue/task_producer.c - + ../../../queue_example.c + ../../../task_consumer.c + ../../../task_producer.c - ../.. ../../../../common/queue ../../../../../src/arch/pic32 + ../../.. Makefile diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.properties b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/project.properties similarity index 100% rename from examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.properties rename to examples/queue/arch/pic32/example_queue_pic32.X/nbproject/project.properties diff --git a/examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.xml b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/project.xml similarity index 100% rename from examples/arch/pic32/queue/example_queue_pic32.X/nbproject/project.xml rename to examples/queue/arch/pic32/example_queue_pic32.X/nbproject/project.xml diff --git a/examples/queue/arch/pic32_arch.c b/examples/queue/arch/pic32_arch.c new file mode 100644 index 0000000..a4ae1d5 --- /dev/null +++ b/examples/queue/arch/pic32_arch.c @@ -0,0 +1,4 @@ + +//-- Include common pic32 source for all examples +#include "../../arch/pic32/pic32_arch.c" + diff --git a/examples/common/queue/queue_example.c b/examples/queue/queue_example.c similarity index 100% rename from examples/common/queue/queue_example.c rename to examples/queue/queue_example.c diff --git a/examples/common/queue/queue_example.h b/examples/queue/queue_example.h similarity index 100% rename from examples/common/queue/queue_example.h rename to examples/queue/queue_example.h diff --git a/examples/common/queue/readme.txt b/examples/queue/readme.txt similarity index 100% rename from examples/common/queue/readme.txt rename to examples/queue/readme.txt diff --git a/examples/common/queue/task_consumer.c b/examples/queue/task_consumer.c similarity index 100% rename from examples/common/queue/task_consumer.c rename to examples/queue/task_consumer.c diff --git a/examples/common/queue/task_consumer.h b/examples/queue/task_consumer.h similarity index 100% rename from examples/common/queue/task_consumer.h rename to examples/queue/task_consumer.h diff --git a/examples/common/queue/task_producer.c b/examples/queue/task_producer.c similarity index 100% rename from examples/common/queue/task_producer.c rename to examples/queue/task_producer.c diff --git a/examples/common/queue/task_producer.h b/examples/queue/task_producer.h similarity index 100% rename from examples/common/queue/task_producer.h rename to examples/queue/task_producer.h From b8ffb63336fd849711ed27909b8bd9dec48f2847 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 02:22:09 +0400 Subject: [PATCH 19/55] refactor in queue example project organization --- examples/queue/arch/example_arch.h | 4 - .../nbproject/configurations.xml | 4 +- examples/queue/arch/queue_example_arch.c | 83 +++++++++++++++++++ examples/queue/arch/queue_example_arch.h | 26 ++++++ examples/queue/queue_example.h | 2 +- examples/queue/task_consumer.c | 31 +------ examples/queue/task_consumer.h | 7 -- examples/queue/task_producer.c | 1 - 8 files changed, 114 insertions(+), 44 deletions(-) delete mode 100644 examples/queue/arch/example_arch.h create mode 100644 examples/queue/arch/queue_example_arch.c create mode 100644 examples/queue/arch/queue_example_arch.h diff --git a/examples/queue/arch/example_arch.h b/examples/queue/arch/example_arch.h deleted file mode 100644 index 5bede5c..0000000 --- a/examples/queue/arch/example_arch.h +++ /dev/null @@ -1,4 +0,0 @@ - -//-- Include common pic32 header for all examples -#include "../../arch/pic32/example_arch.h" - diff --git a/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml index c1f5ccf..e7c5517 100644 --- a/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml +++ b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml @@ -15,6 +15,7 @@ ../../pic32_arch.c ../../../../../src/arch/pic32/tn_arch_pic32_int_vec1.S + ../../queue_example_arch.c ../../../queue_example.c ../../../task_consumer.c @@ -89,7 +90,7 @@ + value="../../;../../../;../../../../../src"/> @@ -224,7 +225,6 @@ - diff --git a/examples/queue/arch/queue_example_arch.c b/examples/queue/arch/queue_example_arch.c new file mode 100644 index 0000000..4178e5b --- /dev/null +++ b/examples/queue/arch/queue_example_arch.c @@ -0,0 +1,83 @@ +/** + * \file + * + * Data queue usage example. + * + * For general information about the pattern, refer to the top of + * queue_example.h file + */ + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include +#include "queue_example_arch.h" +#include "task_consumer.h" + + + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + + +//-- gpio register to work with, you might want to switch it to different port +// NOTE: in real programs, it's better to use some peripheral library. +// I strongly dislike plib as well as "brand new" Harmony by Microchip, +// I use my own peripheral library, but for example such as this one, +// I decided to write it as simple as possible. +#define TRIS_REG TRISE +#define TRIS_REG_CLR TRISECLR +#define TRIS_REG_SET TRISESET +#define TRIS_REG_INV TRISEINV + +#define PORT_REG PORTE +#define PORT_REG_CLR PORTECLR +#define PORT_REG_SET PORTESET +#define PORT_REG_INV PORTEINV + + +#define TASK_CONS_PIN_MASK (0 \ + | (1 << TASK_CONS_PIN__0) \ + | (1 << TASK_CONS_PIN__1) \ + | (1 << TASK_CONS_PIN__2) \ + ) + + + + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +/** + * See comments in the header file + */ +void queue_example_arch_init(void) +{ + //-- set output for needed pins + TRIS_REG_CLR = TASK_CONS_PIN_MASK; + + //-- clear current port value + PORT_REG_CLR = TASK_CONS_PIN_MASK; +} + +/** + * See comments in the header file + */ +void queue_example_arch_pins_toggle(int pin_mask) +{ + PORT_REG_INV = pin_mask; +} + + diff --git a/examples/queue/arch/queue_example_arch.h b/examples/queue/arch/queue_example_arch.h new file mode 100644 index 0000000..8d0349e --- /dev/null +++ b/examples/queue/arch/queue_example_arch.h @@ -0,0 +1,26 @@ + +#ifndef _QUEUE_EXAMPLE_ARCH_H +#define _QUEUE_EXAMPLE_ARCH_H + + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +//-- Include common pic32 header for all examples +#include "../../arch/pic32/example_arch.h" + +/** + * At least, we need to initialize GPIO pins here + */ +void queue_example_arch_init(void); + +/** + * Toggle pins specified by pin_mask + */ +void queue_example_arch_pins_toggle(int pin_mask); + + +#endif // _QUEUE_EXAMPLE_ARCH_H + diff --git a/examples/queue/queue_example.h b/examples/queue/queue_example.h index 1adebc6..71832b6 100644 --- a/examples/queue/queue_example.h +++ b/examples/queue/queue_example.h @@ -77,7 +77,7 @@ //-- include architecture-specific things, // at least, there is SOFTWARE_BREAK macro -#include "example_arch.h" +#include "queue_example_arch.h" /******************************************************************************* diff --git a/examples/queue/task_consumer.c b/examples/queue/task_consumer.c index 1348a39..59fe436 100644 --- a/examples/queue/task_consumer.c +++ b/examples/queue/task_consumer.c @@ -11,7 +11,6 @@ * INCLUDED FILES ******************************************************************************/ -#include #include "task_consumer.h" #include "task_producer.h" #include "queue_example.h" @@ -37,23 +36,6 @@ -//-- gpio register to work with, you might want to switch it to different port -// NOTE: in real programs, it's better to use some peripheral library. -// I strongly dislike plib as well as "brand new" Harmony by Microchip, -// I use my own peripheral library, but in order to write simple example, -// I decided to write it as simple as possible. -#define TRIS_REG TRISE -#define TRIS_REG_CLR TRISECLR -#define TRIS_REG_SET TRISESET -#define TRIS_REG_INV TRISEINV - -#define PORT_REG PORTE -#define PORT_REG_CLR PORTECLR -#define PORT_REG_SET PORTESET -#define PORT_REG_INV PORTEINV - - - /******************************************************************************* * PRIVATE FUNCTION PROTOTYPES ******************************************************************************/ @@ -113,16 +95,7 @@ TN_FMEM_BUF_DEF(cons_fmem_buf, struct TaskConsumerMsg, CONS_QUE_BUF_SIZE); static void task_consumer_body(void *par) { //-- init things specific to consumer task - - //-- configure LED port pins {{{ - { - //-- set output for needed pins - TRIS_REG_CLR = TASK_CONS_PIN_MASK; - - //-- clear current port value - PORT_REG_CLR = TASK_CONS_PIN_MASK; - } - // }}} + queue_example_arch_init(); //-- create memory pool SYSRETVAL_CHECK( @@ -167,7 +140,7 @@ static void task_consumer_body(void *par) case TASK_CONS_CMD__PIN_TOGGLE: //-- toggle specified bit - PORT_REG_INV = (1 << p_msg->pin_num); + queue_example_arch_pins_toggle(1 << p_msg->pin_num); break; default: diff --git a/examples/queue/task_consumer.h b/examples/queue/task_consumer.h index 807f55a..73e33a3 100644 --- a/examples/queue/task_consumer.h +++ b/examples/queue/task_consumer.h @@ -37,13 +37,6 @@ enum E_TaskConsPin { * DEFINITIONS ******************************************************************************/ -#define TASK_CONS_PIN_MASK (0 \ - | (1 << TASK_CONS_PIN__0) \ - | (1 << TASK_CONS_PIN__1) \ - | (1 << TASK_CONS_PIN__2) \ - ) - - /******************************************************************************* * PUBLIC FUNCTION PROTOTYPES diff --git a/examples/queue/task_producer.c b/examples/queue/task_producer.c index c70656a..bc76d58 100644 --- a/examples/queue/task_producer.c +++ b/examples/queue/task_producer.c @@ -8,7 +8,6 @@ */ -#include #include "task_producer.h" #include "task_consumer.h" #include "queue_example.h" From b094bab0b7c3b34ff3f469d3352350a31e77b261 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 02:32:00 +0400 Subject: [PATCH 20/55] refactor in example projects organization, again --- .../arch/pic32}/.vimprj/.indexer_files | 0 .../basic => basic/arch/pic32}/.vimprj/my.vim | 0 .../src => basic/arch/pic32}/tn_cfg_appl.h | 0 .../pic32}/tn_pic32_example_basic.X/Makefile | 0 .../nbproject/configurations.xml | 69 +++++++++++++++++-- .../nbproject/project.properties | 0 .../nbproject/project.xml | 2 +- .../arch/pic32}/tn_pic32_example_basic.c | 0 .../{ => common}/arch/pic32/example_arch.h | 0 examples/{ => common}/arch/pic32/pic32_arch.c | 0 .../nbproject/configurations.xml | 6 +- examples/queue/arch/{ => pic32}/pic32_arch.c | 2 +- .../arch/{ => pic32}/queue_example_arch.c | 0 .../arch/{ => pic32}/queue_example_arch.h | 2 +- 14 files changed, 69 insertions(+), 12 deletions(-) rename examples/{arch/pic32/basic => basic/arch/pic32}/.vimprj/.indexer_files (100%) rename examples/{arch/pic32/basic => basic/arch/pic32}/.vimprj/my.vim (100%) rename examples/{arch/pic32/basic/src => basic/arch/pic32}/tn_cfg_appl.h (100%) rename examples/{arch/pic32/basic => basic/arch/pic32}/tn_pic32_example_basic.X/Makefile (100%) rename examples/{arch/pic32/basic => basic/arch/pic32}/tn_pic32_example_basic.X/nbproject/configurations.xml (70%) rename examples/{arch/pic32/basic => basic/arch/pic32}/tn_pic32_example_basic.X/nbproject/project.properties (100%) rename examples/{arch/pic32/basic => basic/arch/pic32}/tn_pic32_example_basic.X/nbproject/project.xml (88%) rename examples/{arch/pic32/basic/src => basic/arch/pic32}/tn_pic32_example_basic.c (100%) rename examples/{ => common}/arch/pic32/example_arch.h (100%) rename examples/{ => common}/arch/pic32/pic32_arch.c (100%) rename examples/queue/arch/{ => pic32}/pic32_arch.c (50%) rename examples/queue/arch/{ => pic32}/queue_example_arch.c (100%) rename examples/queue/arch/{ => pic32}/queue_example_arch.h (86%) diff --git a/examples/arch/pic32/basic/.vimprj/.indexer_files b/examples/basic/arch/pic32/.vimprj/.indexer_files similarity index 100% rename from examples/arch/pic32/basic/.vimprj/.indexer_files rename to examples/basic/arch/pic32/.vimprj/.indexer_files diff --git a/examples/arch/pic32/basic/.vimprj/my.vim b/examples/basic/arch/pic32/.vimprj/my.vim similarity index 100% rename from examples/arch/pic32/basic/.vimprj/my.vim rename to examples/basic/arch/pic32/.vimprj/my.vim diff --git a/examples/arch/pic32/basic/src/tn_cfg_appl.h b/examples/basic/arch/pic32/tn_cfg_appl.h similarity index 100% rename from examples/arch/pic32/basic/src/tn_cfg_appl.h rename to examples/basic/arch/pic32/tn_cfg_appl.h diff --git a/examples/arch/pic32/basic/tn_pic32_example_basic.X/Makefile b/examples/basic/arch/pic32/tn_pic32_example_basic.X/Makefile similarity index 100% rename from examples/arch/pic32/basic/tn_pic32_example_basic.X/Makefile rename to examples/basic/arch/pic32/tn_pic32_example_basic.X/Makefile diff --git a/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml b/examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/configurations.xml similarity index 70% rename from examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml rename to examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/configurations.xml index 6248059..9306e2f 100644 --- a/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/configurations.xml +++ b/examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/configurations.xml @@ -12,8 +12,8 @@ - ../../../../src/arch/pic32/tn_arch_pic32_int_vec1.S - ../src/tn_pic32_example_basic.c + ../../../../../src/arch/pic32/tn_arch_pic32_int_vec1.S + ../tn_pic32_example_basic.c - ../src ../../../../src/arch/pic32 + ../../../../../src/arch/pic32 + .. Makefile @@ -42,12 +43,12 @@ - - + @@ -183,6 +184,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.properties b/examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/project.properties similarity index 100% rename from examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.properties rename to examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/project.properties diff --git a/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml b/examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/project.xml similarity index 88% rename from examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml rename to examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/project.xml index 3eeb33a..a14bf5d 100644 --- a/examples/arch/pic32/basic/tn_pic32_example_basic.X/nbproject/project.xml +++ b/examples/basic/arch/pic32/tn_pic32_example_basic.X/nbproject/project.xml @@ -11,7 +11,7 @@ UTF-8 - ../../../../src/arch/pic32/tneokernel.X + ../../../../../src/arch/pic32/tneokernel.X diff --git a/examples/arch/pic32/basic/src/tn_pic32_example_basic.c b/examples/basic/arch/pic32/tn_pic32_example_basic.c similarity index 100% rename from examples/arch/pic32/basic/src/tn_pic32_example_basic.c rename to examples/basic/arch/pic32/tn_pic32_example_basic.c diff --git a/examples/arch/pic32/example_arch.h b/examples/common/arch/pic32/example_arch.h similarity index 100% rename from examples/arch/pic32/example_arch.h rename to examples/common/arch/pic32/example_arch.h diff --git a/examples/arch/pic32/pic32_arch.c b/examples/common/arch/pic32/pic32_arch.c similarity index 100% rename from examples/arch/pic32/pic32_arch.c rename to examples/common/arch/pic32/pic32_arch.c diff --git a/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml index e7c5517..cd8df9b 100644 --- a/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml +++ b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml @@ -13,9 +13,9 @@ displayName="Source Files" projectFiles="true"> - ../../pic32_arch.c + ../pic32_arch.c + ../queue_example_arch.c ../../../../../src/arch/pic32/tn_arch_pic32_int_vec1.S - ../../queue_example_arch.c ../../../queue_example.c ../../../task_consumer.c @@ -90,7 +90,7 @@ + value="../;../../../;../../../../../src"/> diff --git a/examples/queue/arch/pic32_arch.c b/examples/queue/arch/pic32/pic32_arch.c similarity index 50% rename from examples/queue/arch/pic32_arch.c rename to examples/queue/arch/pic32/pic32_arch.c index a4ae1d5..63efb43 100644 --- a/examples/queue/arch/pic32_arch.c +++ b/examples/queue/arch/pic32/pic32_arch.c @@ -1,4 +1,4 @@ //-- Include common pic32 source for all examples -#include "../../arch/pic32/pic32_arch.c" +#include "../../../common/arch/pic32/pic32_arch.c" diff --git a/examples/queue/arch/queue_example_arch.c b/examples/queue/arch/pic32/queue_example_arch.c similarity index 100% rename from examples/queue/arch/queue_example_arch.c rename to examples/queue/arch/pic32/queue_example_arch.c diff --git a/examples/queue/arch/queue_example_arch.h b/examples/queue/arch/pic32/queue_example_arch.h similarity index 86% rename from examples/queue/arch/queue_example_arch.h rename to examples/queue/arch/pic32/queue_example_arch.h index 8d0349e..b3ee7a9 100644 --- a/examples/queue/arch/queue_example_arch.h +++ b/examples/queue/arch/pic32/queue_example_arch.h @@ -9,7 +9,7 @@ ******************************************************************************/ //-- Include common pic32 header for all examples -#include "../../arch/pic32/example_arch.h" +#include "../../../common/arch/pic32/example_arch.h" /** * At least, we need to initialize GPIO pins here From 5833119d5265342968cd7b2df362544bb1d0ab13 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 02:39:46 +0400 Subject: [PATCH 21/55] queue example: architecture-dependent init is called from appl_init() --- examples/queue/queue_example.h | 3 +++ examples/queue/task_consumer.c | 1 - examples/queue/task_producer.c | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/queue/queue_example.h b/examples/queue/queue_example.h index 71832b6..3879763 100644 --- a/examples/queue/queue_example.h +++ b/examples/queue/queue_example.h @@ -46,6 +46,9 @@ * objects are created: in this project, there's just a single * object: event group that is used to synchronize task * initialization. See `enum E_QueExampleFlag`. + * - `queue_example_arch_init()` is called, in which architecture + * dependent things are initialized: at least, we need to setup + * some GPIO pins so that consumer could use them. * - create consumer task * - wait until it is initialized (i.e. wait for the flag * QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT in the event group returned diff --git a/examples/queue/task_consumer.c b/examples/queue/task_consumer.c index 59fe436..0c1013a 100644 --- a/examples/queue/task_consumer.c +++ b/examples/queue/task_consumer.c @@ -95,7 +95,6 @@ TN_FMEM_BUF_DEF(cons_fmem_buf, struct TaskConsumerMsg, CONS_QUE_BUF_SIZE); static void task_consumer_body(void *par) { //-- init things specific to consumer task - queue_example_arch_init(); //-- create memory pool SYSRETVAL_CHECK( diff --git a/examples/queue/task_producer.c b/examples/queue/task_producer.c index bc76d58..01ae4a4 100644 --- a/examples/queue/task_producer.c +++ b/examples/queue/task_producer.c @@ -51,6 +51,9 @@ static void appl_init(void) //-- init common application objects queue_example_init(); + //-- init architecture-dependent stuff + queue_example_arch_init(); + //-- create all the rest application tasks: //-- create the consumer task {{{ From 2f929bb97caa8333687ce8099bf21e5f718d82c5 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 02:42:30 +0400 Subject: [PATCH 22/55] queue example: detailed information is put to the readme.txt file --- examples/queue/queue_example.c | 5 +-- examples/queue/queue_example.h | 69 +--------------------------------- examples/queue/readme.txt | 67 ++++++++++++++++++++++++++++++++- examples/queue/task_consumer.c | 5 +-- examples/queue/task_consumer.h | 5 +-- examples/queue/task_producer.c | 5 +-- examples/queue/task_producer.h | 5 +-- 7 files changed, 72 insertions(+), 89 deletions(-) diff --git a/examples/queue/queue_example.c b/examples/queue/queue_example.c index 8c77b4a..3b3ad77 100644 --- a/examples/queue/queue_example.c +++ b/examples/queue/queue_example.c @@ -1,10 +1,7 @@ /** * \file * - * Data queue usage example. - * - * For general information about the pattern, refer to the top of - * queue_example.h file + * Example project that demonstrates usage of queues in TNeoKernel. */ diff --git a/examples/queue/queue_example.h b/examples/queue/queue_example.h index 3879763..c45f248 100644 --- a/examples/queue/queue_example.h +++ b/examples/queue/queue_example.h @@ -1,74 +1,7 @@ /** * \file * - * This is an usage example of queues in TNeoKernel. - * - * When you need to build a queue of some data objects from one task to - * another, the common pattern in the TNeoKernel is to use fixed memory pool - * and queue together. Number of elements in the memory pool is equal to the - * number of elements in the queue. So when you need to send the message, - * you get block from the memory pool, fill it with data, and send pointer - * to it by means of the queue. - * - * When you receive the message, you handle the data, and release the memory - * back to the memory pool. - * - * This example project demonstrates this technique: there are two tasks: - * - * - producer task (which sends messages to the consumer) - * - consumer task (which receives messages from the producer) - * - * Actual functionality is, of course, very simple: each message contains - * the following: - * - * - command to perform: currently there is just a single command: toggle pin - * - pin number: obviously, number of the pin for which command should - * be performed. - * - * For the message structure, refer to `struct TaskConsumerMsg` in the - * file `task_consumer.c`. Note, however, that it is a "private" structure: - * producer doesn't touch it directly; instead, it uses special function: - * `task_consumer_msg_send()`, which sends message to the queue. - * - * So the producer just sends new message each 100 ms, specifying - * one of three pins. Consumer, consequently, toggles the pin specified - * in the message. - * - * Before we can get to the business, we need to initialize things. - * How it is done: - * - * - `init_task_create()`, which is called from the `tn_sys_start()`, - * creates first application task: the producer task. - * - producer task gets executed and first of all it initializes the - * essential application stuff, by calling `appl_init()`: - * - * - `queue_example_init()` is called, in which common application - * objects are created: in this project, there's just a single - * object: event group that is used to synchronize task - * initialization. See `enum E_QueExampleFlag`. - * - `queue_example_arch_init()` is called, in which architecture - * dependent things are initialized: at least, we need to setup - * some GPIO pins so that consumer could use them. - * - create consumer task - * - wait until it is initialized (i.e. wait for the flag - * QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT in the event group returned - * by `queue_example_eventgrp_get()`) - * - * - consumer task gets executed - * - gpio is configured: we need to set up certain pins to output - * as well as initially clear them - * - consumer initializes its own objects: - * - fixed memory pool - * - queue - * - sets the flag QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT - * - enters endless loop in which it will receive and - * handle messages. Now, it is going to wait for first message. - * - * - producer task gets executed and enters its endless loop in which - * it will send messages to the consumer. - * - * At this point, everything is initialized. - * + * Example project that demonstrates usage of queues in TNeoKernel. */ #ifndef _QUEUE_EXAMPLE_H diff --git a/examples/queue/readme.txt b/examples/queue/readme.txt index cf3692f..f8598b9 100644 --- a/examples/queue/readme.txt +++ b/examples/queue/readme.txt @@ -1,4 +1,69 @@ This is an usage example of queues in TNeoKernel. -For detailed explanation, see comments at the top of the file queue_example.h + +When you need to build a queue of some data objects from one task to +another, the common pattern in the TNeoKernel is to use fixed memory pool +and queue together. Number of elements in the memory pool is equal to the +number of elements in the queue. So when you need to send the message, +you get block from the memory pool, fill it with data, and send pointer +to it by means of the queue. + +When you receive the message, you handle the data, and release the memory +back to the memory pool. + +This example project demonstrates this technique: there are two tasks: + +- producer task (which sends messages to the consumer) +- consumer task (which receives messages from the producer) + +Actual functionality is, of course, very simple: each message contains +the following: + +- command to perform: currently there is just a single command: toggle pin +- pin number: obviously, number of the pin for which command should + be performed. + +For the message structure, refer to `struct TaskConsumerMsg` in the +file `task_consumer.c`. Note, however, that it is a "private" structure: +producer doesn't touch it directly; instead, it uses special function: +`task_consumer_msg_send()`, which sends message to the queue. + +So the producer just sends new message each 100 ms, specifying +one of three pins. Consumer, consequently, toggles the pin specified +in the message. + +Before we can get to the business, we need to initialize things. +How it is done: + +- `init_task_create()`, which is called from the `tn_sys_start()`, + creates first application task: the producer task. +- producer task gets executed and first of all it initializes the + essential application stuff, by calling `appl_init()`: + + - `queue_example_init()` is called, in which common application + objects are created: in this project, there's just a single + object: event group that is used to synchronize task + initialization. See `enum E_QueExampleFlag`. + - `queue_example_arch_init()` is called, in which architecture + dependent things are initialized: at least, we need to setup + some GPIO pins so that consumer could use them. + - create consumer task + - wait until it is initialized (i.e. wait for the flag + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT in the event group returned + by `queue_example_eventgrp_get()`) + +- consumer task gets executed + - gpio is configured: we need to set up certain pins to output + as well as initially clear them + - consumer initializes its own objects: + - fixed memory pool + - queue + - sets the flag QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT + - enters endless loop in which it will receive and + handle messages. Now, it is going to wait for first message. + +- producer task gets executed and enters its endless loop in which + it will send messages to the consumer. + +At this point, everything is initialized. diff --git a/examples/queue/task_consumer.c b/examples/queue/task_consumer.c index 0c1013a..cc5ddd9 100644 --- a/examples/queue/task_consumer.c +++ b/examples/queue/task_consumer.c @@ -1,10 +1,7 @@ /** * \file * - * Data queue usage example. - * - * For general information about the pattern, refer to the top of - * queue_example.h file + * Example project that demonstrates usage of queues in TNeoKernel. */ /******************************************************************************* diff --git a/examples/queue/task_consumer.h b/examples/queue/task_consumer.h index 73e33a3..c21adb3 100644 --- a/examples/queue/task_consumer.h +++ b/examples/queue/task_consumer.h @@ -1,10 +1,7 @@ /** * \file * - * Data queue usage example. - * - * For general information about the pattern, refer to the top of - * queue_example.h file + * Example project that demonstrates usage of queues in TNeoKernel. */ diff --git a/examples/queue/task_producer.c b/examples/queue/task_producer.c index 01ae4a4..437ae7e 100644 --- a/examples/queue/task_producer.c +++ b/examples/queue/task_producer.c @@ -1,10 +1,7 @@ /** * \file * - * Data queue usage example. - * - * For general information about the pattern, refer to the top of - * queue_example.h file + * Example project that demonstrates usage of queues in TNeoKernel. */ diff --git a/examples/queue/task_producer.h b/examples/queue/task_producer.h index dba61be..43e8ba0 100644 --- a/examples/queue/task_producer.h +++ b/examples/queue/task_producer.h @@ -1,10 +1,7 @@ /** * \file * - * Data queue usage example. - * - * For general information about the pattern, refer to the top of - * queue_example.h file + * Example project that demonstrates usage of queues in TNeoKernel. */ From 0456b85efd106dffb6cc624c9110f799765add1f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 03:10:22 +0400 Subject: [PATCH 23/55] dqueue and fixed memory pool references now have a link to queue example --- src/core/tn_dqueue.h | 3 +++ src/core/tn_fmem.h | 5 +++++ stuff/doc_pages/quick_guide.dox | 4 ++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/tn_dqueue.h b/src/core/tn_dqueue.h index 28a9820..18340f4 100644 --- a/src/core/tn_dqueue.h +++ b/src/core/tn_dqueue.h @@ -53,6 +53,9 @@ * received can be interpreted as a pointer or an integer and may have value 0 * (`NULL`). * + * For the useful pattern on how to use queue together with \ref tn_fmem.h + * "fixed memory pool", refer to the example: `examples/queue`. Be sure + * to examine the readme there. */ #ifndef _TN_DQUEUE_H diff --git a/src/core/tn_fmem.h b/src/core/tn_fmem.h index 6c1d396..61ef0ad 100644 --- a/src/core/tn_fmem.h +++ b/src/core/tn_fmem.h @@ -45,6 +45,11 @@ * no free memory blocks, a task trying to acquire a memory block will be * placed into the wait queue until a free memory block arrives (another task * returns it to the memory pool). + * + * + * For the useful pattern on how to use fixed memory pool together with \ref + * tn_dqueue.h "queue", refer to the example: `examples/queue`. Be sure to + * examine the readme there. */ #ifndef _TN_MEM_H diff --git a/stuff/doc_pages/quick_guide.dox b/stuff/doc_pages/quick_guide.dox index 87c6bce..2e5a804 100644 --- a/stuff/doc_pages/quick_guide.dox +++ b/stuff/doc_pages/quick_guide.dox @@ -68,7 +68,7 @@ tn_soft_isr(_TIMER_5_VECTOR) ### Basic example for PIC32 This example project can be found in the TNeoKernel repository, in the -`examples/arch/pic32/basic` directory. +`examples/basic/arch/pic32` directory. \attention Before trying to build examples, please read \ref building page carefully: you need to copy configuration file in the tneokernel directory to @@ -76,7 +76,7 @@ build it. Each example has `tn_cfg_appl.h` file, and you should either create a symbolic link to this file from `tneokernel/src/tn_cfg.h` or just copy this file as `tneokernel/src/tn_cfg.h`. -\include arch/pic32/basic/src/tn_pic32_example_basic.c +\include basic/arch/pic32/tn_pic32_example_basic.c \section round_robin Round-robin scheduling From f1f83cdb043e99943a5573cf97d0b4cefab06721 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 03:19:27 +0400 Subject: [PATCH 24/55] changelog updated: examples/queue is mentioned there --- stuff/doc_pages/changelog.dox | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 44c7cae..c799d06 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -13,6 +13,9 @@ TNeoKernel changelog - Application is now available to specify how many priority levels does it need for, it helps to save a bit of RAM. For details, refer to `#TN_PRIORITIES_CNT`. + - Added example project `examples/queue` that demonstrates the pattern on + how to use \ref tn_dqueue.h "queue" together with \ref tn_fmem.h + "fixed memory pool" effectively. \section changelog_v1_02 v1.02 From 4e9df36fe822c639d48070458815c4f14746c350 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Thu, 16 Oct 2014 19:24:45 +0400 Subject: [PATCH 25/55] queue example: queue_example_arch_init() call moved to queue_example_init() --- examples/queue/queue_example.c | 3 +++ examples/queue/task_producer.c | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/queue/queue_example.c b/examples/queue/queue_example.c index 3b3ad77..b8661eb 100644 --- a/examples/queue/queue_example.c +++ b/examples/queue/queue_example.c @@ -48,6 +48,9 @@ void queue_example_init(void) //-- create application events // (see enum E_QueExampleFlag in the header) SYSRETVAL_CHECK(tn_eventgrp_create(&que_example_events, (0))); + + //-- init architecture-dependent stuff + queue_example_arch_init(); } /** diff --git a/examples/queue/task_producer.c b/examples/queue/task_producer.c index 437ae7e..d3c4fcc 100644 --- a/examples/queue/task_producer.c +++ b/examples/queue/task_producer.c @@ -48,16 +48,13 @@ static void appl_init(void) //-- init common application objects queue_example_init(); - //-- init architecture-dependent stuff - queue_example_arch_init(); - //-- create all the rest application tasks: //-- create the consumer task {{{ { task_consumer_create(); - //-- wait until producer task initialized + //-- wait until consumer task initialized SYSRETVAL_CHECK( tn_eventgrp_wait( queue_example_eventgrp_get(), From a494f172d051347c5cb1dfb97a12cd4705a0da70 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 17 Oct 2014 00:09:58 +0400 Subject: [PATCH 26/55] * Added event group connecting mechanism, queue now has this functionality, but it is untested yet. * Event group now uses TN_UWord type for flags pattern * Added macro _TN_BUG_ON(), empty if TN_DEBUG is 0 --- src/core/tn_dqueue.c | 53 ++++++++++++++++++ src/core/tn_dqueue.h | 43 ++++++++++++++ src/core/tn_eventgrp.c | 124 ++++++++++++++++++++++++++++++++++------- src/core/tn_eventgrp.h | 43 +++++++++----- src/core/tn_internal.h | 41 ++++++++++++++ 5 files changed, 270 insertions(+), 34 deletions(-) diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c index ea3d08f..87a05b5 100644 --- a/src/core/tn_dqueue.c +++ b/src/core/tn_dqueue.c @@ -133,6 +133,10 @@ static enum TN_RCode _fifo_write(struct TN_DQueue *dque, void *p_data) dque->head_idx = 0; } + //-- set flag in the connected event group (if any) + // indicating that there are messages in the queue + _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); + out: return rc; } @@ -161,6 +165,12 @@ static enum TN_RCode _fifo_read(struct TN_DQueue *dque, void **pp_data) dque->tail_idx = 0; } + if (dque->filled_items_cnt == 0){ + //-- clear flag in the connected event group (if any) + // indicating that there are no messages in the queue + _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); + } + out: return rc; } @@ -434,6 +444,9 @@ enum TN_RCode tn_queue_create( dque->data_fifo = data_fifo; dque->items_cnt = items_cnt; + + _tn_eventgrp_link_reset(&dque->eventgrp_link); + if (dque->data_fifo == NULL){ dque->items_cnt = 0; } @@ -549,5 +562,45 @@ enum TN_RCode tn_queue_ireceive_polling(struct TN_DQueue *dque, void **pp_data) return _dqueue_job_iperform(dque, _JOB_TYPE__RECEIVE, pp_data); } +/* + * See comments in the header file (tn_dqueue.h) + */ +enum TN_RCode tn_queue_eventgrp_connect( + struct TN_DQueue *dque, + struct TN_EventGrp *eventgrp, + TN_UWord pattern + ) +{ + int sr_saved; + enum TN_RCode rc = _check_param_generic(dque); + + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + _tn_eventgrp_link_set(&dque->eventgrp_link, eventgrp, pattern); + tn_arch_sr_restore(sr_saved); + } + + return rc; +} + +/* + * See comments in the header file (tn_dqueue.h) + */ +enum TN_RCode tn_queue_eventgrp_disconnect( + struct TN_DQueue *dque + ) +{ + int sr_saved; + enum TN_RCode rc = _check_param_generic(dque); + + if (rc == TN_RC_OK){ + sr_saved = tn_arch_sr_save_int_dis(); + _tn_eventgrp_link_reset(&dque->eventgrp_link); + tn_arch_sr_restore(sr_saved); + } + + return rc; +} + diff --git a/src/core/tn_dqueue.h b/src/core/tn_dqueue.h index 18340f4..faddfe5 100644 --- a/src/core/tn_dqueue.h +++ b/src/core/tn_dqueue.h @@ -67,9 +67,14 @@ #include "tn_list.h" #include "tn_common.h" +#include "tn_eventgrp.h" +/******************************************************************************* + * EXTERN TYPES + ******************************************************************************/ + #ifdef __cplusplus @@ -109,6 +114,9 @@ struct TN_DQueue { /// /// id for object validity verification enum TN_ObjId id_dque; + /// + /// connected event group + struct TN_EGrpLink eventgrp_link; }; /** @@ -307,6 +315,41 @@ enum TN_RCode tn_queue_ireceive_polling( ); +/** + * Connect an event group to the queue. TODO: explain it in detail. + * + * @param dque + * queue to which event group should connected + * @param eventgrp + * event groupt to connect + * @param pattern + * flags pattern that should be managed by the queue automatically + * + * $(TN_CALL_FROM_TASK) + * $(TN_CALL_FROM_ISR) + * $(TN_LEGEND_LINK) + */ +enum TN_RCode tn_queue_eventgrp_connect( + struct TN_DQueue *dque, + struct TN_EventGrp *eventgrp, + TN_UWord pattern + ); + + +/** + * Disconnect a connected event group from the queue. + * If there is no event group connected, nothing is changed. + * TODO: explain it in detail. + * + * @param dque queue from which event group should be disconnected + * + * $(TN_CALL_FROM_TASK) + * $(TN_CALL_FROM_ISR) + * $(TN_LEGEND_LINK) + */ +enum TN_RCode tn_queue_eventgrp_disconnect( + struct TN_DQueue *dque + ); #ifdef __cplusplus diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index 9bee3d9..670fae5 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -77,7 +77,7 @@ static inline enum TN_RCode _check_param_generic( static inline enum TN_RCode _check_param_job_perform( struct TN_EventGrp *eventgrp, - unsigned int pattern + TN_UWord pattern ) { enum TN_RCode rc = _check_param_generic(eventgrp); @@ -111,11 +111,14 @@ static inline enum TN_RCode _check_param_create( static BOOL _cond_check( - struct TN_EventGrp *eventgrp, + struct TN_EventGrp *eventgrp, enum TN_EGrpWaitMode wait_mode, - int wait_pattern + TN_UWord wait_pattern ) { + //-- interrupts should be disabled here + _TN_BUG_ON( !TN_IS_INT_DISABLED() ); + BOOL cond = FALSE; switch (wait_mode){ @@ -140,7 +143,7 @@ static BOOL _cond_check( static void _clear_pattern_if_needed( struct TN_EventGrp *eventgrp, enum TN_EGrpWaitMode wait_mode, - unsigned int pattern + TN_UWord pattern ) { //-- probably, we should add one more wait mode flag, like @@ -149,6 +152,9 @@ static void _clear_pattern_if_needed( static void _scan_event_waitqueue(struct TN_EventGrp *eventgrp) { + //-- interrupts should be disabled here + _TN_BUG_ON( !TN_IS_INT_DISABLED() ); + struct TN_Task *task; struct TN_Task *tmp_task; @@ -182,11 +188,14 @@ static void _scan_event_waitqueue(struct TN_EventGrp *eventgrp) static enum TN_RCode _eventgrp_wait( struct TN_EventGrp *eventgrp, - unsigned int wait_pattern, + TN_UWord wait_pattern, enum TN_EGrpWaitMode wait_mode, - unsigned int *p_flags_pattern + TN_UWord *p_flags_pattern ) { + //-- interrupts should be disabled here + _TN_BUG_ON( !TN_IS_INT_DISABLED() ); + enum TN_RCode rc = TN_RC_OK; rc = _check_param_job_perform(eventgrp, wait_pattern); @@ -213,9 +222,12 @@ static enum TN_RCode _eventgrp_wait( static enum TN_RCode _eventgrp_modify( struct TN_EventGrp *eventgrp, enum TN_EGrpOp operation, - unsigned int pattern + TN_UWord pattern ) { + //-- interrupts should be disabled here + _TN_BUG_ON( !TN_IS_INT_DISABLED() ); + enum TN_RCode rc = TN_RC_OK; rc = _check_param_job_perform(eventgrp, pattern); @@ -229,8 +241,10 @@ static enum TN_RCode _eventgrp_modify( break; case TN_EVENTGRP_OP_SET: - eventgrp->pattern |= pattern; - _scan_event_waitqueue(eventgrp); + if ((eventgrp->pattern & pattern) != pattern){ + eventgrp->pattern |= pattern; + _scan_event_waitqueue(eventgrp); + } break; case TN_EVENTGRP_OP_TOGGLE: @@ -256,8 +270,8 @@ static enum TN_RCode _eventgrp_modify( * See comments in the header file (tn_eventgrp.h) */ enum TN_RCode tn_eventgrp_create( - struct TN_EventGrp *eventgrp, - unsigned int initial_pattern //-- initial value of the pattern + struct TN_EventGrp *eventgrp, + TN_UWord initial_pattern //-- initial value of the pattern ) { enum TN_RCode rc = TN_RC_OK; @@ -317,9 +331,9 @@ enum TN_RCode tn_eventgrp_delete(struct TN_EventGrp *eventgrp) */ enum TN_RCode tn_eventgrp_wait( struct TN_EventGrp *eventgrp, - unsigned int wait_pattern, + TN_UWord wait_pattern, enum TN_EGrpWaitMode wait_mode, - unsigned int *p_flags_pattern, + TN_UWord *p_flags_pattern, TN_Timeout timeout ) { @@ -377,9 +391,9 @@ enum TN_RCode tn_eventgrp_wait( */ enum TN_RCode tn_eventgrp_wait_polling( struct TN_EventGrp *eventgrp, - unsigned int wait_pattern, + TN_UWord wait_pattern, enum TN_EGrpWaitMode wait_mode, - unsigned int *p_flags_pattern + TN_UWord *p_flags_pattern ) { TN_INTSAVE_DATA; @@ -406,9 +420,9 @@ enum TN_RCode tn_eventgrp_wait_polling( */ enum TN_RCode tn_eventgrp_iwait_polling( struct TN_EventGrp *eventgrp, - unsigned int wait_pattern, + TN_UWord wait_pattern, enum TN_EGrpWaitMode wait_mode, - unsigned int *p_flags_pattern + TN_UWord *p_flags_pattern ) { TN_INTSAVE_DATA_INT; @@ -436,7 +450,7 @@ enum TN_RCode tn_eventgrp_iwait_polling( enum TN_RCode tn_eventgrp_modify( struct TN_EventGrp *eventgrp, enum TN_EGrpOp operation, - unsigned int pattern + TN_UWord pattern ) { TN_INTSAVE_DATA; @@ -465,7 +479,7 @@ enum TN_RCode tn_eventgrp_modify( enum TN_RCode tn_eventgrp_imodify( struct TN_EventGrp *eventgrp, enum TN_EGrpOp operation, - unsigned int pattern + TN_UWord pattern ) { TN_INTSAVE_DATA_INT; @@ -487,3 +501,75 @@ enum TN_RCode tn_eventgrp_imodify( } + + +/******************************************************************************* + * PROTECTED FUNCTIONS + ******************************************************************************/ + +/** + * See comments in the file tn_internal.h + */ +enum TN_RCode _tn_eventgrp_link_set( + struct TN_EGrpLink *eventgrp_link, + struct TN_EventGrp *eventgrp, + TN_UWord pattern + ) +{ + //-- interrupts should be disabled here + _TN_BUG_ON( !TN_IS_INT_DISABLED() ); + + enum TN_RCode rc = TN_RC_OK; + + if (eventgrp == NULL || pattern == (0)){ + rc = TN_RC_WPARAM; + } else { + eventgrp_link->eventgrp = eventgrp; + eventgrp_link->pattern = pattern; + } + + return rc; +} + + +/** + * See comments in the file tn_internal.h + */ +enum TN_RCode _tn_eventgrp_link_reset( + struct TN_EGrpLink *eventgrp_link + ) +{ + enum TN_RCode rc = TN_RC_OK; + + eventgrp_link->eventgrp = NULL; + eventgrp_link->pattern = (0); + + return rc; +} + + +/** + * See comments in the file tn_internal.h + */ +enum TN_RCode _tn_eventgrp_link_manage( + struct TN_EGrpLink *eventgrp_link, + BOOL set + ) +{ + //-- interrupts should be disabled here + _TN_BUG_ON( !TN_IS_INT_DISABLED() ); + + enum TN_RCode rc = TN_RC_OK; + + if (eventgrp_link->eventgrp != NULL){ + _eventgrp_modify( + eventgrp_link->eventgrp, + (set ? TN_EVENTGRP_OP_SET : TN_EVENTGRP_OP_CLEAR), + eventgrp_link->pattern + ); + } + + return rc; +} + + diff --git a/src/core/tn_eventgrp.h b/src/core/tn_eventgrp.h index 5bee781..7c65083 100644 --- a/src/core/tn_eventgrp.h +++ b/src/core/tn_eventgrp.h @@ -39,7 +39,7 @@ * * Event group. * - * An event group has an internal variable (of type `unsigned int`), which is + * An event group has an internal variable (of type `TN_UWord`), which is * interpreted as a bit pattern where each bit represents an event. An event * group also has a wait queue for the tasks waiting on these events. A task * may set specified bits when an event occurs and may clear specified bits @@ -60,6 +60,7 @@ #include "tn_list.h" #include "tn_common.h" +#include "tn_sys.h" @@ -112,7 +113,7 @@ enum TN_EGrpOp { */ struct TN_EventGrp { struct TN_ListItem wait_queue; //!< task wait queue - unsigned int pattern; //!< current flags pattern + TN_UWord pattern; //!< current flags pattern enum TN_ObjId id_event; //!< id for object validity verification }; @@ -123,15 +124,27 @@ struct TN_EventGrp { struct TN_EGrpTaskWait { /// /// event wait pattern - int wait_pattern; + TN_UWord wait_pattern; /// /// event wait mode: `AND` or `OR` enum TN_EGrpWaitMode wait_mode; /// /// pattern that caused task to finish waiting - int actual_pattern; + TN_UWord actual_pattern; }; +/** + * A link to event group: used when event group can be connected to + * some kernel object, such as queue. + */ +struct TN_EGrpLink { + /// + /// event group whose event(s) should be managed by other kernel object + struct TN_EventGrp *eventgrp; + /// + /// event pattern to manage + TN_UWord pattern; +}; /******************************************************************************* @@ -167,8 +180,8 @@ struct TN_EGrpTaskWait { * is available: `#TN_RC_WPARAM`. */ enum TN_RCode tn_eventgrp_create( - struct TN_EventGrp *eventgrp, - unsigned int initial_pattern + struct TN_EventGrp *eventgrp, + TN_UWord initial_pattern ); /** @@ -210,7 +223,7 @@ enum TN_RCode tn_eventgrp_delete(struct TN_EventGrp *eventgrp); * `wait_pattern` to be set, or for just **any** of them * (see enum `#TN_EGrpWaitMode`) * @param p_flags_pattern - * Pointer to the `unsigned int` variable in which actual event pattern + * Pointer to the `TN_UWord` variable in which actual event pattern * that caused task to stop waiting will be stored. * May be `NULL`. * @param timeout @@ -227,9 +240,9 @@ enum TN_RCode tn_eventgrp_delete(struct TN_EventGrp *eventgrp); */ enum TN_RCode tn_eventgrp_wait( struct TN_EventGrp *eventgrp, - unsigned int wait_pattern, + TN_UWord wait_pattern, enum TN_EGrpWaitMode wait_mode, - unsigned int *p_flags_pattern, + TN_UWord *p_flags_pattern, TN_Timeout timeout ); @@ -242,9 +255,9 @@ enum TN_RCode tn_eventgrp_wait( */ enum TN_RCode tn_eventgrp_wait_polling( struct TN_EventGrp *eventgrp, - unsigned int wait_pattern, + TN_UWord wait_pattern, enum TN_EGrpWaitMode wait_mode, - unsigned int *p_flags_pattern + TN_UWord *p_flags_pattern ); /** @@ -256,9 +269,9 @@ enum TN_RCode tn_eventgrp_wait_polling( */ enum TN_RCode tn_eventgrp_iwait_polling( struct TN_EventGrp *eventgrp, - unsigned int wait_pattern, + TN_UWord wait_pattern, enum TN_EGrpWaitMode wait_mode, - unsigned int *p_flags_pattern + TN_UWord *p_flags_pattern ); /** @@ -286,7 +299,7 @@ enum TN_RCode tn_eventgrp_iwait_polling( enum TN_RCode tn_eventgrp_modify( struct TN_EventGrp *eventgrp, enum TN_EGrpOp operation, - unsigned int pattern + TN_UWord pattern ); /** @@ -299,7 +312,7 @@ enum TN_RCode tn_eventgrp_modify( enum TN_RCode tn_eventgrp_imodify( struct TN_EventGrp *eventgrp, enum TN_EGrpOp operation, - unsigned int pattern + TN_UWord pattern ); diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index 300c339..41296c3 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -140,6 +140,20 @@ extern struct TN_Task tn_idle_task; #endif +#ifndef TN_DEBUG +# error TN_DEBUG is not defined +#endif + +#if TN_DEBUG +#define _TN_BUG_ON(cond, ...){ \ + if (cond){ \ + _TN_FATAL_ERROR(__VA_ARGS__); \ + } \ +} +#else +#define _TN_BUG_ON(cond) /* nothing */ +#endif + /******************************************************************************* @@ -486,6 +500,33 @@ TN_Timeout _tn_timer_time_left(struct TN_Timer *timer); } /* extern "C" */ #endif + + +/******************************************************************************* + * tn_eventgrp.c + ******************************************************************************/ + +/** + * Establish link to the event group + */ +enum TN_RCode _tn_eventgrp_link_set( + struct TN_EGrpLink *eventgrp_link, + struct TN_EventGrp *eventgrp, + TN_UWord pattern + ); + +/** + * Reset link to the event group (no matter whether it is already established) + */ +enum TN_RCode _tn_eventgrp_link_reset( + struct TN_EGrpLink *eventgrp_link + ); + +enum TN_RCode _tn_eventgrp_link_manage( + struct TN_EGrpLink *eventgrp_link, + BOOL set + ); + #endif // _TN_INTERNAL_H From d4bdce3868c19f19539c157037bf6673aaaa8001 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 17 Oct 2014 00:12:56 +0400 Subject: [PATCH 27/55] fixed typo --- src/core/tn_dqueue.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/tn_dqueue.h b/src/core/tn_dqueue.h index faddfe5..622312b 100644 --- a/src/core/tn_dqueue.h +++ b/src/core/tn_dqueue.h @@ -319,7 +319,7 @@ enum TN_RCode tn_queue_ireceive_polling( * Connect an event group to the queue. TODO: explain it in detail. * * @param dque - * queue to which event group should connected + * queue to which event group should be connected * @param eventgrp * event groupt to connect * @param pattern From 71406a56a946b9acf1128c48d919b947922334bc Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 17 Oct 2014 00:26:35 +0400 Subject: [PATCH 28/55] queue: added comments --- src/core/tn_dqueue.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c index 87a05b5..22347cf 100644 --- a/src/core/tn_dqueue.c +++ b/src/core/tn_dqueue.c @@ -133,7 +133,7 @@ static enum TN_RCode _fifo_write(struct TN_DQueue *dque, void *p_data) dque->head_idx = 0; } - //-- set flag in the connected event group (if any) + //-- set flag in the connected event group (if any), // indicating that there are messages in the queue _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); @@ -166,7 +166,7 @@ static enum TN_RCode _fifo_read(struct TN_DQueue *dque, void **pp_data) } if (dque->filled_items_cnt == 0){ - //-- clear flag in the connected event group (if any) + //-- clear flag in the connected event group (if any), // indicating that there are no messages in the queue _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); } @@ -222,6 +222,15 @@ static enum TN_RCode _queue_send( { enum TN_RCode rc = TN_RC_OK; + //-- first of all, we check whether there are task(s) that + // waits for receive message from the queue. + // + // If yes, we just pass new message to the first task + // from the waiting tasks list, and don't modify messages + // fifo at all. + // + // Otherwise (no waiting tasks), we add new message to the fifo. + if ( !_tn_task_first_wait_complete( &dque->wait_receive_list, TN_RC_OK, _cb_before_task_wait_complete__send, p_data, NULL From 3dae2523dca6ed9cbd7085cf8bdaa176b870c8a5 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 17 Oct 2014 01:13:32 +0400 Subject: [PATCH 29/55] event group connection documented --- src/core/tn_dqueue.h | 22 +++++++++++++++++++--- src/core/tn_eventgrp.h | 31 +++++++++++++++++++++++++++++++ stuff/vimwiki/index.wiki | 6 +++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/core/tn_dqueue.h b/src/core/tn_dqueue.h index 622312b..6136140 100644 --- a/src/core/tn_dqueue.h +++ b/src/core/tn_dqueue.h @@ -44,7 +44,9 @@ * data element into the FIFO. If there is no space left in the FIFO, the task * is switched to the waiting state and placed in the data queue's `wait_send` * queue until space appears (another task gets a data element from the data - * queue). A task that receives a data element tries to get a data element + * queue). + * + * A task that receives a data element tries to get a data element * from the FIFO. If the FIFO is empty (there is no data in the data queue), * the task is switched to the waiting state and placed in the data queue's * `wait_receive` queue until data element arrive (another task puts some data @@ -56,6 +58,14 @@ * For the useful pattern on how to use queue together with \ref tn_fmem.h * "fixed memory pool", refer to the example: `examples/queue`. Be sure * to examine the readme there. + * + * TNeoKernel offers a way to wait for a message from multiple queues in just a + * single call, refer to the section \ref eventgrp_connect for details. Related + * queue services: + * + * - `tn_queue_eventgrp_connect()` + * - `tn_queue_eventgrp_disconnect()` + * */ #ifndef _TN_DQUEUE_H @@ -316,7 +326,12 @@ enum TN_RCode tn_queue_ireceive_polling( /** - * Connect an event group to the queue. TODO: explain it in detail. + * Connect an event group to the queue. + * Refer to the section \ref eventgrp_connect for details. + * + * Only one event group can be connected to the queue at a time. If you + * connect event group while another event group is already connected, + * the old link is discarded. * * @param dque * queue to which event group should be connected @@ -338,8 +353,9 @@ enum TN_RCode tn_queue_eventgrp_connect( /** * Disconnect a connected event group from the queue. + * Refer to the section \ref eventgrp_connect for details. + * * If there is no event group connected, nothing is changed. - * TODO: explain it in detail. * * @param dque queue from which event group should be disconnected * diff --git a/src/core/tn_eventgrp.h b/src/core/tn_eventgrp.h index 7c65083..cfa5a1f 100644 --- a/src/core/tn_eventgrp.h +++ b/src/core/tn_eventgrp.h @@ -49,6 +49,37 @@ * queue. An event group is a very suitable synchronization object for cases * where (for some reasons) one task has to wait for many tasks, or vice versa, * many tasks have to wait for one task. + * + * \section eventgrp_connect Connecting an event group to other system objects + * + * Sometimes task needs to wait for different system events: the most common + * example is to wait for messages from multiple queues. + * + * If the kernel doesn't offer a mechanism for that, programmer usually have to + * use polling services on these queues and sleep for a few system ticks. + * Obviously, this approach has serious drawbacks: we have a lot of useless + * context switches, and response for the message gets much slower. Actually, + * we lost the main goal of the preemtive kernel when we use polling services + * like that. + * + * TNeoKernel offers a solution: an event group can be connected to other + * kernel objects, and these objects will maintain certain flags inside that + * event group automatically. + * + * So, in case of multiple queues, we can act as follows (assume we have two + * queues: Q1 and Q2) : + * + * - create event group EG; + * - connect EG with flag 1 to Q1; + * - connect EG with flag 2 to Q2; + * - when task needs to receive a message from either Q1 or Q2, it just waits + * for the any of flags 1 or 2 in the EG, this is done in the single call + * to `tn_eventgrp_wait()`. + * - when that event happened, task checks which flag is set, and receive + * message from the appropriate queue. + * + * For the information on system services related to queue, refer to the \ref + * tn_dqueue.h "queue reference". */ #ifndef _TN_EVENTGRP_H diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki index 41261ec..a0163c7 100644 --- a/stuff/vimwiki/index.wiki +++ b/stuff/vimwiki/index.wiki @@ -11,7 +11,11 @@ == Plans == - * [ ] Event group connection (at least, it would be useful to connect event group to queue) + * [.] Event group connection (at least, it would be useful to connect event group to queue) + * [X] implement functionality + * [ ] implement tests + * [ ] implement example + * [ ] add the link to example from `tn_eventgrp.h` * [ ] Exchange object: see AVIX User Guide & Reference Guide, 6.6.8 * [ ] Event group: probably, add mask of flags that should be cleared automatically when any task `wait`s for it * [ ] Add a destroy callback for each task, this callback (if not NULL) should be called whenever task is terminated with `tn_task_exit()` or `tn_task_terminate()`. From eb4e494a8c9448be5d6d3dbc487832fba94ba4fb Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 17 Oct 2014 01:20:12 +0400 Subject: [PATCH 30/55] changelog: added a note about event group connection --- stuff/doc_pages/changelog.dox | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index c799d06..8c7fe62 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -7,6 +7,10 @@ TNeoKernel changelog \section changelog_current Current development version (BETA) + - Added a capability to connect an \ref tn_eventgrp.h "event group" to other + system objects, particularly to the \ref tn_dqueue.h "queue". This offers a + way to wait for messages from multiple queues with just a single system + call. Refer to the section \ref eventgrp_connect for details. - Priority 0 is now allowed to use by application (in the original TNKernel, it was reserved for the timer task, but TNeoKernel \ref tnkernel_diff_timer_task "does not have timer task") From 01ebe8ea7cc4e444304eee8723639af3f0173a84 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Fri, 17 Oct 2014 19:11:01 +0400 Subject: [PATCH 31/55] dev wiki: added thoughts about interrupt context saving --- stuff/vimwiki/thoughts.wiki | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stuff/vimwiki/thoughts.wiki b/stuff/vimwiki/thoughts.wiki index 525731f..6dcda40 100644 --- a/stuff/vimwiki/thoughts.wiki +++ b/stuff/vimwiki/thoughts.wiki @@ -44,3 +44,15 @@ What about some object like EventCollector? (not sure how to implement it) Well, we want to be able to wait for multiple events atomically: say, we want to wait for message from the queue and for the semaphore. It seems really hard to do that in single call to something like `tn_collector_wait()`, because then `tn_collector_wait()` should take all the possible arguments for all possible waiting events: void *p_data for sending the data to queue, void **pp_data for receiving, the same for fmem, etc. So, "event connection" approach seems much better. + +== interrupt context save: pre-push or post-push == + + * pre-push: save context on the interrupt stack + * post-push: save context on the interrupted task's stack + +PIC32 port of TNeoKernel uses pre-push. It doesn't allow us to immediately switch to another task after ISR returns, but instead we have the following advantages: + + * we can use shadow register set + * even for ISRs that don't use shadow register set, the number of registers to save is less: we shouldn't save callee-saved registers `$s0 - $s9` + * we save 34 words of RAM for each task: interrupt can happen when scheduler is saving/restoring context, so, if we use post-push, context can be saved on the stack twice. + From 241be76db61b74b1d41a908fc14516610d19fe12 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 18 Oct 2014 20:36:18 +0400 Subject: [PATCH 32/55] arch.dependent functions renamed, indicating whether context switch is pending or performed immediately --- src/arch/pic32/tn_arch_pic32mx_xc32.S | 16 ++++++++-------- src/arch/tn_arch.h | 21 ++++++++++++++++----- src/core/tn_dqueue.c | 4 ++-- src/core/tn_eventgrp.c | 6 +++--- src/core/tn_fmem.c | 6 +++--- src/core/tn_internal.h | 4 ++-- src/core/tn_mutex.c | 6 +++--- src/core/tn_sem.c | 4 ++-- src/core/tn_sys.c | 2 +- src/core/tn_tasks.c | 20 ++++++++++---------- 10 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/arch/pic32/tn_arch_pic32mx_xc32.S b/src/arch/pic32/tn_arch_pic32mx_xc32.S index c1c122a..f6122e3 100644 --- a/src/arch/pic32/tn_arch_pic32mx_xc32.S +++ b/src/arch/pic32/tn_arch_pic32mx_xc32.S @@ -47,8 +47,8 @@ /* Public functions declared in this file */ - .global _tn_arch_context_switch_nosave - .global _tn_arch_context_switch + .global _tn_arch_context_switch_now_nosave + .global _tn_arch_context_switch_pend .global _tn_arch_inside_isr .global tn_arch_sr_save_int_dis .global tn_arch_sr_restore @@ -66,9 +66,9 @@ *----------------------------------------------------------------------------*/ .set noreorder .set noat - .ent _tn_arch_context_switch_nosave + .ent _tn_arch_context_switch_now_nosave -_tn_arch_context_switch_nosave: +_tn_arch_context_switch_now_nosave: /* get new task's sp */ @@ -80,16 +80,16 @@ _tn_arch_context_switch_nosave: sw $t1, 0($t0) /* *t0 = t1; */ /* i.e. tn_curr_run_task = tn_next_task_to_run; */ - .end _tn_arch_context_switch_nosave + .end _tn_arch_context_switch_now_nosave /*---------------------------------------------------------------------------- * *----------------------------------------------------------------------------*/ .set noreorder .set noat - .ent _tn_arch_context_switch + .ent _tn_arch_context_switch_pend -_tn_arch_context_switch: +_tn_arch_context_switch_pend: /* pend CS0 interrupt */ lui $t0, %hi(IFS0SET) @@ -100,7 +100,7 @@ _tn_arch_context_switch: jr $ra nop - .end _tn_arch_context_switch + .end _tn_arch_context_switch_pend /*---------------------------------------------------------------------------- * diff --git a/src/arch/tn_arch.h b/src/arch/tn_arch.h index 9595eef..46d2bba 100644 --- a/src/arch/tn_arch.h +++ b/src/arch/tn_arch.h @@ -129,8 +129,9 @@ TN_UWord *_tn_arch_stack_top_get( * Should put initial CPU context to the provided stack pointer for new task * and return current stack pointer. * - * When resulting context gets restored by `_tn_arch_context_switch_nosave()` - * or `_tn_arch_context_switch()`, the following conditions should be met: + * When resulting context gets restored by + * `_tn_arch_context_switch_now_nosave()` or `_tn_arch_context_switch_pend()`, + * the following conditions should be met: * * - Interrupts are enabled; * - Return address is set to `tn_task_exit()`, so that when task body function @@ -160,13 +161,21 @@ int _tn_arch_inside_isr(void); /** * Called whenever we need to switch context from one task to another. * + * This function typically does NOT switch context; it merely pends it, + * that is, it sets appropriate interrupt flag. If current level is an + * application level, interrupt is fired immediately, and context gets + * switched. + * + * But, if it's hard or impossible on particular platform to use dedicated + * interrupt flag, this function may just switch the context on its own. + * * **Preconditions:** * * - interrupts are enabled; * - `tn_curr_run_task` points to currently running (preempted) task; * - `tn_next_task_to_run` points to new task to run. * - * **Actions to perform:** + * **Actions to perform in actual context switching routine:** * * - save context of the preempted task to its stack; * - set `tn_curr_run_task` to `tn_next_task_to_run`; @@ -175,7 +184,7 @@ int _tn_arch_inside_isr(void); * @see `tn_curr_run_task` * @see `tn_next_task_to_run` */ -void _tn_arch_context_switch(void); +void _tn_arch_context_switch_pend(void); /** * Called whenever we need to switch context to new task, but don't save @@ -183,6 +192,8 @@ void _tn_arch_context_switch(void); * - At system start, inside `tn_sys_start()`; * - At task exit, inside `tn_task_exit()` * + * This function doesn't pend context switch, it switches context immediately. + * * **Preconditions:** * * - interrupts are disabled; @@ -196,7 +207,7 @@ void _tn_arch_context_switch(void); * @see `tn_curr_run_task` * @see `tn_next_task_to_run` */ -void _tn_arch_context_switch_nosave(void); +void _tn_arch_context_switch_now_nosave(void); #ifdef __cplusplus } /* extern "C" */ diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c index 22347cf..05b89c0 100644 --- a/src/core/tn_dqueue.c +++ b/src/core/tn_dqueue.c @@ -361,7 +361,7 @@ static enum TN_RCode _dqueue_job_perform( #endif TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); if (waited){ //-- get wait result @@ -502,7 +502,7 @@ enum TN_RCode tn_queue_delete(struct TN_DQueue * dque) //-- we might need to switch context if _tn_wait_queue_notify_deleted() // has woken up some high-priority task - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index 670fae5..996fae8 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -319,7 +319,7 @@ enum TN_RCode tn_eventgrp_delete(struct TN_EventGrp *eventgrp) //-- we might need to switch context if _tn_wait_queue_notify_deleted() // has woken up some high-priority task - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; @@ -368,7 +368,7 @@ enum TN_RCode tn_eventgrp_wait( #endif TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); if (waited_for_event){ //-- get wait result rc = tn_curr_run_task->task_wait_rc; @@ -466,7 +466,7 @@ enum TN_RCode tn_eventgrp_modify( rc = _eventgrp_modify(eventgrp, operation, pattern); TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; diff --git a/src/core/tn_fmem.c b/src/core/tn_fmem.c index 60618ba..015540c 100644 --- a/src/core/tn_fmem.c +++ b/src/core/tn_fmem.c @@ -292,7 +292,7 @@ enum TN_RCode tn_fmem_delete(struct TN_FMem *fmem) //-- we might need to switch context if _tn_wait_queue_notify_deleted() // has woken up some high-priority task - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; @@ -336,7 +336,7 @@ enum TN_RCode tn_fmem_get( } TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); if (waited_for_data){ //-- get wait result @@ -436,7 +436,7 @@ enum TN_RCode tn_fmem_release(struct TN_FMem *fmem, void *p_data) rc = _fmem_release(fmem, p_data); TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; diff --git a/src/core/tn_internal.h b/src/core/tn_internal.h index 41296c3..12754e8 100644 --- a/src/core/tn_internal.h +++ b/src/core/tn_internal.h @@ -191,10 +191,10 @@ static inline BOOL _tn_need_context_switch(void) return (tn_curr_run_task != tn_next_task_to_run); } -static inline void _tn_switch_context_if_needed(void) +static inline void _tn_context_switch_pend_if_needed(void) { if (_tn_need_context_switch()){ - _tn_arch_context_switch(); + _tn_arch_context_switch_pend(); } } diff --git a/src/core/tn_mutex.c b/src/core/tn_mutex.c index e86c6ee..703afc4 100644 --- a/src/core/tn_mutex.c +++ b/src/core/tn_mutex.c @@ -640,7 +640,7 @@ enum TN_RCode tn_mutex_delete(struct TN_Mutex *mutex) //-- we might need to switch context if _tn_wait_queue_notify_deleted() // has woken up some high-priority task - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; @@ -730,7 +730,7 @@ enum TN_RCode tn_mutex_lock(struct TN_Mutex *mutex, TN_Timeout timeout) #endif TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); if (waited_for_mutex){ //-- get wait result rc = tn_curr_run_task->task_wait_rc; @@ -795,7 +795,7 @@ enum TN_RCode tn_mutex_unlock(struct TN_Mutex *mutex) out_ei: TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; diff --git a/src/core/tn_sem.c b/src/core/tn_sem.c index b28b652..bff04c3 100644 --- a/src/core/tn_sem.c +++ b/src/core/tn_sem.c @@ -157,7 +157,7 @@ static inline enum TN_RCode _sem_job_perform( #endif TN_INT_RESTORE(); //-- restore previous interrupts state - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); if (waited_for_sem){ //-- get wait result rc = tn_curr_run_task->task_wait_rc; @@ -305,7 +305,7 @@ enum TN_RCode tn_sem_delete(struct TN_Sem * sem) //-- we might need to switch context if _tn_wait_queue_notify_deleted() // has woken up some high-priority task - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 9fe21b5..b002da9 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -284,7 +284,7 @@ void tn_sys_start( tn_sys_state |= TN_STATE_FLAG__SYS_RUNNING; //-- Run OS - first context switch - _tn_arch_context_switch_nosave(); + _tn_arch_context_switch_now_nosave(); } diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index d80122b..6064313 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -222,7 +222,7 @@ static inline enum TN_RCode _task_job_perform( rc = p_worker(task); TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; @@ -507,7 +507,7 @@ enum TN_RCode tn_task_suspend(struct TN_Task *task) out_ei: TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; @@ -548,7 +548,7 @@ enum TN_RCode tn_task_resume(struct TN_Task *task) out_ei: TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; @@ -578,7 +578,7 @@ enum TN_RCode tn_task_sleep(TN_Timeout timeout) _tn_task_curr_to_wait_action(NULL, TN_WAIT_REASON_SLEEP, timeout); TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); rc = tn_curr_run_task->task_wait_rc; out: @@ -644,8 +644,8 @@ void tn_task_exit(enum TN_TaskExitOpt opts) //-- it is here only for TN_INT_DIS_SAVE() normal operation, // but actually we don't need to save current interrupt status: // this function never returns, and interrupt status is restored - // from different task's stack inside `_tn_arch_context_switch_nosave()` - // call. + // from different task's stack inside + // `_tn_arch_context_switch_now_nosave()` call. TN_INTSAVE_DATA; if (!tn_is_task_context()){ @@ -663,8 +663,8 @@ void tn_task_exit(enum TN_TaskExitOpt opts) _task_delete(task); } - //-- interrupts will be enabled inside _tn_arch_context_switch_nosave() - _tn_arch_context_switch_nosave(); + //-- interrupts will be enabled inside _tn_arch_context_switch_now_nosave() + _tn_arch_context_switch_now_nosave(); out: return; @@ -719,7 +719,7 @@ enum TN_RCode tn_task_terminate(struct TN_Task *task) } TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; @@ -821,7 +821,7 @@ enum TN_RCode tn_task_change_priority(struct TN_Task *task, int new_priority) } TN_INT_RESTORE(); - _tn_switch_context_if_needed(); + _tn_context_switch_pend_if_needed(); out: return rc; From 3c203e7c9919890b87286406ebb500dca07f2092 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 18 Oct 2014 21:38:48 +0400 Subject: [PATCH 33/55] added _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED() which is called from interrupt services, ISR macro don't pend context switch --- src/arch/example/tn_arch_example.h | 5 +++++ src/arch/pic32/tn_arch_pic32.h | 29 +++++++---------------------- src/core/tn_dqueue.c | 1 + src/core/tn_eventgrp.c | 2 ++ src/core/tn_fmem.c | 2 ++ src/core/tn_sem.c | 1 + src/core/tn_sys.c | 1 + src/core/tn_tasks.c | 1 + 8 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/arch/example/tn_arch_example.h b/src/arch/example/tn_arch_example.h index afdcf9d..f0960e8 100644 --- a/src/arch/example/tn_arch_example.h +++ b/src/arch/example/tn_arch_example.h @@ -189,6 +189,11 @@ typedef unsigned int TN_UWord; */ #define TN_IS_INT_DISABLED() ((__builtin_mfc0(12, 0) & 1) == 0) +/** + * Pend context switch from interrupt. + */ +#define _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED() \ + _tn_context_switch_pend_if_needed() diff --git a/src/arch/pic32/tn_arch_pic32.h b/src/arch/pic32/tn_arch_pic32.h index 790c768..4623517 100644 --- a/src/arch/pic32/tn_arch_pic32.h +++ b/src/arch/pic32/tn_arch_pic32.h @@ -243,6 +243,13 @@ typedef unsigned int TN_UWord; */ #define TN_IS_INT_DISABLED() ((__builtin_mfc0(12, 0) & 1) == 0) +/** + * Pend context switch from interrupt. + */ +#define _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED() \ + _tn_context_switch_pend_if_needed() + + #endif //-- DOXYGEN_SHOULD_SKIP_THIS @@ -346,17 +353,6 @@ void __attribute__((naked, nomips16)) \ asm volatile("jalr $t0"); \ asm volatile("sw $v1, 0($sp)"); \ \ - /* Pend context switch if needed */ \ - asm volatile("lw $t0, tn_curr_run_task"); \ - asm volatile("lw $t1, tn_next_task_to_run"); \ - asm volatile("lw $t0, 0($t0)"); \ - asm volatile("lw $t1, 0($t1)"); \ - asm volatile("lui $t2, %hi(IFS0SET)"); \ - asm volatile("beq $t0, $t1, 1f"); \ - asm volatile("ori $t1, $zero, 2"); \ - asm volatile("sw $t1, %lo(IFS0SET)($t2)"); \ - \ - asm volatile("1:"); \ /* Restore registers */ \ asm volatile("lw $v1, 0($sp)"); \ asm volatile("lw $v0, 4($sp)"); \ @@ -488,17 +484,6 @@ void __attribute__((naked, nomips16)) \ asm volatile("jalr $t0"); \ asm volatile("sw $v1, 0($sp)"); \ \ - /* Pend context switch if needed */ \ - asm volatile("lw $t0, tn_curr_run_task"); \ - asm volatile("lw $t1, tn_next_task_to_run"); \ - asm volatile("lw $t0, 0($t0)"); \ - asm volatile("lw $t1, 0($t1)"); \ - asm volatile("lui $t2, %hi(IFS0SET)"); \ - asm volatile("beq $t0, $t1, 1f"); \ - asm volatile("ori $t1, $zero, 2"); \ - asm volatile("sw $t1, %lo(IFS0SET)($t2)"); \ - \ - asm volatile("1:"); \ /* Restore registers */ \ asm volatile("lw $v1, 0($sp)"); \ asm volatile("lw $v0, 4($sp)"); \ diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c index 05b89c0..f41ecf1 100644 --- a/src/core/tn_dqueue.c +++ b/src/core/tn_dqueue.c @@ -419,6 +419,7 @@ static enum TN_RCode _dqueue_job_iperform( } TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index 996fae8..6525c6c 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -438,6 +438,7 @@ enum TN_RCode tn_eventgrp_iwait_polling( rc = _eventgrp_wait(eventgrp, wait_pattern, wait_mode, p_flags_pattern); TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; @@ -495,6 +496,7 @@ enum TN_RCode tn_eventgrp_imodify( rc = _eventgrp_modify(eventgrp, operation, pattern); TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; diff --git a/src/core/tn_fmem.c b/src/core/tn_fmem.c index 015540c..4da5f0e 100644 --- a/src/core/tn_fmem.c +++ b/src/core/tn_fmem.c @@ -407,6 +407,7 @@ enum TN_RCode tn_fmem_iget_polling(struct TN_FMem *fmem, void **p_data) rc = _fmem_get(fmem, p_data); TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; @@ -466,6 +467,7 @@ enum TN_RCode tn_fmem_irelease(struct TN_FMem *fmem, void *p_data) rc = _fmem_release(fmem, p_data); TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; diff --git a/src/core/tn_sem.c b/src/core/tn_sem.c index bff04c3..c6d20ce 100644 --- a/src/core/tn_sem.c +++ b/src/core/tn_sem.c @@ -196,6 +196,7 @@ static inline enum TN_RCode _sem_job_iperform( TN_INT_IDIS_SAVE(); //-- disable interrupts rc = p_worker(sem); //-- call actual worker function TN_INT_IRESTORE(); //-- restore previous interrupts state + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index b002da9..800870b 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -314,6 +314,7 @@ enum TN_RCode tn_tick_int_processing(void) _tn_timers_tick_proceed(); TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index 6064313..a50bd9f 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -251,6 +251,7 @@ static inline enum TN_RCode _task_job_iperform( rc = p_worker(task); TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); out: return rc; From eaca94b920935de47a5e619b16e68c7c30129d6b Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 18 Oct 2014 23:15:09 +0400 Subject: [PATCH 34/55] added self-check of the application that includes TNeoKernel as a library: tn_arch_pic32_int_vec1.S file must be included to the main project itself --- src/arch/pic32/tn_arch_pic32.c | 24 ++++++++++++++++++- src/arch/pic32/tn_arch_pic32_int_vec1.S | 31 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/arch/pic32/tn_arch_pic32.c b/src/arch/pic32/tn_arch_pic32.c index 1110a25..18a90e8 100644 --- a/src/arch/pic32/tn_arch_pic32.c +++ b/src/arch/pic32/tn_arch_pic32.c @@ -38,6 +38,26 @@ extern unsigned long _gp; +/** + * Self-check for the application that uses TNeoKernel: + * + * PIC32 application must include the file tn_arch_pic32_int_vec1.S + * in order to dispatch vector_1 correctly, but if it is forgotten, + * no error is generated at the build time: we just get to the + * _DefaultInterrupt when we should switch context. + * + * Note that we can't include that file to the TNeoKernel library + * project: it doesn't work. + * + * So, dummy function was invented, and if we forgot to + * include that file, we got an error at the link time. + * + * That function merely returns 0. + */ +extern int +_you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project(void); + + //---------------------------------------------------------------------------- // Context layout // @@ -83,7 +103,6 @@ TN_UWord *_tn_arch_stack_top_get( return stack_low_address + stack_size; } - //---------------------------------------------------------------------------- // Processor specific routine - here for MIPS4K // @@ -95,6 +114,9 @@ TN_UWord *_tn_arch_stack_init( void *param ) { + //-- see comments of the following function for details: + _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project(); + //-- filling register's position in the stack - for debugging only *(--stack_top) = 0; //-- ABI argument area *(--stack_top) = 0; diff --git a/src/arch/pic32/tn_arch_pic32_int_vec1.S b/src/arch/pic32/tn_arch_pic32_int_vec1.S index 7cf9462..3637426 100644 --- a/src/arch/pic32/tn_arch_pic32_int_vec1.S +++ b/src/arch/pic32/tn_arch_pic32_int_vec1.S @@ -46,6 +46,37 @@ */ + +/* + * Self-check for the application that uses TNeoKernel: + * + * PIC32 application must include the file tn_arch_pic32_int_vec1.S + * in order to dispatch vector_1 correctly, but if it is forgotten, + * no error is generated at the build time: we just get to the + * _DefaultInterrupt when we should switch context. + * + * Note that we can't include that file to the TNeoKernel library + * project: it doesn't work. + * + * So, dummy function was invented, and if we forgot to + * include that file, we got an error at the link time. + * + * That function merely returns 0. + */ + .global _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project + .ent _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project +_you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project: + + li $v0, 0 + jr $ra + nop + + .end _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project + + + + + .section .vector_1,code .align 2 .set nomips16 From cc1db29ffec55111933e343c89530ac584b494c8 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sat, 18 Oct 2014 23:42:44 +0400 Subject: [PATCH 35/55] added more comments about self-check --- src/arch/pic32/tn_arch_pic32.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/arch/pic32/tn_arch_pic32.c b/src/arch/pic32/tn_arch_pic32.c index 18a90e8..435547a 100644 --- a/src/arch/pic32/tn_arch_pic32.c +++ b/src/arch/pic32/tn_arch_pic32.c @@ -41,7 +41,8 @@ extern unsigned long _gp; /** * Self-check for the application that uses TNeoKernel: * - * PIC32 application must include the file tn_arch_pic32_int_vec1.S + * PIC32 application must include the file + * src/arch/pic32/tn_arch_pic32_int_vec1.S to the main project, * in order to dispatch vector_1 correctly, but if it is forgotten, * no error is generated at the build time: we just get to the * _DefaultInterrupt when we should switch context. @@ -114,9 +115,21 @@ TN_UWord *_tn_arch_stack_init( void *param ) { - //-- see comments of the following function for details: + //-- if you got "undefined reference" error here, it means that you + // forgot to include the file + // + // /src/arch/pic32/tn_arch_pic32_int_vec1.S + // + // to the main project. + // This requirement is stated in the documentation, here: + // + // http://dfrank.bitbucket.org/tneokernel_api/latest/html/pic32_details.html + // + //-- see comments above of the following function for a bit more details: _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project(); + + //-- filling register's position in the stack - for debugging only *(--stack_top) = 0; //-- ABI argument area *(--stack_top) = 0; From 143bb37eb5a9f2cbd7112aa6e3a609a99170a76f Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 00:01:02 +0400 Subject: [PATCH 36/55] added thanks page to the docs --- stuff/doc_pages/foreword.dox | 2 ++ stuff/doc_pages/mainpage.dox | 1 + stuff/doc_pages/thanks.dox | 21 +++++++++++++++++++++ stuff/doxygen/tn_doxyfile | 1 + 4 files changed, 25 insertions(+) create mode 100644 stuff/doc_pages/thanks.dox diff --git a/stuff/doc_pages/foreword.dox b/stuff/doc_pages/foreword.dox index 2971837..d6297ef 100644 --- a/stuff/doc_pages/foreword.dox +++ b/stuff/doc_pages/foreword.dox @@ -49,4 +49,6 @@ point to the real-time kernels for me. I would also like to thank my chiefs in the [ORION](http://orionspb.ru/) company, Alexey Morozov and Alexey Gromov, for being flexible about my time. +For the full thanks list, refer to the page \ref thanks. + */ diff --git a/stuff/doc_pages/mainpage.dox b/stuff/doc_pages/mainpage.dox index 44b13fc..d4255df 100644 --- a/stuff/doc_pages/mainpage.dox +++ b/stuff/doc_pages/mainpage.dox @@ -29,6 +29,7 @@ Related pages: - \ref unit_tests - \ref plans - \ref changelog + - \ref thanks - \ref legend API reference: diff --git a/stuff/doc_pages/thanks.dox b/stuff/doc_pages/thanks.dox new file mode 100644 index 0000000..afa68c9 --- /dev/null +++ b/stuff/doc_pages/thanks.dox @@ -0,0 +1,21 @@ +/** +\page thanks Thanks + +\tableofcontents + +There are people that I would like to thank: + +- **Yuri Tiomkin** - for original TNKernel. Although the implementation of TNKernel is far from perfect in my opinion, the +ideas behind the implementation are generally really nice (that's why I decided +to reimplement it instead of starting from scratch), and it was great entry +point to the real-time kernels for me; +- **Anders Montonen** - for original implementation of TNKernel-PIC32 port; +- **Alex Borisov** - for his TNKernel port which I was using for a long time; +- **Alexey Morozov** and **Alexey Gromov**, my chiefs in the [ORION](http://orionspb.ru/) +company, for being flexible about my time; +- **Robert White** - for his nice ideas. + +Thank you guys. TNeoKernel would never be what it is without you. + +*/ + diff --git a/stuff/doxygen/tn_doxyfile b/stuff/doxygen/tn_doxyfile index 183090b..97234d2 100644 --- a/stuff/doxygen/tn_doxyfile +++ b/stuff/doxygen/tn_doxyfile @@ -770,6 +770,7 @@ INPUT = ../../src \ ../../stuff/doc_pages/unit_tests.dox \ ../../stuff/doc_pages/plans.dox \ ../../stuff/doc_pages/changelog.dox \ + ../../stuff/doc_pages/thanks.dox \ ../../stuff/doc_pages/legend.dox # This tag can be used to specify the character encoding of the source files From d4e5ae557e66cbcfd758acff78dbf603c4dd9796 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 00:04:21 +0400 Subject: [PATCH 37/55] A bit of spelling correction. Thanks to Robert White --- src/arch/pic32/tn_arch_pic32.c | 4 ++-- src/arch/pic32/tn_arch_pic32_int_vec1.S | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/arch/pic32/tn_arch_pic32.c b/src/arch/pic32/tn_arch_pic32.c index 435547a..d15c942 100644 --- a/src/arch/pic32/tn_arch_pic32.c +++ b/src/arch/pic32/tn_arch_pic32.c @@ -56,7 +56,7 @@ extern unsigned long _gp; * That function merely returns 0. */ extern int -_you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project(void); +_you_should_add_file___tn_arch_pic32_int_vec1_S___to_the_project(void); //---------------------------------------------------------------------------- @@ -126,7 +126,7 @@ TN_UWord *_tn_arch_stack_init( // http://dfrank.bitbucket.org/tneokernel_api/latest/html/pic32_details.html // //-- see comments above of the following function for a bit more details: - _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project(); + _you_should_add_file___tn_arch_pic32_int_vec1_S___to_the_project(); diff --git a/src/arch/pic32/tn_arch_pic32_int_vec1.S b/src/arch/pic32/tn_arch_pic32_int_vec1.S index 3637426..3205bbd 100644 --- a/src/arch/pic32/tn_arch_pic32_int_vec1.S +++ b/src/arch/pic32/tn_arch_pic32_int_vec1.S @@ -63,15 +63,15 @@ * * That function merely returns 0. */ - .global _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project - .ent _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project -_you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project: + .global _you_should_add_file___tn_arch_pic32_int_vec1_S___to_the_project + .ent _you_should_add_file___tn_arch_pic32_int_vec1_S___to_the_project +_you_should_add_file___tn_arch_pic32_int_vec1_S___to_the_project: li $v0, 0 jr $ra nop - .end _you_should_include_file___tn_arch_pic32_int_vec1_S___to_the_project + .end _you_should_add_file___tn_arch_pic32_int_vec1_S___to_the_project From 3653bafbb145c0a5a90d63b075eb214e6398b760 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 00:08:00 +0400 Subject: [PATCH 38/55] docs changed a bit --- stuff/doc_pages/thanks.dox | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stuff/doc_pages/thanks.dox b/stuff/doc_pages/thanks.dox index afa68c9..f43b615 100644 --- a/stuff/doc_pages/thanks.dox +++ b/stuff/doc_pages/thanks.dox @@ -10,10 +10,10 @@ ideas behind the implementation are generally really nice (that's why I decided to reimplement it instead of starting from scratch), and it was great entry point to the real-time kernels for me; - **Anders Montonen** - for original implementation of TNKernel-PIC32 port; -- **Alex Borisov** - for his TNKernel port which I was using for a long time; +- **Alex Borisov** - for TNKernel port which I was using for a long time; - **Alexey Morozov** and **Alexey Gromov**, my chiefs in the [ORION](http://orionspb.ru/) company, for being flexible about my time; -- **Robert White** - for his nice ideas. +- **Robert White** - for nice ideas. Thank you guys. TNeoKernel would never be what it is without you. From 0fc5dd9e1855fdb1d5eba30d57b70add798e0a98 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 15:23:09 +0400 Subject: [PATCH 39/55] interrupt docs improved --- stuff/doc_pages/changelog.dox | 7 ++++ stuff/doc_pages/interrupts.dox | 54 +++++++++++++++++++++++++++++++ stuff/doc_pages/mainpage.dox | 1 + stuff/doc_pages/pic32_details.dox | 33 +++++++++++++------ stuff/doxygen/tn_doxyfile | 1 + 5 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 stuff/doc_pages/interrupts.dox diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 8c7fe62..1783655 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -11,6 +11,13 @@ TNeoKernel changelog system objects, particularly to the \ref tn_dqueue.h "queue". This offers a way to wait for messages from multiple queues with just a single system call. Refer to the section \ref eventgrp_connect for details. + - PIC32 Interrupts: this isn't a mandatory anymore to use kernel-provided macros + `tn_soft_isr()` or `tn_srs_isr()`: interrupts can be defined with standard + way too: this particular ISR will use task's stack instead of interrupt + stack, therefore it takes much more RAM and works a bit faster. There are + no additional constraints on ISR defined without kernel-provided macro: in + either ISR, you can call the same set of kernel services. Refer + to the page \ref interrupts for details. - Priority 0 is now allowed to use by application (in the original TNKernel, it was reserved for the timer task, but TNeoKernel \ref tnkernel_diff_timer_task "does not have timer task") diff --git a/stuff/doc_pages/interrupts.dox b/stuff/doc_pages/interrupts.dox new file mode 100644 index 0000000..200ad3d --- /dev/null +++ b/stuff/doc_pages/interrupts.dox @@ -0,0 +1,54 @@ +/** +\page interrupts Interrupts + +\tableofcontents + +\section interrupt_stack Interrupt stack + +TNeoKernel provides a separate stack for interrupt handlers. This approach +could save a lot of RAM: interrupt can happen at any moment of time, and if +there's no separate interrupt stack, then each task should have enough stack +space for the worse case of interrupt nesting. + +Assume application's ISRs take max 64 words (64 * 4 = 256 bytes on PIC32) and application has 4 +tasks (plus one idle task). Then, each of 5 tasks must have 64 words for +interrupts: 64 * 5 * 4 = 1280 bytes of RAM just for 64 words for ISR. + +With separate stack for interrupts, these 64 words should be allocated just once. +Interrupt stack array should be given to `#tn_sys_start()`. For additional +information, refer to the section \ref starting_the_kernel. + +In order to make particular ISR use separate interrupt stack, this ISR should +be defined by kernel-provided macro, which is platform-dependent: see \ref +pic32_details. + +In spite of the fact that the kernel provides separate stack for interrupt, +this isn't a mandatory: you're able to define your ISR in a standard way, +making it use stask of interrupted task and work a bit faster. There is always +a tradeoff. There are **no additional constraints** on ISR defined without +kernel-provided macro: in either ISR, you can call the same set of kernel +services. + +When you make a decision on whether particular ISR should use separate stack, +consider the following: + +- When ISR is defined in a standard way, and no function is called from that + ISR, only necessary registers are saved on stack. If you have such an ISR + (that doesn't call any function), and this ISR should work very fast, + consider using standard way instead of kernel-provided macro. +- When ISR is defined in a standard way, but it calls any function and doesn't + use shadow register set, compiler saves (almost) full context **on the task's + stack**, because it doesn't know which registers are used inside the function. + In this case, it usually makes more sense to use kernel-provided macro (see + below). +- Kernel-provided interrupt macros switch stack pointer between interrupt stack + and task stack automatically, it takes additional time: e.g. on PIC32 it's + about 20 cycles. +- Kernel-provided interrupt macro that doesn't use shadow register set always + saves (almost) full context **on the interrupt stack**, independently of + whether any function is called from an ISR. +- Kernel-provided interrupt macro that uses shadow register set saves a little + amount of registers **on the interrupt stack**. + + +*/ diff --git a/stuff/doc_pages/mainpage.dox b/stuff/doc_pages/mainpage.dox index d4255df..0a39bea 100644 --- a/stuff/doc_pages/mainpage.dox +++ b/stuff/doc_pages/mainpage.dox @@ -22,6 +22,7 @@ Related pages: - \ref foreword - \ref quick_guide + - \ref interrupts - \ref building - \ref pic32_details - \ref why_reimplement diff --git a/stuff/doc_pages/pic32_details.dox b/stuff/doc_pages/pic32_details.dox index ccf0006..cda32e5 100644 --- a/stuff/doc_pages/pic32_details.dox +++ b/stuff/doc_pages/pic32_details.dox @@ -21,10 +21,23 @@ configured to use shadow register sets. \attention if tneokernel is built as a separate library, then the file `src/arch/pic32/tn_arch_pic32_int_vec1.S` must be included in the main project -itself, in order to dispatch vector1 (core software interrupt 0) correctly. +itself, in order to dispatch vector1 (core software interrupt 0) correctly. Do +note that if we include this file in the TNeoKernel library project, it doesn't +work for vector, unfortunately. + +\attention If you forgot to include this file, you got an error on the link step, +like this: +\code +undefined reference to `_you_should_add_file___tn_arch_pic32_int_vec1_S___to_the_project' +\endcode +Which is much more informative than if you just get to `_DefaultInterrupt` when +it's time to switch context. \section pic32_interrupts Interrupts +For detailed information about interrupts in TNeoKernel, refer to the page \ref +interrupts. + PIC32 port supports nested interrupts. The kernel provides C-language macros for calling C-language interrupt service routines, which can use either MIPS32 or MIPS16e mode. Both software and shadow register interrupt context @@ -44,15 +57,17 @@ tn_srs_isr(_UART_1_VECTOR) } \endcode -\attention every ISR in your system should use kernel-provided macro, either -`tn_soft_isr` or `tn_srs_isr`, instead of `__ISR` macro. +Alternatively, you can define your ISR in a standard way, like this: -\section pic32_interrupt_stack Interrupt stack +\code{.c} +void __ISR(_TIMER_1_VECTOR) timer_1_isr(void) +{ + /* here is your ISR code, including clearing of interrupt flag, and so on */ +} +\endcode -PIC32 uses a separate stack for interrupt handlers. Switching stack pointers is -done automatically in the ISR handler wrapper macros. User should allocate -array for interrupt stack and pass it as an argument to `tn_sys_start()`. -Refer to the \ref starting_the_kernel "Starting the kernel" section for the -usage example and additional comments. +Then, context is saved on the task's stack instead of interrupt stack (and +takes therefore much more RAM), but you save about 20 cycles for each +interrupt. See the page \ref interrupts for details. */ diff --git a/stuff/doxygen/tn_doxyfile b/stuff/doxygen/tn_doxyfile index 97234d2..cadbdb3 100644 --- a/stuff/doxygen/tn_doxyfile +++ b/stuff/doxygen/tn_doxyfile @@ -763,6 +763,7 @@ INPUT = ../../src \ ../../stuff/doc_pages/mainpage.dox \ ../../stuff/doc_pages/foreword.dox \ ../../stuff/doc_pages/quick_guide.dox \ + ../../stuff/doc_pages/interrupts.dox \ ../../stuff/doc_pages/building.dox \ ../../stuff/doc_pages/pic32_details.dox \ ../../stuff/doc_pages/why_reimplement.dox \ From db400fe5d55f8750aaf76265d5571dc1cd695092 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 16:05:16 +0400 Subject: [PATCH 40/55] working on switching from 'goto' to 'else if' approach, tn_mutex.c is done, tn_tasks.c is partially done --- src/core/tn_mutex.c | 265 ++++++++++++++++++++------------------------ src/core/tn_tasks.c | 23 ++-- 2 files changed, 129 insertions(+), 159 deletions(-) diff --git a/src/core/tn_mutex.c b/src/core/tn_mutex.c index 703afc4..1304527 100644 --- a/src/core/tn_mutex.c +++ b/src/core/tn_mutex.c @@ -568,26 +568,25 @@ enum TN_RCode tn_mutex_create( int ceil_priority ) { - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_create(mutex, protocol, ceil_priority); - rc = _check_param_create(mutex, protocol, ceil_priority); if (rc != TN_RC_OK){ - goto out; - } + //-- just return rc as it is + } else { - tn_list_reset(&(mutex->wait_queue)); - tn_list_reset(&(mutex->mutex_queue)); + tn_list_reset(&(mutex->wait_queue)); + tn_list_reset(&(mutex->mutex_queue)); #if TN_MUTEX_DEADLOCK_DETECT - tn_list_reset(&(mutex->deadlock_list)); + tn_list_reset(&(mutex->deadlock_list)); #endif - mutex->protocol = protocol; - mutex->holder = NULL; - mutex->ceil_priority = ceil_priority; - mutex->cnt = 0; - mutex->id_mutex = TN_ID_MUTEX; + mutex->protocol = protocol; + mutex->holder = NULL; + mutex->ceil_priority = ceil_priority; + mutex->cnt = 0; + mutex->id_mutex = TN_ID_MUTEX; + } -out: return rc; } @@ -598,51 +597,46 @@ enum TN_RCode tn_mutex_delete(struct TN_Mutex *mutex) { TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(mutex); - rc = _check_param_generic(mutex); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - //-- mutex can be deleted if only it isn't held - if (mutex->holder != NULL && mutex->holder != tn_curr_run_task){ - rc = TN_RC_ILLEGAL_USE; - goto out_ei; - } + //-- mutex can be deleted if only it isn't held + if (mutex->holder != NULL && mutex->holder != tn_curr_run_task){ + rc = TN_RC_ILLEGAL_USE; + } else { - //-- Remove all tasks (if any) from mutex's wait queue - // NOTE: we might sleep there - _tn_wait_queue_notify_deleted(&(mutex->wait_queue)); + //-- Remove all tasks (if any) from mutex's wait queue + _tn_wait_queue_notify_deleted(&(mutex->wait_queue)); - if (mutex->holder != NULL){ - //-- If the mutex is locked - _mutex_do_unlock(mutex); + if (mutex->holder != NULL){ + //-- If the mutex is locked + _mutex_do_unlock(mutex); - //-- NOTE: redundant reset, because it will anyway - // be reset in tn_mutex_create() - // - // Probably we need to remove it. - tn_list_reset(&(mutex->mutex_queue)); - } + //-- NOTE: redundant reset, because it will anyway + // be reset in tn_mutex_create() + // + // Probably we need to remove it. + tn_list_reset(&(mutex->mutex_queue)); + } - mutex->id_mutex = 0; //-- mutex does not exist now + mutex->id_mutex = 0; //-- mutex does not exist now -out_ei: - TN_INT_RESTORE(); + } - //-- we might need to switch context if _tn_wait_queue_notify_deleted() - // has woken up some high-priority task - _tn_context_switch_pend_if_needed(); + TN_INT_RESTORE(); + + //-- we might need to switch context if _tn_wait_queue_notify_deleted() + // has woken up some high-priority task + _tn_context_switch_pend_if_needed(); + } -out: return rc; } @@ -653,92 +647,76 @@ enum TN_RCode tn_mutex_lock(struct TN_Mutex *mutex, TN_Timeout timeout) { TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(mutex); BOOL waited_for_mutex = FALSE; - rc = _check_param_generic(mutex); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - if (tn_curr_run_task == mutex->holder){ - //-- mutex is already locked by current task - // if recursive locking enabled (TN_MUTEX_REC), increment lock count, - // otherwise error is returned - __mutex_lock_cnt_change(mutex, 1); - rc = __MUTEX_REC_LOCK_RETVAL; - goto out_ei; - } + if (tn_curr_run_task == mutex->holder){ + //-- mutex is already locked by current task + // if recursive locking enabled (TN_MUTEX_REC), increment lock count, + // otherwise error is returned + __mutex_lock_cnt_change(mutex, 1); + rc = __MUTEX_REC_LOCK_RETVAL; - if ( - mutex->protocol == TN_MUTEX_PROT_CEILING - && tn_curr_run_task->base_priority < mutex->ceil_priority - ) - { - //-- base priority of current task higher - rc = TN_RC_ILLEGAL_USE; - goto out_ei; - } + } else if ( + mutex->protocol == TN_MUTEX_PROT_CEILING + && tn_curr_run_task->base_priority < mutex->ceil_priority + ) + { + //-- base priority of current task higher + rc = TN_RC_ILLEGAL_USE; + + } else if (mutex->holder == NULL){ + //-- mutex is not locked, let's lock it + + //-- TODO: probably, we should add special flat to _mutex_do_lock, + // something like "other_tasks_can_wait", and set it to false here. + // When _mutex_do_lock() is called from _mutex_do_unlock(), this flag + // should be set to true there. + // _mutex_do_lock() should forward this flag to _find_max_priority_by_mutex(), + // and if that flag is false, _find_max_priority_by_mutex() should not + // call _find_max_blocked_priority(). + // We could save about 30 cycles then. =) + _mutex_do_lock(mutex, tn_curr_run_task); - if (mutex->holder == NULL){ - //-- mutex is not locked, let's lock it - - //-- TODO: probably, we should add special flat to _mutex_do_lock, - // something like "other_tasks_can_wait", and set it to false here. - // When _mutex_do_lock() is called from _mutex_do_unlock(), this flag - // should be set to true there. - // _mutex_do_lock() should forward this flag to _find_max_priority_by_mutex(), - // and if that flag is false, _find_max_priority_by_mutex() should not - // call _find_max_blocked_priority(). - // We could save about 30 cycles then. =) - _mutex_do_lock(mutex, tn_curr_run_task); - goto out_ei; - } else { - //-- mutex is already locked - if (timeout == 0){ - //-- in polling mode, just return TN_RC_TIMEOUT - rc = TN_RC_TIMEOUT; - goto out_ei; } else { - //-- timeout specified, so, wait until mutex is free or timeout expired - _add_curr_task_to_mutex_wait_queue(mutex, timeout); + //-- mutex is already locked - waited_for_mutex = TRUE; - - //-- rc will be set later to tn_curr_run_task->task_wait_rc; - goto out_ei; - } - } + if (timeout == 0){ + //-- in polling mode, just return TN_RC_TIMEOUT + rc = TN_RC_TIMEOUT; + } else { + //-- timeout specified, so, wait until mutex is free or timeout expired + _add_curr_task_to_mutex_wait_queue(mutex, timeout); - //-- should never be here - rc = TN_RC_INTERNAL; - goto out_ei; + waited_for_mutex = TRUE; -out_ei: + //-- rc will be set later to tn_curr_run_task->task_wait_rc; + } + } #if TN_DEBUG - if (!_tn_need_context_switch() && waited_for_mutex){ - _TN_FATAL_ERROR(""); - } + if (!_tn_need_context_switch() && waited_for_mutex){ + _TN_FATAL_ERROR(""); + } #endif - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); - if (waited_for_mutex){ - //-- get wait result - rc = tn_curr_run_task->task_wait_rc; + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + if (waited_for_mutex){ + //-- get wait result + rc = tn_curr_run_task->task_wait_rc; + } } -out: return rc; - } /* @@ -755,49 +733,44 @@ enum TN_RCode tn_mutex_lock_polling(struct TN_Mutex *mutex) */ enum TN_RCode tn_mutex_unlock(struct TN_Mutex *mutex) { - enum TN_RCode rc = TN_RC_OK; - TN_INTSAVE_DATA; - rc = _check_param_generic(mutex); - if (rc != TN_RC_OK){ - goto out; - } + enum TN_RCode rc = _check_param_generic(mutex); - if (!tn_is_task_context()){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INT_DIS_SAVE(); - TN_INT_DIS_SAVE(); + //-- unlocking is enabled only for the owner and already locked mutex + if (tn_curr_run_task != mutex->holder){ + rc = TN_RC_ILLEGAL_USE; + } else { - //-- unlocking is enabled only for the owner and already locked mutex - if (tn_curr_run_task != mutex->holder){ - rc = TN_RC_ILLEGAL_USE; - goto out_ei; - } + //-- decrement lock count (if recursive locking is enabled) + __mutex_lock_cnt_change(mutex, -1); + + if (mutex->cnt > 0){ + //-- there was recursive lock, so here we just decremented counter, + // but don't unlock the mutex. + // We're done, TN_RC_OK will be returned. + } else if (mutex->cnt < 0){ + //-- should never be here: lock count is negative. + // bug in the kernel. + _TN_FATAL_ERROR(); + } else { + //-- lock counter is 0, so, unlock mutex + _mutex_do_unlock(mutex); + } - __mutex_lock_cnt_change(mutex, -1); + } - if (mutex->cnt > 0){ - //-- there was recursive lock, so here we just decremented counter, - // but don't unlock the mutex. TN_RC_OK will be returned. - goto out_ei; - } else if (mutex->cnt < 0){ - //-- should never be here: lock count is negative. - // bug in RTOS. - _TN_FATAL_ERROR(); - } else { - //-- lock counter is 0, so, unlock mutex - _mutex_do_unlock(mutex); - goto out_ei; + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); } -out_ei: - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); - -out: return rc; } diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index a50bd9f..9b4e648 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -205,26 +205,23 @@ static inline enum TN_RCode _task_job_perform( ) { TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(task); - rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + //-- proceed to real job - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - rc = p_worker(task); + rc = p_worker(task); - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); -out: + } return rc; } From 9e9a589c32abd586db900c33b9232b09b284f1d5 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 16:19:34 +0400 Subject: [PATCH 41/55] working on switching from 'goto' to 'else if' approach, tn_tasks.c is done --- src/core/tn_mutex.c | 10 +- src/core/tn_tasks.c | 333 +++++++++++++++++++------------------------- 2 files changed, 148 insertions(+), 195 deletions(-) diff --git a/src/core/tn_mutex.c b/src/core/tn_mutex.c index 1304527..4fe1836 100644 --- a/src/core/tn_mutex.c +++ b/src/core/tn_mutex.c @@ -595,8 +595,6 @@ enum TN_RCode tn_mutex_create( */ enum TN_RCode tn_mutex_delete(struct TN_Mutex *mutex) { - TN_INTSAVE_DATA; - enum TN_RCode rc = _check_param_generic(mutex); if (rc != TN_RC_OK){ @@ -604,6 +602,7 @@ enum TN_RCode tn_mutex_delete(struct TN_Mutex *mutex) } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; } else { + TN_INTSAVE_DATA; TN_INT_DIS_SAVE(); @@ -645,8 +644,6 @@ enum TN_RCode tn_mutex_delete(struct TN_Mutex *mutex) */ enum TN_RCode tn_mutex_lock(struct TN_Mutex *mutex, TN_Timeout timeout) { - TN_INTSAVE_DATA; - enum TN_RCode rc = _check_param_generic(mutex); BOOL waited_for_mutex = FALSE; @@ -655,6 +652,7 @@ enum TN_RCode tn_mutex_lock(struct TN_Mutex *mutex, TN_Timeout timeout) } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; } else { + TN_INTSAVE_DATA; TN_INT_DIS_SAVE(); @@ -733,8 +731,6 @@ enum TN_RCode tn_mutex_lock_polling(struct TN_Mutex *mutex) */ enum TN_RCode tn_mutex_unlock(struct TN_Mutex *mutex) { - TN_INTSAVE_DATA; - enum TN_RCode rc = _check_param_generic(mutex); if (rc != TN_RC_OK){ @@ -742,6 +738,8 @@ enum TN_RCode tn_mutex_unlock(struct TN_Mutex *mutex) } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; } else { + TN_INTSAVE_DATA; + TN_INT_DIS_SAVE(); //-- unlocking is enabled only for the owner and already locked mutex diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index 9b4e648..080edc6 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -204,7 +204,6 @@ static inline enum TN_RCode _task_job_perform( int (p_worker)(struct TN_Task *task) ) { - TN_INTSAVE_DATA; enum TN_RCode rc = _check_param_generic(task); if (rc != TN_RC_OK){ @@ -213,6 +212,7 @@ static inline enum TN_RCode _task_job_perform( rc = TN_RC_WCONTEXT; } else { //-- proceed to real job + TN_INTSAVE_DATA; TN_INT_DIS_SAVE(); @@ -220,7 +220,6 @@ static inline enum TN_RCode _task_job_perform( TN_INT_RESTORE(); _tn_context_switch_pend_if_needed(); - } return rc; } @@ -231,26 +230,22 @@ static inline enum TN_RCode _task_job_iperform( ) { TN_INTSAVE_DATA_INT; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(task); - rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_isr_context()){ + //-- just return rc as it is + } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { - TN_INT_IDIS_SAVE(); + TN_INT_IDIS_SAVE(); - rc = p_worker(task); + rc = p_worker(task); - TN_INT_IRESTORE(); - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); -out: + } return rc; } @@ -477,37 +472,33 @@ enum TN_RCode tn_task_create( */ enum TN_RCode tn_task_suspend(struct TN_Task *task) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(task); - rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - if (_tn_task_is_suspended(task) || _tn_task_is_dormant(task)){ - rc = TN_RC_WSTATE; - goto out_ei; - } + if (_tn_task_is_suspended(task) || _tn_task_is_dormant(task)){ + rc = TN_RC_WSTATE; + } else { - if (_tn_task_is_runnable(task)){ - _tn_task_clear_runnable(task); - } + if (_tn_task_is_runnable(task)){ + _tn_task_clear_runnable(task); + } - _tn_task_set_suspended(task); + _tn_task_set_suspended(task); -out_ei: - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); + } -out: + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + + } return rc; } @@ -516,39 +507,35 @@ enum TN_RCode tn_task_suspend(struct TN_Task *task) */ enum TN_RCode tn_task_resume(struct TN_Task *task) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(task); - rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - if (!_tn_task_is_suspended(task)){ - rc = TN_RC_WSTATE; - goto out_ei; - } + if (!_tn_task_is_suspended(task)){ + rc = TN_RC_WSTATE; + } else { - _tn_task_clear_suspended(task); + _tn_task_clear_suspended(task); - if (!_tn_task_is_waiting(task)){ - //-- The task is not in the WAIT-SUSPEND state, - // so we need to make it runnable and probably switch context - _tn_task_set_runnable(task); - } + if (!_tn_task_is_waiting(task)){ + //-- The task is not in the WAIT-SUSPEND state, + // so we need to make it runnable and probably switch context + _tn_task_set_runnable(task); + } -out_ei: - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); + } -out: + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + + } return rc; } @@ -558,30 +545,26 @@ enum TN_RCode tn_task_resume(struct TN_Task *task) */ enum TN_RCode tn_task_sleep(TN_Timeout timeout) { - TN_INTSAVE_DATA; enum TN_RCode rc; if (timeout == 0){ rc = TN_RC_TIMEOUT; - goto out; - } - - if (!tn_is_task_context()){ + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - _tn_task_curr_to_wait_action(NULL, TN_WAIT_REASON_SLEEP, timeout); + _tn_task_curr_to_wait_action(NULL, TN_WAIT_REASON_SLEEP, timeout); - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); - rc = tn_curr_run_task->task_wait_rc; + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + rc = tn_curr_run_task->task_wait_rc; -out: - return rc; + } + return rc; } /* @@ -638,33 +621,33 @@ enum TN_RCode tn_task_irelease_wait(struct TN_Task *task) void tn_task_exit(enum TN_TaskExitOpt opts) { struct TN_Task *task; - - //-- it is here only for TN_INT_DIS_SAVE() normal operation, - // but actually we don't need to save current interrupt status: - // this function never returns, and interrupt status is restored - // from different task's stack inside - // `_tn_arch_context_switch_now_nosave()` call. - TN_INTSAVE_DATA; if (!tn_is_task_context()){ - goto out; - } + //-- do nothing, just return + } else { - TN_INT_DIS_SAVE(); + //-- it is here only for TN_INT_DIS_SAVE() normal operation, + // but actually we don't need to save current interrupt status: + // this function never returns, and interrupt status is restored + // from different task's stack inside + // `_tn_arch_context_switch_now_nosave()` call. + TN_INTSAVE_DATA; - task = tn_curr_run_task; - _tn_task_clear_runnable(task); + TN_INT_DIS_SAVE(); - _task_terminate(task); + task = tn_curr_run_task; + _tn_task_clear_runnable(task); - if ((opts & TN_TASK_EXIT_OPT_DELETE)){ - _task_delete(task); - } + _task_terminate(task); - //-- interrupts will be enabled inside _tn_arch_context_switch_now_nosave() - _tn_arch_context_switch_now_nosave(); + if ((opts & TN_TASK_EXIT_OPT_DELETE)){ + _task_delete(task); + } + + //-- interrupts will be enabled inside _tn_arch_context_switch_now_nosave() + _tn_arch_context_switch_now_nosave(); + } -out: return; } @@ -673,53 +656,46 @@ void tn_task_exit(enum TN_TaskExitOpt opts) */ enum TN_RCode tn_task_terminate(struct TN_Task *task) { - TN_INTSAVE_DATA; - - enum TN_RCode rc; + enum TN_RCode rc = _check_param_generic(task); - rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ - rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); - - rc = TN_RC_OK; - - if (_tn_task_is_dormant(task)){ - //-- The task is already terminated - rc = TN_RC_WSTATE; - } else if (tn_curr_run_task == task){ - //-- Cannot terminate currently running task - // (use tn_task_exit() instead) + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; } else { + TN_INTSAVE_DATA; - if (_tn_task_is_runnable(task)){ - _tn_task_clear_runnable(task); - } else if (_tn_task_is_waiting(task)){ - _tn_task_clear_waiting( - task, - TN_RC_OK //-- doesn't matter: nobody will read it - ); - } + TN_INT_DIS_SAVE(); - if (_tn_task_is_suspended(task)){ - _tn_task_clear_suspended(task); + if (_tn_task_is_dormant(task)){ + //-- The task is already terminated + rc = TN_RC_WSTATE; + } else if (tn_curr_run_task == task){ + //-- Cannot terminate currently running task + // (use tn_task_exit() instead) + rc = TN_RC_WCONTEXT; + } else { + + if (_tn_task_is_runnable(task)){ + _tn_task_clear_runnable(task); + } else if (_tn_task_is_waiting(task)){ + _tn_task_clear_waiting( + task, + TN_RC_OK //-- doesn't matter: nobody will read it + ); + } + + if (_tn_task_is_suspended(task)){ + _tn_task_clear_suspended(task); + } + + _task_terminate(task); } - _task_terminate(task); - } - - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); -out: + } return rc; } @@ -728,26 +704,20 @@ enum TN_RCode tn_task_terminate(struct TN_Task *task) */ enum TN_RCode tn_task_delete(struct TN_Task *task) { - TN_INTSAVE_DATA; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_generic(task); - rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); - - rc = _task_delete(task); + } else { + TN_INTSAVE_DATA; - TN_INT_RESTORE(); + TN_INT_DIS_SAVE(); + rc = _task_delete(task); + TN_INT_RESTORE(); -out: + } return rc; } @@ -761,23 +731,16 @@ enum TN_RCode tn_task_state_get( { enum TN_RCode rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - TN_INTSAVE_DATA; - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); - - *p_state = task->task_state; - - TN_INT_RESTORE(); + } else { + TN_INTSAVE_DATA; -out: + TN_INT_DIS_SAVE(); + *p_state = task->task_state; + TN_INT_RESTORE(); + } return rc; } @@ -786,42 +749,34 @@ enum TN_RCode tn_task_state_get( */ enum TN_RCode tn_task_change_priority(struct TN_Task *task, int new_priority) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(task); - rc = _check_param_generic(task); if (rc != TN_RC_OK){ - goto out; - } - - if (new_priority < 0 || new_priority >= (TN_PRIORITIES_CNT - 1)){ + //-- just return rc as it is + } else if (new_priority < 0 || new_priority >= (TN_PRIORITIES_CNT - 1)){ rc = TN_RC_WPARAM; - goto out; - } - - if (!tn_is_task_context()){ + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); + } else { + TN_INTSAVE_DATA; - if(new_priority == 0){ - new_priority = task->base_priority; - } + TN_INT_DIS_SAVE(); - rc = TN_RC_OK; + if (new_priority == 0){ + new_priority = task->base_priority; + } - if (_tn_task_is_dormant(task)){ - rc = TN_RC_WSTATE; - } else { - _tn_change_task_priority(task, new_priority); - } + rc = TN_RC_OK; - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); + if (_tn_task_is_dormant(task)){ + rc = TN_RC_WSTATE; + } else { + _tn_change_task_priority(task, new_priority); + } -out: + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + } return rc; } From e70077ce85daaa3fa01e536ff3ffcfd680b5f6c0 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 16:25:51 +0400 Subject: [PATCH 42/55] working on switching from 'goto' to 'else if' approach, tn_dqueue.c is done --- src/core/tn_dqueue.c | 292 ++++++++++++++++++++----------------------- src/core/tn_tasks.c | 2 +- 2 files changed, 139 insertions(+), 155 deletions(-) diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c index f41ecf1..46d3419 100644 --- a/src/core/tn_dqueue.c +++ b/src/core/tn_dqueue.c @@ -122,56 +122,51 @@ static enum TN_RCode _fifo_write(struct TN_DQueue *dque, void *p_data) if (dque->filled_items_cnt >= dque->items_cnt){ //-- no space for new data rc = TN_RC_TIMEOUT; - goto out; - } + } else { + + //-- write data + dque->data_fifo[dque->head_idx] = p_data; + dque->filled_items_cnt++; + dque->head_idx++; + if (dque->head_idx >= dque->items_cnt){ + dque->head_idx = 0; + } - //-- write data - dque->data_fifo[dque->head_idx] = p_data; - dque->filled_items_cnt++; - dque->head_idx++; - if (dque->head_idx >= dque->items_cnt){ - dque->head_idx = 0; + //-- set flag in the connected event group (if any), + // indicating that there are messages in the queue + _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); } - //-- set flag in the connected event group (if any), - // indicating that there are messages in the queue - _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); - -out: return rc; } static enum TN_RCode _fifo_read(struct TN_DQueue *dque, void **pp_data) { - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_read(pp_data); - rc = _check_param_read(pp_data); if (rc != TN_RC_OK){ - goto out; - } - - if (dque->filled_items_cnt == 0){ + //-- just return rc as it is + } else if (dque->filled_items_cnt == 0){ //-- nothing to read rc = TN_RC_TIMEOUT; - goto out; - } - - //-- read data - *pp_data = dque->data_fifo[dque->tail_idx]; - dque->filled_items_cnt--; - dque->tail_idx++; - if (dque->tail_idx >= dque->items_cnt){ - dque->tail_idx = 0; - } + } else { + + //-- read data + *pp_data = dque->data_fifo[dque->tail_idx]; + dque->filled_items_cnt--; + dque->tail_idx++; + if (dque->tail_idx >= dque->items_cnt){ + dque->tail_idx = 0; + } - if (dque->filled_items_cnt == 0){ - //-- clear flag in the connected event group (if any), - // indicating that there are no messages in the queue - _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); + if (dque->filled_items_cnt == 0){ + //-- clear flag in the connected event group (if any), + // indicating that there are no messages in the queue + _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); + } } -out: return rc; } // }}} @@ -303,87 +298,83 @@ static enum TN_RCode _dqueue_job_perform( TN_Timeout timeout ) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; BOOL waited = FALSE; void **pp_data = (void **)p_data; + enum TN_RCode rc = _check_param_generic(dque); - rc = _check_param_generic(dque); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - switch (job_type){ - case _JOB_TYPE__SEND: - rc = _queue_send(dque, p_data); + switch (job_type){ + case _JOB_TYPE__SEND: + rc = _queue_send(dque, p_data); + + if (rc == TN_RC_TIMEOUT && timeout != 0){ + //-- save user-provided data in the dqueue.data_elem task field, + // and put current task to wait until there's room in the queue. + tn_curr_run_task->subsys_wait.dqueue.data_elem = p_data; + _tn_task_curr_to_wait_action( + &(dque->wait_send_list), + TN_WAIT_REASON_DQUE_WSEND, + timeout + ); + + waited = TRUE; + } - if (rc == TN_RC_TIMEOUT && timeout != 0){ - //-- save user-provided data in the dqueue.data_elem task field, - // and put current task to wait until there's room in the queue. - tn_curr_run_task->subsys_wait.dqueue.data_elem = p_data; - _tn_task_curr_to_wait_action( - &(dque->wait_send_list), - TN_WAIT_REASON_DQUE_WSEND, - timeout - ); + break; + case _JOB_TYPE__RECEIVE: + rc = _queue_receive(dque, pp_data); - waited = TRUE; - } + if (rc == TN_RC_TIMEOUT && timeout != 0){ + //-- put current task to wait until new data comes. + _tn_task_curr_to_wait_action( + &(dque->wait_receive_list), + TN_WAIT_REASON_DQUE_WRECEIVE, + timeout + ); - break; - case _JOB_TYPE__RECEIVE: - rc = _queue_receive(dque, pp_data); - - if (rc == TN_RC_TIMEOUT && timeout != 0){ - //-- put current task to wait until new data comes. - _tn_task_curr_to_wait_action( - &(dque->wait_receive_list), - TN_WAIT_REASON_DQUE_WRECEIVE, - timeout - ); - - waited = TRUE; - } - break; - } + waited = TRUE; + } + break; + } #if TN_DEBUG - if (!_tn_need_context_switch() && waited){ - _TN_FATAL_ERROR(""); - } + if (!_tn_need_context_switch() && waited){ + _TN_FATAL_ERROR(""); + } #endif - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); - if (waited){ - - //-- get wait result - rc = tn_curr_run_task->task_wait_rc; - - switch (job_type){ - case _JOB_TYPE__SEND: - //-- do nothing special - break; - case _JOB_TYPE__RECEIVE: - //-- if wait result is TN_RC_OK, copy received pointer to the - // user's location - if (rc == TN_RC_OK){ - //-- dqueue.data_elem should contain valid value now, - // return it to caller - *pp_data = tn_curr_run_task->subsys_wait.dqueue.data_elem; - } - break; + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + if (waited){ + + //-- get wait result + rc = tn_curr_run_task->task_wait_rc; + + switch (job_type){ + case _JOB_TYPE__SEND: + //-- do nothing special + break; + case _JOB_TYPE__RECEIVE: + //-- if wait result is TN_RC_OK, copy received pointer to the + // user's location + if (rc == TN_RC_OK){ + //-- dqueue.data_elem should contain valid value now, + // return it to caller + *pp_data = tn_curr_run_task->subsys_wait.dqueue.data_elem; + } + break; + } } - } -out: + } return rc; } @@ -393,35 +384,31 @@ static enum TN_RCode _dqueue_job_iperform( void *p_data //-- used for _JOB_TYPE__SEND ) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc = TN_RC_OK; void **pp_data = (void **)p_data; + enum TN_RCode rc = _check_param_generic(dque); - rc = _check_param_generic(dque); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_isr_context()){ + //-- just return rc as it is + } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA_INT; - TN_INT_IDIS_SAVE(); + TN_INT_IDIS_SAVE(); - switch (job_type){ - case _JOB_TYPE__SEND: - rc = _queue_send(dque, p_data); - break; - case _JOB_TYPE__RECEIVE: - rc = _queue_receive(dque, pp_data); - break; - } + switch (job_type){ + case _JOB_TYPE__SEND: + rc = _queue_send(dque, p_data); + break; + case _JOB_TYPE__RECEIVE: + rc = _queue_receive(dque, pp_data); + break; + } - TN_INT_IRESTORE(); - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + } -out: return rc; } @@ -446,28 +433,27 @@ enum TN_RCode tn_queue_create( rc = _check_param_create(dque, data_fifo, items_cnt); if (rc != TN_RC_OK){ - goto out; - } - - tn_list_reset(&(dque->wait_send_list)); - tn_list_reset(&(dque->wait_receive_list)); + //-- just return rc as it is + } else { + tn_list_reset(&(dque->wait_send_list)); + tn_list_reset(&(dque->wait_receive_list)); - dque->data_fifo = data_fifo; - dque->items_cnt = items_cnt; + dque->data_fifo = data_fifo; + dque->items_cnt = items_cnt; - _tn_eventgrp_link_reset(&dque->eventgrp_link); + _tn_eventgrp_link_reset(&dque->eventgrp_link); - if (dque->data_fifo == NULL){ - dque->items_cnt = 0; - } + if (dque->data_fifo == NULL){ + dque->items_cnt = 0; + } - dque->filled_items_cnt = 0; - dque->tail_idx = 0; - dque->head_idx = 0; + dque->filled_items_cnt = 0; + dque->tail_idx = 0; + dque->head_idx = 0; - dque->id_dque = TN_ID_DATAQUEUE; + dque->id_dque = TN_ID_DATAQUEUE; + } -out: return rc; } @@ -477,35 +463,33 @@ enum TN_RCode tn_queue_create( */ enum TN_RCode tn_queue_delete(struct TN_DQueue * dque) { - TN_INTSAVE_DATA; enum TN_RCode rc = TN_RC_OK; rc = _check_param_generic(dque); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - //-- notify waiting tasks that the object is deleted - // (TN_RC_DELETED is returned) - _tn_wait_queue_notify_deleted(&(dque->wait_send_list)); - _tn_wait_queue_notify_deleted(&(dque->wait_receive_list)); + //-- notify waiting tasks that the object is deleted + // (TN_RC_DELETED is returned) + _tn_wait_queue_notify_deleted(&(dque->wait_send_list)); + _tn_wait_queue_notify_deleted(&(dque->wait_receive_list)); - dque->id_dque = 0; //-- data queue does not exist now + dque->id_dque = 0; //-- data queue does not exist now - TN_INT_RESTORE(); + TN_INT_RESTORE(); - //-- we might need to switch context if _tn_wait_queue_notify_deleted() - // has woken up some high-priority task - _tn_context_switch_pend_if_needed(); + //-- we might need to switch context if _tn_wait_queue_notify_deleted() + // has woken up some high-priority task + _tn_context_switch_pend_if_needed(); + + } -out: return rc; } diff --git a/src/core/tn_tasks.c b/src/core/tn_tasks.c index 080edc6..714cccd 100644 --- a/src/core/tn_tasks.c +++ b/src/core/tn_tasks.c @@ -229,7 +229,6 @@ static inline enum TN_RCode _task_job_iperform( int (p_worker)(struct TN_Task *task) ) { - TN_INTSAVE_DATA_INT; enum TN_RCode rc = _check_param_generic(task); if (rc != TN_RC_OK){ @@ -237,6 +236,7 @@ static inline enum TN_RCode _task_job_iperform( } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; } else { + TN_INTSAVE_DATA_INT; TN_INT_IDIS_SAVE(); From cab828ef28611b5bc71c766f745a5a315e89ca54 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 16:34:03 +0400 Subject: [PATCH 43/55] working on switching from 'goto' to 'else if' approach, tn_eventgrp.c is done --- src/core/tn_eventgrp.c | 262 ++++++++++++++++++++--------------------- 1 file changed, 128 insertions(+), 134 deletions(-) diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index 6525c6c..fe20c7c 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -196,26 +196,25 @@ static enum TN_RCode _eventgrp_wait( //-- interrupts should be disabled here _TN_BUG_ON( !TN_IS_INT_DISABLED() ); - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_job_perform(eventgrp, wait_pattern); - rc = _check_param_job_perform(eventgrp, wait_pattern); if (rc != TN_RC_OK){ - goto out; - } + //-- just return rc as it is + } else { - //-- Check release condition + //-- Check release condition - if (_cond_check(eventgrp, wait_mode, wait_pattern)){ - if (p_flags_pattern != NULL){ - *p_flags_pattern = eventgrp->pattern; + if (_cond_check(eventgrp, wait_mode, wait_pattern)){ + if (p_flags_pattern != NULL){ + *p_flags_pattern = eventgrp->pattern; + } + _clear_pattern_if_needed(eventgrp, wait_mode, wait_pattern); + rc = TN_RC_OK; + } else { + rc = TN_RC_TIMEOUT; } - _clear_pattern_if_needed(eventgrp, wait_mode, wait_pattern); - rc = TN_RC_OK; - } else { - rc = TN_RC_TIMEOUT; - } -out: + } return rc; } @@ -228,32 +227,31 @@ static enum TN_RCode _eventgrp_modify( //-- interrupts should be disabled here _TN_BUG_ON( !TN_IS_INT_DISABLED() ); - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_job_perform(eventgrp, pattern); - rc = _check_param_job_perform(eventgrp, pattern); if (rc != TN_RC_OK){ - goto out; - } + //-- just return rc as it is + } else { - switch (operation){ - case TN_EVENTGRP_OP_CLEAR: - eventgrp->pattern &= ~pattern; - break; + switch (operation){ + case TN_EVENTGRP_OP_CLEAR: + eventgrp->pattern &= ~pattern; + break; + + case TN_EVENTGRP_OP_SET: + if ((eventgrp->pattern & pattern) != pattern){ + eventgrp->pattern |= pattern; + _scan_event_waitqueue(eventgrp); + } + break; - case TN_EVENTGRP_OP_SET: - if ((eventgrp->pattern & pattern) != pattern){ - eventgrp->pattern |= pattern; + case TN_EVENTGRP_OP_TOGGLE: + eventgrp->pattern ^= pattern; _scan_event_waitqueue(eventgrp); - } - break; + break; + } - case TN_EVENTGRP_OP_TOGGLE: - eventgrp->pattern ^= pattern; - _scan_event_waitqueue(eventgrp); - break; } - -out: return rc; } @@ -274,19 +272,18 @@ enum TN_RCode tn_eventgrp_create( TN_UWord initial_pattern //-- initial value of the pattern ) { - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_create(eventgrp); - rc = _check_param_create(eventgrp); if (rc != TN_RC_OK){ - goto out; - } + //-- just return rc as it is + } else { - tn_list_reset(&(eventgrp->wait_queue)); + tn_list_reset(&(eventgrp->wait_queue)); - eventgrp->pattern = initial_pattern; - eventgrp->id_event = TN_ID_EVENTGRP; + eventgrp->pattern = initial_pattern; + eventgrp->id_event = TN_ID_EVENTGRP; -out: + } return rc; } @@ -296,32 +293,28 @@ enum TN_RCode tn_eventgrp_create( */ enum TN_RCode tn_eventgrp_delete(struct TN_EventGrp *eventgrp) { - enum TN_RCode rc = TN_RC_OK; - TN_INTSAVE_DATA; + enum TN_RCode rc = _check_param_generic(eventgrp); - rc = _check_param_generic(eventgrp); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - _tn_wait_queue_notify_deleted(&(eventgrp->wait_queue)); + _tn_wait_queue_notify_deleted(&(eventgrp->wait_queue)); - eventgrp->id_event = 0; //-- event does not exist now + eventgrp->id_event = 0; //-- event does not exist now - TN_INT_RESTORE(); + TN_INT_RESTORE(); - //-- we might need to switch context if _tn_wait_queue_notify_deleted() - // has woken up some high-priority task - _tn_context_switch_pend_if_needed(); + //-- we might need to switch context if _tn_wait_queue_notify_deleted() + // has woken up some high-priority task + _tn_context_switch_pend_if_needed(); -out: + } return rc; } @@ -337,51 +330,52 @@ enum TN_RCode tn_eventgrp_wait( TN_Timeout timeout ) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; BOOL waited_for_event = FALSE; + enum TN_RCode rc = _check_param_generic(eventgrp); - if (!tn_is_task_context()){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - rc = _eventgrp_wait(eventgrp, wait_pattern, wait_mode, p_flags_pattern); + rc = _eventgrp_wait(eventgrp, wait_pattern, wait_mode, p_flags_pattern); - if (rc == TN_RC_TIMEOUT && timeout != 0){ - tn_curr_run_task->subsys_wait.eventgrp.wait_mode = wait_mode; - tn_curr_run_task->subsys_wait.eventgrp.wait_pattern = wait_pattern; - _tn_task_curr_to_wait_action( - &(eventgrp->wait_queue), - TN_WAIT_REASON_EVENT, - timeout - ); - waited_for_event = TRUE; - } + if (rc == TN_RC_TIMEOUT && timeout != 0){ + tn_curr_run_task->subsys_wait.eventgrp.wait_mode = wait_mode; + tn_curr_run_task->subsys_wait.eventgrp.wait_pattern = wait_pattern; + _tn_task_curr_to_wait_action( + &(eventgrp->wait_queue), + TN_WAIT_REASON_EVENT, + timeout + ); + waited_for_event = TRUE; + } #if TN_DEBUG - if (!_tn_need_context_switch() && waited_for_event){ - _TN_FATAL_ERROR(""); - } + if (!_tn_need_context_switch() && waited_for_event){ + _TN_FATAL_ERROR(""); + } #endif - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); - if (waited_for_event){ - //-- get wait result - rc = tn_curr_run_task->task_wait_rc; - - //-- if wait result is TN_RC_OK, and p_flags_pattern is provided, - // copy actual_pattern there - if (rc == TN_RC_OK && p_flags_pattern != NULL ){ - *p_flags_pattern = - tn_curr_run_task->subsys_wait.eventgrp.actual_pattern; + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + if (waited_for_event){ + //-- get wait result + rc = tn_curr_run_task->task_wait_rc; + + //-- if wait result is TN_RC_OK, and p_flags_pattern is provided, + // copy actual_pattern there + if (rc == TN_RC_OK && p_flags_pattern != NULL ){ + *p_flags_pattern = + tn_curr_run_task->subsys_wait.eventgrp.actual_pattern; + } } - } -out: + } return rc; } @@ -396,21 +390,19 @@ enum TN_RCode tn_eventgrp_wait_polling( TN_UWord *p_flags_pattern ) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(eventgrp); - if (!tn_is_task_context()){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); - - rc = _eventgrp_wait(eventgrp, wait_pattern, wait_mode, p_flags_pattern); - - TN_INT_RESTORE(); + } else { + TN_INTSAVE_DATA; -out: + TN_INT_DIS_SAVE(); + rc = _eventgrp_wait(eventgrp, wait_pattern, wait_mode, p_flags_pattern); + TN_INT_RESTORE(); + } return rc; } @@ -425,22 +417,23 @@ enum TN_RCode tn_eventgrp_iwait_polling( TN_UWord *p_flags_pattern ) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_generic(eventgrp); - if (!tn_is_isr_context()){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA_INT; - TN_INT_IDIS_SAVE(); + TN_INT_IDIS_SAVE(); - rc = _eventgrp_wait(eventgrp, wait_pattern, wait_mode, p_flags_pattern); + rc = _eventgrp_wait(eventgrp, wait_pattern, wait_mode, p_flags_pattern); - TN_INT_IRESTORE(); - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); -out: + } return rc; } @@ -454,22 +447,23 @@ enum TN_RCode tn_eventgrp_modify( TN_UWord pattern ) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(eventgrp); - if (!tn_is_task_context()){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - rc = _eventgrp_modify(eventgrp, operation, pattern); + rc = _eventgrp_modify(eventgrp, operation, pattern); - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); -out: + } return rc; } @@ -483,22 +477,22 @@ enum TN_RCode tn_eventgrp_imodify( TN_UWord pattern ) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_generic(eventgrp); - if (!tn_is_isr_context()){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_IDIS_SAVE(); + } else { + TN_INTSAVE_DATA_INT; - rc = _eventgrp_modify(eventgrp, operation, pattern); + TN_INT_IDIS_SAVE(); - TN_INT_IRESTORE(); - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + rc = _eventgrp_modify(eventgrp, operation, pattern); -out: + TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + } return rc; } From 2be2cd53027221292ba2f3ce9d263c23cbf30502 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 16:41:26 +0400 Subject: [PATCH 44/55] working on switching from 'goto' to 'else if' approach, tn_fmem.c is done --- src/core/tn_fmem.c | 186 +++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 107 deletions(-) diff --git a/src/core/tn_fmem.c b/src/core/tn_fmem.c index 4da5f0e..d66cc42 100644 --- a/src/core/tn_fmem.c +++ b/src/core/tn_fmem.c @@ -268,33 +268,29 @@ enum TN_RCode tn_fmem_create( */ enum TN_RCode tn_fmem_delete(struct TN_FMem *fmem) { - TN_INTSAVE_DATA; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_fmem_delete(fmem); - rc = _check_param_fmem_delete(fmem); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - //-- remove all tasks (if any) from fmem's wait queue - _tn_wait_queue_notify_deleted(&(fmem->wait_queue)); + //-- remove all tasks (if any) from fmem's wait queue + _tn_wait_queue_notify_deleted(&(fmem->wait_queue)); - fmem->id_fmp = 0; //-- Fixed-size memory pool does not exist now + fmem->id_fmp = 0; //-- Fixed-size memory pool does not exist now - TN_INT_RESTORE(); + TN_INT_RESTORE(); - //-- we might need to switch context if _tn_wait_queue_notify_deleted() - // has woken up some high-priority task - _tn_context_switch_pend_if_needed(); + //-- we might need to switch context if _tn_wait_queue_notify_deleted() + // has woken up some high-priority task + _tn_context_switch_pend_if_needed(); -out: + } return rc; } @@ -308,49 +304,45 @@ enum TN_RCode tn_fmem_get( TN_Timeout timeout ) { - TN_INTSAVE_DATA; - enum TN_RCode rc; BOOL waited_for_data = FALSE; + enum TN_RCode rc = _check_param_job_perform(fmem, p_data); - rc = _check_param_job_perform(fmem, p_data); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); + TN_INT_DIS_SAVE(); - rc = _fmem_get(fmem, p_data); + rc = _fmem_get(fmem, p_data); - if (rc == TN_RC_TIMEOUT && timeout > 0){ - _tn_task_curr_to_wait_action( - &(fmem->wait_queue), - TN_WAIT_REASON_WFIXMEM, - timeout - ); - waited_for_data = TRUE; - } + if (rc == TN_RC_TIMEOUT && timeout > 0){ + _tn_task_curr_to_wait_action( + &(fmem->wait_queue), + TN_WAIT_REASON_WFIXMEM, + timeout + ); + waited_for_data = TRUE; + } - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); - if (waited_for_data){ + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + if (waited_for_data){ - //-- get wait result - rc = tn_curr_run_task->task_wait_rc; + //-- get wait result + rc = tn_curr_run_task->task_wait_rc; + + //-- if wait result is TN_RC_OK, copy memory block pointer to the + // user's location + if (rc == TN_RC_OK){ + *p_data = tn_curr_run_task->subsys_wait.fmem.data_elem; + } - //-- if wait result is TN_RC_OK, copy memory block pointer to the - // user's location - if (rc == TN_RC_OK){ - *p_data = tn_curr_run_task->subsys_wait.fmem.data_elem; } } - -out: return rc; } @@ -360,26 +352,20 @@ enum TN_RCode tn_fmem_get( */ enum TN_RCode tn_fmem_get_polling(struct TN_FMem *fmem,void **p_data) { - TN_INTSAVE_DATA; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_job_perform(fmem, p_data); - rc = _check_param_job_perform(fmem, p_data); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); - - rc = _fmem_get(fmem, p_data); + } else { + TN_INTSAVE_DATA; - TN_INT_RESTORE(); + TN_INT_DIS_SAVE(); + rc = _fmem_get(fmem, p_data); + TN_INT_RESTORE(); + } -out: return rc; } @@ -389,27 +375,22 @@ enum TN_RCode tn_fmem_get_polling(struct TN_FMem *fmem,void **p_data) */ enum TN_RCode tn_fmem_iget_polling(struct TN_FMem *fmem, void **p_data) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_job_perform(fmem, p_data); - rc = _check_param_job_perform(fmem, p_data); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_isr_context()){ + //-- just return rc as it is + } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_IDIS_SAVE(); + } else { + TN_INTSAVE_DATA_INT; - rc = _fmem_get(fmem, p_data); + TN_INT_IDIS_SAVE(); - TN_INT_IRESTORE(); - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + rc = _fmem_get(fmem, p_data); -out: + TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + } return rc; } @@ -419,27 +400,22 @@ enum TN_RCode tn_fmem_iget_polling(struct TN_FMem *fmem, void **p_data) */ enum TN_RCode tn_fmem_release(struct TN_FMem *fmem, void *p_data) { - TN_INTSAVE_DATA; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_job_perform(fmem, p_data); - rc = _check_param_job_perform(fmem, p_data); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); + } else { + TN_INTSAVE_DATA; - rc = _fmem_release(fmem, p_data); + TN_INT_DIS_SAVE(); - TN_INT_RESTORE(); - _tn_context_switch_pend_if_needed(); + rc = _fmem_release(fmem, p_data); -out: + TN_INT_RESTORE(); + _tn_context_switch_pend_if_needed(); + } return rc; } @@ -449,27 +425,23 @@ enum TN_RCode tn_fmem_release(struct TN_FMem *fmem, void *p_data) */ enum TN_RCode tn_fmem_irelease(struct TN_FMem *fmem, void *p_data) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc; + enum TN_RCode rc = _check_param_job_perform(fmem, p_data); - rc = _check_param_job_perform(fmem, p_data); if (rc != TN_RC_OK){ - goto out; - } - - if (!tn_is_isr_context()){ + //-- just return rc as it is + } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA_INT; - TN_INT_IDIS_SAVE(); + TN_INT_IDIS_SAVE(); - rc = _fmem_release(fmem, p_data); + rc = _fmem_release(fmem, p_data); - TN_INT_IRESTORE(); - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + } -out: return rc; } From d53eed2ab1a1ef51727d6fbe156c88b9a60c08a1 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 16:47:02 +0400 Subject: [PATCH 45/55] working on switching from 'goto' to 'else if' approach, tn_sem.c is done --- src/core/tn_sem.c | 139 ++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 79 deletions(-) diff --git a/src/core/tn_sem.c b/src/core/tn_sem.c index c6d20ce..8f43d42 100644 --- a/src/core/tn_sem.c +++ b/src/core/tn_sem.c @@ -120,50 +120,44 @@ static inline enum TN_RCode _sem_job_perform( TN_Timeout timeout ) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(sem); BOOL waited_for_sem = FALSE; - //-- perform additional params checking (if enabled by TN_CHECK_PARAM) - rc = _check_param_generic(sem); if (rc != TN_RC_OK){ - goto out; - } - - //-- check that function is called from task context - if (!tn_is_task_context()){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA; - TN_INT_DIS_SAVE(); //-- disable interrupts - rc = p_worker(sem); //-- call actual worker function + TN_INT_DIS_SAVE(); //-- disable interrupts + rc = p_worker(sem); //-- call actual worker function - //-- if we should wait, put current task to wait - if (rc == TN_RC_TIMEOUT && timeout != 0){ - _tn_task_curr_to_wait_action( - &(sem->wait_queue), TN_WAIT_REASON_SEM, timeout - ); + //-- if we should wait, put current task to wait + if (rc == TN_RC_TIMEOUT && timeout != 0){ + _tn_task_curr_to_wait_action( + &(sem->wait_queue), TN_WAIT_REASON_SEM, timeout + ); - //-- rc will be set later thanks to waited_for_sem - waited_for_sem = TRUE; - } + //-- rc will be set later thanks to waited_for_sem + waited_for_sem = TRUE; + } #if TN_DEBUG - //-- if we're going to wait, _tn_need_context_switch() must return TRUE - if (!_tn_need_context_switch() && waited_for_sem){ - _TN_FATAL_ERROR(""); - } + //-- if we're going to wait, _tn_need_context_switch() must return TRUE + if (!_tn_need_context_switch() && waited_for_sem){ + _TN_FATAL_ERROR(""); + } #endif - TN_INT_RESTORE(); //-- restore previous interrupts state - _tn_context_switch_pend_if_needed(); - if (waited_for_sem){ - //-- get wait result - rc = tn_curr_run_task->task_wait_rc; - } + TN_INT_RESTORE(); //-- restore previous interrupts state + _tn_context_switch_pend_if_needed(); + if (waited_for_sem){ + //-- get wait result + rc = tn_curr_run_task->task_wait_rc; + } -out: + } return rc; } @@ -178,27 +172,21 @@ static inline enum TN_RCode _sem_job_iperform( int (p_worker)(struct TN_Sem *sem) ) { - TN_INTSAVE_DATA_INT; - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(sem); //-- perform additional params checking (if enabled by TN_CHECK_PARAM) - rc = _check_param_generic(sem); if (rc != TN_RC_OK){ - goto out; - } - - //-- check that function is called from interrupt context - if (!tn_is_isr_context()){ + //-- just return rc as it is + } else if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_IDIS_SAVE(); //-- disable interrupts - rc = p_worker(sem); //-- call actual worker function - TN_INT_IRESTORE(); //-- restore previous interrupts state - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + } else { + TN_INTSAVE_DATA_INT; -out: + TN_INT_IDIS_SAVE(); //-- disable interrupts + rc = p_worker(sem); //-- call actual worker function + TN_INT_IRESTORE(); //-- restore previous interrupts state + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + } return rc; } @@ -258,21 +246,20 @@ enum TN_RCode tn_sem_create( int max_count ) { - enum TN_RCode rc = TN_RC_OK; - //-- perform additional params checking (if enabled by TN_CHECK_PARAM) - rc = _check_param_create(sem, start_count, max_count); + enum TN_RCode rc = _check_param_create(sem, start_count, max_count); + if (rc != TN_RC_OK){ - goto out; - } + //-- just return rc as it is + } else { - tn_list_reset(&(sem->wait_queue)); + tn_list_reset(&(sem->wait_queue)); - sem->count = start_count; - sem->max_count = max_count; - sem->id_sem = TN_ID_SEMAPHORE; + sem->count = start_count; + sem->max_count = max_count; + sem->id_sem = TN_ID_SEMAPHORE; -out: + } return rc; } @@ -281,34 +268,28 @@ enum TN_RCode tn_sem_create( */ enum TN_RCode tn_sem_delete(struct TN_Sem * sem) { - TN_INTSAVE_DATA; - enum TN_RCode rc = TN_RC_OK; - //-- perform additional params checking (if enabled by TN_CHECK_PARAM) - rc = _check_param_generic(sem); - if (rc != TN_RC_OK){ - goto out; - } + enum TN_RCode rc = _check_param_generic(sem); - //-- check that function is called from task context - if (!tn_is_task_context()){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - TN_INT_DIS_SAVE(); + } else { + TN_INTSAVE_DATA; - //-- Remove all tasks from wait queue, returning the TN_RC_DELETED code. - _tn_wait_queue_notify_deleted(&(sem->wait_queue)); + TN_INT_DIS_SAVE(); - sem->id_sem = 0; //-- Semaphore does not exist now - TN_INT_RESTORE(); + //-- Remove all tasks from wait queue, returning the TN_RC_DELETED code. + _tn_wait_queue_notify_deleted(&(sem->wait_queue)); - //-- we might need to switch context if _tn_wait_queue_notify_deleted() - // has woken up some high-priority task - _tn_context_switch_pend_if_needed(); + sem->id_sem = 0; //-- Semaphore does not exist now + TN_INT_RESTORE(); -out: + //-- we might need to switch context if _tn_wait_queue_notify_deleted() + // has woken up some high-priority task + _tn_context_switch_pend_if_needed(); + } return rc; } From 6865c9403af978de88dd05725a2fb082b9b0a425 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 16:49:30 +0400 Subject: [PATCH 46/55] working on switching from 'goto' to 'else if' approach, tn_sys.c and tn_timer.c are done --- src/core/tn_sys.c | 47 +++++++++++++++++++-------------------------- src/core/tn_timer.c | 22 ++++++++++----------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c index 800870b..cec854a 100644 --- a/src/core/tn_sys.c +++ b/src/core/tn_sys.c @@ -294,29 +294,28 @@ void tn_sys_start( */ enum TN_RCode tn_tick_int_processing(void) { - TN_INTSAVE_DATA_INT; enum TN_RCode rc = TN_RC_OK; if (!tn_is_isr_context()){ rc = TN_RC_WCONTEXT; - goto out; - } + } else { + TN_INTSAVE_DATA_INT; - TN_INT_IDIS_SAVE(); + TN_INT_IDIS_SAVE(); - //-- increment system timer - tn_sys_time_count++; + //-- increment system timer + tn_sys_time_count++; - //-- manage round-robin (if used) - _round_robin_manage(); + //-- manage round-robin (if used) + _round_robin_manage(); - //-- manage timers - _tn_timers_tick_proceed(); + //-- manage timers + _tn_timers_tick_proceed(); - TN_INT_IRESTORE(); - _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); + TN_INT_IRESTORE(); + _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED(); -out: + } return rc; } @@ -327,26 +326,20 @@ enum TN_RCode tn_sys_tslice_set(int priority, int ticks) { enum TN_RCode rc = TN_RC_OK; - TN_INTSAVE_DATA; if (!tn_is_task_context()){ rc = TN_RC_WCONTEXT; - goto out; - } - - if ( priority < 0 || priority >= (TN_PRIORITIES_CNT - 1) + } else if (0 + || priority < 0 || priority >= (TN_PRIORITIES_CNT - 1) || ticks < 0 || ticks > TN_MAX_TIME_SLICE) { rc = TN_RC_WPARAM; - goto out; - } - - TN_INT_DIS_SAVE(); - - tn_tslice_ticks[priority] = ticks; - - TN_INT_RESTORE(); + } else { + TN_INTSAVE_DATA; -out: + TN_INT_DIS_SAVE(); + tn_tslice_ticks[priority] = ticks; + TN_INT_RESTORE(); + } return rc; } diff --git a/src/core/tn_timer.c b/src/core/tn_timer.c index d39e307..e79da5a 100644 --- a/src/core/tn_timer.c +++ b/src/core/tn_timer.c @@ -144,16 +144,14 @@ enum TN_RCode tn_timer_create( void *p_user_data ) { - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_create(timer, func); - rc = _check_param_create(timer, func); if (rc != TN_RC_OK){ - goto out; + //-- just return rc as it is + } else { + rc = _tn_timer_create(timer, func, p_user_data); } - rc = _tn_timer_create(timer, func, p_user_data); - -out: return rc; } @@ -480,15 +478,15 @@ enum TN_RCode _tn_timer_create( enum TN_RCode rc = _tn_timer_set_func(timer, func, p_user_data); if (rc != TN_RC_OK){ - goto out; - } + //-- just return rc as it is + } else { - tn_list_reset(&(timer->timer_queue)); + tn_list_reset(&(timer->timer_queue)); - timer->timeout_cur = 0; - timer->id_timer = TN_ID_TIMER; + timer->timeout_cur = 0; + timer->id_timer = TN_ID_TIMER; -out: + } return rc; } From a79ddbfd4fe3f85fd892f48f7d2f95a3357504a4 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 18:03:03 +0400 Subject: [PATCH 47/55] eventgrp: removed double check --- src/core/tn_eventgrp.c | 64 +++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index fe20c7c..0fcc961 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -196,26 +196,19 @@ static enum TN_RCode _eventgrp_wait( //-- interrupts should be disabled here _TN_BUG_ON( !TN_IS_INT_DISABLED() ); - enum TN_RCode rc = _check_param_job_perform(eventgrp, wait_pattern); + //-- Check release condition - if (rc != TN_RC_OK){ - //-- just return rc as it is - } else { - - //-- Check release condition - - if (_cond_check(eventgrp, wait_mode, wait_pattern)){ - if (p_flags_pattern != NULL){ - *p_flags_pattern = eventgrp->pattern; - } - _clear_pattern_if_needed(eventgrp, wait_mode, wait_pattern); - rc = TN_RC_OK; - } else { - rc = TN_RC_TIMEOUT; + if (_cond_check(eventgrp, wait_mode, wait_pattern)){ + if (p_flags_pattern != NULL){ + *p_flags_pattern = eventgrp->pattern; } - + _clear_pattern_if_needed(eventgrp, wait_mode, wait_pattern); + rc = TN_RC_OK; + } else { + rc = TN_RC_TIMEOUT; } - return rc; + + return TN_RC_OK; } static enum TN_RCode _eventgrp_modify( @@ -227,32 +220,25 @@ static enum TN_RCode _eventgrp_modify( //-- interrupts should be disabled here _TN_BUG_ON( !TN_IS_INT_DISABLED() ); - enum TN_RCode rc = _check_param_job_perform(eventgrp, pattern); - - if (rc != TN_RC_OK){ - //-- just return rc as it is - } else { - - switch (operation){ - case TN_EVENTGRP_OP_CLEAR: - eventgrp->pattern &= ~pattern; - break; - - case TN_EVENTGRP_OP_SET: - if ((eventgrp->pattern & pattern) != pattern){ - eventgrp->pattern |= pattern; - _scan_event_waitqueue(eventgrp); - } - break; + switch (operation){ + case TN_EVENTGRP_OP_CLEAR: + eventgrp->pattern &= ~pattern; + break; - case TN_EVENTGRP_OP_TOGGLE: - eventgrp->pattern ^= pattern; + case TN_EVENTGRP_OP_SET: + if ((eventgrp->pattern & pattern) != pattern){ + eventgrp->pattern |= pattern; _scan_event_waitqueue(eventgrp); - break; - } + } + break; + case TN_EVENTGRP_OP_TOGGLE: + eventgrp->pattern ^= pattern; + _scan_event_waitqueue(eventgrp); + break; } - return rc; + + return TN_RC_OK; } From c305b856e1ebdb547a4968af86514a72506bb6bf Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 19:31:41 +0400 Subject: [PATCH 48/55] fix: build-time error --- src/core/tn_eventgrp.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index 0fcc961..3100836 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -196,19 +196,26 @@ static enum TN_RCode _eventgrp_wait( //-- interrupts should be disabled here _TN_BUG_ON( !TN_IS_INT_DISABLED() ); - //-- Check release condition + enum TN_RCode rc = TN_RC_OK; - if (_cond_check(eventgrp, wait_mode, wait_pattern)){ - if (p_flags_pattern != NULL){ - *p_flags_pattern = eventgrp->pattern; - } - _clear_pattern_if_needed(eventgrp, wait_mode, wait_pattern); - rc = TN_RC_OK; + if (rc != TN_RC_OK){ + //-- just return rc as it is } else { - rc = TN_RC_TIMEOUT; - } - return TN_RC_OK; + //-- Check release condition + + if (_cond_check(eventgrp, wait_mode, wait_pattern)){ + if (p_flags_pattern != NULL){ + *p_flags_pattern = eventgrp->pattern; + } + _clear_pattern_if_needed(eventgrp, wait_mode, wait_pattern); + rc = TN_RC_OK; + } else { + rc = TN_RC_TIMEOUT; + } + + } + return rc; } static enum TN_RCode _eventgrp_modify( From 2318faa18398fd096de5ab1e5d8a1f970a3d992b Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 19:59:35 +0400 Subject: [PATCH 49/55] FIX queue: eventgrp connection: flag wasn't cleared when there are no messages in the queue. FIX eventgrp: accidentally removed check when waiting for event --- src/core/tn_dqueue.c | 2 +- src/core/tn_eventgrp.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c index 46d3419..e8dc51e 100644 --- a/src/core/tn_dqueue.c +++ b/src/core/tn_dqueue.c @@ -163,7 +163,7 @@ static enum TN_RCode _fifo_read(struct TN_DQueue *dque, void **pp_data) if (dque->filled_items_cnt == 0){ //-- clear flag in the connected event group (if any), // indicating that there are no messages in the queue - _tn_eventgrp_link_manage(&dque->eventgrp_link, TRUE); + _tn_eventgrp_link_manage(&dque->eventgrp_link, FALSE); } } diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index 3100836..0fe4ade 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -80,7 +80,7 @@ static inline enum TN_RCode _check_param_job_perform( TN_UWord pattern ) { - enum TN_RCode rc = _check_param_generic(eventgrp); + enum TN_RCode rc = TN_RC_OK; if (pattern == 0){ rc = TN_RC_WPARAM; @@ -196,7 +196,7 @@ static enum TN_RCode _eventgrp_wait( //-- interrupts should be disabled here _TN_BUG_ON( !TN_IS_INT_DISABLED() ); - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_job_perform(eventgrp, wait_pattern); if (rc != TN_RC_OK){ //-- just return rc as it is From 8398e606300aa4bef16da767298a5ab9c11faaeb Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Sun, 19 Oct 2014 20:57:07 +0400 Subject: [PATCH 50/55] eventgrp connection: error checking improved --- src/core/tn_dqueue.c | 4 ++-- src/core/tn_eventgrp.c | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c index e8dc51e..15465fb 100644 --- a/src/core/tn_dqueue.c +++ b/src/core/tn_dqueue.c @@ -570,7 +570,7 @@ enum TN_RCode tn_queue_eventgrp_connect( if (rc == TN_RC_OK){ sr_saved = tn_arch_sr_save_int_dis(); - _tn_eventgrp_link_set(&dque->eventgrp_link, eventgrp, pattern); + rc = _tn_eventgrp_link_set(&dque->eventgrp_link, eventgrp, pattern); tn_arch_sr_restore(sr_saved); } @@ -589,7 +589,7 @@ enum TN_RCode tn_queue_eventgrp_disconnect( if (rc == TN_RC_OK){ sr_saved = tn_arch_sr_save_int_dis(); - _tn_eventgrp_link_reset(&dque->eventgrp_link); + rc = _tn_eventgrp_link_reset(&dque->eventgrp_link); tn_arch_sr_restore(sr_saved); } diff --git a/src/core/tn_eventgrp.c b/src/core/tn_eventgrp.c index 0fe4ade..deaaccb 100644 --- a/src/core/tn_eventgrp.c +++ b/src/core/tn_eventgrp.c @@ -508,9 +508,11 @@ enum TN_RCode _tn_eventgrp_link_set( //-- interrupts should be disabled here _TN_BUG_ON( !TN_IS_INT_DISABLED() ); - enum TN_RCode rc = TN_RC_OK; + enum TN_RCode rc = _check_param_generic(eventgrp); - if (eventgrp == NULL || pattern == (0)){ + if (rc != TN_RC_OK){ + //-- just return rc as it is + } else if (pattern == (0)){ rc = TN_RC_WPARAM; } else { eventgrp_link->eventgrp = eventgrp; From 9075b69fa7fbff106288712590802ac74d12ab25 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Mon, 20 Oct 2014 13:50:33 +0400 Subject: [PATCH 51/55] docs improved: why_reimplement --- stuff/doc_pages/why_reimplement.dox | 84 +++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/stuff/doc_pages/why_reimplement.dox b/stuff/doc_pages/why_reimplement.dox index 076c1e1..0a701b3 100644 --- a/stuff/doc_pages/why_reimplement.dox +++ b/stuff/doc_pages/why_reimplement.dox @@ -44,9 +44,6 @@ int my_function(void) } \endcode -The code above is simplified; in the real TNKernel code, things are usually -more complicated. - If you have multiple `return` statements or, even more, if you have to perform some action before return (`tn_enable_interrupt()` in the example above), it's great job for `goto`: @@ -54,28 +51,97 @@ great job for `goto`: \code{.c} int my_function(void) { - int ret = SUCCESS; + int rc = SUCCESS; tn_disable_interrupt(); //-- do something if (error()){ //-- do something - ret = ERROR; + rc = ERROR; goto out; } //-- do something out: tn_enable_interrupt(); - return ret; + return rc; } \endcode I understand there are a lot of people that don't agree with me on this (mostly because they religiously believe that `goto` is unconditionally evil), but -anyway I decided to explain it. And yeah, original TNKernel code contains zero -`goto` statement occurrences, but a lot of code like the one in first example -above. +anyway I decided to explain it. And, let's go further: + +While multiple `goto`-s to single label are better than multiple `return` +statements, it becomes less useful as we get to something more complicated. +Imagine we need to perform some checks *before* disabling interrupts, and +perform some other checks *after* disabling them. Then, we have to create two +labels, like that: + +\code{.c} +int my_function(void) +{ + int rc = SUCCESS; + + if (error1()){ + rc = ERROR1; + goto out; + } + + tn_disable_interrupt(); + + if (error2()){ + rc = ERROR2; + goto out_ei; + } + + if (error3()){ + rc = ERROR3; + goto out_ei; + } + + //-- perform job + +out_ei: + tn_enable_interrupt(); + +out: + return rc; +} +\endcode + +For each error handling, we should specify the label explicitly, and it's easy +to mix labels up, especially if we add some new case to check in the future. +So, I believe this approach is a superior: + +\code{.c} +int my_function(void) +{ + int rc = SUCCESS; + + if (error1()){ + rc = ERROR1; + } else { + tn_disable_interrupt(); + + if (error2()){ + rc = ERROR2; + } else if (error3()){ + rc = ERROR3; + } else { + //-- perform job + } + + tn_enable_interrupt(); + } + + return rc; +} +\endcode + +Then, for each new error handling, we should just add new `else if` block, and +there's no need to care where to go if error happened. Let the compiler do the +branching job for you. More, this code looks more compact. Needless to say, I already found such bug in original TNKernel 2.7 code. The function `tn_sys_tslice_ticks()` looks as follows: From 20b4f9d132d47554c2f0010610f91880facc2257 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Mon, 20 Oct 2014 18:08:15 +0400 Subject: [PATCH 52/55] added example project: queue_eventgrp_conn --- .../.vimprj/.indexer_files | 6 + examples/queue_eventgrp_conn/.vimprj/my.vim | 68 +++ .../Makefile | 113 +++++ .../nbproject/configurations.xml | 254 +++++++++++ .../nbproject/project.properties | 0 .../nbproject/project.xml | 18 + .../arch/pic32/pic32_arch.c | 4 + .../arch/pic32/queue_example_arch.c | 94 ++++ .../arch/pic32/queue_example_arch.h | 36 ++ examples/queue_eventgrp_conn/queue_example.c | 69 +++ examples/queue_eventgrp_conn/queue_example.h | 152 +++++++ examples/queue_eventgrp_conn/readme.txt | 37 ++ examples/queue_eventgrp_conn/task_consumer.c | 412 ++++++++++++++++++ examples/queue_eventgrp_conn/task_consumer.h | 81 ++++ examples/queue_eventgrp_conn/task_producer.c | 194 +++++++++ examples/queue_eventgrp_conn/task_producer.h | 43 ++ src/core/tn_eventgrp.h | 12 +- src/tn.h | 1 + 18 files changed, 1592 insertions(+), 2 deletions(-) create mode 100644 examples/queue_eventgrp_conn/.vimprj/.indexer_files create mode 100644 examples/queue_eventgrp_conn/.vimprj/my.vim create mode 100644 examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/Makefile create mode 100644 examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/configurations.xml create mode 100644 examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/project.properties create mode 100644 examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/project.xml create mode 100644 examples/queue_eventgrp_conn/arch/pic32/pic32_arch.c create mode 100644 examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.c create mode 100644 examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.h create mode 100644 examples/queue_eventgrp_conn/queue_example.c create mode 100644 examples/queue_eventgrp_conn/queue_example.h create mode 100644 examples/queue_eventgrp_conn/readme.txt create mode 100644 examples/queue_eventgrp_conn/task_consumer.c create mode 100644 examples/queue_eventgrp_conn/task_consumer.h create mode 100644 examples/queue_eventgrp_conn/task_producer.c create mode 100644 examples/queue_eventgrp_conn/task_producer.h diff --git a/examples/queue_eventgrp_conn/.vimprj/.indexer_files b/examples/queue_eventgrp_conn/.vimprj/.indexer_files new file mode 100644 index 0000000..ecedcda --- /dev/null +++ b/examples/queue_eventgrp_conn/.vimprj/.indexer_files @@ -0,0 +1,6 @@ + +[queue_eventgrp_conn] +option:ctags_params = "--langmap=c:.c.h --languages=c" + +$INDEXER_PROJECT_ROOT + diff --git a/examples/queue_eventgrp_conn/.vimprj/my.vim b/examples/queue_eventgrp_conn/.vimprj/my.vim new file mode 100644 index 0000000..2e1fc79 --- /dev/null +++ b/examples/queue_eventgrp_conn/.vimprj/my.vim @@ -0,0 +1,68 @@ +" config file for Vim's plugin "vimprj" + +" path to .vimprj folder +let s:sPath = expand(':p:h') + +" set .indexer_file to use +let g:indexer_indexerListFilename = s:sPath.'/.indexer_files' + +let g:indexer_getAllSubdirsFromIndexerListFile = 1 + +" project-specific format settings +let &tabstop = 3 +let &shiftwidth = 3 + +let s:o_dir = $INDEXER_PROJECT_ROOT.'/output/tmp/compiled_in_vim' + +if !isdirectory(s:o_dir) + call mkdir(s:o_dir, "p") +endif + +let g:indexer_handlePath = 0 + +call CheckNeededSymbols( + \ "The following items needs to be defined in your vimfiles/machine_local_conf/current/variables.vim file to make things work: ", + \ "", + \ [ + \ '$MACHINE_LOCAL_CONF__PATH__MICROCHIP_XC32', + \ ] + \ ) + +let s:sCompilerExecutable = '' +if has('win32') || has('win64') + let s:sCompilerExecutable = 'xc32-gcc.exe' +else + let s:sCompilerExecutable = 'xc32-gcc' +endif + +" Path to MPLAB .mcp project +let s:sProject = 'arch/pic32/example_queue_pic32.X' +call envcontrol#set_project_file($INDEXER_PROJECT_ROOT.'/'.s:sProject, 'MPLAB_X', { + \ 'parser_params': { + \ 'compiler_command_without_includes' : '' + \ .'"'.s:sCompilerExecutable.'" ' + \ .' -g -D__DEBUG -x c -c -mprocessor=32MX440F512H -Wall -mips16' + \ .' -Os -MMD -MF "'.s:o_dir.'/%:t:r.o.d" -o "'.s:o_dir.'/%:t:r.o"', + \ }, + \ 'handle_clang' : 1, + \ 'add_paths' : [ + \ $MACHINE_LOCAL_CONF__PATH__MICROCHIP_XC32.'/lib/gcc/pic32mx/4.5.2/include', + \ $MACHINE_LOCAL_CONF__PATH__MICROCHIP_XC32.'/pic32mx/include', + \ ], + \ 'clang_add_params' : '' + \ .' -D __C32__' + \ .' -D __C32_VERSION__=1210' + \ .' -D __CLANG_FOR_COMPLETION__' + \ .' -D __LANGUAGE_C__' + \ .' -D __PIC32MX__' + \ .' -D __32MX440F512H__' + \ .' -D __PIC32_FEATURE_SET__=440' + \ .' -D __DEBUG' + \ .' -Wno-builtin-requires-header' + \ .' -Wno-unused-value' + \ .' -Wno-implicit-function-declaration' + \ .' -Wno-attributes' + \ .' -Wno-invalid-source-encoding' + \ , + \ }) + diff --git a/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/Makefile b/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/Makefile new file mode 100644 index 0000000..fca8e2c --- /dev/null +++ b/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/Makefile @@ -0,0 +1,113 @@ +# +# There exist several targets which are by default empty and which can be +# used for execution of your targets. These targets are usually executed +# before and after some main targets. They are: +# +# .build-pre: called before 'build' target +# .build-post: called after 'build' target +# .clean-pre: called before 'clean' target +# .clean-post: called after 'clean' target +# .clobber-pre: called before 'clobber' target +# .clobber-post: called after 'clobber' target +# .all-pre: called before 'all' target +# .all-post: called after 'all' target +# .help-pre: called before 'help' target +# .help-post: called after 'help' target +# +# Targets beginning with '.' are not intended to be called on their own. +# +# Main targets can be executed directly, and they are: +# +# build build a specific configuration +# clean remove built files from a configuration +# clobber remove all built files +# all build all configurations +# help print help mesage +# +# Targets .build-impl, .clean-impl, .clobber-impl, .all-impl, and +# .help-impl are implemented in nbproject/makefile-impl.mk. +# +# Available make variables: +# +# CND_BASEDIR base directory for relative paths +# CND_DISTDIR default top distribution directory (build artifacts) +# CND_BUILDDIR default top build directory (object files, ...) +# CONF name of current configuration +# CND_ARTIFACT_DIR_${CONF} directory of build artifact (current configuration) +# CND_ARTIFACT_NAME_${CONF} name of build artifact (current configuration) +# CND_ARTIFACT_PATH_${CONF} path to build artifact (current configuration) +# CND_PACKAGE_DIR_${CONF} directory of package (current configuration) +# CND_PACKAGE_NAME_${CONF} name of package (current configuration) +# CND_PACKAGE_PATH_${CONF} path to package (current configuration) +# +# NOCDDL + + +# Environment +MKDIR=mkdir +CP=cp +CCADMIN=CCadmin +RANLIB=ranlib + + +# build +build: .build-post + +.build-pre: +# Add your pre 'build' code here... + +.build-post: .build-impl +# Add your post 'build' code here... + + +# clean +clean: .clean-post + +.clean-pre: +# Add your pre 'clean' code here... +# WARNING: the IDE does not call this target since it takes a long time to +# simply run make. Instead, the IDE removes the configuration directories +# under build and dist directly without calling make. +# This target is left here so people can do a clean when running a clean +# outside the IDE. + +.clean-post: .clean-impl +# Add your post 'clean' code here... + + +# clobber +clobber: .clobber-post + +.clobber-pre: +# Add your pre 'clobber' code here... + +.clobber-post: .clobber-impl +# Add your post 'clobber' code here... + + +# all +all: .all-post + +.all-pre: +# Add your pre 'all' code here... + +.all-post: .all-impl +# Add your post 'all' code here... + + +# help +help: .help-post + +.help-pre: +# Add your pre 'help' code here... + +.help-post: .help-impl +# Add your post 'help' code here... + + + +# include project implementation makefile +include nbproject/Makefile-impl.mk + +# include project make variables +include nbproject/Makefile-variables.mk diff --git a/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/configurations.xml b/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/configurations.xml new file mode 100644 index 0000000..2e8a591 --- /dev/null +++ b/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/configurations.xml @@ -0,0 +1,254 @@ + + + + + + + + + + ../pic32_arch.c + ../queue_example_arch.c + ../../../../../src/arch/pic32/tn_arch_pic32_int_vec1.S + + ../../../queue_example.c + ../../../task_consumer.c + ../../../task_producer.c + + + Makefile + + + + ../../../../common/queue + ../../../../../src/arch/pic32 + ../../.. + + Makefile + + + + localhost + PIC32MX440F512H + + + ICD3PlatformTool + XC32 + 1.21 + 2 + + + + + + + + + + + + false + false + + + + + false + + false + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/project.properties b/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/project.properties new file mode 100644 index 0000000..e69de29 diff --git a/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/project.xml b/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/project.xml new file mode 100644 index 0000000..c09924e --- /dev/null +++ b/examples/queue_eventgrp_conn/arch/pic32/example_queue_eventgrp_conn_pic32.X/nbproject/project.xml @@ -0,0 +1,18 @@ + + com.microchip.mplab.nbide.embedded.makeproject + + + example_queue_eventgrp_conn_pic32 + e6b3e7cb-7b49-4750-89c9-cf229d92ec7f + 0 + c + + + + UTF-8 + + ../../../../../src/arch/pic32/tneokernel.X + + + + diff --git a/examples/queue_eventgrp_conn/arch/pic32/pic32_arch.c b/examples/queue_eventgrp_conn/arch/pic32/pic32_arch.c new file mode 100644 index 0000000..63efb43 --- /dev/null +++ b/examples/queue_eventgrp_conn/arch/pic32/pic32_arch.c @@ -0,0 +1,4 @@ + +//-- Include common pic32 source for all examples +#include "../../../common/arch/pic32/pic32_arch.c" + diff --git a/examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.c b/examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.c new file mode 100644 index 0000000..d6bb138 --- /dev/null +++ b/examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.c @@ -0,0 +1,94 @@ +/** + * \file + * + * Data queue usage example. + * + * For general information about the pattern, refer to the top of + * queue_example.h file + */ + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include +#include "queue_example_arch.h" +#include "task_consumer.h" + + + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + + +//-- gpio register to work with, you might want to switch it to different port +// NOTE: in real programs, it's better to use some peripheral library. +// I strongly dislike plib as well as "brand new" Harmony by Microchip, +// I use my own peripheral library, but for example such as this one, +// I decided to write it as simple as possible. +#define TRIS_REG TRISE +#define TRIS_REG_CLR TRISECLR +#define TRIS_REG_SET TRISESET +#define TRIS_REG_INV TRISEINV + +#define PORT_REG PORTE +#define PORT_REG_CLR PORTECLR +#define PORT_REG_SET PORTESET +#define PORT_REG_INV PORTEINV + + + + + + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +/** + * See comments in the header file + */ +void queue_example_arch_init(void) +{ + //-- set output for needed pins + TRIS_REG_CLR = TASK_CONS_PIN_MASK; + + //-- clear current port value + PORT_REG_CLR = TASK_CONS_PIN_MASK; +} + +/** + * See comments in the header file + */ +void queue_example_arch_pins_toggle(int pin_mask) +{ + PORT_REG_INV = pin_mask; +} + +/** + * See comments in the header file + */ +void queue_example_arch_pins_set(int pin_mask) +{ + PORT_REG_SET = pin_mask; +} + +/** + * See comments in the header file + */ +void queue_example_arch_pins_clear(int pin_mask) +{ + PORT_REG_CLR = pin_mask; +} + + diff --git a/examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.h b/examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.h new file mode 100644 index 0000000..657b5d1 --- /dev/null +++ b/examples/queue_eventgrp_conn/arch/pic32/queue_example_arch.h @@ -0,0 +1,36 @@ + +#ifndef _QUEUE_EXAMPLE_ARCH_H +#define _QUEUE_EXAMPLE_ARCH_H + + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +//-- Include common pic32 header for all examples +#include "../../../common/arch/pic32/example_arch.h" + +/** + * At least, we need to initialize GPIO pins here + */ +void queue_example_arch_init(void); + +/** + * Toggle pins specified by pin_mask + */ +void queue_example_arch_pins_toggle(int pin_mask); + +/** + * Set pins specified by pin_mask + */ +void queue_example_arch_pins_set(int pin_mask); + +/** + * Clear pins specified by pin_mask + */ +void queue_example_arch_pins_clear(int pin_mask); + + +#endif // _QUEUE_EXAMPLE_ARCH_H + diff --git a/examples/queue_eventgrp_conn/queue_example.c b/examples/queue_eventgrp_conn/queue_example.c new file mode 100644 index 0000000..b8661eb --- /dev/null +++ b/examples/queue_eventgrp_conn/queue_example.c @@ -0,0 +1,69 @@ +/** + * \file + * + * Example project that demonstrates usage of queues in TNeoKernel. + */ + + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include "queue_example.h" + +#include "task_consumer.h" +#include "task_producer.h" + +#include "tn.h" + + + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +static struct TN_EventGrp que_example_events; + + + + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +/** + * Each example should define this funtion: it creates first application task + */ +void init_task_create(void) +{ + task_producer_create(); +} + + +/** + * See comments in the header file + */ +void queue_example_init(void) +{ + //-- create application events + // (see enum E_QueExampleFlag in the header) + SYSRETVAL_CHECK(tn_eventgrp_create(&que_example_events, (0))); + + //-- init architecture-dependent stuff + queue_example_arch_init(); +} + +/** + * See comments in the header file + */ +struct TN_EventGrp *queue_example_eventgrp_get(void) +{ + return &que_example_events; +} + +/******************************************************************************* + * end of file + ******************************************************************************/ + + + diff --git a/examples/queue_eventgrp_conn/queue_example.h b/examples/queue_eventgrp_conn/queue_example.h new file mode 100644 index 0000000..2d6fc95 --- /dev/null +++ b/examples/queue_eventgrp_conn/queue_example.h @@ -0,0 +1,152 @@ +/** + * \file + * + * Example project that demonstrates usage of queues in TNeoKernel. + */ + +#ifndef _QUEUE_EXAMPLE_H +#define _QUEUE_EXAMPLE_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +//-- include architecture-specific things, +// at least, there is SOFTWARE_BREAK macro +#include "queue_example_arch.h" + + +/******************************************************************************* + * EXTERN TYPES + ******************************************************************************/ + +/// Application common event group (see `enum E_QueExampleFlag`) +struct TN_EventGrp; + + + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +enum E_QueExampleFlag { + /// + /// Flag indicating that consumer task is initialized + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT = (1 << 0), + /// + /// Flag indicating that producer task is initialized + QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT = (1 << 1), + /// + /// Flag indicating that consumer's queue A is not empty + QUE_EXAMPLE_FLAG__MSG_A = (1 << 2), + /// + /// Flag indicating that consumer's queue B is not empty + QUE_EXAMPLE_FLAG__MSG_B = (1 << 3), +}; + + + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/** + * Macro for checking value returned from system service. + * + * If you ignore the value returned by any system service, it is almost always + * a BAD idea: if something goes wrong, the sooner you know it, the better. + * But, checking error for every system service is a kind of annoying, so, + * simple macro was invented for that. + * + * In most situations, any values other than `#TN_RC_OK` or `#TN_RC_TIMEOUT` + * are not allowed (i.e. should never happen in normal device operation). + * + * So, if error value is neither `#TN_RC_OK` nor `#TN_RC_TIMEOUT`, this macro + * issues a software break. + * + * If you need to allow `#TN_RC_OK` only, use `SYSRETVAL_CHECK()` instead (see + * below) + * + * Usage is as follows: + * + * \code{.c} + * enum TN_RCode rc + * = SYSRETVAL_CHECK_TO(tn_queue_send(&my_queue, p_data, MY_TIMEOUT)); + * + * switch (rc){ + * case TN_RC_OK: + * //-- handle successfull operation + * break; + * case TN_RC_TIMEOUT: + * //-- handle timeout + * break; + * default: + * //-- should never be here + * break; + * } + * \endcode + * + */ +#define SYSRETVAL_CHECK_TO(x) \ + ({ \ + int __rv = (x); \ + if (__rv != TN_RC_OK && __rv != TN_RC_TIMEOUT){ \ + SOFTWARE_BREAK(); \ + } \ + /* like, return __rv */ \ + __rv; \ + }) + +/** + * The same as `SYSRETVAL_CHECK_TO()`, but it allows `#TN_RC_OK` only. + * + * Since there is only one return code allowed, usage is simple: + * + * \code{.c} + * SYSRETVAL_CHECK(tn_queue_send(&my_queue, p_data, MY_TIMEOUT)); + * \endcode + */ +#define SYSRETVAL_CHECK(x) \ + ({ \ + int __rv = (x); \ + if (__rv != TN_RC_OK){ \ + SOFTWARE_BREAK(); \ + } \ + /* like, return __rv */ \ + __rv; \ + }) + + + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + +/** + * Each example should define this funtion: it creates first application task + */ +void init_task_create(void); + +/** + * Initialization of application common objects, must be called once + * from the initial application task (which is created in init_task_create()) + */ +void queue_example_init(void); + +/** + * Returns pointer to the application event group. + * See `enum E_QueExampleFlag` + * + * Do note that you must call `queue_example_init()` before + * you can get eventgrp. + */ +struct TN_EventGrp *queue_example_eventgrp_get(void); + +#endif // _QUEUE_EXAMPLE_H + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + diff --git a/examples/queue_eventgrp_conn/readme.txt b/examples/queue_eventgrp_conn/readme.txt new file mode 100644 index 0000000..1df4a68 --- /dev/null +++ b/examples/queue_eventgrp_conn/readme.txt @@ -0,0 +1,37 @@ + +This is an usage example of waiting for messages from multiple queues +at once in TNeoKernel. + +This example is similar to the plain "queue" example (which you might +want to examine before this one), but in this project, task_consumer +waits for messages from two queues at a time. + +To this end, TNeoKernel offers a way to connect the event group to +another kernel object (currently, only to queue). When an event group +with specified flag pattern is connected to the queue, the queue maintains +specified flag pattern automatically: if queue contains is non-empty, +the flag is set, if queue becomes empty, the flag is clear. + +Please note that there's no need to clear the flag manually: queue +maintains it completely. + +So in this example project, task_producer creates and starts kernel timer as a +part of its initialization routine. Timer fires after 750 system ticks, and +timer callback sends the message to the second queue and restarts timer again. + +task_consumer waits for one of the two flags, each flag indicates whether +appropriate queue is non-empty. When either queue becomes non-empty, flag +is automatically set, so task_consumer wakes up, checks which flag is +set, receives message from the appropriate queue and handles it. + +In this example project, the message from the second queue could just make +task_consumer clear all the LEDs or, conversely, set them all at once. + +Event group connection is a perfectly fine solution. If the kernel doesn't +offer a mechanism for that, programmer usually have to use polling services on +these queues and sleep for a few system ticks. Obviously, this approach has +serious drawbacks: we have a lot of useless context switches, and response for +the message gets much slower. Actually, we lost the main goal of the preemtive +kernel when we use polling services like that. + + diff --git a/examples/queue_eventgrp_conn/task_consumer.c b/examples/queue_eventgrp_conn/task_consumer.c new file mode 100644 index 0000000..62720f5 --- /dev/null +++ b/examples/queue_eventgrp_conn/task_consumer.c @@ -0,0 +1,412 @@ +/** + * \file + * + * Example project that demonstrates usage of queues in TNeoKernel. + */ + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +#include "task_consumer.h" +#include "task_producer.h" +#include "queue_example.h" +#include "tn.h" + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +//-- task stack size +#define TASK_CONSUMER_STACK_SIZE (TN_MIN_STACK_SIZE + 96) + +//-- priority of consumer task: the highest one +#define TASK_CONSUMER_PRIORITY 0 + +//-- number of items in the consumer message queue A +#define CONS_QUE_A_BUF_SIZE 4 + +//-- number of items in the consumer message queue B +#define CONS_QUE_B_BUF_SIZE 2 + +//-- max timeout for waiting for memory and free message +#define WAIT_TIMEOUT 10 + + + + + +/******************************************************************************* + * PRIVATE FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE TYPES + ******************************************************************************/ + +/** + * Type of message that consumer receives. + */ +struct TaskConsumerMsgA { + enum E_TaskConsCmd cmd; //-- command to perform + // (well, now there's just one command) + enum E_TaskConsPin pin_num; //-- number of pin for which command + // should be performed +}; + +struct TaskConsumerMsgB { + int bool_on; //-- whether all pins should be turned on or off +}; + + + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +//-- define array for task stack +TN_STACK_ARR_DEF(task_consumer_stack, TASK_CONSUMER_STACK_SIZE); + +//-- task descriptor: it's better to explicitly zero it +static struct TN_Task task_consumer = {}; + +//-- define queue A and buffer for it +struct TN_DQueue cons_que_a; +void *cons_que_a_buf[ CONS_QUE_A_BUF_SIZE ]; + +//-- define fixed memory pool for queue A and buffer for it +struct TN_FMem cons_fmem_a; +TN_FMEM_BUF_DEF(cons_fmem_a_buf, struct TaskConsumerMsgA, CONS_QUE_A_BUF_SIZE); + +//-- define queue B and buffer for it +struct TN_DQueue cons_que_b; +void *cons_que_b_buf[ CONS_QUE_B_BUF_SIZE ]; + +//-- define fixed memory pool for queue B and buffer for it +struct TN_FMem cons_fmem_b; +TN_FMEM_BUF_DEF(cons_fmem_b_buf, struct TaskConsumerMsgB, CONS_QUE_B_BUF_SIZE); + + + + + +/******************************************************************************* + * PUBLIC DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL DATA + ******************************************************************************/ + +/******************************************************************************* + * EXTERNAL FUNCTION PROTOTYPES + ******************************************************************************/ + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +static void msg_a_handle(struct TaskConsumerMsgA *p_msg) +{ + //-- message successfully received, let's check command + switch (p_msg->cmd){ + + case TASK_CONS_CMD__PIN_TOGGLE: + //-- toggle specified bit + queue_example_arch_pins_toggle(1 << p_msg->pin_num); + break; + + default: + //-- should never be here + SOFTWARE_BREAK(); + break; + } +} + +static void msg_b_handle(struct TaskConsumerMsgB *p_msg) +{ + //-- message successfully received, let's check command + if (p_msg->bool_on){ + queue_example_arch_pins_set(TASK_CONS_PIN_MASK); + } else { + queue_example_arch_pins_clear(TASK_CONS_PIN_MASK); + } +} + +static void task_consumer_body(void *par) +{ + //-- init things specific to consumer task + + //-- init queue A {{{ + //-- create memory pool for queue A + SYSRETVAL_CHECK( + tn_fmem_create( + &cons_fmem_a, + cons_fmem_a_buf, + TN_MAKE_ALIG_SIZE(sizeof(struct TaskConsumerMsgA)), + CONS_QUE_A_BUF_SIZE + ) + ); + + //-- create queue A + SYSRETVAL_CHECK( + tn_queue_create(&cons_que_a, (void *)cons_que_a_buf, CONS_QUE_A_BUF_SIZE) + ); + // }}} + + //-- init queue B {{{ + //-- create memory pool for queue B + SYSRETVAL_CHECK( + tn_fmem_create( + &cons_fmem_b, + cons_fmem_b_buf, + TN_MAKE_ALIG_SIZE(sizeof(struct TaskConsumerMsgB)), + CONS_QUE_B_BUF_SIZE + ) + ); + + //-- create queue B + SYSRETVAL_CHECK( + tn_queue_create(&cons_que_b, (void *)cons_que_b_buf, CONS_QUE_B_BUF_SIZE) + ); + // }}} + + + //-- connect application's common event group to both queues with + // appropriate flags + SYSRETVAL_CHECK( + tn_queue_eventgrp_connect( + &cons_que_a, + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__MSG_A + ) + ); + + SYSRETVAL_CHECK( + tn_queue_eventgrp_connect( + &cons_que_b, + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__MSG_B + ) + ); + + + + //-- cry that consumer task has initialized + SYSRETVAL_CHECK( + tn_eventgrp_modify( + queue_example_eventgrp_get(), + TN_EVENTGRP_OP_SET, + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT + ) + ); + + //-- enter endless loop in which we will receive and handle messages + for (;;) + { + //-- flags pattern that is filled by tn_eventgrp_wait() when event happens + TN_UWord flags_pattern = 0; + + //-- wait for messages (i.e. for connected flags), potentially can wait forever + // (if there are no messages) + enum TN_RCode rc = SYSRETVAL_CHECK( + tn_eventgrp_wait( + queue_example_eventgrp_get(), + (QUE_EXAMPLE_FLAG__MSG_A | QUE_EXAMPLE_FLAG__MSG_B), + TN_EVENTGRP_WMODE_OR, + &flags_pattern, + TN_WAIT_INFINITE + ) + ); + + if (rc == TN_RC_OK){ + //-- At least, one of the queues contains new message. + // Let's check which flags are set and receive message + // from appropriate queue. + // + // We specify zero timeout here, because message + // should be already available: there aren't other + // customers that could "steal" that message from this task. + + if (flags_pattern & QUE_EXAMPLE_FLAG__MSG_A){ + //-- message A is ready to receive, let's receive it. + struct TaskConsumerMsgA *p_msg_a; + rc = SYSRETVAL_CHECK( + tn_queue_receive(&cons_que_a, (void *)&p_msg_a, 0) + ); + if (rc == TN_RC_OK){ + //-- handle the message + msg_a_handle(p_msg_a); + + //-- free memory + SYSRETVAL_CHECK(tn_fmem_release(&cons_fmem_a, (void *)p_msg_a)); + } else { + //-- failed to receive message: should never be here + SOFTWARE_BREAK(); + } + + } + + if (flags_pattern & QUE_EXAMPLE_FLAG__MSG_B){ + //-- message B is ready to receive, let's receive it. + struct TaskConsumerMsgB *p_msg_b; + rc = SYSRETVAL_CHECK( + tn_queue_receive(&cons_que_b, (void *)&p_msg_b, 0) + ); + if (rc == TN_RC_OK){ + //-- handle the message + msg_b_handle(p_msg_b); + + //-- free memory + SYSRETVAL_CHECK(tn_fmem_release(&cons_fmem_b, (void *)p_msg_b)); + } else { + //-- failed to receive message: should never be here + SOFTWARE_BREAK(); + } + + } + } else { + //-- failed to wait for events: should never be here, + // since we wait infinitely + SOFTWARE_BREAK(); + } + + } +} + + + + + + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +/** + * See comments in the header file + */ +void task_consumer_create(void) +{ + + SYSRETVAL_CHECK( + tn_task_create( + &task_consumer, + task_consumer_body, + TASK_CONSUMER_PRIORITY, + task_consumer_stack, + TASK_CONSUMER_STACK_SIZE, + NULL, + (TN_TASK_CREATE_OPT_START) + ) + ); + +} + +/** + * See comments in the header file + */ +int task_consumer_msg_a_send(enum E_TaskConsCmd cmd, enum E_TaskConsPin pin_num) +{ + int ret = 0; + + struct TaskConsumerMsgA *p_msg; + enum TN_RCode tn_rc; + + //-- get memory block from memory pool + tn_rc = SYSRETVAL_CHECK_TO( + tn_is_task_context() + ? tn_fmem_get(&cons_fmem_a, (void *)&p_msg, WAIT_TIMEOUT) + : tn_fmem_iget_polling(&cons_fmem_a, (void *)&p_msg) + ); + if (tn_rc == TN_RC_OK){ + + //-- put correct data to the allocated memory + p_msg->cmd = cmd; + p_msg->pin_num = pin_num; + + //-- send it to the consumer task + tn_rc = SYSRETVAL_CHECK_TO( + tn_is_task_context() + ? tn_queue_send(&cons_que_a, (void *)p_msg, WAIT_TIMEOUT) + : tn_queue_isend_polling(&cons_que_a, (void *)p_msg) + ); + + if (tn_rc == TN_RC_OK){ + ret = 1/*success*/; + } else { + //-- there was some error while sending the message, + // so, we should free buffer that we've allocated + SYSRETVAL_CHECK( + tn_is_task_context() + ? tn_fmem_release(&cons_fmem_a, (void *)p_msg) + : tn_fmem_irelease(&cons_fmem_a, (void *)p_msg) + ); + } + } else { + //-- there was some error while allocating memory, + // nothing to do here: ret is still 0, and it is + // going to be returned + } + + return ret; +} + +/** + * See comments in the header file + */ +int task_consumer_msg_b_send(int bool_on) +{ + int ret = 0; + + struct TaskConsumerMsgB *p_msg; + enum TN_RCode tn_rc; + + //-- get memory block from memory pool + tn_rc = SYSRETVAL_CHECK_TO( + tn_is_task_context() + ? tn_fmem_get(&cons_fmem_b, (void *)&p_msg, WAIT_TIMEOUT) + : tn_fmem_iget_polling(&cons_fmem_b, (void *)&p_msg) + ); + if (tn_rc == TN_RC_OK){ + + //-- put correct data to the allocated memory + p_msg->bool_on = bool_on; + + //-- send it to the consumer task + tn_rc = SYSRETVAL_CHECK_TO( + tn_is_task_context() + ? tn_queue_send(&cons_que_b, (void *)p_msg, WAIT_TIMEOUT) + : tn_queue_isend_polling(&cons_que_b, (void *)p_msg) + ); + + if (tn_rc == TN_RC_OK){ + ret = 1/*success*/; + } else { + //-- there was some error while sending the message, + // so, we should free buffer that we've allocated + SYSRETVAL_CHECK( + tn_is_task_context() + ? tn_fmem_release(&cons_fmem_b, (void *)p_msg) + : tn_fmem_irelease(&cons_fmem_b, (void *)p_msg) + ); + } + } else { + //-- there was some error while allocating memory, + // nothing to do here: ret is still 0, and it is + // going to be returned + } + + return ret; +} + + + + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + + diff --git a/examples/queue_eventgrp_conn/task_consumer.h b/examples/queue_eventgrp_conn/task_consumer.h new file mode 100644 index 0000000..06b2454 --- /dev/null +++ b/examples/queue_eventgrp_conn/task_consumer.h @@ -0,0 +1,81 @@ +/** + * \file + * + * Example project that demonstrates usage of queues in TNeoKernel. + */ + + +#ifndef _TASK_CONSUMER_H +#define _TASK_CONSUMER_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +enum E_TaskConsCmd { + TASK_CONS_CMD__PIN_TOGGLE, +}; + +enum E_TaskConsPin { + TASK_CONS_PIN__0, + TASK_CONS_PIN__1, + TASK_CONS_PIN__2, +}; + +#define TASK_CONS_PIN_MASK (0 \ + | (1 << TASK_CONS_PIN__0) \ + | (1 << TASK_CONS_PIN__1) \ + | (1 << TASK_CONS_PIN__2) \ + ) + +/******************************************************************************* + * GLOBAL VARIABLES + ******************************************************************************/ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + +/** + * Create consumer task. + */ +void task_consumer_create(void); + + +/** + * Send a message to the consumer. + * In this simple example, consumer will just toggle given pin number. + * + * This function can be called from either task or ISR. + * + * @param pin_num + * pin number to toggle. + * + * @return + * - 1 on success + * - 0 on failure + */ +int task_consumer_msg_a_send(enum E_TaskConsCmd cmd, enum E_TaskConsPin pin_num); + +/** + * TODO + */ +int task_consumer_msg_b_send(int bool_on); + +#endif // _TASK_CONSUMER_H + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + diff --git a/examples/queue_eventgrp_conn/task_producer.c b/examples/queue_eventgrp_conn/task_producer.c new file mode 100644 index 0000000..594158f --- /dev/null +++ b/examples/queue_eventgrp_conn/task_producer.c @@ -0,0 +1,194 @@ +/** + * \file + * + * Example project that demonstrates usage of queues in TNeoKernel. + */ + + +#include "task_producer.h" +#include "task_consumer.h" +#include "queue_example.h" +#include "tn.h" + + + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +//-- task stack size +#define TASK_PRODUCER_STACK_SIZE (TN_MIN_STACK_SIZE + 96) + +//-- highest priority +#define TASK_PRODUCER_PRIORITY 0 + +//-- timer period, in system ticks +#define MY_TIMER_PERIOD 750 + + +/******************************************************************************* + * PRIVATE DATA + ******************************************************************************/ + +//-- define array for task stack +TN_STACK_ARR_DEF(task_producer_stack, TASK_PRODUCER_STACK_SIZE); + +//-- task descriptor: it's better to explicitly zero it +static struct TN_Task task_producer = {}; + +static struct TN_Timer my_timer = {}; + + +/******************************************************************************* + * PRIVATE FUNCTIONS + ******************************************************************************/ + +/** + * Callback function called by kernel timer (that is, from ISR context) + */ +static void my_timer_callback(struct TN_Timer *timer, void *p_user_data) +{ + //-- restart timer again + tn_timer_start(&my_timer, MY_TIMER_PERIOD); + + //-- whether we should turn all LEDs on or off + static int bool_all_on = 0; + bool_all_on = !bool_all_on; + + if (!task_consumer_msg_b_send(bool_all_on)){ + //-- failed to send the message + // in this partucilar application, it should never happen, + // so, halt the debugger + SOFTWARE_BREAK(); + } +} + +/** + * Application init: called from the first created application task + */ +static void appl_init(void) +{ + //-- init common application objects + queue_example_init(); + + //-- create all the rest application tasks: + + //-- create the consumer task {{{ + { + task_consumer_create(); + + //-- wait until consumer task initialized + SYSRETVAL_CHECK( + tn_eventgrp_wait( + queue_example_eventgrp_get(), + QUE_EXAMPLE_FLAG__TASK_CONSUMER_INIT, + TN_EVENTGRP_WMODE_AND, + NULL, + TN_WAIT_INFINITE + ) + ); + } + // }}} + + //-- create and start timer for sending message B {{{ + { + tn_timer_create(&my_timer, my_timer_callback, NULL); + tn_timer_start(&my_timer, MY_TIMER_PERIOD); + } + //}}} + +} + +/** + * Body function for producer task + */ +static void task_producer_body(void *par) +{ + //-- in this particular application, producer task is the first application + // task that is started, so, we should perform all the app initialization + // here, and then start other tasks. All of this is done in the appl_init(). + appl_init(); + + //-- cry that producer task has initialized + SYSRETVAL_CHECK( + tn_eventgrp_modify( + queue_example_eventgrp_get(), + TN_EVENTGRP_OP_SET, + QUE_EXAMPLE_FLAG__TASK_PRODUCER_INIT + ) + ); + + //-- at this point, application is completely initialized, and we can + // get to business: enter endless loop and repeatedly send + // messages to the consumer + for (;;) + { + int i; + + for (i = 0; i < 3/*pins count*/;i++){ + //-- Wait before sending message + SYSRETVAL_CHECK_TO( tn_task_sleep(100) ); + + enum E_TaskConsPin pin_num = 0; + + //-- determine pin_num + switch (i){ + case 0: + pin_num = TASK_CONS_PIN__0; + break; + case 1: + pin_num = TASK_CONS_PIN__1; + break; + case 2: + pin_num = TASK_CONS_PIN__2; + break; + default: + //-- should never be here + SOFTWARE_BREAK(); + break; + } + + //-- Send the message to consumer + if (!task_consumer_msg_a_send( + TASK_CONS_CMD__PIN_TOGGLE, + pin_num + ) + ) + { + //-- failed to send the message + // in this partucilar application, it should never happen, + // so, halt the debugger + SOFTWARE_BREAK(); + } + } + + } +} + + + +/******************************************************************************* + * PUBLIC FUNCTIONS + ******************************************************************************/ + +/** + * See comments in the header file + */ +void task_producer_create(void) +{ + + SYSRETVAL_CHECK( + tn_task_create( + &task_producer, + task_producer_body, + TASK_PRODUCER_PRIORITY, + task_producer_stack, + TASK_PRODUCER_STACK_SIZE, + NULL, + (TN_TASK_CREATE_OPT_START) + ) + ); + +} + + diff --git a/examples/queue_eventgrp_conn/task_producer.h b/examples/queue_eventgrp_conn/task_producer.h new file mode 100644 index 0000000..43e8ba0 --- /dev/null +++ b/examples/queue_eventgrp_conn/task_producer.h @@ -0,0 +1,43 @@ +/** + * \file + * + * Example project that demonstrates usage of queues in TNeoKernel. + */ + + +#ifndef _TASK_PRODUCER_H +#define _TASK_PRODUCER_H + +/******************************************************************************* + * INCLUDED FILES + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC TYPES + ******************************************************************************/ + +/******************************************************************************* + * GLOBAL VARIABLES + ******************************************************************************/ + +/******************************************************************************* + * DEFINITIONS + ******************************************************************************/ + +/******************************************************************************* + * PUBLIC FUNCTION PROTOTYPES + ******************************************************************************/ + +/** + * Create producer task. + */ +void task_producer_create(void); + +#endif // _TASK_PRODUCER_H + + +/******************************************************************************* + * end of file + ******************************************************************************/ + + diff --git a/src/core/tn_eventgrp.h b/src/core/tn_eventgrp.h index cfa5a1f..22b7a97 100644 --- a/src/core/tn_eventgrp.h +++ b/src/core/tn_eventgrp.h @@ -52,8 +52,12 @@ * * \section eventgrp_connect Connecting an event group to other system objects * - * Sometimes task needs to wait for different system events: the most common - * example is to wait for messages from multiple queues. + * Sometimes task needs to wait for different system events, the most common + * examples are: + * + * - wait for a message from the queue(s) plus wait for some + * application-dependent event; + * - wait for messages from multiple queues. * * If the kernel doesn't offer a mechanism for that, programmer usually have to * use polling services on these queues and sleep for a few system ticks. @@ -77,6 +81,10 @@ * to `tn_eventgrp_wait()`. * - when that event happened, task checks which flag is set, and receive * message from the appropriate queue. + * + * Please note that task waiting for the event should **not** clear the flag + * manually: this flag is maintained completely by the queue. If the queue is + * non-empty, the flag is set. If the queue becomes empty, the flag is cleared. * * For the information on system services related to queue, refer to the \ref * tn_dqueue.h "queue reference". diff --git a/src/tn.h b/src/tn.h index 3f4c08e..ed4c359 100644 --- a/src/tn.h +++ b/src/tn.h @@ -57,6 +57,7 @@ #include "core/tn_sem.h" #include "core/tn_tasks.h" #include "core/tn_list.h" +#include "core/tn_timer.h" //-- include old symbols for compatibility with old projects From 3ae7a568dcb1bb01297baf29cfa23584641d8251 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Mon, 20 Oct 2014 18:12:11 +0400 Subject: [PATCH 53/55] documentation now mentions the example queue_eventgrp_conn --- src/core/tn_dqueue.h | 4 ++++ src/core/tn_eventgrp.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/core/tn_dqueue.h b/src/core/tn_dqueue.h index 6136140..d0a845a 100644 --- a/src/core/tn_dqueue.h +++ b/src/core/tn_dqueue.h @@ -66,6 +66,10 @@ * - `tn_queue_eventgrp_connect()` * - `tn_queue_eventgrp_disconnect()` * + * There is an example project available that demonstrates event group + * connection technique: `examples/queue_eventgrp_conn`. Be sure to examine the + * readme there. + * */ #ifndef _TN_DQUEUE_H diff --git a/src/core/tn_eventgrp.h b/src/core/tn_eventgrp.h index 22b7a97..1e67004 100644 --- a/src/core/tn_eventgrp.h +++ b/src/core/tn_eventgrp.h @@ -88,6 +88,11 @@ * * For the information on system services related to queue, refer to the \ref * tn_dqueue.h "queue reference". + * + * There is an example project available that demonstrates event group + * connection technique: `examples/queue_eventgrp_conn`. Be sure to examine the + * readme there. + * */ #ifndef _TN_EVENTGRP_H From 7cdc5d5bfa18ed46820ec711bf964892c06f796d Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Mon, 20 Oct 2014 18:19:40 +0400 Subject: [PATCH 54/55] changelog updated --- stuff/doc_pages/changelog.dox | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 1783655..520ce97 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -10,7 +10,9 @@ TNeoKernel changelog - Added a capability to connect an \ref tn_eventgrp.h "event group" to other system objects, particularly to the \ref tn_dqueue.h "queue". This offers a way to wait for messages from multiple queues with just a single system - call. Refer to the section \ref eventgrp_connect for details. + call. Refer to the section \ref eventgrp_connect for details. Example + project that demonstrates that technique is also available: + `examples/queue_eventgrp_conn`. - PIC32 Interrupts: this isn't a mandatory anymore to use kernel-provided macros `tn_soft_isr()` or `tn_srs_isr()`: interrupts can be defined with standard way too: this particular ISR will use task's stack instead of interrupt From 4a94d51444a9e59ddda68db12f2e110687720ab8 Mon Sep 17 00:00:00 2001 From: Dmitry Frank Date: Mon, 20 Oct 2014 18:21:45 +0400 Subject: [PATCH 55/55] changelog is updated for release v1.03 --- stuff/doc_pages/changelog.dox | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stuff/doc_pages/changelog.dox b/stuff/doc_pages/changelog.dox index 520ce97..2448be7 100644 --- a/stuff/doc_pages/changelog.dox +++ b/stuff/doc_pages/changelog.dox @@ -5,7 +5,10 @@ TNeoKernel changelog -\section changelog_current Current development version (BETA) +\section changelog_v1_03 v1.03 + +Release date: 2014-10-20. + - Added a capability to connect an \ref tn_eventgrp.h "event group" to other system objects, particularly to the \ref tn_dqueue.h "queue". This offers a