diff --git a/examples/pic32/basic/.vimprj/.indexer_files b/examples/basic/arch/pic32/.vimprj/.indexer_files
similarity index 100%
rename from examples/pic32/basic/.vimprj/.indexer_files
rename to examples/basic/arch/pic32/.vimprj/.indexer_files
diff --git a/examples/pic32/basic/.vimprj/my.vim b/examples/basic/arch/pic32/.vimprj/my.vim
similarity index 100%
rename from examples/pic32/basic/.vimprj/my.vim
rename to examples/basic/arch/pic32/.vimprj/my.vim
diff --git a/examples/pic32/basic/src/tn_cfg_appl.h b/examples/basic/arch/pic32/tn_cfg_appl.h
similarity index 100%
rename from examples/pic32/basic/src/tn_cfg_appl.h
rename to examples/basic/arch/pic32/tn_cfg_appl.h
diff --git a/examples/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/pic32/basic/tn_pic32_example_basic.X/Makefile
rename to examples/basic/arch/pic32/tn_pic32_example_basic.X/Makefile
diff --git a/examples/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 95%
rename from examples/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 9fe8435..9306e2f 100644
--- a/examples/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 @@
-
-
+
@@ -182,7 +183,7 @@
-
+
@@ -216,10 +217,10 @@
-
+
-
+
@@ -228,10 +229,8 @@
-
-
@@ -241,7 +240,7 @@
-
+
diff --git a/examples/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/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/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/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/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/pic32/basic/src/tn_pic32_example_basic.c b/examples/basic/arch/pic32/tn_pic32_example_basic.c
similarity index 96%
rename from examples/pic32/basic/src/tn_pic32_example_basic.c
rename to examples/basic/arch/pic32/tn_pic32_example_basic.c
index cddfc2f..661fafc 100644
--- a/examples/pic32/basic/src/tn_pic32_example_basic.c
+++ b/examples/basic/arch/pic32/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);
@@ -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/common/arch/pic32/example_arch.h b/examples/common/arch/pic32/example_arch.h
new file mode 100644
index 0000000..1c0d38d
--- /dev/null
+++ b/examples/common/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/common/arch/pic32/pic32_arch.c b/examples/common/arch/pic32/pic32_arch.c
new file mode 100644
index 0000000..318343d
--- /dev/null
+++ b/examples/common/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/queue/arch/pic32/example_queue_pic32.X/Makefile b/examples/queue/arch/pic32/example_queue_pic32.X/Makefile
new file mode 100644
index 0000000..fca8e2c
--- /dev/null
+++ b/examples/queue/arch/pic32/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/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml
new file mode 100644
index 0000000..cd8df9b
--- /dev/null
+++ b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/configurations.xml
@@ -0,0 +1,253 @@
+
+
+
+
+
+
+
+
+
+ ../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/arch/pic32/example_queue_pic32.X/nbproject/project.properties b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/project.properties
new file mode 100644
index 0000000..e69de29
diff --git a/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/project.xml b/examples/queue/arch/pic32/example_queue_pic32.X/nbproject/project.xml
new file mode 100644
index 0000000..f6b4300
--- /dev/null
+++ b/examples/queue/arch/pic32/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/queue/arch/pic32/pic32_arch.c b/examples/queue/arch/pic32/pic32_arch.c
new file mode 100644
index 0000000..63efb43
--- /dev/null
+++ b/examples/queue/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/arch/pic32/queue_example_arch.c b/examples/queue/arch/pic32/queue_example_arch.c
new file mode 100644
index 0000000..4178e5b
--- /dev/null
+++ b/examples/queue/arch/pic32/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/pic32/queue_example_arch.h b/examples/queue/arch/pic32/queue_example_arch.h
new file mode 100644
index 0000000..b3ee7a9
--- /dev/null
+++ b/examples/queue/arch/pic32/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 "../../../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);
+
+
+#endif // _QUEUE_EXAMPLE_ARCH_H
+
diff --git a/examples/queue/queue_example.c b/examples/queue/queue_example.c
new file mode 100644
index 0000000..b8661eb
--- /dev/null
+++ b/examples/queue/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/queue_example.h b/examples/queue/queue_example.h
new file mode 100644
index 0000000..c45f248
--- /dev/null
+++ b/examples/queue/queue_example.h
@@ -0,0 +1,146 @@
+/**
+ * \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),
+};
+
+
+
+/*******************************************************************************
+ * 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/readme.txt b/examples/queue/readme.txt
new file mode 100644
index 0000000..f8598b9
--- /dev/null
+++ b/examples/queue/readme.txt
@@ -0,0 +1,69 @@
+
+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.
+
diff --git a/examples/queue/task_consumer.c b/examples/queue/task_consumer.c
new file mode 100644
index 0000000..cc5ddd9
--- /dev/null
+++ b/examples/queue/task_consumer.c
@@ -0,0 +1,234 @@
+/**
+ * \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
+#define CONS_QUE_BUF_SIZE 4
+
+//-- 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 TaskConsumerMsg {
+ 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
+};
+
+
+
+/*******************************************************************************
+ * 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 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);
+
+
+
+/*******************************************************************************
+ * PUBLIC DATA
+ ******************************************************************************/
+
+/*******************************************************************************
+ * EXTERNAL DATA
+ ******************************************************************************/
+
+/*******************************************************************************
+ * EXTERNAL FUNCTION PROTOTYPES
+ ******************************************************************************/
+
+/*******************************************************************************
+ * PRIVATE FUNCTIONS
+ ******************************************************************************/
+
+static void task_consumer_body(void *par)
+{
+ //-- 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)
+ );
+
+ //-- 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
+ )
+ );
+
+
+ struct TaskConsumerMsg *p_msg;
+
+ //-- enter endless loop in which we will receive and handle messages
+ for (;;)
+ {
+ //-- 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
+ queue_example_arch_pins_toggle(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));
+ }
+
+ }
+}
+
+
+
+
+
+
+/*******************************************************************************
+ * 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_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;
+}
+
+
+
+
+
+/*******************************************************************************
+ * end of file
+ ******************************************************************************/
+
+
+
diff --git a/examples/queue/task_consumer.h b/examples/queue/task_consumer.h
new file mode 100644
index 0000000..c21adb3
--- /dev/null
+++ b/examples/queue/task_consumer.h
@@ -0,0 +1,68 @@
+/**
+ * \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,
+};
+
+/*******************************************************************************
+ * 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.
+ *
+ * @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
+
+
+/*******************************************************************************
+ * end of file
+ ******************************************************************************/
+
+
diff --git a/examples/queue/task_producer.c b/examples/queue/task_producer.c
new file mode 100644
index 0000000..d3c4fcc
--- /dev/null
+++ b/examples/queue/task_producer.c
@@ -0,0 +1,161 @@
+/**
+ * \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
+
+
+
+/*******************************************************************************
+ * 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 = {};
+
+
+
+/*******************************************************************************
+ * 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 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
+ )
+ );
+ }
+ // }}}
+
+}
+
+/**
+ * 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_send(
+ TASK_CONS_CMD__PIN_TOGGLE,
+ pin_num
+ )
+ )
+ {
+ //-- failed to send the message
+ }
+ }
+
+ }
+}
+
+
+
+/*******************************************************************************
+ * 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/task_producer.h b/examples/queue/task_producer.h
new file mode 100644
index 0000000..43e8ba0
--- /dev/null
+++ b/examples/queue/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/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/arch/example/tn_arch_example.h b/src/arch/example/tn_arch_example.h
index b861c35..f0960e8 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.
*/
@@ -103,13 +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`.
*
- * @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`
@@ -117,7 +117,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
@@ -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.c b/src/arch/pic32/tn_arch_pic32.c
index 6bb3576..d15c942 100644
--- a/src/arch/pic32/tn_arch_pic32.c
+++ b/src/arch/pic32/tn_arch_pic32.c
@@ -38,6 +38,27 @@
extern unsigned long _gp;
+/**
+ * Self-check for the application that uses TNeoKernel:
+ *
+ * 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.
+ *
+ * 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_add_file___tn_arch_pic32_int_vec1_S___to_the_project(void);
+
+
//----------------------------------------------------------------------------
// Context layout
//
@@ -83,55 +104,69 @@ TN_UWord *_tn_arch_stack_top_get(
return stack_low_address + stack_size;
}
-
//----------------------------------------------------------------------------
// Processor specific routine - here for MIPS4K
//
// 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
)
{
+ //-- 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_add_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;
*(--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_pic32.h b/src/arch/pic32/tn_arch_pic32.h
index 682cf33..4623517 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.
*/
@@ -136,11 +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`.
+ *
+ * @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`
@@ -241,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
@@ -344,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)"); \
@@ -486,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/arch/pic32/tn_arch_pic32_int_vec1.S b/src/arch/pic32/tn_arch_pic32_int_vec1.S
index 6dbc45d..3205bbd 100644
--- a/src/arch/pic32/tn_arch_pic32_int_vec1.S
+++ b/src/arch/pic32/tn_arch_pic32_int_vec1.S
@@ -34,6 +34,49 @@
*
******************************************************************************/
+/**
+ * \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.
+ *
+ */
+
+
+
+/*
+ * 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_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_add_file___tn_arch_pic32_int_vec1_S___to_the_project
+
+
+
+
+
.section .vector_1,code
.align 2
.set nomips16
@@ -42,7 +85,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
+
diff --git a/src/arch/pic32/tn_arch_pic32mx_xc32.S b/src/arch/pic32/tn_arch_pic32mx_xc32.S
index d3f3759..f6122e3 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
- .global _tn_arch_system_start
+ .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
@@ -67,30 +66,9 @@
*----------------------------------------------------------------------------*/
.set noreorder
.set noat
- .ent _tn_arch_system_start
+ .ent _tn_arch_context_switch_now_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_now_nosave:
/* get new task's sp */
@@ -102,31 +80,17 @@ _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_now_nosave
/*----------------------------------------------------------------------------
*
*----------------------------------------------------------------------------*/
.set noreorder
.set noat
- .ent _tn_arch_context_switch
-
-_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
+ .ent _tn_arch_context_switch_pend
- sdbbp 0
+_tn_arch_context_switch_pend:
-1:
- */
/* pend CS0 interrupt */
lui $t0, %hi(IFS0SET)
ori $t1, $zero, 2
@@ -136,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 8e12c59..46d2bba 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,22 @@ 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_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
+ * returns, `tn_task_exit()` gets automatially called;
+ * - Argument 0 contains `param` pointer
*
* @param task_func
* Pointer to task body function.
@@ -137,7 +147,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,64 +159,55 @@ unsigned int *_tn_arch_stack_init(
int _tn_arch_inside_isr(void);
/**
- * Called whenever we need to switch context to other task.
- *
- * **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:**
+ * Called whenever we need to switch context from one task to another.
*
- * * save context of the preempted task to its stack;
- * * set `tn_curr_run_task` to `tn_next_task_to_run`;
- * * switch context to it.
+ * 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.
*
- * @see `tn_curr_run_task`
- * @see `tn_next_task_to_run`
- */
-void _tn_arch_context_switch(void);
-
-/**
- * Called when some task calls `tn_task_exit()`.
+ * 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 disabled;
- * * `tn_next_task_to_run` is already set to other task.
+ * - 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:**
*
- * * 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`
*/
-void _tn_arch_context_switch_exit(void);
+void _tn_arch_context_switch_pend(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()`
+ *
+ * This function doesn't pend context switch, it switches context immediately.
*
* **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_now_nosave(void);
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/core/tn_dqueue.c b/src/core/tn_dqueue.c
index ea3d08f..15465fb 100644
--- a/src/core/tn_dqueue.c
+++ b/src/core/tn_dqueue.c
@@ -122,46 +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);
}
-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;
- }
+ } 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;
+ }
- //-- 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, FALSE);
+ }
}
-out:
return rc;
}
// }}}
@@ -212,6 +217,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
@@ -284,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_switch_context_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;
}
@@ -374,34 +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_INT_IRESTORE();
+ _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED();
+ }
-out:
return rc;
}
@@ -426,25 +433,27 @@ enum TN_RCode tn_queue_create(
rc = _check_param_create(dque, data_fifo, items_cnt);
if (rc != TN_RC_OK){
- goto out;
- }
+ //-- just return rc as it is
+ } else {
+ tn_list_reset(&(dque->wait_send_list));
+ tn_list_reset(&(dque->wait_receive_list));
- 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;
- if (dque->data_fifo == NULL){
- dque->items_cnt = 0;
- }
+ _tn_eventgrp_link_reset(&dque->eventgrp_link);
+
+ 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;
}
@@ -454,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_switch_context_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;
}
@@ -549,5 +556,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();
+ rc = _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();
+ rc = _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 28a9820..d0a845a 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
@@ -53,6 +55,21 @@
* 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.
+ *
+ * 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()`
+ *
+ * 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
@@ -64,8 +81,13 @@
#include "tn_list.h"
#include "tn_common.h"
+#include "tn_eventgrp.h"
+
+/*******************************************************************************
+ * EXTERN TYPES
+ ******************************************************************************/
@@ -106,6 +128,9 @@ struct TN_DQueue {
///
/// id for object validity verification
enum TN_ObjId id_dque;
+ ///
+ /// connected event group
+ struct TN_EGrpLink eventgrp_link;
};
/**
@@ -304,6 +329,47 @@ enum TN_RCode tn_queue_ireceive_polling(
);
+/**
+ * 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
+ * @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.
+ * Refer to the section \ref eventgrp_connect for details.
+ *
+ * If there is no event group connected, nothing is changed.
+ *
+ * @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..deaaccb 100644
--- a/src/core/tn_eventgrp.c
+++ b/src/core/tn_eventgrp.c
@@ -77,10 +77,10 @@ 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);
+ enum TN_RCode rc = TN_RC_OK;
if (pattern == 0){
rc = TN_RC_WPARAM;
@@ -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,46 +188,44 @@ 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
)
{
- enum TN_RCode rc = TN_RC_OK;
+ //-- interrupts should be disabled here
+ _TN_BUG_ON( !TN_IS_INT_DISABLED() );
+
+ 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;
}
static enum TN_RCode _eventgrp_modify(
struct TN_EventGrp *eventgrp,
enum TN_EGrpOp operation,
- unsigned int pattern
+ TN_UWord pattern
)
{
- enum TN_RCode rc = TN_RC_OK;
-
- rc = _check_param_job_perform(eventgrp, pattern);
- if (rc != TN_RC_OK){
- goto out;
- }
+ //-- interrupts should be disabled here
+ _TN_BUG_ON( !TN_IS_INT_DISABLED() );
switch (operation){
case TN_EVENTGRP_OP_CLEAR:
@@ -229,8 +233,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:
@@ -239,8 +245,7 @@ static enum TN_RCode _eventgrp_modify(
break;
}
-out:
- return rc;
+ return TN_RC_OK;
}
@@ -256,23 +261,22 @@ 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;
+ 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;
}
@@ -282,32 +286,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_switch_context_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;
}
@@ -317,57 +317,58 @@ 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
)
{
- 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_switch_context_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;
}
@@ -377,26 +378,24 @@ 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;
- 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;
}
@@ -406,26 +405,28 @@ 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;
- 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_INT_IRESTORE();
+ _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED();
-out:
+ }
return rc;
}
@@ -436,25 +437,26 @@ 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;
- 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_switch_context_if_needed();
+ TN_INT_RESTORE();
+ _tn_context_switch_pend_if_needed();
-out:
+ }
return rc;
}
@@ -465,24 +467,99 @@ 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;
- 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();
+
+ rc = _eventgrp_modify(eventgrp, operation, pattern);
+
+ TN_INT_IRESTORE();
+ _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED();
}
+ return rc;
+}
+
+
+
+
+/*******************************************************************************
+ * PROTECTED FUNCTIONS
+ ******************************************************************************/
- TN_INT_IDIS_SAVE();
+/**
+ * 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() );
- rc = _eventgrp_modify(eventgrp, operation, pattern);
+ enum TN_RCode rc = _check_param_generic(eventgrp);
- TN_INT_IRESTORE();
+ if (rc != TN_RC_OK){
+ //-- just return rc as it is
+ } else if (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
+ );
+ }
-out:
return rc;
}
diff --git a/src/core/tn_eventgrp.h b/src/core/tn_eventgrp.h
index 5bee781..1e67004 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
@@ -49,6 +49,50 @@
* 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
+ * 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.
+ * 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.
+ *
+ * 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".
+ *
+ * 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
@@ -60,6 +104,7 @@
#include "tn_list.h"
#include "tn_common.h"
+#include "tn_sys.h"
@@ -112,7 +157,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 +168,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 +224,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 +267,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 +284,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 +299,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 +313,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 +343,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 +356,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_fmem.c b/src/core/tn_fmem.c
index 60618ba..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_switch_context_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_switch_context_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,26 +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();
+ rc = _fmem_get(fmem, p_data);
-out:
+ TN_INT_IRESTORE();
+ _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED();
+ }
return rc;
}
@@ -418,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_switch_context_if_needed();
+ rc = _fmem_release(fmem, p_data);
-out:
+ TN_INT_RESTORE();
+ _tn_context_switch_pend_if_needed();
+ }
return rc;
}
@@ -448,26 +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_INT_IRESTORE();
+ _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED();
+ }
-out:
return rc;
}
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/src/core/tn_internal.h b/src/core/tn_internal.h
index ce4d066..12754e8 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,
@@ -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
+
/*******************************************************************************
@@ -148,7 +162,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);
@@ -178,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();
}
}
@@ -487,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
diff --git a/src/core/tn_mutex.c b/src/core/tn_mutex.c
index 60d7054..4fe1836 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)
)
)
{
@@ -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;
}
@@ -596,53 +595,47 @@ enum TN_RCode tn_mutex_create(
*/
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_INTSAVE_DATA;
- 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_switch_context_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;
}
@@ -651,94 +644,77 @@ 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 = 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_INTSAVE_DATA;
- 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);
-
- waited_for_mutex = TRUE;
+ //-- mutex is already locked
- //-- 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_switch_context_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 +731,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;
+ enum TN_RCode rc = _check_param_generic(mutex);
- TN_INTSAVE_DATA;
-
- 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_INTSAVE_DATA;
- 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;
- goto out_ei;
- }
+ //-- unlocking is enabled only for the owner and already locked mutex
+ if (tn_curr_run_task != mutex->holder){
+ rc = TN_RC_ILLEGAL_USE;
+ } else {
- __mutex_lock_cnt_change(mutex, -1);
+ //-- 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);
+ }
- 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;
- }
+ }
-out_ei:
- TN_INT_RESTORE();
- _tn_switch_context_if_needed();
+ TN_INT_RESTORE();
+ _tn_context_switch_pend_if_needed();
+ }
-out:
return rc;
}
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_sem.c b/src/core/tn_sem.c
index 19ee77e..8f43d42 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,81 +107,86 @@ 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),
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;
- rc = _check_param_generic(sem);
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 = p_worker(sem);
+ TN_INT_DIS_SAVE(); //-- disable interrupts
+ rc = p_worker(sem); //-- call actual worker function
- 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 (!_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();
- _tn_switch_context_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;
}
+/**
+ * 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)
)
{
- TN_INTSAVE_DATA_INT;
- enum TN_RCode rc = TN_RC_OK;
+ enum TN_RCode rc = _check_param_generic(sem);
- rc = _check_param_generic(sem);
+ //-- perform additional params checking (if enabled by TN_CHECK_PARAM)
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();
-
- rc = p_worker(sem);
-
- TN_INT_IRESTORE();
+ } 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;
}
@@ -186,6 +194,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 +217,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 {
@@ -234,20 +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)
+ enum TN_RCode rc = _check_param_create(sem, start_count, max_count);
- 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;
}
@@ -256,32 +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)
+ enum TN_RCode rc = _check_param_generic(sem);
- rc = _check_param_generic(sem);
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();
-
- _tn_wait_queue_notify_deleted(&(sem->wait_queue));
+ } else {
+ TN_INTSAVE_DATA;
- sem->id_sem = 0; //-- Semaphore does not exist now
+ TN_INT_DIS_SAVE();
- 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_switch_context_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;
}
diff --git a/src/core/tn_sys.c b/src/core/tn_sys.c
index 2cf67ae..cec854a 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
@@ -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_now_nosave();
}
@@ -290,28 +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_INT_IRESTORE();
+ _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED();
-out:
+ }
return rc;
}
@@ -322,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)
- || ticks < 0 || ticks > TN_MAX_TIME_SLICE)
+ } 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_sys.h b/src/core/tn_sys.h
index eea4a15..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
@@ -306,7 +322,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/src/core/tn_tasks.c b/src/core/tn_tasks.c
index b177730..714cccd 100644
--- a/src/core/tn_tasks.c
+++ b/src/core/tn_tasks.c
@@ -204,27 +204,23 @@ static inline enum TN_RCode _task_job_perform(
int (p_worker)(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;
- }
-
- TN_INT_DIS_SAVE();
+ } else {
+ //-- proceed to real job
+ TN_INTSAVE_DATA;
- rc = p_worker(task);
+ TN_INT_DIS_SAVE();
- TN_INT_RESTORE();
- _tn_switch_context_if_needed();
+ rc = p_worker(task);
-out:
+ TN_INT_RESTORE();
+ _tn_context_switch_pend_if_needed();
+ }
return rc;
}
@@ -233,26 +229,23 @@ static inline enum TN_RCode _task_job_iperform(
int (p_worker)(struct TN_Task *task)
)
{
- 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_INTSAVE_DATA_INT;
- TN_INT_IDIS_SAVE();
+ TN_INT_IDIS_SAVE();
- rc = p_worker(task);
+ rc = p_worker(task);
- TN_INT_IRESTORE();
+ TN_INT_IRESTORE();
+ _TN_CONTEXT_SWITCH_IPEND_IF_NEEDED();
-out:
+ }
return rc;
}
@@ -383,9 +376,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;
@@ -482,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;
+ } else {
- if (_tn_task_is_suspended(task) || _tn_task_is_dormant(task)){
- rc = TN_RC_WSTATE;
- goto out_ei;
- }
+ 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_switch_context_if_needed();
+ TN_INT_RESTORE();
+ _tn_context_switch_pend_if_needed();
-out:
+ }
return rc;
}
@@ -521,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_switch_context_if_needed();
+ TN_INT_RESTORE();
+ _tn_context_switch_pend_if_needed();
-out:
+ }
return rc;
}
@@ -563,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_switch_context_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;
}
/*
@@ -643,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_exit()`
- // 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_exit()
- _tn_arch_context_switch_exit();
+ 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;
}
@@ -678,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_switch_context_if_needed();
+ TN_INT_RESTORE();
+ _tn_context_switch_pend_if_needed();
-out:
+ }
return rc;
}
@@ -733,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;
}
@@ -766,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;
}
@@ -791,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_switch_context_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;
}
@@ -1071,7 +1021,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..cd372dd 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;
@@ -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/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;
}
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
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 7846f84..2448be7 100644
--- a/stuff/doc_pages/changelog.dox
+++ b/stuff/doc_pages/changelog.dox
@@ -5,6 +5,34 @@
TNeoKernel changelog
+\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
+ way to wait for messages from multiple queues with just a single system
+ 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
+ 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")
+ - 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
Release date: 2014-10-14.
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/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 44b13fc..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
@@ -29,6 +30,7 @@ Related pages:
- \ref unit_tests
- \ref plans
- \ref changelog
+ - \ref thanks
- \ref legend
API reference:
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/doc_pages/quick_guide.dox b/stuff/doc_pages/quick_guide.dox
index a8dcd3b..2e5a804 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) { ... }`,
@@ -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/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 pic32/basic/src/tn_pic32_example_basic.c
+\include basic/arch/pic32/tn_pic32_example_basic.c
\section round_robin Round-robin scheduling
diff --git a/stuff/doc_pages/thanks.dox b/stuff/doc_pages/thanks.dox
new file mode 100644
index 0000000..f43b615
--- /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 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 nice ideas.
+
+Thank you guys. TNeoKernel would never be what it is without you.
+
+*/
+
diff --git a/stuff/doc_pages/tnkernel_diff.dox b/stuff/doc_pages/tnkernel_diff.dox
index 90dce55..81bb1c6 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:
@@ -194,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
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:
diff --git a/stuff/doxygen/tn_doxyfile b/stuff/doxygen/tn_doxyfile
index 183090b..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 \
@@ -770,6 +771,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
diff --git a/stuff/vimwiki/index.wiki b/stuff/vimwiki/index.wiki
index aabffa2..a0163c7 100644
--- a/stuff/vimwiki/index.wiki
+++ b/stuff/vimwiki/index.wiki
@@ -11,8 +11,12 @@
== 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)
+ * [.] 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()`.
* [ ] Examples
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.
+