TNeoKernel  v1.03
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Quick guide

Table of Contents

This page contains quick guide on system startup and important implementation details.

Time ticks

For the purpose of calculating timeouts, the kernel uses a time tick timer. In TNeoKernel, this time tick timer must to be a some kind of hardware timer that produces interrupt for time ticks processing. Throughout this text, this timer is referred to as system timer. The period of this timer is determined by user (typically 1 ms, but user is free to set different value). In the ISR for this timer, it is only necessary to call the tn_tick_int_processing() function:

//-- example for PIC32, hardware timer 5 interrupt:
tn_soft_isr(_TIMER_5_VECTOR)
{
INTClearFlag(INT_T5);
}

Starting the kernel

Quick guide on startup process

Basic example for PIC32

This example project can be found in the TNeoKernel repository, in the examples/basic/arch/pic32 directory.

Attention
Before trying to build examples, please read Building the project page carefully: you need to copy configuration file in the tneokernel directory to 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.
/**
* TNeoKernel PIC32 basic example
*/
/*******************************************************************************
* INCLUDED FILES
******************************************************************************/
#include <xc.h>
#include <plib.h>
#include <stdint.h>
#include "tn.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
******************************************************************************/
//-- instruction that causes debugger to halt
#define PIC32_SOFTWARE_BREAK() __asm__ volatile ("sdbbp 0")
//-- 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)
//-- stack sizes of user tasks
#define TASK_A_STK_SIZE (TN_MIN_STACK_SIZE + 96)
#define TASK_B_STK_SIZE (TN_MIN_STACK_SIZE + 96)
#define TASK_C_STK_SIZE (TN_MIN_STACK_SIZE + 96)
//-- user task priorities
#define TASK_A_PRIORITY 7
#define TASK_B_PRIORITY 6
#define TASK_C_PRIORITY 5
/*******************************************************************************
* 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);
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);
//-- task structures
struct TN_Task task_a = {};
struct TN_Task task_b = {};
struct TN_Task task_c = {};
/*******************************************************************************
* ISRs
******************************************************************************/
/**
* system timer ISR
*/
tn_soft_isr(_TIMER_5_VECTOR)
{
INTClearFlag(INT_T5);
}
/*******************************************************************************
* FUNCTIONS
******************************************************************************/
void appl_init(void);
void task_a_body(void *par)
{
//-- this is a first created application task, so it needs to perform
// all the application initialization.
appl_init();
//-- and then, let's get to the primary job of the task
// (job for which task was created at all)
for(;;)
{
mPORTEToggleBits(BIT_0);
}
}
void task_b_body(void *par)
{
for(;;)
{
mPORTEToggleBits(BIT_1);
}
}
void task_c_body(void *par)
{
for(;;)
{
mPORTEToggleBits(BIT_2);
}
}
/**
* 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);
}
/**
* Application init: called from the first created application task
*/
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)
//-- initialize various program modules
// (in this sample project there's nothing to init)
//-- create all the rest application tasks
&task_b,
task_b_body,
TASK_B_PRIORITY,
task_b_stack,
TASK_B_STK_SIZE,
);
&task_c,
task_c_body,
TASK_C_PRIORITY,
task_c_stack,
TASK_C_STK_SIZE,
);
}
//-- idle callback that is called periodically from idle task
void idle_task_callback (void)
{
}
//-- create first application task(s)
void init_task_create(void)
{
//-- task A performs complete application initialization,
// it's the first created application task
&task_a, //-- task structure
task_a_body, //-- task body function
TASK_A_PRIORITY, //-- task priority
task_a_stack, //-- task stack
TASK_A_STK_SIZE, //-- task stack size (in words)
NULL, //-- task function parameter
TN_TASK_CREATE_OPT_START //-- creation option
);
}
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
//-- init hardware
hw_init();
//-- call to tn_sys_start() never returns
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)
{
PIC32_SOFTWARE_BREAK();
for (;;) ;
}

Round-robin scheduling

TNKernel has the ability to make round robin scheduling for tasks with identical priority. By default, round robin scheduling is turned off for all priorities. To enable round robin scheduling for tasks on certain priority level and to set time slices for these priority, user must call the tn_sys_tslice_set() function. The time slice value is the same for all tasks with identical priority but may be different for each priority level. If the round robin scheduling is enabled, every system time tick interrupt increments the currently running task time slice counter. When the time slice interval is completed, the task is placed at the tail of the ready to run queue of its priority level (this queue contains tasks in the RUNNABLE state) and the time slice counter is cleared. Then the task may be preempted by tasks of higher or equal priority.

In most cases, there is no reason to enable round robin scheduling. For applications running multiple copies of the same code, however, (GUI windows, etc), round robin scheduling is an acceptable solution.