|
IntrOS
|
The kernel does not depend on the hardware: everything that depends on the processor, the compiler or the device is in the port layer (port directory). This page describes what a port must provide; the existing ports are the best reference:
| port | description |
|---|---|
| port/cortexm | ARM Cortex-M (CMSIS), compilers armcc, clang, csmcc, gnucc, iarcc, devices STM32, LM4F |
| port/stm8 | STM8 (8-bit), compilers csmc, iarc, sdcc |
| port/avr | AVR (8-bit), compiler gcc |
| port/x86 | PC (Linux, Windows): the system runs in a process of the host, for testing purposes |
IntrOS is cooperative: a task gives up the processor only in the functions of the kernel. The context switch is a function call: the kernel saves the context of the current task with setjmp and loads the context of the next task with longjmp. So the port does not need an interrupt of the context switch, only the critical section, the initial context of a task and the system timer.
A port consists of the files common to the processor and compiler (compiler) and the files specific to the device (device):
| file | contents |
|---|---|
| port/<arch>/compiler/<cc>/osdefs.h | definitions of the compiler (see Compiler definitions) |
| port/<arch>/compiler/<cc>/oscore.h | types, context of the task, critical section, inline functions of the core |
| port/<arch>/compiler/<cc>/oscore.c | optional: own setjmp / longjmp (or oscore.s in assembler) |
| port/<arch>/device/<dev>/osport.h | frequencies and sizes of the timers, inline functions of the system timer |
| port/<arch>/device/<dev>/osport.c | port_sys_init, interrupt handler of the system timer |
| port/<arch>/common/*.h | optional: headers common to all devices (e.g. osmpu.h) |
The build scripts select the port with the variables INTROS_ARCH, INTROS_COMPILER and INTROS_DEVICE (make/<board>/makefile.<cc>, cmake/config-<board>-<cc>.cmake), add the directories to the include path and compile oscore.c, oscore.s, oslibc.c and osport.c if they exist.
The headers are included in this order: oskernel.h → oscore.h → osbase.h → osport.h → osconfig.h (configuration of the application, skipped with NOCONFIG) and osdefs.h. So osport.h can not use the types of the kernel (e.g. cnt_t), oscore.h can.
osdefs.h provides the definitions of the compiler used by the kernel. On Cortex-M most of them come from CMSIS (cmsis_compiler.h), so osdefs.h defines only __CONSTRUCTOR (if the compiler supports it); the other ports define all of them (see port/x86/compiler/gcc/osdefs.h):
| definition | required | description |
|---|---|---|
| __STATIC_INLINE | yes | static inline function |
| __NO_RETURN | yes | function that never returns |
| __COMPILER_BARRIER() | yes | compiler memory barrier (critical section) |
| __PACKED_STRUCT | yes | structure without padding (header of a message in the message queue) |
| __ALIGNED(x) | no | alignment of a variable to x bytes (task stacks with MPU, system heap); default: no alignment |
| __CONSTRUCTOR | no | function called before main: sys_init is called automatically and the macros that start an object at its definition are available (OS_TSK_START, OS_WRK_START, OS_TMR_START, OS_TMR_UNTIL, ...); without it the application calls sys_init at the beginning of main |
__MPU_USED and __FPU_USED come from the header of the device (CMSIS), the default value is 0.
The port sets the default values of the configuration; the application can change them in osconfig.h:
| definition | file | description |
|---|---|---|
| CPU_FREQUENCY | osport.h | frequency of the processor in Hz |
| OS_FREQUENCY | osport.h | frequency of the system timer in Hz (ticks per second) |
| OS_TIMER_SIZE | osport.h | bit size of the system time cnt_t: 16, 32 or 64 |
| OS_STACK_SIZE | oscore.h | default size of the task stack in bytes |
| OS_LOCK_LEVEL | oscore.h | optional, specific to the port: interrupts masked by the critical section |
The port also defines HW_TIMER_SIZE (internal, it must not be set in osconfig.h): the bit size of the hardware timer in tick-less mode, or 0 in tick mode. The ports choose the tick-less mode for OS_FREQUENCY above 1000 Hz.
A port should reject the configuration that it does not support with #error, e.g. OS_ATOMICS on a core without atomic operations.
They are defined in oscore.h:
| definition | description |
|---|---|
| lck_t | state of the interrupts saved by port_get_lock |
| stk_t | unit of the task stack; its size is the alignment of the stack (e.g. uint64_t on Cortex-M) |
| ctx_t | context of the task: the layout of the jmp_buf of the C library (ctx_t and jmp_buf share the memory) |
| _CTX_INIT() | static initializer of ctx_t |
| MAIN_TOP | optional: top of the stack of the main task (e.g. the symbol of the linker script) |
ctx_t must name the fields of jmp_buf that hold the stack pointer and the return address: port_ctx_init sets them, so the first longjmp to a new task starts core_tsk_loop (or core_tsk_exec with OS_TASK_EXIT) on the stack of the task.
The functions are usually __STATIC_INLINE in oscore.h or osport.h, port_sys_init and port_sys_time (when it is not inline) are in osport.c.
| function | description |
|---|---|
| port_get_lock() | return the current state of the interrupts |
| port_put_lock(lck) | restore the state of the interrupts |
| port_set_lock() | mask the interrupts that use the kernel (begin of the critical section) |
| port_clr_lock() | unmask the interrupts (end of the critical section) |
| port_get_sp() | current stack pointer (stack checks in DEBUG mode) |
| port_ctx_init(ctx, sp, pc) | initialize the context ctx of a new task: the stack pointer sp (top of the stack) and the function pc |
| port_sys_init() | configure the system timer and the MPU; called once by sys_init |
| port_sys_time() | tick-less mode: current system time read from the hardware timer |
| port_fpu_save(ctx) | with FPU (__FPU_USED): save the state of the FPU not kept by setjmp (e.g. FPSCR) in ctx |
| port_fpu_load(ctx) | with FPU (__FPU_USED): load the state saved by port_fpu_save |
| port_mpu_* | optional: memory protection (__MPU_USED), see port/cortexm/common/osmpu.h |
If setjmp / longjmp of the C library do not save the registers of the FPU that a function must preserve, the port provides its own versions (see port/cortexm/compiler/gnucc/oscore.c).
| function | caller |
|---|---|
| core_sys_tick() | tick mode: interrupt of the system timer with frequency OS_FREQUENCY; tick-less mode with HW_TIMER_SIZE < OS_TIMER_SIZE: overflow of the hardware timer |
In tick-less mode the hardware timer counts with frequency OS_FREQUENCY. The kernel checks the delays and the timers when a task gives up the processor, so the port does not generate the timer interrupts for them.